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}