Skip to main content

hugr_model/v0/ast/
print.rs

1use std::{borrow::Cow, fmt::Display};
2
3use base64::{Engine as _, prelude::BASE64_STANDARD};
4use pretty::{Arena, DocAllocator as _, RefDoc};
5
6use crate::v0::{Literal, RegionKind};
7
8use super::{
9    LinkName, Module, Node, Operation, Package, Param, Region, SeqPart, Symbol, SymbolIdent,
10    SymbolName, Term, VarName, Visibility,
11};
12
13struct Printer<'a> {
14    /// The arena in which to allocate the pretty-printed documents.
15    arena: &'a Arena<'a>,
16    /// Parts of the document to be concatenated.
17    docs: Vec<RefDoc<'a>>,
18    /// Stack of indices into `docs` denoting the current nesting.
19    docs_stack: Vec<usize>,
20}
21
22impl<'a> Printer<'a> {
23    fn new(arena: &'a Arena<'a>) -> Self {
24        Self {
25            arena,
26            docs: Vec::new(),
27            docs_stack: Vec::new(),
28        }
29    }
30
31    fn finish(self) -> RefDoc<'a> {
32        let sep = self
33            .arena
34            .concat([self.arena.hardline(), self.arena.hardline()]);
35        self.arena.intersperse(self.docs, sep).into_doc()
36    }
37
38    fn parens_enter(&mut self) {
39        self.delim_open();
40    }
41
42    fn parens_exit(&mut self) {
43        self.delim_close("(", ")", 2);
44    }
45
46    fn brackets_enter(&mut self) {
47        self.delim_open();
48    }
49
50    fn brackets_exit(&mut self) {
51        self.delim_close("[", "]", 1);
52    }
53
54    fn group_enter(&mut self) {
55        self.delim_open();
56    }
57
58    fn group_exit(&mut self) {
59        self.delim_close("", "", 0);
60    }
61
62    fn delim_open(&mut self) {
63        self.docs_stack.push(self.docs.len());
64    }
65
66    fn delim_close(&mut self, open: &'static str, close: &'static str, nesting: isize) {
67        let docs = self.docs.drain(self.docs_stack.pop().unwrap()..);
68        let doc = self.arena.concat([
69            self.arena.text(open),
70            self.arena
71                .intersperse(docs, self.arena.line())
72                .nest(nesting)
73                .group(),
74            self.arena.text(close),
75        ]);
76        self.docs.push(doc.into_doc());
77    }
78
79    fn text(&mut self, text: impl Into<Cow<'a, str>>) {
80        self.docs.push(self.arena.text(text).into_doc());
81    }
82
83    fn int(&mut self, value: u64) {
84        self.text(format!("{value}"));
85    }
86
87    fn string(&mut self, string: &str) {
88        let mut output = String::with_capacity(string.len() + 2);
89        output.push('"');
90
91        for c in string.chars() {
92            match c {
93                '\\' => output.push_str("\\\\"),
94                '"' => output.push_str("\\\""),
95                '\n' => output.push_str("\\n"),
96                '\r' => output.push_str("\\r"),
97                '\t' => output.push_str("\\t"),
98                _ => output.push(c),
99            }
100        }
101
102        output.push('"');
103        self.text(output);
104    }
105
106    fn bytes(&mut self, bytes: &[u8]) {
107        // every 3 bytes are encoded into 4 characters
108        let mut output = String::with_capacity(2 + bytes.len().div_ceil(3) * 4);
109        output.push('"');
110        BASE64_STANDARD.encode_string(bytes, &mut output);
111        output.push('"');
112        self.text(output);
113    }
114}
115
116fn print_term<'a>(printer: &mut Printer<'a>, term: &'a Term) {
117    match term {
118        Term::Wildcard => printer.text("_"),
119        Term::Var(var) => print_var_name(printer, var),
120        Term::Apply(symbol, terms) => {
121            if terms.is_empty() {
122                print_symbol_ident(printer, symbol);
123            } else {
124                printer.parens_enter();
125                print_symbol_ident(printer, symbol);
126
127                for term in terms.iter() {
128                    print_term(printer, term);
129                }
130
131                printer.parens_exit();
132            }
133        }
134        Term::List(list_parts) => {
135            printer.brackets_enter();
136            print_list_parts(printer, list_parts);
137            printer.brackets_exit();
138        }
139        Term::Literal(literal) => {
140            print_literal(printer, literal);
141        }
142        Term::Tuple(tuple_parts) => {
143            printer.parens_enter();
144            printer.text("tuple");
145            print_tuple_parts(printer, tuple_parts);
146            printer.parens_exit();
147        }
148        Term::Func(region) => {
149            printer.parens_enter();
150            printer.text("fn");
151            print_region(printer, region);
152            printer.parens_exit();
153        }
154    }
155}
156
157fn print_literal<'a>(printer: &mut Printer<'a>, literal: &'a Literal) {
158    match literal {
159        Literal::Str(str) => {
160            printer.string(str);
161        }
162        Literal::Nat(nat) => {
163            printer.int(*nat);
164        }
165        Literal::Bytes(bytes) => {
166            printer.parens_enter();
167            printer.text("bytes");
168            printer.bytes(bytes);
169            printer.parens_exit();
170        }
171        Literal::Float(float) => {
172            // The debug representation of a float always includes a decimal point.
173            printer.text(format!("{:.?}", float.into_inner()));
174        }
175    }
176}
177
178fn print_seq_splice<'a>(printer: &mut Printer<'a>, term: &'a Term) {
179    printer.group_enter();
180    print_term(printer, term);
181    printer.text("...");
182    printer.group_exit();
183}
184
185/// Print a [`SeqPart`] in isolation for the [`Display`] instance.
186fn print_seq_part<'a>(printer: &mut Printer<'a>, part: &'a SeqPart) {
187    match part {
188        SeqPart::Item(term) => print_term(printer, term),
189        SeqPart::Splice(term) => print_seq_splice(printer, term),
190    }
191}
192
193/// Print the parts of a list [`Term`], merging spread lists.
194fn print_list_parts<'a>(printer: &mut Printer<'a>, parts: &'a [SeqPart]) {
195    for part in parts {
196        match part {
197            SeqPart::Item(term) => print_term(printer, term),
198            SeqPart::Splice(Term::List(nested)) => print_list_parts(printer, nested),
199            SeqPart::Splice(term) => print_seq_splice(printer, term),
200        }
201    }
202}
203
204/// Print the parts of a tuple [`Term`], merging spread tuples.
205fn print_tuple_parts<'a>(printer: &mut Printer<'a>, parts: &'a [SeqPart]) {
206    for part in parts {
207        match part {
208            SeqPart::Item(term) => print_term(printer, term),
209            SeqPart::Splice(Term::Tuple(nested)) => print_tuple_parts(printer, nested),
210            SeqPart::Splice(term) => print_seq_splice(printer, term),
211        }
212    }
213}
214
215fn print_symbol_name<'a>(printer: &mut Printer<'a>, name: &'a SymbolName) {
216    printer.text(name.0.as_str());
217}
218
219fn print_symbol_ident<'a>(printer: &mut Printer<'a>, symbol_ident: &'a SymbolIdent) {
220    print_versioned_symbol_name(printer, &symbol_ident.name, symbol_ident.version.as_ref());
221}
222
223fn print_versioned_symbol_name<'a>(
224    printer: &mut Printer<'a>,
225    name: &'a SymbolName,
226    version: Option<&semver::Version>,
227) {
228    match version {
229        Some(version) => printer.text(format!("{name}@{version}")),
230        None => print_symbol_name(printer, name),
231    }
232}
233
234fn print_var_name<'a>(printer: &mut Printer<'a>, name: &'a VarName) {
235    printer.text(format!("{name}"));
236}
237
238fn print_link_name<'a>(printer: &mut Printer<'a>, name: &'a LinkName) {
239    printer.text(format!("{name}"));
240}
241
242fn print_port_lists<'a>(
243    printer: &mut Printer<'a>,
244    inputs: &'a [LinkName],
245    outputs: &'a [LinkName],
246) {
247    // If the node/region has no ports, we avoid printing the port lists.
248    // This is especially important for the syntax of nodes that introduce symbols
249    // since these nodes never have any input or output ports.
250    if inputs.is_empty() && outputs.is_empty() {
251        return;
252    }
253
254    // The group encodes the preference that the port lists occur on the same
255    // line whenever possible.
256    printer.group_enter();
257    printer.brackets_enter();
258    for input in inputs {
259        print_link_name(printer, input);
260    }
261    printer.brackets_exit();
262    printer.brackets_enter();
263    for output in outputs {
264        print_link_name(printer, output);
265    }
266    printer.brackets_exit();
267    printer.group_exit();
268}
269
270fn print_package<'a>(printer: &mut Printer<'a>, package: &'a Package) {
271    printer.parens_enter();
272    printer.text("hugr");
273    printer.text("0");
274    printer.parens_exit();
275
276    for module in &package.modules {
277        printer.parens_enter();
278        printer.text("mod");
279        printer.parens_exit();
280
281        print_module(printer, module);
282    }
283}
284
285fn print_module<'a>(printer: &mut Printer<'a>, module: &'a Module) {
286    for meta in &module.root.meta {
287        print_meta_item(printer, meta);
288    }
289
290    for child in &module.root.children {
291        print_node(printer, child);
292    }
293}
294
295fn print_node<'a>(printer: &mut Printer<'a>, node: &'a Node) {
296    printer.parens_enter();
297
298    printer.group_enter();
299    match &node.operation {
300        Operation::Invalid => printer.text("invalid"),
301        Operation::Dfg => printer.text("dfg"),
302        Operation::Cfg => printer.text("cfg"),
303        Operation::Block => printer.text("block"),
304        Operation::DefineFunc(symbol_signature) => {
305            printer.text("define-func");
306            print_symbol(printer, symbol_signature);
307        }
308        Operation::DeclareFunc(symbol_signature) => {
309            printer.text("declare-func");
310            print_symbol(printer, symbol_signature);
311        }
312        Operation::Custom(term) => {
313            print_term(printer, term);
314        }
315        Operation::DefineAlias(symbol_signature, value) => {
316            printer.text("define-alias");
317            print_symbol(printer, symbol_signature);
318            print_term(printer, value);
319        }
320        Operation::DeclareAlias(symbol_signature) => {
321            printer.text("declare-alias");
322            print_symbol(printer, symbol_signature);
323        }
324        Operation::TailLoop => printer.text("tail-loop"),
325        Operation::Conditional => printer.text("cond"),
326        Operation::DeclareConstructor(symbol_signature) => {
327            printer.text("declare-ctr");
328            print_symbol(printer, symbol_signature);
329        }
330        Operation::DeclareOperation(symbol_signature) => {
331            printer.text("declare-operation");
332            print_symbol(printer, symbol_signature);
333        }
334        Operation::Import(symbol) => {
335            printer.text("import");
336            print_symbol_ident(printer, symbol);
337        }
338    }
339
340    print_port_lists(printer, &node.inputs, &node.outputs);
341    printer.group_exit();
342
343    if let Some(signature) = &node.signature {
344        print_signature(printer, signature);
345    }
346
347    for meta in &node.meta {
348        print_meta_item(printer, meta);
349    }
350
351    for region in &node.regions {
352        print_region(printer, region);
353    }
354
355    printer.parens_exit();
356}
357
358fn print_region<'a>(printer: &mut Printer<'a>, region: &'a Region) {
359    printer.parens_enter();
360    printer.group_enter();
361
362    printer.text(match region.kind {
363        RegionKind::DataFlow => "dfg",
364        RegionKind::ControlFlow => "cfg",
365        RegionKind::Module => "mod",
366    });
367
368    print_port_lists(printer, &region.sources, &region.targets);
369    printer.group_exit();
370
371    if let Some(signature) = &region.signature {
372        print_signature(printer, signature);
373    }
374
375    for meta in &region.meta {
376        print_meta_item(printer, meta);
377    }
378
379    for child in &region.children {
380        print_node(printer, child);
381    }
382
383    printer.parens_exit();
384}
385
386fn print_symbol<'a>(printer: &mut Printer<'a>, symbol: &'a Symbol) {
387    printer.group_enter();
388
389    match symbol.visibility {
390        None => (),
391        Some(Visibility::Private) => printer.text("private"),
392        Some(Visibility::Public) => printer.text("public"),
393    }
394
395    print_versioned_symbol_name(printer, &symbol.name, symbol.version.as_ref());
396
397    for param in &symbol.params {
398        print_param(printer, param);
399    }
400
401    for constraint in &symbol.constraints {
402        print_constraint(printer, constraint);
403    }
404
405    print_term(printer, &symbol.signature);
406    printer.group_exit();
407}
408
409fn print_param<'a>(printer: &mut Printer<'a>, param: &'a Param) {
410    printer.parens_enter();
411    printer.text("param");
412    print_var_name(printer, &param.name);
413    print_term(printer, &param.r#type);
414    printer.parens_exit();
415}
416
417fn print_constraint<'a>(printer: &mut Printer<'a>, constraint: &'a Term) {
418    printer.parens_enter();
419    printer.text("where");
420    print_term(printer, constraint);
421    printer.parens_exit();
422}
423
424fn print_meta_item<'a>(printer: &mut Printer<'a>, meta: &'a Term) {
425    printer.parens_enter();
426    printer.text("meta");
427    print_term(printer, meta);
428    printer.parens_exit();
429}
430
431fn print_signature<'a>(printer: &mut Printer<'a>, signature: &'a Term) {
432    printer.parens_enter();
433    printer.text("signature");
434    print_term(printer, signature);
435    printer.parens_exit();
436}
437
438macro_rules! impl_display {
439    ($t:ident, $print:expr) => {
440        impl Display for $t {
441            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
442                let arena = Arena::new();
443                let mut printer = Printer::new(&arena);
444                $print(&mut printer, self);
445                let doc = printer.finish();
446                doc.render_fmt(80, f)
447            }
448        }
449    };
450}
451
452impl_display!(Package, print_package);
453impl_display!(Module, print_module);
454impl_display!(Node, print_node);
455impl_display!(Region, print_region);
456impl_display!(Param, print_param);
457impl_display!(Term, print_term);
458impl_display!(SeqPart, print_seq_part);
459impl_display!(Literal, print_literal);
460impl_display!(Symbol, print_symbol);
461impl_display!(SymbolIdent, print_symbol_ident);
462
463impl Display for VarName {
464    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
465        write!(f, "?{}", self.0)
466    }
467}
468
469impl Display for SymbolName {
470    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
471        write!(f, "{}", self.0)
472    }
473}
474
475impl Display for LinkName {
476    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
477        write!(f, "%{}", self.0)
478    }
479}