graphix_compiler/expr/
print.rs

1use super::{parser, Bind, Expr, ExprKind, Lambda, ModuleKind};
2use compact_str::{format_compact, CompactString};
3use netidx::{
4    path::Path,
5    utils::{self, Either},
6};
7use netidx_value::Value;
8use std::fmt::{self, Write};
9
10impl ExprKind {
11    pub fn to_string_pretty(&self, col_limit: usize) -> String {
12        let mut buf = String::new();
13        self.pretty_print(0, col_limit, true, &mut buf).unwrap();
14        buf
15    }
16
17    fn pretty_print(
18        &self,
19        indent: usize,
20        limit: usize,
21        newline: bool,
22        buf: &mut String,
23    ) -> fmt::Result {
24        macro_rules! kill_newline {
25            ($buf:expr) => {
26                if let Some('\n') = $buf.chars().next_back() {
27                    $buf.pop();
28                }
29            };
30        }
31        macro_rules! try_single_line {
32            ($trunc:ident) => {{
33                let len = buf.len();
34                let (start, indent) = if newline {
35                    push_indent(indent, buf);
36                    (len, indent)
37                } else {
38                    (buf.rfind('\n').unwrap_or(0), 0)
39                };
40                writeln!(buf, "{}", self)?;
41                if buf.len() - start <= limit {
42                    return Ok(());
43                } else {
44                    if $trunc {
45                        buf.truncate(len + indent)
46                    }
47                    len + indent
48                }
49            }};
50        }
51        macro_rules! binop {
52            ($sep:literal, $lhs:expr, $rhs:expr) => {{
53                try_single_line!(true);
54                write!(buf, "(")?;
55                writeln!(buf, "{} {}", $lhs, $sep)?;
56                $rhs.kind.pretty_print(indent, limit, true, buf)?;
57                write!(buf, ")")
58            }};
59        }
60        let mut tbuf = CompactString::new("");
61        macro_rules! typ {
62            ($typ:expr) => {{
63                match $typ {
64                    None => "",
65                    Some(typ) => {
66                        tbuf.clear();
67                        write!(tbuf, ": {typ}")?;
68                        tbuf.as_str()
69                    }
70                }
71            }};
72        }
73        fn push_indent(indent: usize, buf: &mut String) {
74            buf.extend((0..indent).into_iter().map(|_| ' '));
75        }
76        fn pretty_print_exprs_int<'a, A, F: Fn(&'a A) -> &'a Expr>(
77            indent: usize,
78            limit: usize,
79            buf: &mut String,
80            exprs: &'a [A],
81            open: &str,
82            close: &str,
83            sep: &str,
84            f: F,
85        ) -> fmt::Result {
86            writeln!(buf, "{}", open)?;
87            for i in 0..exprs.len() {
88                f(&exprs[i]).kind.pretty_print(indent + 2, limit, true, buf)?;
89                if i < exprs.len() - 1 {
90                    kill_newline!(buf);
91                    writeln!(buf, "{}", sep)?
92                }
93            }
94            push_indent(indent, buf);
95            writeln!(buf, "{}", close)
96        }
97        fn pretty_print_exprs(
98            indent: usize,
99            limit: usize,
100            buf: &mut String,
101            exprs: &[Expr],
102            open: &str,
103            close: &str,
104            sep: &str,
105        ) -> fmt::Result {
106            pretty_print_exprs_int(indent, limit, buf, exprs, open, close, sep, |a| a)
107        }
108        let exp = |export| if export { "pub " } else { "" };
109        match self {
110            ExprKind::Constant(_)
111            | ExprKind::Use { .. }
112            | ExprKind::Ref { .. }
113            | ExprKind::StructRef { .. }
114            | ExprKind::TupleRef { .. }
115            | ExprKind::TypeDef { .. }
116            | ExprKind::ArrayRef { .. }
117            | ExprKind::ArraySlice { .. }
118            | ExprKind::StringInterpolate { .. }
119            | ExprKind::Module {
120                name: _,
121                export: _,
122                value: ModuleKind::Unresolved | ModuleKind::Resolved(_),
123            } => {
124                if newline {
125                    push_indent(indent, buf);
126                }
127                writeln!(buf, "{self}")
128            }
129            ExprKind::Bind(b) => {
130                let Bind { doc, pattern, typ, export, value } = &**b;
131                try_single_line!(true);
132                if let Some(doc) = doc {
133                    if doc == "" {
134                        writeln!(buf, "///")?;
135                    } else {
136                        for line in doc.lines() {
137                            writeln!(buf, "///{line}")?;
138                        }
139                    }
140                }
141                writeln!(buf, "{}let {pattern}{} = ", exp(*export), typ!(typ))?;
142                value.kind.pretty_print(indent + 2, limit, false, buf)
143            }
144            ExprKind::StructWith { source, replace } => {
145                try_single_line!(true);
146                match &source.kind {
147                    ExprKind::Ref { .. } => writeln!(buf, "{{ {source} with")?,
148                    _ => writeln!(buf, "{{ ({source}) with")?,
149                }
150                let indent = indent + 2;
151                for (i, (name, e)) in replace.iter().enumerate() {
152                    push_indent(indent, buf);
153                    match &e.kind {
154                        ExprKind::Ref { name: n }
155                            if Path::dirname(&**n).is_none()
156                                && Path::basename(&**n) == Some(&**name) =>
157                        {
158                            write!(buf, "{name}")?
159                        }
160                        e => {
161                            write!(buf, "{name}: ")?;
162                            e.pretty_print(indent + 2, limit, false, buf)?;
163                        }
164                    }
165                    if i < replace.len() - 1 {
166                        kill_newline!(buf);
167                        writeln!(buf, ",")?
168                    }
169                }
170                writeln!(buf, "}}")
171            }
172            ExprKind::Module { name, export, value: ModuleKind::Inline(exprs) } => {
173                try_single_line!(true);
174                write!(buf, "{}mod {name} ", exp(*export))?;
175                pretty_print_exprs(indent, limit, buf, exprs, "{", "}", ";")
176            }
177            ExprKind::Do { exprs } => {
178                try_single_line!(true);
179                pretty_print_exprs(indent, limit, buf, exprs, "{", "}", ";")
180            }
181            ExprKind::Connect { name, value, deref } => {
182                try_single_line!(true);
183                let deref = if *deref { "*" } else { "" };
184                writeln!(buf, "{deref}{name} <- ")?;
185                value.kind.pretty_print(indent + 2, limit, false, buf)
186            }
187            ExprKind::TypeCast { expr, typ } => {
188                try_single_line!(true);
189                writeln!(buf, "cast<{typ}>(")?;
190                expr.kind.pretty_print(indent + 2, limit, true, buf)?;
191                writeln!(buf, ")")
192            }
193            ExprKind::Array { args } => {
194                try_single_line!(true);
195                pretty_print_exprs(indent, limit, buf, args, "[", "]", ",")
196            }
197            ExprKind::Any { args } => {
198                try_single_line!(true);
199                write!(buf, "any")?;
200                pretty_print_exprs(indent, limit, buf, args, "(", ")", ",")
201            }
202            ExprKind::Tuple { args } => {
203                try_single_line!(true);
204                pretty_print_exprs(indent, limit, buf, args, "(", ")", ",")
205            }
206            ExprKind::Variant { tag: _, args } if args.len() == 0 => {
207                if newline {
208                    push_indent(indent, buf)
209                }
210                write!(buf, "{self}")
211            }
212            ExprKind::Variant { tag, args } => {
213                try_single_line!(true);
214                write!(buf, "`{tag}")?;
215                pretty_print_exprs(indent, limit, buf, args, "(", ")", ",")
216            }
217            ExprKind::Struct { args } => {
218                try_single_line!(true);
219                writeln!(buf, "{{")?;
220                for (i, (n, e)) in args.iter().enumerate() {
221                    push_indent(indent + 2, buf);
222                    match &e.kind {
223                        ExprKind::Ref { name }
224                            if Path::dirname(&**name).is_none()
225                                && Path::basename(&**name) == Some(&**n) =>
226                        {
227                            write!(buf, "{n}")?
228                        }
229                        _ => {
230                            write!(buf, "{n}: ")?;
231                            e.kind.pretty_print(indent + 2, limit, false, buf)?;
232                        }
233                    }
234                    if i < args.len() - 1 {
235                        kill_newline!(buf);
236                        writeln!(buf, ", ")?
237                    }
238                }
239                push_indent(indent, buf);
240                writeln!(buf, "}}")
241            }
242            ExprKind::Qop(e) => {
243                try_single_line!(true);
244                e.kind.pretty_print(indent, limit, true, buf)?;
245                kill_newline!(buf);
246                writeln!(buf, "?")
247            }
248            ExprKind::Apply { function, args } => {
249                try_single_line!(true);
250                match &function.kind {
251                    ExprKind::Ref { .. } => {
252                        function.kind.pretty_print(indent, limit, true, buf)?
253                    }
254                    e => {
255                        write!(buf, "(")?;
256                        e.pretty_print(indent, limit, true, buf)?;
257                        kill_newline!(buf);
258                        write!(buf, ")")?;
259                    }
260                }
261                kill_newline!(buf);
262                writeln!(buf, "(")?;
263                for i in 0..args.len() {
264                    match &args[i].0 {
265                        None => {
266                            args[i].1.kind.pretty_print(indent + 2, limit, true, buf)?
267                        }
268                        Some(name) => match &args[i].1.kind {
269                            ExprKind::Ref { name: n }
270                                if Path::dirname(&n.0).is_none()
271                                    && Path::basename(&n.0) == Some(name.as_str()) =>
272                            {
273                                writeln!(buf, "#{name}")?
274                            }
275                            _ => {
276                                write!(buf, "#{name}: ")?;
277                                args[i].1.kind.pretty_print(
278                                    indent + 2,
279                                    limit,
280                                    false,
281                                    buf,
282                                )?
283                            }
284                        },
285                    }
286                    if i < args.len() - 1 {
287                        kill_newline!(buf);
288                        writeln!(buf, ",")?
289                    }
290                }
291                writeln!(buf, ")")
292            }
293            ExprKind::Lambda(l) => {
294                let Lambda { args, vargs, rtype, constraints, body } = &**l;
295                try_single_line!(true);
296                for (i, (tvar, typ)) in constraints.iter().enumerate() {
297                    write!(buf, "{tvar}: {typ}")?;
298                    if i < constraints.len() - 1 {
299                        write!(buf, ", ")?;
300                    }
301                }
302                write!(buf, "|")?;
303                for (i, a) in args.iter().enumerate() {
304                    match &a.labeled {
305                        None => {
306                            write!(buf, "{}", a.pattern)?;
307                            buf.push_str(typ!(&a.constraint));
308                        }
309                        Some(def) => {
310                            write!(buf, "#{}", a.pattern)?;
311                            buf.push_str(typ!(&a.constraint));
312                            if let Some(def) = def {
313                                write!(buf, " = {def}")?;
314                            }
315                        }
316                    }
317                    if vargs.is_some() || i < args.len() - 1 {
318                        write!(buf, ", ")?
319                    }
320                }
321                if let Some(typ) = vargs {
322                    write!(buf, "@args{}", typ!(typ))?;
323                }
324                write!(buf, "| ")?;
325                if let Some(t) = rtype {
326                    write!(buf, "-> {t} ")?
327                }
328                match body {
329                    Either::Right(builtin) => {
330                        writeln!(buf, "'{builtin}")
331                    }
332                    Either::Left(body) => match &body.kind {
333                        ExprKind::Do { exprs } => {
334                            pretty_print_exprs(indent, limit, buf, exprs, "{", "}", ";")
335                        }
336                        _ => body.kind.pretty_print(indent, limit, false, buf),
337                    },
338                }
339            }
340            ExprKind::Eq { lhs, rhs } => binop!("==", lhs, rhs),
341            ExprKind::Ne { lhs, rhs } => binop!("!=", lhs, rhs),
342            ExprKind::Lt { lhs, rhs } => binop!("<", lhs, rhs),
343            ExprKind::Gt { lhs, rhs } => binop!(">", lhs, rhs),
344            ExprKind::Lte { lhs, rhs } => binop!("<=", lhs, rhs),
345            ExprKind::Gte { lhs, rhs } => binop!(">=", lhs, rhs),
346            ExprKind::And { lhs, rhs } => binop!("&&", lhs, rhs),
347            ExprKind::Or { lhs, rhs } => binop!("||", lhs, rhs),
348            ExprKind::Add { lhs, rhs } => binop!("+", lhs, rhs),
349            ExprKind::Sub { lhs, rhs } => binop!("-", lhs, rhs),
350            ExprKind::Mul { lhs, rhs } => binop!("*", lhs, rhs),
351            ExprKind::Div { lhs, rhs } => binop!("/", lhs, rhs),
352            ExprKind::Mod { lhs, rhs } => binop!("%", lhs, rhs),
353            ExprKind::Sample { lhs, rhs } => binop!("~", lhs, rhs),
354            ExprKind::Not { expr } => {
355                try_single_line!(true);
356                match &expr.kind {
357                    ExprKind::Do { exprs } => {
358                        pretty_print_exprs(indent, limit, buf, exprs, "!{", "}", ";")
359                    }
360                    _ => {
361                        writeln!(buf, "!(")?;
362                        expr.kind.pretty_print(indent + 2, limit, true, buf)?;
363                        push_indent(indent, buf);
364                        writeln!(buf, ")")
365                    }
366                }
367            }
368            ExprKind::ByRef(e) => {
369                try_single_line!(true);
370                write!(buf, "&")?;
371                e.kind.pretty_print(indent + 2, limit, false, buf)
372            }
373            ExprKind::Deref(e) => match &e.kind {
374                ExprKind::Connect { .. } | ExprKind::Qop(_) => {
375                    try_single_line!(true);
376                    writeln!(buf, "*(")?;
377                    e.kind.pretty_print(indent + 2, limit, newline, buf)?;
378                    writeln!(buf, ")")
379                }
380                _ => {
381                    try_single_line!(true);
382                    write!(buf, "*")?;
383                    e.kind.pretty_print(indent + 2, limit, false, buf)
384                }
385            },
386            ExprKind::Select { arg, arms } => {
387                try_single_line!(true);
388                write!(buf, "select ")?;
389                arg.kind.pretty_print(indent, limit, false, buf)?;
390                kill_newline!(buf);
391                writeln!(buf, " {{")?;
392                for (i, (pat, expr)) in arms.iter().enumerate() {
393                    if let Some(tp) = &pat.type_predicate {
394                        write!(buf, "{tp} as ")?;
395                    }
396                    write!(buf, "{} ", pat.structure_predicate)?;
397                    if let Some(guard) = &pat.guard {
398                        write!(buf, "if ")?;
399                        guard.kind.pretty_print(indent + 2, limit, false, buf)?;
400                        kill_newline!(buf);
401                        write!(buf, " ")?;
402                    }
403                    write!(buf, "=> ")?;
404                    if let ExprKind::Do { exprs } = &expr.kind {
405                        let term = if i < arms.len() - 1 { "}," } else { "}" };
406                        pretty_print_exprs(
407                            indent + 2,
408                            limit,
409                            buf,
410                            exprs,
411                            "{",
412                            term,
413                            ";",
414                        )?;
415                    } else if i < arms.len() - 1 {
416                        expr.kind.pretty_print(indent + 2, limit, false, buf)?;
417                        kill_newline!(buf);
418                        writeln!(buf, ",")?
419                    } else {
420                        expr.kind.pretty_print(indent, limit, false, buf)?;
421                    }
422                }
423                push_indent(indent, buf);
424                writeln!(buf, "}}")
425            }
426        }
427    }
428}
429
430impl fmt::Display for ExprKind {
431    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
432        fn write_binop(
433            f: &mut fmt::Formatter,
434            op: &str,
435            lhs: &Expr,
436            rhs: &Expr,
437        ) -> fmt::Result {
438            write!(f, "(")?;
439            write!(f, "{lhs} {op} {rhs}")?;
440            write!(f, ")")
441        }
442        fn print_exprs(
443            f: &mut fmt::Formatter,
444            exprs: &[Expr],
445            open: &str,
446            close: &str,
447            sep: &str,
448        ) -> fmt::Result {
449            write!(f, "{open}")?;
450            for i in 0..exprs.len() {
451                write!(f, "{}", &exprs[i])?;
452                if i < exprs.len() - 1 {
453                    write!(f, "{sep}")?
454                }
455            }
456            write!(f, "{close}")
457        }
458        let mut tbuf = CompactString::new("");
459        macro_rules! typ {
460            ($typ:expr) => {{
461                match $typ {
462                    None => "",
463                    Some(typ) => {
464                        tbuf.clear();
465                        write!(tbuf, ": {typ}")?;
466                        tbuf.as_str()
467                    }
468                }
469            }};
470        }
471        let exp = |export| if export { "pub " } else { "" };
472        match self {
473            ExprKind::Constant(v) => v.fmt_ext(f, &parser::GRAPHIX_ESC, true),
474            ExprKind::Bind(b) => {
475                let Bind { doc, pattern, typ, export, value } = &**b;
476                if let Some(doc) = doc {
477                    if doc == "" {
478                        writeln!(f, "///")?
479                    } else {
480                        for line in doc.lines() {
481                            writeln!(f, "///{line}")?
482                        }
483                    }
484                }
485                write!(f, "{}let {pattern}{} = {value}", exp(*export), typ!(typ))
486            }
487            ExprKind::StructWith { source, replace } => {
488                match &source.kind {
489                    ExprKind::Ref { .. } => write!(f, "{{ {source} with ")?,
490                    _ => write!(f, "{{ ({source}) with ")?,
491                }
492                for (i, (name, e)) in replace.iter().enumerate() {
493                    match &e.kind {
494                        ExprKind::Ref { name: n }
495                            if Path::dirname(&**n).is_none()
496                                && Path::basename(&**n) == Some(&**name) =>
497                        {
498                            write!(f, "{name}")?
499                        }
500                        _ => write!(f, "{name}: {e}")?,
501                    }
502                    if i < replace.len() - 1 {
503                        write!(f, ", ")?
504                    }
505                }
506                write!(f, " }}")
507            }
508            ExprKind::Connect { name, value, deref } => {
509                let deref = if *deref { "*" } else { "" };
510                write!(f, "{deref}{name} <- {value}")
511            }
512            ExprKind::Use { name } => {
513                write!(f, "use {name}")
514            }
515            ExprKind::Ref { name } => {
516                write!(f, "{name}")
517            }
518            ExprKind::StructRef { source, field } => match &source.kind {
519                ExprKind::Ref { .. } => {
520                    write!(f, "{source}.{field}")
521                }
522                source => write!(f, "({source}).{field}"),
523            },
524            ExprKind::TupleRef { source, field } => match &source.kind {
525                ExprKind::Ref { .. } => {
526                    write!(f, "{source}.{field}")
527                }
528                source => write!(f, "({source}).{field}"),
529            },
530            ExprKind::Module { name, export, value } => {
531                write!(f, "{}mod {name}", exp(*export))?;
532                match value {
533                    ModuleKind::Resolved(_) | ModuleKind::Unresolved => Ok(()),
534                    ModuleKind::Inline(exprs) => print_exprs(f, &**exprs, "{", "}", "; "),
535                }
536            }
537            ExprKind::TypeCast { expr, typ } => write!(f, "cast<{typ}>({expr})"),
538            ExprKind::TypeDef { name, params, typ } => {
539                write!(f, "type {name}")?;
540                if !params.is_empty() {
541                    write!(f, "<")?;
542                    for (i, (tv, ct)) in params.iter().enumerate() {
543                        write!(f, "{tv}")?;
544                        if let Some(ct) = ct {
545                            write!(f, ": {ct}")?;
546                        }
547                        if i < params.len() - 1 {
548                            write!(f, ", ")?;
549                        }
550                    }
551                    write!(f, ">")?;
552                }
553                write!(f, " = {typ}")
554            }
555            ExprKind::Do { exprs } => print_exprs(f, &**exprs, "{", "}", "; "),
556            ExprKind::Lambda(l) => {
557                let Lambda { args, vargs, rtype, constraints, body } = &**l;
558                for (i, (tvar, typ)) in constraints.iter().enumerate() {
559                    write!(f, "{tvar}: {typ}")?;
560                    if i < constraints.len() - 1 {
561                        write!(f, ", ")?;
562                    }
563                }
564                write!(f, "|")?;
565                for (i, a) in args.iter().enumerate() {
566                    match &a.labeled {
567                        None => {
568                            write!(f, "{}", a.pattern)?;
569                            write!(f, "{}", typ!(&a.constraint))?;
570                        }
571                        Some(def) => {
572                            write!(f, "#{}", a.pattern)?;
573                            write!(f, "{}", typ!(&a.constraint))?;
574                            if let Some(def) = def {
575                                write!(f, " = {def}")?;
576                            }
577                        }
578                    }
579                    if vargs.is_some() || i < args.len() - 1 {
580                        write!(f, ", ")?
581                    }
582                }
583                if let Some(typ) = vargs {
584                    write!(f, "@args{}", typ!(typ))?;
585                }
586                write!(f, "| ")?;
587                if let Some(t) = rtype {
588                    write!(f, "-> {t} ")?
589                }
590                match body {
591                    Either::Right(builtin) => write!(f, "'{builtin}"),
592                    Either::Left(body) => write!(f, "{body}"),
593                }
594            }
595            ExprKind::Array { args } => print_exprs(f, args, "[", "]", ", "),
596            ExprKind::Any { args } => {
597                write!(f, "any")?;
598                print_exprs(f, args, "(", ")", ", ")
599            }
600            ExprKind::Tuple { args } => print_exprs(f, args, "(", ")", ", "),
601            ExprKind::Variant { tag, args } if args.len() == 0 => {
602                write!(f, "`{tag}")
603            }
604            ExprKind::Variant { tag, args } => {
605                write!(f, "`{tag}")?;
606                print_exprs(f, args, "(", ")", ", ")
607            }
608            ExprKind::Struct { args } => {
609                write!(f, "{{ ")?;
610                for (i, (n, e)) in args.iter().enumerate() {
611                    match &e.kind {
612                        ExprKind::Ref { name }
613                            if Path::dirname(&**name).is_none()
614                                && Path::basename(&**name) == Some(&**n) =>
615                        {
616                            write!(f, "{n}")?
617                        }
618                        _ => write!(f, "{n}: {e}")?,
619                    }
620                    if i < args.len() - 1 {
621                        write!(f, ", ")?
622                    }
623                }
624                write!(f, " }}")
625            }
626            ExprKind::Qop(e) => write!(f, "{}?", e),
627            ExprKind::StringInterpolate { args } => {
628                write!(f, "\"")?;
629                for s in args.iter() {
630                    match &s.kind {
631                        ExprKind::Constant(Value::String(s)) if s.len() > 0 => {
632                            let es = utils::escape(&*s, '\\', &parser::GRAPHIX_ESC);
633                            write!(f, "{es}",)?;
634                        }
635                        s => {
636                            write!(f, "[{s}]")?;
637                        }
638                    }
639                }
640                write!(f, "\"")
641            }
642            ExprKind::ArrayRef { source, i } => match &source.kind {
643                ExprKind::Ref { .. } => {
644                    write!(f, "{}[{}]", source, i)
645                }
646                _ => write!(f, "({})[{}]", &source, &i),
647            },
648            ExprKind::ArraySlice { source, start, end } => {
649                let s = match start.as_ref() {
650                    None => "",
651                    Some(e) => &format_compact!("{e}"),
652                };
653                let e = match &end.as_ref() {
654                    None => "",
655                    Some(e) => &format_compact!("{e}"),
656                };
657                match &source.kind {
658                    ExprKind::Ref { .. } => {
659                        write!(f, "{}[{}..{}]", source, s, e)
660                    }
661                    _ => write!(f, "({})[{}..{}]", source, s, e),
662                }
663            }
664            ExprKind::Apply { args, function } => {
665                match &function.kind {
666                    ExprKind::Ref { name: _ } => write!(f, "{function}")?,
667                    function => write!(f, "({function})")?,
668                }
669                write!(f, "(")?;
670                for i in 0..args.len() {
671                    match &args[i].0 {
672                        None => write!(f, "{}", &args[i].1)?,
673                        Some(name) => match &args[i].1.kind {
674                            ExprKind::Ref { name: n }
675                                if Path::dirname(&n.0).is_none()
676                                    && Path::basename(&n.0) == Some(name.as_str()) =>
677                            {
678                                write!(f, "#{name}")?
679                            }
680                            _ => write!(f, "#{name}: {}", &args[i].1)?,
681                        },
682                    }
683                    if i < args.len() - 1 {
684                        write!(f, ", ")?
685                    }
686                }
687                write!(f, ")")
688            }
689            ExprKind::Select { arg, arms } => {
690                write!(f, "select {arg} {{")?;
691                for (i, (pat, rhs)) in arms.iter().enumerate() {
692                    if let Some(tp) = &pat.type_predicate {
693                        write!(f, "{tp} as ")?;
694                    }
695                    write!(f, "{} ", pat.structure_predicate)?;
696                    if let Some(guard) = &pat.guard {
697                        write!(f, "if {guard} ")?;
698                    }
699                    write!(f, "=> {rhs}")?;
700                    if i < arms.len() - 1 {
701                        write!(f, ", ")?
702                    }
703                }
704                write!(f, "}}")
705            }
706            ExprKind::Eq { lhs, rhs } => write_binop(f, "==", lhs, rhs),
707            ExprKind::Ne { lhs, rhs } => write_binop(f, "!=", lhs, rhs),
708            ExprKind::Gt { lhs, rhs } => write_binop(f, ">", lhs, rhs),
709            ExprKind::Lt { lhs, rhs } => write_binop(f, "<", lhs, rhs),
710            ExprKind::Gte { lhs, rhs } => write_binop(f, ">=", lhs, rhs),
711            ExprKind::Lte { lhs, rhs } => write_binop(f, "<=", lhs, rhs),
712            ExprKind::And { lhs, rhs } => write_binop(f, "&&", lhs, rhs),
713            ExprKind::Or { lhs, rhs } => write_binop(f, "||", lhs, rhs),
714            ExprKind::Add { lhs, rhs } => write_binop(f, "+", lhs, rhs),
715            ExprKind::Sub { lhs, rhs } => write_binop(f, "-", lhs, rhs),
716            ExprKind::Mul { lhs, rhs } => write_binop(f, "*", lhs, rhs),
717            ExprKind::Div { lhs, rhs } => write_binop(f, "/", lhs, rhs),
718            ExprKind::Mod { lhs, rhs } => write_binop(f, "%", lhs, rhs),
719            ExprKind::Sample { lhs, rhs } => write_binop(f, "~", lhs, rhs),
720            ExprKind::ByRef(e) => write!(f, "&{e}"),
721            ExprKind::Deref(e) => match &e.kind {
722                ExprKind::Qop(e) => write!(f, "*({e}?)"),
723                ExprKind::Connect { .. } => write!(f, "*({e})"),
724                _ => write!(f, "*{e}"),
725            },
726            ExprKind::Not { expr } => {
727                write!(f, "(!{expr})")
728            }
729        }
730    }
731}