1use alloc::{boxed::Box, fmt, format, string::ToString, sync::Arc, vec};
2use std::{fs::File, io::Write, path::Path};
3
4use miden_core::{prettier::PrettyPrint, utils::Serializable};
5use midenc_hir_symbol::Symbol;
6
7use crate::{OutputMode, OutputType, Session};
8
9pub trait Emit {
10 fn name(&self) -> Option<Symbol>;
12 fn output_type(&self, mode: OutputMode) -> OutputType;
14 fn write_to_stdout(&self, session: &Session) -> std::io::Result<()> {
17 use std::io::IsTerminal;
18 let stdout = std::io::stdout().lock();
19 let mode = if stdout.is_terminal() {
20 OutputMode::Text
21 } else {
22 OutputMode::Binary
23 };
24 self.write_to(stdout, mode, session)
25 }
26 fn write_to_file(
28 &self,
29 path: &Path,
30 mode: OutputMode,
31 session: &Session,
32 ) -> std::io::Result<()> {
33 if let Some(dir) = path.parent() {
34 std::fs::create_dir_all(dir)?;
35 }
36 let file = File::create(path)?;
37 self.write_to(file, mode, session)
38 }
39 fn write_to<W: Write>(
42 &self,
43 writer: W,
44 mode: OutputMode,
45 session: &Session,
46 ) -> std::io::Result<()>;
47}
48
49impl<'a, T: Emit> Emit for &'a T {
50 #[inline]
51 fn name(&self) -> Option<Symbol> {
52 (**self).name()
53 }
54
55 #[inline]
56 fn output_type(&self, mode: OutputMode) -> OutputType {
57 (**self).output_type(mode)
58 }
59
60 #[inline]
61 fn write_to_stdout(&self, session: &Session) -> std::io::Result<()> {
62 (**self).write_to_stdout(session)
63 }
64
65 #[inline]
66 fn write_to_file(
67 &self,
68 path: &Path,
69 mode: OutputMode,
70 session: &Session,
71 ) -> std::io::Result<()> {
72 (**self).write_to_file(path, mode, session)
73 }
74
75 #[inline]
76 fn write_to<W: Write>(
77 &self,
78 writer: W,
79 mode: OutputMode,
80 session: &Session,
81 ) -> std::io::Result<()> {
82 (**self).write_to(writer, mode, session)
83 }
84}
85
86impl<'a, T: Emit> Emit for &'a mut T {
87 #[inline]
88 fn name(&self) -> Option<Symbol> {
89 (**self).name()
90 }
91
92 #[inline]
93 fn output_type(&self, mode: OutputMode) -> OutputType {
94 (**self).output_type(mode)
95 }
96
97 #[inline]
98 fn write_to_stdout(&self, session: &Session) -> std::io::Result<()> {
99 (**self).write_to_stdout(session)
100 }
101
102 #[inline]
103 fn write_to_file(
104 &self,
105 path: &Path,
106 mode: OutputMode,
107 session: &Session,
108 ) -> std::io::Result<()> {
109 (**self).write_to_file(path, mode, session)
110 }
111
112 #[inline]
113 fn write_to<W: Write>(
114 &self,
115 writer: W,
116 mode: OutputMode,
117 session: &Session,
118 ) -> std::io::Result<()> {
119 (**self).write_to(writer, mode, session)
120 }
121}
122
123impl<T: Emit> Emit for Box<T> {
124 #[inline]
125 fn name(&self) -> Option<Symbol> {
126 (**self).name()
127 }
128
129 #[inline]
130 fn output_type(&self, mode: OutputMode) -> OutputType {
131 (**self).output_type(mode)
132 }
133
134 #[inline]
135 fn write_to_stdout(&self, session: &Session) -> std::io::Result<()> {
136 (**self).write_to_stdout(session)
137 }
138
139 #[inline]
140 fn write_to_file(
141 &self,
142 path: &Path,
143 mode: OutputMode,
144 session: &Session,
145 ) -> std::io::Result<()> {
146 (**self).write_to_file(path, mode, session)
147 }
148
149 #[inline]
150 fn write_to<W: Write>(
151 &self,
152 writer: W,
153 mode: OutputMode,
154 session: &Session,
155 ) -> std::io::Result<()> {
156 (**self).write_to(writer, mode, session)
157 }
158}
159
160impl<T: Emit> Emit for Arc<T> {
161 #[inline]
162 fn name(&self) -> Option<Symbol> {
163 (**self).name()
164 }
165
166 #[inline]
167 fn output_type(&self, mode: OutputMode) -> OutputType {
168 (**self).output_type(mode)
169 }
170
171 #[inline]
172 fn write_to_stdout(&self, session: &Session) -> std::io::Result<()> {
173 (**self).write_to_stdout(session)
174 }
175
176 #[inline]
177 fn write_to_file(
178 &self,
179 path: &Path,
180 mode: OutputMode,
181 session: &Session,
182 ) -> std::io::Result<()> {
183 (**self).write_to_file(path, mode, session)
184 }
185
186 #[inline]
187 fn write_to<W: Write>(
188 &self,
189 writer: W,
190 mode: OutputMode,
191 session: &Session,
192 ) -> std::io::Result<()> {
193 (**self).write_to(writer, mode, session)
194 }
195}
196
197impl Emit for miden_assembly::ast::Module {
198 fn name(&self) -> Option<Symbol> {
199 Some(Symbol::intern(self.path().to_string()))
200 }
201
202 fn output_type(&self, _mode: OutputMode) -> OutputType {
203 OutputType::Masm
204 }
205
206 fn write_to<W: Write>(
207 &self,
208 mut writer: W,
209 mode: OutputMode,
210 _session: &Session,
211 ) -> std::io::Result<()> {
212 assert_eq!(mode, OutputMode::Text, "masm syntax trees do not support binary mode");
213 writer.write_fmt(format_args!("{}\n", self))
214 }
215}
216
217macro_rules! serialize_into {
218 ($serializable:ident, $writer:ident) => {
219 std::panic::catch_unwind(move || {
224 let mut writer = $writer;
225 $serializable.write_into(&mut writer)
226 })
227 .map_err(|p| {
228 match p.downcast::<std::io::Error>() {
229 Ok(err) => unsafe { core::ptr::read(&*err) },
231 Err(err) => std::panic::resume_unwind(err),
233 }
234 })
235 };
236}
237
238impl Emit for miden_assembly::Library {
239 fn name(&self) -> Option<Symbol> {
240 None
241 }
242
243 fn output_type(&self, mode: OutputMode) -> OutputType {
244 match mode {
245 OutputMode::Text => OutputType::Mast,
246 OutputMode::Binary => OutputType::Masl,
247 }
248 }
249
250 fn write_to<W: Write>(
251 &self,
252 mut writer: W,
253 mode: OutputMode,
254 _session: &Session,
255 ) -> std::io::Result<()> {
256 struct LibraryTextFormatter<'a>(&'a miden_assembly::Library);
257 impl<'a> miden_core::prettier::PrettyPrint for LibraryTextFormatter<'a> {
258 fn render(&self) -> miden_core::prettier::Document {
259 use miden_core::prettier::*;
260
261 let mast_forest = self.0.mast_forest();
262 let mut library_doc = Document::Empty;
263 for module_info in self.0.module_infos() {
264 let mut fragments = vec![];
265 for (_, info) in module_info.procedures() {
266 if let Some(proc_node_id) = mast_forest.find_procedure_root(info.digest) {
267 let proc = mast_forest
268 .get_node_by_id(proc_node_id)
269 .expect("malformed mast forest")
270 .to_pretty_print(mast_forest)
271 .render();
272 fragments.push(indent(
273 4,
274 display(format!("procedure {} ({})", &info.name, &info.digest))
275 + nl()
276 + proc
277 + nl()
278 + const_text("end"),
279 ));
280 }
281 }
282 let module_doc = indent(
283 4,
284 display(format!("module {}", module_info.path()))
285 + nl()
286 + fragments
287 .into_iter()
288 .reduce(|l, r| l + nl() + nl() + r)
289 .unwrap_or_default()
290 + const_text("end"),
291 );
292 if matches!(library_doc, Document::Empty) {
293 library_doc = module_doc;
294 } else {
295 library_doc += nl() + nl() + module_doc;
296 }
297 }
298 library_doc
299 }
300 }
301 impl<'a> fmt::Display for LibraryTextFormatter<'a> {
302 #[inline]
303 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
304 self.pretty_print(f)
305 }
306 }
307
308 match mode {
309 OutputMode::Text => writer.write_fmt(format_args!("{}", LibraryTextFormatter(self))),
310 OutputMode::Binary => {
311 self.write_into(&mut writer);
312 Ok(())
313 }
314 }
315 }
316}
317
318impl Emit for miden_core::Program {
319 fn name(&self) -> Option<Symbol> {
320 None
321 }
322
323 fn output_type(&self, mode: OutputMode) -> OutputType {
324 match mode {
325 OutputMode::Text => OutputType::Mast,
326 OutputMode::Binary => OutputType::Masl,
327 }
328 }
329
330 fn write_to_file(
331 &self,
332 path: &Path,
333 mode: OutputMode,
334 session: &Session,
335 ) -> std::io::Result<()> {
336 if let Some(dir) = path.parent() {
337 std::fs::create_dir_all(dir)?;
338 }
339 let mut file = std::fs::File::create(path)?;
340 match mode {
341 OutputMode::Text => self.write_to(&mut file, mode, session),
342 OutputMode::Binary => serialize_into!(self, file),
343 }
344 }
345
346 fn write_to_stdout(&self, session: &Session) -> std::io::Result<()> {
347 use std::io::IsTerminal;
348 let mut stdout = std::io::stdout().lock();
349 let mode = if stdout.is_terminal() {
350 OutputMode::Text
351 } else {
352 OutputMode::Binary
353 };
354 match mode {
355 OutputMode::Text => self.write_to(&mut stdout, mode, session),
356 OutputMode::Binary => serialize_into!(self, stdout),
357 }
358 }
359
360 fn write_to<W: Write>(
361 &self,
362 mut writer: W,
363 mode: OutputMode,
364 _session: &Session,
365 ) -> std::io::Result<()> {
366 match mode {
367 OutputMode::Text => unimplemented!("emitting mast in text form is currently broken"),
369 OutputMode::Binary => {
370 self.write_into(&mut writer);
371 Ok(())
372 }
373 }
374 }
375}