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 arena: &'a Arena<'a>,
16 docs: Vec<RefDoc<'a>>,
18 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 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 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
185fn 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
193fn 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
204fn 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 inputs.is_empty() && outputs.is_empty() {
251 return;
252 }
253
254 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, ®ion.sources, ®ion.targets);
369 printer.group_exit();
370
371 if let Some(signature) = ®ion.signature {
372 print_signature(printer, signature);
373 }
374
375 for meta in ®ion.meta {
376 print_meta_item(printer, meta);
377 }
378
379 for child in ®ion.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, ¶m.name);
413 print_term(printer, ¶m.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}