calyx_ir/
printer.rs

1//! Implements a formatter for the in-memory representation of Components.
2//! The printing operation clones inner nodes and doesn't perform any mutation
3//! to the Component.
4use crate::{self as ir, RRC};
5use calyx_frontend::PrimitiveInfo;
6use itertools::Itertools;
7use std::io;
8use std::path::Path;
9use std::rc::Rc;
10
11/// Printer for the IR.
12pub struct Printer;
13
14impl Printer {
15    /// Format attributes of the form `@static(1)`.
16    /// Returns the empty string if the `attrs` is empty.
17    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    /// Format attributes of the form `<"static"=1>`.
32    /// Returns the empty string if the `attrs` is empty.
33    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    /// Formats port definitions in signatures
47    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    /// Prints out the program context.
80    /// If `skip_primitives` is true, the printer will skip printing primitives defined outside the source file.
81    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    /// Formats and writes extern statements.
113    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    /// Formats and writes the Component to the formatter.
181    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                // Cell signature stores the ports in reversed direction.
189                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        // Add the cells
211        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        // Add the wires
225        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        // Write the continuous assignments
246        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        // Add the control program.
257        // Since the syntax doesn't allow combinational components to have a control block, the attributes will always be empty
258        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    /// Format and write a cell.
278    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    /// Format and write an assignment.
320    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    /// Convinience method to get string representation of [ir::Assignment].
335    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    /// Convinience method to get string representation of [ir::Control].
345    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    /// Format and write a combinational group.
352    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    /// Format and write a group.
372    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    /// Format and write a static group.
392    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    /// Format and write a static control program
417    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    /// Format and write a control program
568    pub fn write_control<F: io::Write>(
569        control: &ir::Control,
570        indent_level: usize,
571        f: &mut F,
572    ) -> io::Result<()> {
573        // write_static_control will indent already so we don't want to indent twice
574        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    /// Generate a String-based representation for a guard.
724    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    /// Get the port access expression.
765    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    /// Formats the top-level metadata if present
816    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}