1use crate::{self as ir, RRC};
5use calyx_frontend::PrimitiveInfo;
6use itertools::Itertools;
7use std::io;
8use std::path::Path;
9use std::rc::Rc;
10
11pub struct Printer;
13
14impl Printer {
15 pub fn format_at_attributes(attrs: &ir::Attributes) -> String {
18 let mut buf = attrs.to_string_with(" ", |name, val| {
19 if val == 1 {
20 format!("@{}", name)
21 } else {
22 format!("@{}({val})", name)
23 }
24 });
25 if !attrs.is_empty() {
26 buf.push(' ');
27 }
28 buf
29 }
30
31 pub fn format_attributes(attrs: &ir::Attributes) -> String {
34 if attrs.is_empty() {
35 "".to_string()
36 } else {
37 format!(
38 "<{}>",
39 attrs.to_string_with(", ", |name, val| {
40 format!("\"{}\"={}", name, val)
41 })
42 )
43 }
44 }
45
46 pub fn format_ports(ports: &[RRC<ir::Port>]) -> String {
48 ports
49 .iter()
50 .map(|p| {
51 format!(
52 "{}{}: {}",
53 Self::format_at_attributes(&p.borrow().attributes),
54 p.borrow().name.id,
55 p.borrow().width
56 )
57 })
58 .collect::<Vec<_>>()
59 .join(", ")
60 }
61
62 pub fn format_port_def<W: std::fmt::Display>(
63 port_defs: &[&ir::PortDef<W>],
64 ) -> String {
65 port_defs
66 .iter()
67 .map(|pd| {
68 format!(
69 "{}{}: {}",
70 Self::format_at_attributes(&pd.attributes),
71 pd.name(),
72 pd.width
73 )
74 })
75 .collect_vec()
76 .join(", ")
77 }
78
79 pub fn write_context<F: io::Write>(
82 ctx: &ir::Context,
83 skip_primitives: bool,
84 f: &mut F,
85 ) -> io::Result<()> {
86 for prim_info in ctx.lib.prim_infos() {
87 if skip_primitives && !prim_info.is_source() {
88 continue;
89 }
90 match prim_info {
91 PrimitiveInfo::Extern {
92 path, primitives, ..
93 } => {
94 ir::Printer::write_externs(
95 (path, primitives.into_iter().map(|(_, v)| v)),
96 f,
97 )?;
98 }
99 PrimitiveInfo::Inline { primitive, .. } => {
100 ir::Printer::write_primitive(primitive, 0, f)?;
101 }
102 }
103 }
104
105 for comp in &ctx.components {
106 ir::Printer::write_component(comp, f)?;
107 writeln!(f)?
108 }
109 write!(f, "{}", ir::Printer::format_metadata(&ctx.metadata))
110 }
111
112 pub fn write_externs<'a, F, I>(
114 (path, prims): (&Path, I),
115 f: &mut F,
116 ) -> io::Result<()>
117 where
118 F: io::Write,
119 I: Iterator<Item = &'a ir::Primitive>,
120 {
121 writeln!(f, "extern \"{}\" {{", path.to_string_lossy())?;
122 for prim in prims {
123 Self::write_primitive(prim, 2, f)?;
124 }
125 writeln!(f, "}}")
126 }
127
128 pub fn write_primitive<F: io::Write>(
129 prim: &ir::Primitive,
130 indent: usize,
131 f: &mut F,
132 ) -> io::Result<()> {
133 write!(f, "{}", " ".repeat(indent))?;
134 if prim.is_comb {
135 write!(f, "comb ")?;
136 }
137 if let Some(latency_val) = prim.latency {
138 write!(f, "static<{}> ", latency_val)?;
139 }
140 write!(
141 f,
142 "primitive {}{}",
143 prim.name,
144 Self::format_attributes(&prim.attributes)
145 )?;
146 if !prim.params.is_empty() {
147 write!(
148 f,
149 "[{}]",
150 prim.params
151 .iter()
152 .map(|p| p.to_string())
153 .collect_vec()
154 .join(", ")
155 )?
156 }
157 let (mut inputs, mut outputs) = (vec![], vec![]);
158 for pd in &prim.signature {
159 if pd.direction == ir::Direction::Input {
160 inputs.push(pd)
161 } else {
162 outputs.push(pd)
163 }
164 }
165 write!(
166 f,
167 "({}) -> ({})",
168 Self::format_port_def(&inputs),
169 Self::format_port_def(&outputs)
170 )?;
171 if let Some(b) = prim.body.as_ref() {
172 writeln!(f, " {{")?;
173 writeln!(f, "{:indent$}{b}", "", indent = indent + 2)?;
174 writeln!(f, "}}")
175 } else {
176 writeln!(f, ";")
177 }
178 }
179
180 pub fn write_component<F: io::Write>(
182 comp: &ir::Component,
183 f: &mut F,
184 ) -> io::Result<()> {
185 let sig = comp.signature.borrow();
186 let (inputs, outputs): (Vec<_>, Vec<_>) =
187 sig.ports.iter().map(Rc::clone).partition(|p| {
188 matches!(p.borrow().direction, ir::Direction::Output)
190 });
191
192 let pre = if comp.is_comb {
193 "comb ".to_string()
194 } else if comp.latency.is_some() {
195 format!("static<{}> ", comp.latency.unwrap())
196 } else {
197 "".to_string()
198 };
199
200 writeln!(
201 f,
202 "{}component {}{}({}) -> ({}) {{",
203 pre,
204 comp.name.id,
205 Self::format_attributes(&comp.attributes),
206 Self::format_ports(&inputs),
207 Self::format_ports(&outputs),
208 )?;
209
210 write!(f, " cells {{")?;
212 if !comp.cells.is_empty() {
213 writeln!(f)?;
214 }
215 for cell in comp.cells.iter() {
216 Self::write_cell(&cell.borrow(), 4, f)?;
217 }
218 if !comp.cells.is_empty() {
219 writeln!(f, " }}")?;
220 } else {
221 writeln!(f, "}}")?;
222 }
223
224 let empty_wires = comp.groups.is_empty()
226 && comp.static_groups.is_empty()
227 && comp.comb_groups.is_empty()
228 && comp.continuous_assignments.is_empty();
229 write!(f, " wires {{")?;
230 if !empty_wires {
231 writeln!(f)?;
232 }
233 for group in comp.get_groups().iter() {
234 Self::write_group(&group.borrow(), 4, f)?;
235 writeln!(f)?;
236 }
237 for group in comp.get_static_groups().iter() {
238 Self::write_static_group(&group.borrow(), 4, f)?;
239 writeln!(f)?;
240 }
241 for comb_group in comp.comb_groups.iter() {
242 Self::write_comb_group(&comb_group.borrow(), 4, f)?;
243 writeln!(f)?;
244 }
245 for assign in &comp.continuous_assignments {
247 Self::write_assignment(assign, 4, f)?;
248 writeln!(f)?;
249 }
250 if !empty_wires {
251 writeln!(f, " }}")?;
252 } else {
253 writeln!(f, "}}")?;
254 }
255
256 if !comp.is_comb {
259 let con = &*comp.control.borrow();
260 match con {
261 ir::Control::Empty(ir::Empty { attributes })
262 if attributes.is_empty() =>
263 {
264 writeln!(f, " control {{}}")?;
265 }
266 _ => {
267 writeln!(f, " control {{")?;
268 Self::write_control(&comp.control.borrow(), 4, f)?;
269 writeln!(f, " }}")?;
270 }
271 }
272 }
273
274 write!(f, "}}")
275 }
276
277 pub fn write_cell<F: io::Write>(
279 cell: &ir::Cell,
280 indent_level: usize,
281 f: &mut F,
282 ) -> io::Result<()> {
283 match &cell.prototype {
284 ir::CellType::Primitive {
285 name,
286 param_binding,
287 ..
288 } => {
289 write!(f, "{}", " ".repeat(indent_level))?;
290 write!(f, "{}", Self::format_at_attributes(&cell.attributes))?;
291 if cell.is_reference() {
292 write!(f, "ref ")?
293 }
294 write!(f, "{} = ", cell.name().id)?;
295 writeln!(
296 f,
297 "{}({});",
298 name.id,
299 param_binding
300 .iter()
301 .map(|(_, v)| v.to_string())
302 .collect::<Vec<_>>()
303 .join(", ")
304 )
305 }
306 ir::CellType::Component { name, .. } => {
307 write!(f, "{}", " ".repeat(indent_level))?;
308 write!(f, "{}", Self::format_at_attributes(&cell.attributes))?;
309 if cell.is_reference() {
310 write!(f, "ref ")?
311 }
312 writeln!(f, "{} = {}();", cell.name().id, name)
313 }
314 ir::CellType::Constant { .. } => Ok(()),
315 _ => unimplemented!(),
316 }
317 }
318
319 pub fn write_assignment<F: io::Write, T: Clone + ToString + Eq>(
321 assign: &ir::Assignment<T>,
322 indent_level: usize,
323 f: &mut F,
324 ) -> io::Result<()> {
325 write!(f, "{}", " ".repeat(indent_level))?;
326 write!(f, "{}", Self::format_at_attributes(&assign.attributes))?;
327 write!(f, "{} = ", Self::port_to_str(&assign.dst.borrow()))?;
328 if !matches!(&*assign.guard, ir::Guard::True) {
329 write!(f, "{} ? ", Self::guard_str(&assign.guard.clone()))?;
330 }
331 write!(f, "{};", Self::port_to_str(&assign.src.borrow()))
332 }
333
334 pub fn assignment_to_str<T>(assign: &ir::Assignment<T>) -> String
336 where
337 T: ToString + Clone + Eq,
338 {
339 let mut buf = Vec::new();
340 Self::write_assignment(assign, 0, &mut buf).ok();
341 String::from_utf8_lossy(buf.as_slice()).to_string()
342 }
343
344 pub fn control_to_str(assign: &ir::Control) -> String {
346 let mut buf = Vec::new();
347 Self::write_control(assign, 0, &mut buf).ok();
348 String::from_utf8_lossy(buf.as_slice()).to_string()
349 }
350
351 pub fn write_comb_group<F: io::Write>(
353 group: &ir::CombGroup,
354 indent_level: usize,
355 f: &mut F,
356 ) -> io::Result<()> {
357 write!(f, "{}", " ".repeat(indent_level))?;
358 write!(f, "comb group {}", group.name().id)?;
359 if !group.attributes.is_empty() {
360 write!(f, "{}", Self::format_attributes(&group.attributes))?;
361 }
362 writeln!(f, " {{")?;
363
364 for assign in &group.assignments {
365 Self::write_assignment(assign, indent_level + 2, f)?;
366 writeln!(f)?;
367 }
368 write!(f, "{}}}", " ".repeat(indent_level))
369 }
370
371 pub fn write_group<F: io::Write>(
373 group: &ir::Group,
374 indent_level: usize,
375 f: &mut F,
376 ) -> io::Result<()> {
377 write!(f, "{}", " ".repeat(indent_level))?;
378 write!(f, "group {}", group.name().id)?;
379 if !group.attributes.is_empty() {
380 write!(f, "{}", Self::format_attributes(&group.attributes))?;
381 }
382 writeln!(f, " {{")?;
383
384 for assign in &group.assignments {
385 Self::write_assignment(assign, indent_level + 2, f)?;
386 writeln!(f)?;
387 }
388 write!(f, "{}}}", " ".repeat(indent_level))
389 }
390
391 pub fn write_static_group<F: io::Write>(
393 group: &ir::StaticGroup,
394 indent_level: usize,
395 f: &mut F,
396 ) -> io::Result<()> {
397 write!(f, "{}", " ".repeat(indent_level))?;
398 write!(
399 f,
400 "static<{}> group {}",
401 group.get_latency(),
402 group.name().id,
403 )?;
404 if !group.attributes.is_empty() {
405 write!(f, "{}", Self::format_attributes(&group.attributes))?;
406 }
407 writeln!(f, " {{")?;
408
409 for assign in &group.assignments {
410 Self::write_assignment(assign, indent_level + 2, f)?;
411 writeln!(f)?;
412 }
413 write!(f, "{}}}", " ".repeat(indent_level))
414 }
415
416 pub fn write_static_control<F: io::Write>(
418 scontrol: &ir::StaticControl,
419 indent_level: usize,
420 f: &mut F,
421 ) -> io::Result<()> {
422 write!(f, "{}", " ".repeat(indent_level))?;
423 match scontrol {
424 ir::StaticControl::Enable(ir::StaticEnable {
425 group,
426 attributes,
427 }) => {
428 write!(f, "{}", Self::format_at_attributes(attributes))?;
429 writeln!(f, "{};", group.borrow().name().id)
430 }
431 ir::StaticControl::Repeat(ir::StaticRepeat {
432 num_repeats,
433 attributes,
434 body,
435 ..
436 }) => {
437 write!(f, "{}", Self::format_at_attributes(attributes))?;
438 write!(f, "static repeat {} ", num_repeats)?;
439 writeln!(f, "{{")?;
440 Self::write_static_control(body, indent_level + 2, f)?;
441 writeln!(f, "{}}}", " ".repeat(indent_level))
442 }
443 ir::StaticControl::Seq(ir::StaticSeq {
444 stmts,
445 attributes,
446 latency,
447 }) => {
448 write!(f, "{}", Self::format_at_attributes(attributes))?;
449 writeln!(f, "static<{}> seq {{", latency)?;
450 for stmt in stmts {
451 Self::write_static_control(stmt, indent_level + 2, f)?;
452 }
453 writeln!(f, "{}}}", " ".repeat(indent_level))
454 }
455 ir::StaticControl::Par(ir::StaticPar {
456 stmts,
457 attributes,
458 latency,
459 }) => {
460 write!(f, "{}", Self::format_at_attributes(attributes))?;
461 writeln!(f, "static<{}> par {{", latency)?;
462 for stmt in stmts {
463 Self::write_static_control(stmt, indent_level + 2, f)?;
464 }
465 writeln!(f, "{}}}", " ".repeat(indent_level))
466 }
467 ir::StaticControl::Empty(ir::Empty { attributes }) => {
468 if !attributes.is_empty() {
469 writeln!(f, "{};", Self::format_at_attributes(attributes))
470 } else {
471 writeln!(f)
472 }
473 }
474 ir::StaticControl::If(ir::StaticIf {
475 port,
476 latency,
477 tbranch,
478 fbranch,
479 attributes,
480 }) => {
481 write!(f, "{}", Self::format_at_attributes(attributes))?;
482 write!(
483 f,
484 "static<{}> if {} ",
485 latency,
486 Self::port_to_str(&port.borrow()),
487 )?;
488 writeln!(f, "{{")?;
489 Self::write_static_control(tbranch, indent_level + 2, f)?;
490 write!(f, "{}}}", " ".repeat(indent_level))?;
491 if let ir::StaticControl::Empty(_) = **fbranch {
492 writeln!(f)
493 } else {
494 writeln!(f, " else {{")?;
495 Self::write_static_control(fbranch, indent_level + 2, f)?;
496 writeln!(f, "{}}}", " ".repeat(indent_level))
497 }
498 }
499 ir::StaticControl::Invoke(ir::StaticInvoke {
500 comp,
501 latency,
502 inputs,
503 outputs,
504 attributes,
505 ref_cells,
506 comb_group,
507 }) => {
508 write!(f, "{}", Self::format_at_attributes(attributes))?;
509 write!(
510 f,
511 "static<{}> invoke {}",
512 latency,
513 comp.borrow().name()
514 )?;
515 if !ref_cells.is_empty() {
516 write!(f, "[")?;
517 for (i, (outcell, incell)) in ref_cells.iter().enumerate() {
518 write!(
519 f,
520 "{}{} = {}",
521 if i == 0 { "" } else { "," },
522 outcell,
523 incell.borrow().name()
524 )?
525 }
526 write!(f, "]")?;
527 }
528 write!(f, "(")?;
529 for (i, (arg, port)) in inputs.iter().enumerate() {
530 write!(
531 f,
532 "{}\n{}{} = {}",
533 if i == 0 { "" } else { "," },
534 " ".repeat(indent_level + 2),
535 arg,
536 Self::port_to_str(&port.borrow())
537 )?;
538 }
539 if inputs.is_empty() {
540 write!(f, ")(")?;
541 } else {
542 write!(f, "\n{})(", " ".repeat(indent_level))?;
543 }
544 for (i, (arg, port)) in outputs.iter().enumerate() {
545 write!(
546 f,
547 "{}\n{}{} = {}",
548 if i == 0 { "" } else { "," },
549 " ".repeat(indent_level + 2),
550 arg,
551 Self::port_to_str(&port.borrow())
552 )?;
553 }
554 if outputs.is_empty() {
555 write!(f, ")")?;
556 } else {
557 write!(f, "\n{})", " ".repeat(indent_level))?;
558 }
559 if let Some(group) = comb_group {
560 write!(f, " with {}", group.borrow().name)?;
561 }
562 writeln!(f, ";")
563 }
564 }
565 }
566
567 pub fn write_control<F: io::Write>(
569 control: &ir::Control,
570 indent_level: usize,
571 f: &mut F,
572 ) -> io::Result<()> {
573 if !matches!(control, ir::Control::Static(_)) {
575 write!(f, "{}", " ".repeat(indent_level))?;
576 }
577 match control {
578 ir::Control::Enable(ir::Enable { group, attributes }) => {
579 write!(f, "{}", Self::format_at_attributes(attributes))?;
580 writeln!(f, "{};", group.borrow().name().id)
581 }
582 ir::Control::Invoke(ir::Invoke {
583 comp,
584 inputs,
585 outputs,
586 attributes,
587 comb_group,
588 ref_cells,
589 }) => {
590 if !attributes.is_empty() {
591 write!(f, "{}", Self::format_at_attributes(attributes))?
592 }
593 write!(f, "invoke {}", comp.borrow().name())?;
594 if !ref_cells.is_empty() {
595 write!(f, "[")?;
596 for (i, (outcell, incell)) in ref_cells.iter().enumerate() {
597 write!(
598 f,
599 "{}{} = {}",
600 if i == 0 { "" } else { "," },
601 outcell,
602 incell.borrow().name()
603 )?
604 }
605 write!(f, "]")?;
606 }
607 write!(f, "(")?;
608 for (i, (arg, port)) in inputs.iter().enumerate() {
609 write!(
610 f,
611 "{}\n{}{} = {}",
612 if i == 0 { "" } else { "," },
613 " ".repeat(indent_level + 2),
614 arg,
615 Self::port_to_str(&port.borrow())
616 )?;
617 }
618 if inputs.is_empty() {
619 write!(f, ")(")?;
620 } else {
621 write!(f, "\n{})(", " ".repeat(indent_level))?;
622 }
623 for (i, (arg, port)) in outputs.iter().enumerate() {
624 write!(
625 f,
626 "{}\n{}{} = {}",
627 if i == 0 { "" } else { "," },
628 " ".repeat(indent_level + 2),
629 arg,
630 Self::port_to_str(&port.borrow())
631 )?;
632 }
633 if outputs.is_empty() {
634 write!(f, ")")?;
635 } else {
636 write!(f, "\n{})", " ".repeat(indent_level))?;
637 }
638 if let Some(group) = comb_group {
639 writeln!(f, " with {};", group.borrow().name)
640 } else {
641 writeln!(f, ";")
642 }
643 }
644 ir::Control::Seq(ir::Seq { stmts, attributes }) => {
645 write!(f, "{}", Self::format_at_attributes(attributes))?;
646 writeln!(f, "seq {{")?;
647 for stmt in stmts {
648 Self::write_control(stmt, indent_level + 2, f)?;
649 }
650 writeln!(f, "{}}}", " ".repeat(indent_level))
651 }
652 ir::Control::Repeat(ir::Repeat {
653 num_repeats,
654 attributes,
655 body,
656 ..
657 }) => {
658 write!(f, "{}", Self::format_at_attributes(attributes))?;
659 write!(f, "repeat {} ", num_repeats)?;
660 writeln!(f, "{{")?;
661 Self::write_control(body, indent_level + 2, f)?;
662 writeln!(f, "{}}}", " ".repeat(indent_level))
663 }
664 ir::Control::Par(ir::Par { stmts, attributes }) => {
665 write!(f, "{}", Self::format_at_attributes(attributes))?;
666 writeln!(f, "par {{")?;
667 for stmt in stmts {
668 Self::write_control(stmt, indent_level + 2, f)?;
669 }
670 writeln!(f, "{}}}", " ".repeat(indent_level))
671 }
672 ir::Control::If(ir::If {
673 port,
674 cond,
675 tbranch,
676 fbranch,
677 attributes,
678 }) => {
679 write!(f, "{}", Self::format_at_attributes(attributes))?;
680 write!(f, "if {} ", Self::port_to_str(&port.borrow()),)?;
681 if let Some(c) = cond {
682 write!(f, "with {} ", c.borrow().name.id)?;
683 }
684 writeln!(f, "{{")?;
685 Self::write_control(tbranch, indent_level + 2, f)?;
686 write!(f, "{}}}", " ".repeat(indent_level))?;
687 if let ir::Control::Empty(_) = **fbranch {
688 writeln!(f)
689 } else {
690 writeln!(f, " else {{")?;
691 Self::write_control(fbranch, indent_level + 2, f)?;
692 writeln!(f, "{}}}", " ".repeat(indent_level))
693 }
694 }
695 ir::Control::While(ir::While {
696 port,
697 cond,
698 body,
699 attributes,
700 }) => {
701 write!(f, "{}", Self::format_at_attributes(attributes))?;
702 write!(f, "while {} ", Self::port_to_str(&port.borrow()),)?;
703 if let Some(c) = cond {
704 write!(f, "with {} ", c.borrow().name.id)?;
705 }
706 writeln!(f, "{{")?;
707 Self::write_control(body, indent_level + 2, f)?;
708 writeln!(f, "{}}}", " ".repeat(indent_level))
709 }
710 ir::Control::Empty(ir::Empty { attributes }) => {
711 if !attributes.is_empty() {
712 writeln!(f, "{};", Self::format_at_attributes(attributes))
713 } else {
714 writeln!(f)
715 }
716 }
717 ir::Control::Static(sc) => {
718 Self::write_static_control(sc, indent_level, f)
719 }
720 }
721 }
722
723 pub fn guard_str<T: ToString>(guard: &ir::Guard<T>) -> String
725 where
726 T: Eq,
727 {
728 match &guard {
729 ir::Guard::And(l, r) | ir::Guard::Or(l, r) => {
730 let left = if &**l > guard {
731 format!("({})", Self::guard_str(l))
732 } else {
733 Self::guard_str(l)
734 };
735 let right = if &**r > guard {
736 format!("({})", Self::guard_str(r))
737 } else {
738 Self::guard_str(r)
739 };
740 format!("{} {} {}", left, &guard.op_str(), right)
741 }
742 ir::Guard::CompOp(_, l, r) => {
743 format!(
744 "{} {} {}",
745 Self::port_to_str(&l.borrow()),
746 &guard.op_str(),
747 Self::port_to_str(&r.borrow())
748 )
749 }
750 ir::Guard::Not(g) => {
751 let s = if &**g > guard {
752 format!("({})", Self::guard_str(g))
753 } else {
754 Self::guard_str(g)
755 };
756 format!("!{}", s)
757 }
758 ir::Guard::Port(port_ref) => Self::port_to_str(&port_ref.borrow()),
759 ir::Guard::True => "1'b1".to_string(),
760 ir::Guard::Info(i) => i.to_string(),
761 }
762 }
763
764 pub fn port_to_str(port: &ir::Port) -> String {
766 match &port.parent {
767 ir::PortParent::Cell(cell_wref) => {
768 let cell_ref =
769 cell_wref.internal.upgrade().unwrap_or_else(|| {
770 panic!(
771 "Malformed AST: No reference to Cell for port `{}'",
772 port.name
773 )
774 });
775 let cell = cell_ref.borrow();
776 match cell.prototype {
777 ir::CellType::Constant { val, width } => {
778 format!("{}'d{}", width, val)
779 }
780 ir::CellType::ThisComponent => port.name.to_string(),
781 _ => format!("{}.{}", cell.name().id, port.name.id),
782 }
783 }
784 ir::PortParent::Group(group_wref) => format!(
785 "{}[{}]",
786 group_wref
787 .internal
788 .upgrade()
789 .unwrap_or_else(|| panic!(
790 "Malformed AST: No reference to Group for port `{:#?}'",
791 port
792 ))
793 .borrow()
794 .name()
795 .id,
796 port.name.id
797 ),
798 ir::PortParent::StaticGroup(group_wref) => format!(
799 "{}[{}]",
800 group_wref
801 .internal
802 .upgrade()
803 .unwrap_or_else(|| panic!(
804 "Malformed AST: No reference to Group for port `{:#?}'",
805 port
806 ))
807 .borrow()
808 .name()
809 .id,
810 port.name.id
811 ),
812 }
813 }
814
815 pub fn format_metadata(metadata: &Option<String>) -> String {
817 if let Some(metadata_str) = metadata {
818 format!("metadata #{{\n{}\n}}#\n", metadata_str)
819 } else {
820 String::new()
821 }
822 }
823}