graphix_compiler/expr/
print.rs

1use super::Sig;
2use crate::{
3    expr::{parser, Bind, Expr, ExprKind, Lambda, ModuleKind, Sandbox, SigItem, TypeDef},
4    typ::Type,
5};
6use compact_str::{format_compact, CompactString};
7use netidx::{path::Path, utils::Either};
8use netidx_value::{parser::VAL_ESC, Value};
9use std::fmt::{self, Formatter, Write};
10
11impl fmt::Display for TypeDef {
12    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
13        let Self { name, params, typ } = self;
14        write!(f, "type {name}")?;
15        if !params.is_empty() {
16            write!(f, "<")?;
17            for (i, (tv, ct)) in params.iter().enumerate() {
18                write!(f, "{tv}")?;
19                if let Some(ct) = ct {
20                    write!(f, ": {ct}")?;
21                }
22                if i < params.len() - 1 {
23                    write!(f, ", ")?;
24                }
25            }
26            write!(f, ">")?;
27        }
28        write!(f, " = {typ}")
29    }
30}
31
32impl fmt::Display for Sandbox {
33    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
34        macro_rules! write_sandbox {
35            ($kind:literal, $l:expr) => {{
36                write!(f, "sandbox {} [ ", $kind)?;
37                for (i, p) in $l.iter().enumerate() {
38                    if i < $l.len() - 1 {
39                        write!(f, "{}, ", p)?
40                    } else {
41                        write!(f, "{}", p)?
42                    }
43                }
44                write!(f, " ]")
45            }};
46        }
47        match self {
48            Sandbox::Unrestricted => write!(f, "sandbox unrestricted"),
49            Sandbox::Blacklist(l) => write_sandbox!("blacklist", l),
50            Sandbox::Whitelist(l) => write_sandbox!("whitelist", l),
51        }
52    }
53}
54
55impl fmt::Display for SigItem {
56    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
57        match self {
58            SigItem::TypeDef(td) => write!(f, "{td}"),
59            SigItem::Bind(name, typ) => write!(f, "val {name}: {typ}"),
60            SigItem::Module(name, sig) => write!(f, "mod {name}: {sig}"),
61        }
62    }
63}
64
65impl fmt::Display for Sig {
66    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
67        write!(f, "sig {{ ")?;
68        for (i, si) in self.iter().enumerate() {
69            write!(f, "{si}")?;
70            if i < self.len() - 1 {
71                write!(f, "; ")?
72            }
73        }
74        write!(f, " }}")
75    }
76}
77
78fn push_indent(indent: usize, buf: &mut String) {
79    buf.extend((0..indent).into_iter().map(|_| ' '));
80}
81
82fn pretty_print_sandbox(indent: usize, buf: &mut String, s: &Sandbox) -> fmt::Result {
83    macro_rules! write_sandbox {
84        ($kind:literal, $l:expr) => {{
85            writeln!(buf, "sandbox {} [", $kind)?;
86            for (i, p) in $l.iter().enumerate() {
87                push_indent(indent + 4, buf);
88                if i < $l.len() - 1 {
89                    writeln!(buf, "{},", p)?
90                } else {
91                    writeln!(buf, "{}", p)?
92                }
93            }
94            writeln!(buf, "];")
95        }};
96    }
97    push_indent(indent, buf);
98    match s {
99        Sandbox::Unrestricted => writeln!(buf, "sandbox unrestricted;"),
100        Sandbox::Blacklist(l) => write_sandbox!("blacklist", l),
101        Sandbox::Whitelist(l) => write_sandbox!("whitelist", l),
102    }
103}
104
105fn pretty_print_sig_items(
106    indent: usize,
107    buf: &mut String,
108    it: &[SigItem],
109) -> fmt::Result {
110    for (i, si) in it.iter().enumerate() {
111        push_indent(indent, buf);
112        match si {
113            SigItem::TypeDef(td) => write!(buf, "{td}")?,
114            SigItem::Bind(name, typ) => write!(buf, "val {name}: {typ}")?,
115            SigItem::Module(name, sig) => {
116                writeln!(buf, "mod {name}: sig {{")?;
117                pretty_print_sig_items(indent + 2, buf, sig)?;
118                push_indent(indent, buf);
119                write!(buf, "}}")?
120            }
121        }
122        if i < it.len() - 1 {
123            writeln!(buf, ";")?
124        } else {
125            writeln!(buf, "")?
126        }
127    }
128    Ok(())
129}
130
131impl ExprKind {
132    pub fn to_string_pretty(&self, col_limit: usize) -> String {
133        let mut buf = String::new();
134        self.pretty_print(0, col_limit, true, &mut buf).unwrap();
135        buf
136    }
137
138    fn pretty_print(
139        &self,
140        indent: usize,
141        limit: usize,
142        newline: bool,
143        buf: &mut String,
144    ) -> fmt::Result {
145        macro_rules! kill_newline {
146            ($buf:expr) => {
147                if let Some('\n') = $buf.chars().next_back() {
148                    $buf.pop();
149                }
150            };
151        }
152        macro_rules! try_single_line {
153            ($trunc:ident) => {{
154                let len = buf.len();
155                let (start, indent) = if newline {
156                    push_indent(indent, buf);
157                    (len, indent)
158                } else {
159                    (buf.rfind('\n').unwrap_or(0), 0)
160                };
161                writeln!(buf, "{}", self)?;
162                if buf.len() - start <= limit {
163                    return Ok(());
164                } else {
165                    if $trunc {
166                        buf.truncate(len + indent)
167                    }
168                    len + indent
169                }
170            }};
171        }
172        macro_rules! binop {
173            ($sep:literal, $lhs:expr, $rhs:expr) => {{
174                try_single_line!(true);
175                write!(buf, "(")?;
176                writeln!(buf, "{} {}", $lhs, $sep)?;
177                $rhs.kind.pretty_print(indent, limit, true, buf)?;
178                write!(buf, ")")
179            }};
180        }
181        let mut tbuf = CompactString::new("");
182        macro_rules! typ {
183            ($typ:expr) => {{
184                match $typ {
185                    None => "",
186                    Some(typ) => {
187                        tbuf.clear();
188                        write!(tbuf, ": {typ}")?;
189                        tbuf.as_str()
190                    }
191                }
192            }};
193        }
194        fn pretty_print_exprs_int<'a, A, F: Fn(&'a A) -> &'a Expr>(
195            indent: usize,
196            limit: usize,
197            buf: &mut String,
198            exprs: &'a [A],
199            open: &str,
200            close: &str,
201            sep: &str,
202            f: F,
203        ) -> fmt::Result {
204            writeln!(buf, "{}", open)?;
205            for i in 0..exprs.len() {
206                f(&exprs[i]).kind.pretty_print(indent + 2, limit, true, buf)?;
207                if i < exprs.len() - 1 {
208                    kill_newline!(buf);
209                    writeln!(buf, "{}", sep)?
210                }
211            }
212            push_indent(indent, buf);
213            writeln!(buf, "{}", close)
214        }
215        fn pretty_print_exprs(
216            indent: usize,
217            limit: usize,
218            buf: &mut String,
219            exprs: &[Expr],
220            open: &str,
221            close: &str,
222            sep: &str,
223        ) -> fmt::Result {
224            pretty_print_exprs_int(indent, limit, buf, exprs, open, close, sep, |a| a)
225        }
226        let exp = |export| if export { "pub " } else { "" };
227        match self {
228            ExprKind::Constant(_)
229            | ExprKind::Use { .. }
230            | ExprKind::Ref { .. }
231            | ExprKind::StructRef { .. }
232            | ExprKind::TupleRef { .. }
233            | ExprKind::TypeDef { .. }
234            | ExprKind::ArrayRef { .. }
235            | ExprKind::MapRef { .. }
236            | ExprKind::ArraySlice { .. }
237            | ExprKind::StringInterpolate { .. }
238            | ExprKind::Module {
239                name: _,
240                export: _,
241                value: ModuleKind::Unresolved | ModuleKind::Resolved(_),
242            } => {
243                if newline {
244                    push_indent(indent, buf);
245                }
246                writeln!(buf, "{self}")
247            }
248            ExprKind::Bind(b) => {
249                let Bind { rec, doc, pattern, typ, export, value } = &**b;
250                try_single_line!(true);
251                if let Some(doc) = doc {
252                    if doc == "" {
253                        writeln!(buf, "///")?;
254                    } else {
255                        for line in doc.lines() {
256                            writeln!(buf, "///{line}")?;
257                        }
258                    }
259                }
260                let rec = if *rec { " rec" } else { "" };
261                writeln!(buf, "{}let{} {pattern}{} = ", exp(*export), rec, typ!(typ))?;
262                value.kind.pretty_print(indent + 2, limit, false, buf)
263            }
264            ExprKind::StructWith { source, replace } => {
265                try_single_line!(true);
266                match &source.kind {
267                    ExprKind::Ref { .. } => writeln!(buf, "{{ {source} with")?,
268                    _ => writeln!(buf, "{{ ({source}) with")?,
269                }
270                let indent = indent + 2;
271                for (i, (name, e)) in replace.iter().enumerate() {
272                    push_indent(indent, buf);
273                    match &e.kind {
274                        ExprKind::Ref { name: n }
275                            if Path::dirname(&**n).is_none()
276                                && Path::basename(&**n) == Some(&**name) =>
277                        {
278                            write!(buf, "{name}")?
279                        }
280                        e => {
281                            write!(buf, "{name}: ")?;
282                            e.pretty_print(indent + 2, limit, false, buf)?;
283                        }
284                    }
285                    if i < replace.len() - 1 {
286                        kill_newline!(buf);
287                        writeln!(buf, ",")?
288                    }
289                }
290                writeln!(buf, "}}")
291            }
292            ExprKind::Module { name, export, value: ModuleKind::Inline(exprs) } => {
293                try_single_line!(true);
294                write!(buf, "{}mod {name} ", exp(*export))?;
295                pretty_print_exprs(indent, limit, buf, exprs, "{", "}", ";")
296            }
297            ExprKind::Module {
298                name,
299                export,
300                value: ModuleKind::Dynamic { sandbox, sig, source },
301            } => {
302                try_single_line!(true);
303                writeln!(buf, "{}mod {name} dynamic {{", exp(*export))?;
304                pretty_print_sandbox(indent + 2, buf, sandbox)?;
305                push_indent(indent + 2, buf);
306                writeln!(buf, "sig {{")?;
307                pretty_print_sig_items(indent + 2, buf, sig)?;
308                push_indent(indent + 2, buf);
309                writeln!(buf, "}};")?;
310                push_indent(indent + 2, buf);
311                write!(buf, "source ")?;
312                source.kind.pretty_print(indent + 2, limit, false, buf)?;
313                writeln!(buf, "}}")
314            }
315            ExprKind::Do { exprs } => {
316                try_single_line!(true);
317                pretty_print_exprs(indent, limit, buf, exprs, "{", "}", ";")
318            }
319            ExprKind::Connect { name, value, deref } => {
320                try_single_line!(true);
321                let deref = if *deref { "*" } else { "" };
322                writeln!(buf, "{deref}{name} <- ")?;
323                value.kind.pretty_print(indent + 2, limit, false, buf)
324            }
325            ExprKind::TypeCast { expr, typ } => {
326                try_single_line!(true);
327                writeln!(buf, "cast<{typ}>(")?;
328                expr.kind.pretty_print(indent + 2, limit, true, buf)?;
329                writeln!(buf, ")")
330            }
331            ExprKind::Array { args } => {
332                try_single_line!(true);
333                pretty_print_exprs(indent, limit, buf, args, "[", "]", ",")
334            }
335            ExprKind::Map { args } => {
336                try_single_line!(true);
337                writeln!(buf, "{{")?;
338                for (i, (k, v)) in args.iter().enumerate() {
339                    push_indent(indent + 2, buf);
340                    writeln!(buf, "{k} => {v}")?;
341                    if i < args.len() - 1 {
342                        kill_newline!(buf);
343                        writeln!(buf, ",")?
344                    }
345                }
346                push_indent(indent, buf);
347                writeln!(buf, "}}")
348            }
349            ExprKind::Any { args } => {
350                try_single_line!(true);
351                write!(buf, "any")?;
352                pretty_print_exprs(indent, limit, buf, args, "(", ")", ",")
353            }
354            ExprKind::Tuple { args } => {
355                try_single_line!(true);
356                pretty_print_exprs(indent, limit, buf, args, "(", ")", ",")
357            }
358            ExprKind::Variant { tag: _, args } if args.len() == 0 => {
359                if newline {
360                    push_indent(indent, buf)
361                }
362                write!(buf, "{self}")
363            }
364            ExprKind::Variant { tag, args } => {
365                try_single_line!(true);
366                write!(buf, "`{tag}")?;
367                pretty_print_exprs(indent, limit, buf, args, "(", ")", ",")
368            }
369            ExprKind::Struct { args } => {
370                try_single_line!(true);
371                writeln!(buf, "{{")?;
372                for (i, (n, e)) in args.iter().enumerate() {
373                    push_indent(indent + 2, buf);
374                    match &e.kind {
375                        ExprKind::Ref { name }
376                            if Path::dirname(&**name).is_none()
377                                && Path::basename(&**name) == Some(&**n) =>
378                        {
379                            write!(buf, "{n}")?
380                        }
381                        _ => {
382                            write!(buf, "{n}: ")?;
383                            e.kind.pretty_print(indent + 2, limit, false, buf)?;
384                        }
385                    }
386                    if i < args.len() - 1 {
387                        kill_newline!(buf);
388                        writeln!(buf, ", ")?
389                    }
390                }
391                push_indent(indent, buf);
392                writeln!(buf, "}}")
393            }
394            ExprKind::Qop(e) => {
395                try_single_line!(true);
396                e.kind.pretty_print(indent, limit, true, buf)?;
397                kill_newline!(buf);
398                writeln!(buf, "?")
399            }
400            ExprKind::OrNever(e) => {
401                try_single_line!(true);
402                e.kind.pretty_print(indent, limit, true, buf)?;
403                kill_newline!(buf);
404                writeln!(buf, "$")
405            }
406            ExprKind::TryCatch(tc) => {
407                try_single_line!(true);
408                writeln!(buf, "try")?;
409                pretty_print_exprs(indent + 2, limit, buf, &tc.exprs, "", "", "; ")?;
410                match &tc.constraint {
411                    None => write!(buf, "catch({}) => ", tc.bind)?,
412                    Some(t) => write!(buf, "catch({}: {t}) => ", tc.bind)?,
413                }
414                match &tc.handler.kind {
415                    ExprKind::Do { exprs } => {
416                        pretty_print_exprs(indent + 2, limit, buf, exprs, "{", "}", "; ")
417                    }
418                    _ => {
419                        writeln!(buf, "")?;
420                        tc.handler.kind.pretty_print(indent + 2, limit, true, buf)
421                    }
422                }
423            }
424            ExprKind::Apply { function, args } => {
425                try_single_line!(true);
426                match &function.kind {
427                    ExprKind::Ref { .. } => {
428                        function.kind.pretty_print(indent, limit, true, buf)?
429                    }
430                    e => {
431                        write!(buf, "(")?;
432                        e.pretty_print(indent, limit, true, buf)?;
433                        kill_newline!(buf);
434                        write!(buf, ")")?;
435                    }
436                }
437                kill_newline!(buf);
438                writeln!(buf, "(")?;
439                for i in 0..args.len() {
440                    match &args[i].0 {
441                        None => {
442                            args[i].1.kind.pretty_print(indent + 2, limit, true, buf)?
443                        }
444                        Some(name) => match &args[i].1.kind {
445                            ExprKind::Ref { name: n }
446                                if Path::dirname(&n.0).is_none()
447                                    && Path::basename(&n.0) == Some(name.as_str()) =>
448                            {
449                                writeln!(buf, "#{name}")?
450                            }
451                            _ => {
452                                write!(buf, "#{name}: ")?;
453                                args[i].1.kind.pretty_print(
454                                    indent + 2,
455                                    limit,
456                                    false,
457                                    buf,
458                                )?
459                            }
460                        },
461                    }
462                    if i < args.len() - 1 {
463                        kill_newline!(buf);
464                        writeln!(buf, ",")?
465                    }
466                }
467                writeln!(buf, ")")
468            }
469            ExprKind::Lambda(l) => {
470                let Lambda { args, vargs, rtype, constraints, throws, body } = &**l;
471                try_single_line!(true);
472                for (i, (tvar, typ)) in constraints.iter().enumerate() {
473                    write!(buf, "{tvar}: {typ}")?;
474                    if i < constraints.len() - 1 {
475                        write!(buf, ", ")?;
476                    }
477                }
478                write!(buf, "|")?;
479                for (i, a) in args.iter().enumerate() {
480                    match &a.labeled {
481                        None => {
482                            write!(buf, "{}", a.pattern)?;
483                            buf.push_str(typ!(&a.constraint));
484                        }
485                        Some(def) => {
486                            write!(buf, "#{}", a.pattern)?;
487                            buf.push_str(typ!(&a.constraint));
488                            if let Some(def) = def {
489                                write!(buf, " = {def}")?;
490                            }
491                        }
492                    }
493                    if vargs.is_some() || i < args.len() - 1 {
494                        write!(buf, ", ")?
495                    }
496                }
497                if let Some(typ) = vargs {
498                    write!(buf, "@args{}", typ!(typ))?;
499                }
500                write!(buf, "| ")?;
501                if let Some(t) = rtype {
502                    match t {
503                        Type::Fn(ft) => write!(buf, "-> ({ft}) ")?,
504                        Type::ByRef(t) => match &**t {
505                            Type::Fn(ft) => write!(buf, "-> &({ft}) ")?,
506                            t => write!(buf, "-> &{t} ")?,
507                        },
508                        t => write!(buf, "-> {t} ")?,
509                    }
510                }
511                if let Some(t) = throws {
512                    write!(buf, "throws {t} ")?
513                }
514                match body {
515                    Either::Right(builtin) => {
516                        writeln!(buf, "'{builtin}")
517                    }
518                    Either::Left(body) => match &body.kind {
519                        ExprKind::Do { exprs } => {
520                            pretty_print_exprs(indent, limit, buf, exprs, "{", "}", ";")
521                        }
522                        _ => body.kind.pretty_print(indent, limit, false, buf),
523                    },
524                }
525            }
526            ExprKind::Eq { lhs, rhs } => binop!("==", lhs, rhs),
527            ExprKind::Ne { lhs, rhs } => binop!("!=", lhs, rhs),
528            ExprKind::Lt { lhs, rhs } => binop!("<", lhs, rhs),
529            ExprKind::Gt { lhs, rhs } => binop!(">", lhs, rhs),
530            ExprKind::Lte { lhs, rhs } => binop!("<=", lhs, rhs),
531            ExprKind::Gte { lhs, rhs } => binop!(">=", lhs, rhs),
532            ExprKind::And { lhs, rhs } => binop!("&&", lhs, rhs),
533            ExprKind::Or { lhs, rhs } => binop!("||", lhs, rhs),
534            ExprKind::Add { lhs, rhs } => binop!("+", lhs, rhs),
535            ExprKind::Sub { lhs, rhs } => binop!("-", lhs, rhs),
536            ExprKind::Mul { lhs, rhs } => binop!("*", lhs, rhs),
537            ExprKind::Div { lhs, rhs } => binop!("/", lhs, rhs),
538            ExprKind::Mod { lhs, rhs } => binop!("%", lhs, rhs),
539            ExprKind::Sample { lhs, rhs } => binop!("~", lhs, rhs),
540            ExprKind::Not { expr } => {
541                try_single_line!(true);
542                match &expr.kind {
543                    ExprKind::Do { exprs } => {
544                        pretty_print_exprs(indent, limit, buf, exprs, "!{", "}", ";")
545                    }
546                    _ => {
547                        writeln!(buf, "!(")?;
548                        expr.kind.pretty_print(indent + 2, limit, true, buf)?;
549                        push_indent(indent, buf);
550                        writeln!(buf, ")")
551                    }
552                }
553            }
554            ExprKind::ByRef(e) => {
555                try_single_line!(true);
556                write!(buf, "&")?;
557                e.kind.pretty_print(indent + 2, limit, false, buf)
558            }
559            ExprKind::Deref(e) => match &e.kind {
560                ExprKind::Connect { .. } | ExprKind::Qop(_) => {
561                    try_single_line!(true);
562                    writeln!(buf, "*(")?;
563                    e.kind.pretty_print(indent + 2, limit, newline, buf)?;
564                    writeln!(buf, ")")
565                }
566                _ => {
567                    try_single_line!(true);
568                    write!(buf, "*")?;
569                    e.kind.pretty_print(indent + 2, limit, false, buf)
570                }
571            },
572            ExprKind::Select { arg, arms } => {
573                try_single_line!(true);
574                write!(buf, "select ")?;
575                arg.kind.pretty_print(indent, limit, false, buf)?;
576                kill_newline!(buf);
577                writeln!(buf, " {{")?;
578                for (i, (pat, expr)) in arms.iter().enumerate() {
579                    if let Some(tp) = &pat.type_predicate {
580                        write!(buf, "{tp} as ")?;
581                    }
582                    write!(buf, "{} ", pat.structure_predicate)?;
583                    if let Some(guard) = &pat.guard {
584                        write!(buf, "if ")?;
585                        guard.kind.pretty_print(indent + 2, limit, false, buf)?;
586                        kill_newline!(buf);
587                        write!(buf, " ")?;
588                    }
589                    write!(buf, "=> ")?;
590                    if let ExprKind::Do { exprs } = &expr.kind {
591                        let term = if i < arms.len() - 1 { "}," } else { "}" };
592                        pretty_print_exprs(
593                            indent + 2,
594                            limit,
595                            buf,
596                            exprs,
597                            "{",
598                            term,
599                            ";",
600                        )?;
601                    } else if i < arms.len() - 1 {
602                        expr.kind.pretty_print(indent + 2, limit, false, buf)?;
603                        kill_newline!(buf);
604                        writeln!(buf, ",")?
605                    } else {
606                        expr.kind.pretty_print(indent, limit, false, buf)?;
607                    }
608                }
609                push_indent(indent, buf);
610                writeln!(buf, "}}")
611            }
612        }
613    }
614}
615
616impl fmt::Display for ExprKind {
617    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
618        fn write_binop(
619            f: &mut fmt::Formatter,
620            op: &str,
621            lhs: &Expr,
622            rhs: &Expr,
623        ) -> fmt::Result {
624            write!(f, "(")?;
625            write!(f, "{lhs} {op} {rhs}")?;
626            write!(f, ")")
627        }
628        fn print_exprs(
629            f: &mut fmt::Formatter,
630            exprs: &[Expr],
631            open: &str,
632            close: &str,
633            sep: &str,
634        ) -> fmt::Result {
635            write!(f, "{open}")?;
636            for i in 0..exprs.len() {
637                write!(f, "{}", &exprs[i])?;
638                if i < exprs.len() - 1 {
639                    write!(f, "{sep}")?
640                }
641            }
642            write!(f, "{close}")
643        }
644        let mut tbuf = CompactString::new("");
645        macro_rules! typ {
646            ($typ:expr) => {{
647                match $typ {
648                    None => "",
649                    Some(typ) => {
650                        tbuf.clear();
651                        write!(tbuf, ": {typ}")?;
652                        tbuf.as_str()
653                    }
654                }
655            }};
656        }
657        let exp = |export| if export { "pub " } else { "" };
658        match self {
659            ExprKind::Constant(v @ Value::String(_)) => {
660                v.fmt_ext(f, &parser::GRAPHIX_ESC, true)
661            }
662            ExprKind::Constant(v) => v.fmt_ext(f, &VAL_ESC, true),
663            ExprKind::Bind(b) => {
664                let Bind { rec, doc, pattern, typ, export, value } = &**b;
665                if let Some(doc) = doc {
666                    if doc == "" {
667                        writeln!(f, "///")?
668                    } else {
669                        for line in doc.lines() {
670                            writeln!(f, "///{line}")?
671                        }
672                    }
673                }
674                let rec = if *rec { " rec" } else { "" };
675                write!(f, "{}let{} {pattern}{} = {value}", exp(*export), rec, typ!(typ))
676            }
677            ExprKind::StructWith { source, replace } => {
678                match &source.kind {
679                    ExprKind::Ref { .. } => write!(f, "{{ {source} with ")?,
680                    _ => write!(f, "{{ ({source}) with ")?,
681                }
682                for (i, (name, e)) in replace.iter().enumerate() {
683                    match &e.kind {
684                        ExprKind::Ref { name: n }
685                            if Path::dirname(&**n).is_none()
686                                && Path::basename(&**n) == Some(&**name) =>
687                        {
688                            write!(f, "{name}")?
689                        }
690                        _ => write!(f, "{name}: {e}")?,
691                    }
692                    if i < replace.len() - 1 {
693                        write!(f, ", ")?
694                    }
695                }
696                write!(f, " }}")
697            }
698            ExprKind::Connect { name, value, deref } => {
699                let deref = if *deref { "*" } else { "" };
700                write!(f, "{deref}{name} <- {value}")
701            }
702            ExprKind::Use { name } => {
703                write!(f, "use {name}")
704            }
705            ExprKind::Ref { name } => {
706                write!(f, "{name}")
707            }
708            ExprKind::StructRef { source, field } => match &source.kind {
709                ExprKind::Ref { .. } => {
710                    write!(f, "{source}.{field}")
711                }
712                source => write!(f, "({source}).{field}"),
713            },
714            ExprKind::TupleRef { source, field } => match &source.kind {
715                ExprKind::Ref { .. } => {
716                    write!(f, "{source}.{field}")
717                }
718                source => write!(f, "({source}).{field}"),
719            },
720            ExprKind::Module { name, export, value } => {
721                write!(f, "{}mod {name}", exp(*export))?;
722                match value {
723                    ModuleKind::Resolved(_) | ModuleKind::Unresolved => Ok(()),
724                    ModuleKind::Inline(exprs) => print_exprs(f, &**exprs, "{", "}", "; "),
725                    ModuleKind::Dynamic { sandbox, sig, source } => {
726                        write!(f, " dynamic {{ {sandbox};")?;
727                        write!(f, " {sig};")?;
728                        write!(f, " source {source} }}")
729                    }
730                }
731            }
732            ExprKind::TypeCast { expr, typ } => write!(f, "cast<{typ}>({expr})"),
733            ExprKind::TypeDef(td) => write!(f, "{td}"),
734            ExprKind::Do { exprs } => print_exprs(f, &**exprs, "{", "}", "; "),
735            ExprKind::Lambda(l) => {
736                let Lambda { args, vargs, rtype, constraints, throws, body } = &**l;
737                for (i, (tvar, typ)) in constraints.iter().enumerate() {
738                    write!(f, "{tvar}: {typ}")?;
739                    if i < constraints.len() - 1 {
740                        write!(f, ", ")?;
741                    }
742                }
743                write!(f, "|")?;
744                for (i, a) in args.iter().enumerate() {
745                    match &a.labeled {
746                        None => {
747                            write!(f, "{}", a.pattern)?;
748                            write!(f, "{}", typ!(&a.constraint))?;
749                        }
750                        Some(def) => {
751                            write!(f, "#{}", a.pattern)?;
752                            write!(f, "{}", typ!(&a.constraint))?;
753                            if let Some(def) = def {
754                                write!(f, " = {def}")?;
755                            }
756                        }
757                    }
758                    if vargs.is_some() || i < args.len() - 1 {
759                        write!(f, ", ")?
760                    }
761                }
762                if let Some(typ) = vargs {
763                    write!(f, "@args{}", typ!(typ))?;
764                }
765                write!(f, "| ")?;
766                if let Some(t) = rtype {
767                    match t {
768                        Type::Fn(ft) => write!(f, "-> ({ft}) ")?,
769                        Type::ByRef(t) => match &**t {
770                            Type::Fn(ft) => write!(f, "-> &({ft}) ")?,
771                            t => write!(f, "-> &{t} ")?,
772                        },
773                        t => write!(f, "-> {t} ")?,
774                    }
775                }
776                if let Some(t) = throws {
777                    write!(f, "throws {t} ")?
778                }
779                match body {
780                    Either::Right(builtin) => write!(f, "'{builtin}"),
781                    Either::Left(body) => write!(f, "{body}"),
782                }
783            }
784            ExprKind::Array { args } => print_exprs(f, args, "[", "]", ", "),
785            ExprKind::Map { args } => {
786                write!(f, "{{")?;
787                for (i, (k, v)) in args.iter().enumerate() {
788                    write!(f, "{k} => {v}")?;
789                    if i < args.len() - 1 {
790                        write!(f, ", ")?
791                    }
792                }
793                write!(f, "}}")
794            }
795            ExprKind::MapRef { source, key } => match &source.kind {
796                ExprKind::Ref { name } => write!(f, "{name}{{{key}}}"),
797                _ => write!(f, "({source}){{{key}}}"),
798            },
799            ExprKind::Any { args } => {
800                write!(f, "any")?;
801                print_exprs(f, args, "(", ")", ", ")
802            }
803            ExprKind::Tuple { args } => print_exprs(f, args, "(", ")", ", "),
804            ExprKind::Variant { tag, args } if args.len() == 0 => {
805                write!(f, "`{tag}")
806            }
807            ExprKind::Variant { tag, args } => {
808                write!(f, "`{tag}")?;
809                print_exprs(f, args, "(", ")", ", ")
810            }
811            ExprKind::Struct { args } => {
812                write!(f, "{{ ")?;
813                for (i, (n, e)) in args.iter().enumerate() {
814                    match &e.kind {
815                        ExprKind::Ref { name }
816                            if Path::dirname(&**name).is_none()
817                                && Path::basename(&**name) == Some(&**n) =>
818                        {
819                            write!(f, "{n}")?
820                        }
821                        _ => write!(f, "{n}: {e}")?,
822                    }
823                    if i < args.len() - 1 {
824                        write!(f, ", ")?
825                    }
826                }
827                write!(f, " }}")
828            }
829            ExprKind::Qop(e) => write!(f, "{}?", e),
830            ExprKind::OrNever(e) => write!(f, "{}$", e),
831            ExprKind::TryCatch(tc) => {
832                write!(f, "try ")?;
833                print_exprs(f, &tc.exprs, "", "", "; ")?;
834                match &tc.constraint {
835                    None => write!(f, " catch({}) => {}", tc.bind, tc.handler),
836                    Some(t) => write!(f, " catch({}: {t}) => {}", tc.bind, tc.handler),
837                }
838            }
839            ExprKind::StringInterpolate { args } => {
840                write!(f, "\"")?;
841                for s in args.iter() {
842                    match &s.kind {
843                        ExprKind::Constant(Value::String(s)) if s.len() > 0 => {
844                            let es = parser::GRAPHIX_ESC.escape(&*s);
845                            write!(f, "{es}",)?;
846                        }
847                        s => {
848                            write!(f, "[{s}]")?;
849                        }
850                    }
851                }
852                write!(f, "\"")
853            }
854            ExprKind::ArrayRef { source, i } => match &source.kind {
855                ExprKind::Ref { .. } => {
856                    write!(f, "{}[{}]", source, i)
857                }
858                _ => write!(f, "({})[{}]", &source, &i),
859            },
860            ExprKind::ArraySlice { source, start, end } => {
861                let s = match start.as_ref() {
862                    None => "",
863                    Some(e) => &format_compact!("{e}"),
864                };
865                let e = match &end.as_ref() {
866                    None => "",
867                    Some(e) => &format_compact!("{e}"),
868                };
869                match &source.kind {
870                    ExprKind::Ref { .. } => {
871                        write!(f, "{}[{}..{}]", source, s, e)
872                    }
873                    _ => write!(f, "({})[{}..{}]", source, s, e),
874                }
875            }
876            ExprKind::Apply { args, function } => {
877                match &function.kind {
878                    ExprKind::Ref { name: _ } => write!(f, "{function}")?,
879                    function => write!(f, "({function})")?,
880                }
881                write!(f, "(")?;
882                for i in 0..args.len() {
883                    match &args[i].0 {
884                        None => write!(f, "{}", &args[i].1)?,
885                        Some(name) => match &args[i].1.kind {
886                            ExprKind::Ref { name: n }
887                                if Path::dirname(&n.0).is_none()
888                                    && Path::basename(&n.0) == Some(name.as_str()) =>
889                            {
890                                write!(f, "#{name}")?
891                            }
892                            _ => write!(f, "#{name}: {}", &args[i].1)?,
893                        },
894                    }
895                    if i < args.len() - 1 {
896                        write!(f, ", ")?
897                    }
898                }
899                write!(f, ")")
900            }
901            ExprKind::Select { arg, arms } => {
902                write!(f, "select {arg} {{")?;
903                for (i, (pat, rhs)) in arms.iter().enumerate() {
904                    if let Some(tp) = &pat.type_predicate {
905                        write!(f, "{tp} as ")?;
906                    }
907                    write!(f, "{} ", pat.structure_predicate)?;
908                    if let Some(guard) = &pat.guard {
909                        write!(f, "if {guard} ")?;
910                    }
911                    write!(f, "=> {rhs}")?;
912                    if i < arms.len() - 1 {
913                        write!(f, ", ")?
914                    }
915                }
916                write!(f, "}}")
917            }
918            ExprKind::Eq { lhs, rhs } => write_binop(f, "==", lhs, rhs),
919            ExprKind::Ne { lhs, rhs } => write_binop(f, "!=", lhs, rhs),
920            ExprKind::Gt { lhs, rhs } => write_binop(f, ">", lhs, rhs),
921            ExprKind::Lt { lhs, rhs } => write_binop(f, "<", lhs, rhs),
922            ExprKind::Gte { lhs, rhs } => write_binop(f, ">=", lhs, rhs),
923            ExprKind::Lte { lhs, rhs } => write_binop(f, "<=", lhs, rhs),
924            ExprKind::And { lhs, rhs } => write_binop(f, "&&", lhs, rhs),
925            ExprKind::Or { lhs, rhs } => write_binop(f, "||", lhs, rhs),
926            ExprKind::Add { lhs, rhs } => write_binop(f, "+", lhs, rhs),
927            ExprKind::Sub { lhs, rhs } => write_binop(f, "-", lhs, rhs),
928            ExprKind::Mul { lhs, rhs } => write_binop(f, "*", lhs, rhs),
929            ExprKind::Div { lhs, rhs } => write_binop(f, "/", lhs, rhs),
930            ExprKind::Mod { lhs, rhs } => write_binop(f, "%", lhs, rhs),
931            ExprKind::Sample { lhs, rhs } => write_binop(f, "~", lhs, rhs),
932            ExprKind::ByRef(e) => write!(f, "&{e}"),
933            ExprKind::Deref(e) => match &e.kind {
934                ExprKind::Qop(e) => write!(f, "*({e}?)"),
935                ExprKind::Connect { .. } => write!(f, "*({e})"),
936                _ => write!(f, "*{e}"),
937            },
938            ExprKind::Not { expr } => {
939                write!(f, "(!{expr})")
940            }
941        }
942    }
943}