1use crate::{
2 ast::{
3 Annotation, ArgBy, ArgName, ArgVia, AssignmentKind, AssignmentPattern, BinOp,
4 ByteArrayFormatPreference, CAPTURE_VARIABLE, CallArg, CurveType, DataType, Decorator,
5 Definition, Function, LogicalOpChainKind, ModuleConstant, Namespace, OnTestFailure,
6 Pattern, RecordConstructor, RecordConstructorArg, RecordUpdateSpread, Span, TraceKind,
7 TypeAlias, TypedArg, TypedValidator, UnOp, UnqualifiedImport, UntypedArg, UntypedArgVia,
8 UntypedAssignmentKind, UntypedClause, UntypedDefinition, UntypedFunction, UntypedIfBranch,
9 UntypedModule, UntypedPattern, UntypedRecordUpdateArg, Use, Validator,
10 },
11 docvec,
12 expr::{DEFAULT_ERROR_STR, DEFAULT_TODO_STR, FnStyle, TypedExpr, UntypedExpr},
13 parser::{
14 extra::{Comment, ModuleExtra},
15 token::Base,
16 },
17 pretty::{Document, Documentable, break_, concat, flex_break, join, line, lines, nil},
18 tipo::{self, Type},
19};
20use itertools::Itertools;
21use num_bigint::BigInt;
22use ordinal::Ordinal;
23use std::rc::Rc;
24use vec1::Vec1;
25
26pub const INDENT: isize = 2;
27pub const MAX_COLUMNS: isize = 80;
28
29pub fn pretty(writer: &mut String, module: UntypedModule, extra: ModuleExtra, src: &str) {
30 let intermediate = Intermediate {
31 comments: extra
32 .comments
33 .iter()
34 .map(|span| Comment::from((span, src)))
35 .collect(),
36 doc_comments: extra
37 .doc_comments
38 .iter()
39 .map(|span| Comment::from((span, src)))
40 .collect(),
41 empty_lines: &extra.empty_lines,
42 module_comments: extra
43 .module_comments
44 .iter()
45 .map(|span| Comment::from((span, src)))
46 .collect(),
47 };
48
49 Formatter::with_comments(&intermediate)
50 .module(&module)
51 .pretty_print(MAX_COLUMNS, writer);
52}
53
54#[derive(Debug)]
55struct Intermediate<'a> {
56 comments: Vec<Comment<'a>>,
57 doc_comments: Vec<Comment<'a>>,
58 module_comments: Vec<Comment<'a>>,
59 empty_lines: &'a [usize],
60}
61
62#[derive(Debug, Clone, Default)]
64pub struct Formatter<'a> {
65 comments: &'a [Comment<'a>],
66 doc_comments: &'a [Comment<'a>],
67 module_comments: &'a [Comment<'a>],
68 empty_lines: &'a [usize],
69}
70
71impl<'comments> Formatter<'comments> {
72 pub fn new() -> Self {
73 Default::default()
74 }
75
76 fn with_comments(extra: &'comments Intermediate<'comments>) -> Self {
77 Self {
78 comments: &extra.comments,
79 doc_comments: &extra.doc_comments,
80 module_comments: &extra.module_comments,
81 empty_lines: extra.empty_lines,
82 }
83 }
84
85 fn pop_comments(
88 &mut self,
89 limit: usize,
90 ) -> impl Iterator<Item = Option<&'comments str>> + use<'comments> {
91 let (popped, rest, empty_lines) =
92 comments_before(self.comments, self.empty_lines, limit, true);
93
94 self.comments = rest;
95
96 self.empty_lines = empty_lines;
97
98 popped
99 }
100
101 fn pop_doc_comments(&mut self, limit: usize) -> impl Iterator<Item = Option<&'comments str>> {
104 let (popped, rest, empty_lines) =
105 comments_before(self.doc_comments, self.empty_lines, limit, false);
106
107 self.doc_comments = rest;
108
109 self.empty_lines = empty_lines;
110
111 popped
112 }
113
114 fn pop_empty_lines(&mut self, limit: usize) -> bool {
117 let mut end = 0;
118
119 for (i, &position) in self.empty_lines.iter().enumerate() {
120 if position > limit {
121 break;
122 }
123 end = i + 1;
124 }
125
126 self.empty_lines = self
127 .empty_lines
128 .get(end..)
129 .expect("Pop empty lines slicing");
130
131 end != 0
132 }
133
134 pub fn definitions<'a>(&mut self, definitions: &'a [UntypedDefinition]) -> Document<'a> {
135 let mut has_imports = false;
136 let mut has_declarations = false;
137 let mut imports = Vec::new();
138 let mut declarations = Vec::with_capacity(definitions.len());
139
140 for def in definitions {
141 let start = def.location().start;
142
143 match def {
144 Definition::Use(import) => {
145 has_imports = true;
146
147 let comments = self.pop_comments(start);
148
149 let def = self.definition(def);
150
151 imports.push((import, commented(def, comments)))
152 }
153
154 _other => {
155 has_declarations = true;
156
157 let comments = self.pop_comments(start);
158
159 let declaration = self.documented_definition(def);
160
161 declarations.push(commented(declaration, comments))
162 }
163 }
164 }
165
166 let imports = join(
167 imports
168 .into_iter()
169 .sorted_by(|(import_a, _), (import_b, _)| {
170 Ord::cmp(&import_a.module, &import_b.module)
171 })
172 .map(|(_, doc)| doc),
173 line(),
174 );
175
176 let declarations = join(declarations, lines(2));
177
178 let sep = if has_imports && has_declarations {
179 lines(2)
180 } else {
181 nil()
182 };
183
184 docvec![imports, sep, declarations]
185 }
186
187 fn module<'a>(&mut self, module: &'a UntypedModule) -> Document<'a> {
188 let defs = self.definitions(&module.definitions);
189
190 let doc_comments = join(
194 self.doc_comments.iter().map(|comment| {
195 "///"
196 .to_doc()
197 .append(Document::String(comment.content.to_string()))
198 }),
199 line(),
200 );
201
202 let comments = match printed_comments(self.pop_comments(usize::MAX), false) {
203 Some(comments) => comments,
204 None => nil(),
205 };
206
207 let module_comments = if !self.module_comments.is_empty() {
208 let comments = self.module_comments.iter().map(|s| {
209 "////"
210 .to_doc()
211 .append(Document::String(s.content.to_string()))
212 });
213
214 join(comments, line()).append(line())
215 } else {
216 nil()
217 };
218
219 let non_empty = vec![module_comments, defs, doc_comments, comments]
220 .into_iter()
221 .filter(|doc| !doc.is_empty());
222
223 join(non_empty, line()).append(line())
224 }
225
226 fn definition<'a>(&mut self, definition: &'a UntypedDefinition) -> Document<'a> {
227 match definition {
228 Definition::Fn(Function {
229 name,
230 arguments: args,
231 body,
232 public,
233 return_annotation,
234 end_position,
235 ..
236 }) => self.definition_fn(
237 public,
238 name,
239 args,
240 return_annotation,
241 body,
242 *end_position,
243 false,
244 ),
245
246 Definition::Validator(Validator {
247 end_position,
248 handlers,
249 fallback,
250 params,
251 name,
252 ..
253 }) => self.definition_validator(name, params, handlers, fallback, *end_position),
254
255 Definition::Test(Function {
256 name,
257 arguments: args,
258 body,
259 end_position,
260 on_test_failure,
261 ..
262 }) => self.definition_test(name, args, body, *end_position, on_test_failure),
263
264 Definition::Benchmark(Function {
265 name,
266 arguments: args,
267 body,
268 end_position,
269 on_test_failure,
270 ..
271 }) => self.definition_benchmark(name, args, body, *end_position, on_test_failure),
272
273 Definition::TypeAlias(TypeAlias {
274 alias,
275 parameters: args,
276 annotation: resolved_type,
277 public,
278 ..
279 }) => self.type_alias(*public, alias, args, resolved_type),
280
281 Definition::DataType(DataType {
282 name,
283 parameters,
284 public,
285 constructors,
286 location,
287 opaque,
288 decorators,
289 ..
290 }) => self.data_type(
291 *public,
292 *opaque,
293 name,
294 parameters,
295 constructors,
296 decorators,
297 location,
298 ),
299
300 Definition::Use(import) => self.import(import),
301
302 Definition::ModuleConstant(ModuleConstant {
303 public,
304 name,
305 annotation,
306 value,
307 ..
308 }) => {
309 let head = pub_(*public).append("const ").append(name.as_str());
310 let head = match annotation {
311 None => head,
312 Some(t) => head.append(": ").append(self.annotation(t)),
313 };
314
315 head.append(" =")
316 .append(break_("", " "))
317 .append(self.expr(value, false))
318 .nest(INDENT)
319 .group()
320 }
321 }
322 }
323
324 fn import<'a>(
325 &mut self,
326 Use {
327 module,
328 as_name,
329 unqualified: (_, unqualified),
330 ..
331 }: &'a Use<()>,
332 ) -> Document<'a> {
333 "use "
334 .to_doc()
335 .append(Document::String(module.join("/")))
336 .append(if unqualified.is_empty() {
337 nil()
338 } else {
339 let unqualified = join(
340 unqualified
341 .iter()
342 .sorted_by(|a, b| a.name.cmp(&b.name))
343 .map(|e| e.to_doc()),
344 flex_break(",", ", "),
345 );
346
347 break_(".{", ".{")
348 .append(unqualified)
349 .nest(INDENT)
350 .append(break_(",", ""))
351 .append("}")
352 .group()
353 })
354 .append(if let Some(name) = as_name {
355 docvec![" as ", name]
356 } else {
357 nil()
358 })
359 }
360
361 pub fn docs_const_expr<'a>(&mut self, name: &'a str, value: &'a TypedExpr) -> Document<'a> {
362 let mut printer = tipo::pretty::Printer::new();
363 let doc = name
364 .to_doc()
365 .append(": ")
366 .append(printer.print(&value.tipo()));
367
368 let value = self.const_expr(value);
370 if value.is_empty() {
371 doc
372 } else {
373 doc.append(" = ").append(value)
374 }
375 }
376
377 pub fn const_expr<'a>(&mut self, value: &'a TypedExpr) -> Document<'a> {
378 match value {
379 TypedExpr::UInt { value, base, .. } => self.int(value, base),
380 TypedExpr::String { value, .. } => self.string(value),
381 TypedExpr::ByteArray {
382 bytes,
383 preferred_format,
384 ..
385 } => self.bytearray(
386 &bytes
387 .iter()
388 .map(|b| (*b, Span::empty()))
389 .collect::<Vec<(u8, Span)>>(),
390 None,
391 preferred_format,
392 ),
393 TypedExpr::CurvePoint {
394 point,
395 preferred_format,
396 ..
397 } => self.bytearray(
398 &point
399 .compress()
400 .into_iter()
401 .map(|b| (b, Span::empty()))
402 .collect::<Vec<(u8, Span)>>(),
403 Some(point.as_ref().into()),
404 preferred_format,
405 ),
406 TypedExpr::Tuple { elems, .. } => {
407 wrap_args(elems.iter().map(|e| (self.const_expr(e), false))).group()
408 }
409 TypedExpr::Pair { fst, snd, .. } => {
410 let elems = [fst, snd];
411 "Pair"
412 .to_doc()
413 .append(wrap_args(elems.iter().map(|e| (self.const_expr(e), false))).group())
414 }
415 TypedExpr::List { elements, .. } => {
416 let comma: fn() -> Document<'a> =
417 if elements.iter().all(TypedExpr::is_simple_expr_to_format) {
418 || flex_break(",", ", ")
419 } else {
420 || break_(",", ", ")
421 };
422
423 list(
424 join(elements.iter().map(|e| self.const_expr(e)), comma()),
425 elements.len(),
426 None,
427 )
428 }
429 TypedExpr::Var { name, .. } => name.to_doc(),
430 _ => Document::Str(""),
431 }
432 }
433
434 fn documented_definition<'a>(&mut self, s: &'a UntypedDefinition) -> Document<'a> {
435 let comments = self.doc_comments(s.location().start);
436 comments.append(self.definition(s).group()).group()
437 }
438
439 fn doc_comments<'a>(&mut self, limit: usize) -> Document<'a> {
440 let mut comments = self.pop_doc_comments(limit).peekable();
441 match comments.peek() {
442 None => nil(),
443 Some(_) => join(
444 comments.map(|c| match c {
445 Some(c) => "///".to_doc().append(Document::String(c.to_string())),
446 None => unreachable!("empty lines dropped by pop_doc_comments"),
447 }),
448 line(),
449 )
450 .append(line())
451 .force_break(),
452 }
453 }
454
455 fn type_annotation_constructor<'a>(
456 &mut self,
457 module: &'a Option<String>,
458 name: &'a str,
459 args: &'a [Annotation],
460 ) -> Document<'a> {
461 let head = module
462 .as_ref()
463 .map(|qualifier| qualifier.to_doc().append(".").append(name))
464 .unwrap_or_else(|| name.to_doc());
465
466 if args.is_empty() {
467 head
468 } else {
469 head.append(self.type_arguments(args))
470 }
471 }
472
473 fn annotation<'a>(&mut self, t: &'a Annotation) -> Document<'a> {
474 match t {
475 Annotation::Hole { name, .. } => name.to_doc(),
476
477 Annotation::Constructor {
478 name,
479 arguments: args,
480 module,
481 ..
482 } => self.type_annotation_constructor(module, name, args),
483
484 Annotation::Fn {
485 arguments: args,
486 ret: retrn,
487 ..
488 } => "fn"
489 .to_doc()
490 .append(wrap_args(args.iter().map(|t| {
491 let comments = self.pop_comments(t.location().start);
492
493 let doc_comments = self.doc_comments(t.location().start);
494
495 let doc = doc_comments.append(self.annotation(t)).group();
496
497 let doc = commented(doc, comments);
498
499 (doc, false)
500 })))
501 .group()
502 .append(" ->")
503 .append(break_("", " ").append(self.annotation(retrn)).nest(INDENT)),
504
505 Annotation::Var { name, .. } => name.to_doc(),
506 Annotation::Tuple { elems, .. } => {
507 wrap_args(elems.iter().map(|t| (self.annotation(t), false)))
508 }
509 Annotation::Pair { fst, snd, .. } => "Pair"
510 .to_doc()
511 .append("<")
512 .append(self.annotation(fst))
513 .append(break_(",", ", "))
514 .append(self.annotation(snd))
515 .append(">")
516 .group(),
517 }
518 .group()
519 }
520
521 pub fn type_arguments<'a>(&mut self, args: &'a [Annotation]) -> Document<'a> {
522 wrap_generics(args.iter().map(|t| self.annotation(t)))
523 }
524
525 pub fn type_alias<'a>(
526 &mut self,
527 public: bool,
528 name: &'a str,
529 args: &'a [String],
530 typ: &'a Annotation,
531 ) -> Document<'a> {
532 let head = pub_(public).append("type ").append(name);
533
534 let head = if args.is_empty() {
535 head
536 } else {
537 head.append(wrap_generics(args.iter().map(|e| e.to_doc())).group())
538 };
539
540 head.append(" =")
541 .append(line().append(self.annotation(typ)).group().nest(INDENT))
542 }
543
544 fn fn_arg<'a>(&mut self, arg: &'a UntypedArg) -> Document<'a> {
545 let comments = self.pop_comments(arg.location.start);
546
547 let doc_comments = self.doc_comments(arg.location.start);
548
549 let mut doc = match arg.by {
550 ArgBy::ByName(ref arg_name) => arg_name.to_doc(),
551 ArgBy::ByPattern(ref pattern) => self.pattern(pattern),
552 };
553
554 doc = match &arg.annotation {
555 None => doc,
556 Some(a) => doc.append(": ").append(self.annotation(a)),
557 }
558 .group();
559
560 let doc = doc_comments.append(doc.group()).group();
561
562 commented(doc, comments)
563 }
564
565 fn fn_arg_via<'a>(&mut self, arg_via: &'a ArgVia<UntypedArg, UntypedExpr>) -> Document<'a> {
566 let comments = self.pop_comments(arg_via.arg.location.start);
567
568 let doc_comments = self.doc_comments(arg_via.arg.location.start);
569
570 let mut doc = match arg_via.arg.by {
571 ArgBy::ByName(ref arg_name) => arg_name.to_doc(),
572 ArgBy::ByPattern(ref pattern) => self.pattern(pattern),
573 };
574
575 doc = match &arg_via.arg.annotation {
576 None => doc,
577 Some(a) => doc.append(": ").append(self.annotation(a)),
578 }
579 .append(" via ")
580 .append(self.expr(&arg_via.via, false))
581 .group();
582
583 let doc = doc_comments.append(doc.group()).group();
584
585 commented(doc, comments)
586 }
587
588 #[allow(clippy::too_many_arguments)]
589 fn definition_fn<'a>(
590 &mut self,
591 public: &'a bool,
592 name: &'a str,
593 args: &'a [UntypedArg],
594 return_annotation: &'a Option<Annotation>,
595 body: &'a UntypedExpr,
596 end_location: usize,
597 is_validator: bool,
598 ) -> Document<'a> {
599 let head = if !is_validator {
601 pub_(*public)
602 .append("fn ")
603 .append(name)
604 .append(wrap_args(args.iter().map(|e| (self.fn_arg(e), false))))
605 } else {
606 name.to_doc()
607 .append(wrap_args(args.iter().map(|e| (self.fn_arg(e), false))))
608 };
609
610 let head = match return_annotation {
612 Some(anno) => {
613 let is_bool = anno.is_logically_equal(&Annotation::boolean(Span::empty()));
614 if is_validator && is_bool {
615 head
616 } else {
617 head.append(" -> ").append(self.annotation(anno))
618 }
619 }
620 None => head,
621 }
622 .group();
623
624 let body = self.expr(body, true);
626
627 let body = match printed_comments(self.pop_comments(end_location), false) {
629 Some(comments) => body.append(line()).append(comments),
630 None => body,
631 };
632
633 head.append(" {")
635 .append(line().append(body).nest(INDENT).group())
636 .append(line())
637 .append("}")
638 }
639
640 #[allow(clippy::too_many_arguments)]
641 fn definition_test_or_bench<'a>(
642 &mut self,
643 keyword: &'static str,
644 name: &'a str,
645 args: &'a [UntypedArgVia],
646 body: &'a UntypedExpr,
647 end_location: usize,
648 on_test_failure: &'a OnTestFailure,
649 ) -> Document<'a> {
650 let head = keyword
652 .to_doc()
653 .append(" ")
654 .append(name)
655 .append(wrap_args(args.iter().map(|e| (self.fn_arg_via(e), false))))
656 .append(if keyword == "test" {
657 match on_test_failure {
658 OnTestFailure::FailImmediately => "",
659 OnTestFailure::SucceedEventually => " fail",
660 OnTestFailure::SucceedImmediately => " fail once",
661 }
662 } else {
663 ""
664 })
665 .group();
666
667 let body = self.expr(body, true);
669
670 let body = match printed_comments(self.pop_comments(end_location), false) {
672 Some(comments) => body.append(line()).append(comments),
673 None => body,
674 };
675
676 head.append(" {")
678 .append(line().append(body).nest(INDENT).group())
679 .append(line())
680 .append("}")
681 }
682
683 #[allow(clippy::too_many_arguments)]
684 fn definition_test<'a>(
685 &mut self,
686 name: &'a str,
687 args: &'a [UntypedArgVia],
688 body: &'a UntypedExpr,
689 end_location: usize,
690 on_test_failure: &'a OnTestFailure,
691 ) -> Document<'a> {
692 self.definition_test_or_bench("test", name, args, body, end_location, on_test_failure)
693 }
694
695 #[allow(clippy::too_many_arguments)]
696 fn definition_benchmark<'a>(
697 &mut self,
698 name: &'a str,
699 args: &'a [UntypedArgVia],
700 body: &'a UntypedExpr,
701 end_location: usize,
702 on_test_failure: &'a OnTestFailure,
703 ) -> Document<'a> {
704 self.definition_test_or_bench("bench", name, args, body, end_location, on_test_failure)
705 }
706
707 fn definition_validator<'a>(
708 &mut self,
709 name: &'a str,
710 params: &'a [UntypedArg],
711 handlers: &'a [UntypedFunction],
712 fallback: &'a UntypedFunction,
713 end_position: usize,
714 ) -> Document<'a> {
715 let v_head = "validator"
717 .to_doc()
718 .append(" ")
719 .append(name)
720 .append(if !params.is_empty() {
721 wrap_args(params.iter().map(|e| (self.fn_arg(e), false)))
722 } else {
723 nil()
724 });
725
726 let mut handler_docs = vec![];
727
728 for handler in handlers.iter() {
729 let fun_comments = self.pop_comments(handler.location.start);
730 let fun_doc_comments = self.doc_comments(handler.location.start);
731
732 let first_fn = self
733 .definition_fn(
734 &handler.public,
735 &handler.name,
736 &handler.arguments,
737 &handler.return_annotation,
738 &handler.body,
739 handler.end_position,
740 true,
741 )
742 .group();
743
744 let first_fn = commented(fun_doc_comments.append(first_fn).group(), fun_comments);
745
746 handler_docs.push(first_fn);
747 }
748
749 let is_exhaustive = handlers.len() >= TypedValidator::available_handler_names().len() - 1;
750
751 if !is_exhaustive || !fallback.is_default_fallback() {
752 let fallback_comments = self.pop_comments(fallback.location.start);
753 let fallback_doc_comments = self.doc_comments(fallback.location.start);
754
755 let fallback_fn = self
756 .definition_fn(
757 &fallback.public,
758 &fallback.name,
759 &fallback.arguments,
760 &fallback.return_annotation,
761 &fallback.body,
762 fallback.end_position,
763 true,
764 )
765 .group();
766
767 let fallback_fn = commented(
768 fallback_doc_comments.append(fallback_fn).group(),
769 fallback_comments,
770 );
771
772 handler_docs.push(fallback_fn);
773 }
774
775 let v_body = line().append(join(handler_docs, lines(2)));
776
777 let v_body = match printed_comments(self.pop_comments(end_position), false) {
778 Some(comments) => v_body.append(lines(2)).append(comments).nest(INDENT),
779 None => v_body.nest(INDENT),
780 };
781
782 v_head
783 .append(" {")
784 .append(v_body)
785 .append(line())
786 .append("}")
787 }
788
789 fn expr_fn<'a>(
790 &mut self,
791 args: &'a [UntypedArg],
792 return_annotation: Option<&'a Annotation>,
793 body: &'a UntypedExpr,
794 ) -> Document<'a> {
795 let args = wrap_args(args.iter().map(|e| (self.fn_arg(e), false))).group();
796 let body = match body {
797 UntypedExpr::Trace { .. }
798 | UntypedExpr::When { .. }
799 | UntypedExpr::LogicalOpChain { .. } => self.expr(body, true).force_break(),
800 _ => self.expr(body, true),
801 };
802
803 let header = "fn".to_doc().append(args);
804
805 let header = match return_annotation {
806 None => header,
807 Some(t) => header.append(" -> ").append(self.annotation(t)),
808 };
809
810 header
811 .append(
812 break_(" {", " { ")
813 .append(body)
814 .nest(INDENT)
815 .append(break_("", " "))
816 .append("}"),
817 )
818 .group()
819 }
820
821 fn sequence<'a>(&mut self, expressions: &'a [UntypedExpr]) -> Document<'a> {
822 let count = expressions.len();
823 let mut documents = Vec::with_capacity(count * 2);
824
825 for (i, expression) in expressions.iter().enumerate() {
826 let preceding_newline = self.pop_empty_lines(expression.start_byte_index());
827
828 if i != 0 && preceding_newline {
829 documents.push(lines(2));
830 } else if i != 0 {
831 documents.push(lines(1));
832 }
833
834 documents.push(self.expr(expression, false).group());
835 }
836
837 documents.to_doc().force_break()
838 }
839
840 fn assignment<'a>(
841 &mut self,
842 patterns: &'a Vec1<AssignmentPattern>,
843 value: &'a UntypedExpr,
844 kind: UntypedAssignmentKind,
845 ) -> Document<'a> {
846 let keyword = match kind {
847 AssignmentKind::Is => unreachable!(),
848 AssignmentKind::Let { .. } => "let",
849 AssignmentKind::Expect { .. } => "expect",
850 };
851
852 let symbol = if kind.is_backpassing() { "<-" } else { "=" };
853
854 match patterns.first() {
855 AssignmentPattern {
856 pattern:
857 UntypedPattern::Constructor {
858 name, module: None, ..
859 },
860 annotation,
861 location: _,
862 } if name == "True"
863 && annotation.is_none()
864 && kind.is_expect()
865 && patterns.len() == 1 =>
866 {
867 keyword.to_doc().append(self.case_clause_value(value))
868 }
869 _ => {
870 let patterns = patterns.into_iter().map(
871 |AssignmentPattern {
872 pattern,
873 annotation,
874 location: _,
875 }| {
876 self.pop_empty_lines(pattern.location().end);
877
878 let pattern = self.pattern(pattern);
879
880 let annotation = annotation
881 .as_ref()
882 .map(|a| ": ".to_doc().append(self.annotation(a)));
883
884 pattern.append(annotation).group()
885 },
886 );
887
888 let pattern_len = patterns.len();
889
890 let assignment = keyword
891 .to_doc()
892 .append(if pattern_len == 1 {
893 " ".to_doc()
894 } else {
895 break_("", " ")
896 })
897 .append(join(patterns, break_(",", ", ")));
898
899 let assignment = if pattern_len == 1 {
900 assignment
901 } else {
902 assignment.nest(INDENT)
903 };
904
905 assignment
906 .append(if pattern_len == 1 {
907 " ".to_doc()
908 } else {
909 break_(",", " ")
910 })
911 .append(symbol)
912 .append(self.case_clause_value(value))
913 }
914 }
915 }
916
917 pub fn bytearray<'a>(
918 &mut self,
919 bytes: &[(u8, Span)],
920 curve: Option<CurveType>,
921 preferred_format: &ByteArrayFormatPreference,
922 ) -> Document<'a> {
923 match preferred_format {
924 ByteArrayFormatPreference::HexadecimalString => "#"
925 .to_doc()
926 .append(Document::String(
927 curve.map(|c| c.to_string()).unwrap_or_default(),
928 ))
929 .append("\"")
930 .append(Document::String(hex::encode(
931 bytes.iter().map(|(b, _)| *b).collect::<Vec<u8>>(),
932 )))
933 .append("\""),
934 ByteArrayFormatPreference::ArrayOfBytes(Base::Decimal { .. }) => "#"
935 .to_doc()
936 .append(Document::String(
937 curve.map(|c| c.to_string()).unwrap_or_default(),
938 ))
939 .append(
940 break_("[", "[")
941 .append(join(
942 bytes.iter().map(|b| {
943 let doc = b.0.to_doc();
944
945 if b.1 == Span::empty() {
946 doc
947 } else {
948 commented(doc, self.pop_comments(b.1.start))
949 }
950 }),
951 break_(",", ", "),
952 ))
953 .nest(INDENT)
954 .append(break_(",", ""))
955 .append("]"),
956 )
957 .group(),
958 ByteArrayFormatPreference::ArrayOfBytes(Base::Hexadecimal) => "#"
959 .to_doc()
960 .append(Document::String(
961 curve.map(|c| c.to_string()).unwrap_or_default(),
962 ))
963 .append(
964 break_("[", "[")
965 .append(join(
966 bytes.iter().map(|b| {
967 let doc = Document::String(if b.0 < 16 {
968 format!("0x0{:x}", b.0)
969 } else {
970 format!("{:#x}", b.0)
971 });
972
973 if b.1 == Span::empty() {
974 doc
975 } else {
976 commented(doc, self.pop_comments(b.1.start))
977 }
978 }),
979 break_(",", ", "),
980 ))
981 .nest(INDENT)
982 .append(break_(",", ""))
983 .append("]"),
984 )
985 .group(),
986 ByteArrayFormatPreference::Utf8String => nil()
987 .append("\"")
988 .append(Document::String(escape(
989 core::str::from_utf8(&bytes.iter().map(|(b, _)| *b).collect::<Vec<u8>>())
990 .unwrap(),
991 )))
992 .append("\""),
993 }
994 }
995
996 pub fn int<'a>(&mut self, s: &'a str, base: &Base) -> Document<'a> {
997 match s.chars().next() {
998 Some('-') => Document::Str("-").append(self.uint(&s[1..], base)),
999 _ => self.uint(s, base),
1000 }
1001 }
1002
1003 pub fn uint<'a>(&mut self, s: &'a str, base: &Base) -> Document<'a> {
1004 match base {
1005 Base::Decimal { numeric_underscore } if *numeric_underscore => {
1006 let s = s
1007 .chars()
1008 .rev()
1009 .enumerate()
1010 .flat_map(|(i, c)| {
1011 if i != 0 && i % 3 == 0 {
1012 Some('_')
1013 } else {
1014 None
1015 }
1016 .into_iter()
1017 .chain(std::iter::once(c))
1018 })
1019 .collect::<String>()
1020 .chars()
1021 .rev()
1022 .collect::<String>();
1023
1024 Document::String(s)
1025 }
1026 Base::Decimal { .. } => s.to_doc(),
1027 Base::Hexadecimal => Document::String(format!(
1028 "0x{}",
1029 BigInt::parse_bytes(s.as_bytes(), 10)
1030 .expect("Invalid parsed hexadecimal digits ?!")
1031 .to_str_radix(16),
1032 )),
1033 }
1034 }
1035
1036 pub fn expr<'a>(&mut self, expr: &'a UntypedExpr, is_top_level: bool) -> Document<'a> {
1037 let comments = self.pop_comments(expr.start_byte_index());
1038
1039 let document = match expr {
1040 UntypedExpr::ByteArray {
1041 bytes,
1042 preferred_format,
1043 ..
1044 } => self.bytearray(bytes, None, preferred_format),
1045
1046 UntypedExpr::CurvePoint {
1047 point,
1048 preferred_format,
1049 ..
1050 } => self.bytearray(
1051 &point
1052 .compress()
1053 .into_iter()
1054 .map(|b| (b, Span::empty()))
1055 .collect::<Vec<(u8, Span)>>(),
1056 Some(point.as_ref().into()),
1057 preferred_format,
1058 ),
1059
1060 UntypedExpr::If {
1061 branches,
1062 final_else,
1063 ..
1064 } => self.if_expr(branches, final_else),
1065
1066 UntypedExpr::LogicalOpChain {
1067 kind, expressions, ..
1068 } => self.logical_op_chain(kind, expressions),
1069
1070 UntypedExpr::PipeLine {
1071 expressions,
1072 one_liner,
1073 } => self.pipeline(expressions, *one_liner),
1074
1075 UntypedExpr::UInt { value, base, .. } => self.uint(value, base),
1076
1077 UntypedExpr::String { value, .. } => self.string(value),
1078
1079 UntypedExpr::Sequence { expressions, .. } => {
1080 let sequence = self.sequence(expressions);
1081
1082 if is_top_level {
1083 sequence
1084 } else {
1085 "{".to_doc()
1086 .append(line().append(sequence).nest(INDENT).group())
1087 .append(line())
1088 .append("}")
1089 }
1090 }
1091
1092 UntypedExpr::Var { name, .. } if name.contains(CAPTURE_VARIABLE) => "_"
1093 .to_doc()
1094 .append(name.split('_').next_back().unwrap_or_default()),
1095
1096 UntypedExpr::Var { name, .. } => name.to_doc(),
1097
1098 UntypedExpr::UnOp { value, op, .. } => self.un_op(value, op),
1099
1100 UntypedExpr::Fn {
1101 fn_style: FnStyle::Capture,
1102 body,
1103 ..
1104 } => self.fn_capture(body),
1105
1106 UntypedExpr::Fn {
1107 fn_style: FnStyle::BinOp(op),
1108 ..
1109 } => op.to_doc(),
1110
1111 UntypedExpr::Fn {
1112 fn_style: FnStyle::Plain,
1113 return_annotation,
1114 arguments: args,
1115 body,
1116 ..
1117 } => self.expr_fn(args, return_annotation.as_ref(), body),
1118
1119 UntypedExpr::List { elements, tail, .. } => self.list(elements, tail.as_deref()),
1120
1121 UntypedExpr::Call {
1122 fun,
1123 arguments: args,
1124 ..
1125 } => self.call(fun, args),
1126
1127 UntypedExpr::BinOp {
1128 name, left, right, ..
1129 } => self.bin_op(name, left, right),
1130
1131 UntypedExpr::Assignment {
1132 value,
1133 patterns,
1134 kind,
1135 ..
1136 } => self.assignment(patterns, value, *kind),
1137
1138 UntypedExpr::Trace {
1139 kind,
1140 label,
1141 then,
1142 arguments,
1143 ..
1144 } => self.trace(kind, label, arguments, then),
1145
1146 UntypedExpr::When {
1147 subject, clauses, ..
1148 } => self.when(subject, clauses),
1149
1150 UntypedExpr::FieldAccess {
1151 label, container, ..
1152 } => self
1153 .expr(container, false)
1154 .append(".")
1155 .append(label.as_str()),
1156
1157 UntypedExpr::RecordUpdate {
1158 constructor,
1159 spread,
1160 arguments: args,
1161 ..
1162 } => self.record_update(constructor, spread, args),
1163
1164 UntypedExpr::Tuple { elems, .. } => {
1165 wrap_args(elems.iter().map(|e| (self.wrap_expr(e), false))).group()
1166 }
1167
1168 UntypedExpr::Pair { fst, snd, .. } => {
1169 let elems = [fst, snd];
1170 "Pair"
1171 .to_doc()
1172 .append(wrap_args(elems.iter().map(|e| (self.wrap_expr(e), false))).group())
1173 }
1174
1175 UntypedExpr::TupleIndex { index, tuple, .. } => {
1176 let suffix = Ordinal(*index + 1).suffix().to_doc();
1177
1178 let expr_doc = self.expr(tuple, false);
1179
1180 let maybe_wrapped_expr = if matches!(&**tuple, UntypedExpr::PipeLine { .. }) {
1181 wrap_args(vec![(expr_doc, false)]).group()
1182 } else {
1183 expr_doc
1184 };
1185
1186 maybe_wrapped_expr
1187 .append(".".to_doc())
1188 .append((index + 1).to_doc())
1189 .append(suffix)
1190 }
1191
1192 UntypedExpr::ErrorTerm { .. } => "fail".to_doc(),
1193
1194 UntypedExpr::TraceIfFalse { value, .. } => self.trace_if_false(value),
1195 };
1196
1197 commented(document, comments)
1198 }
1199
1200 fn string<'a>(&self, string: &'a str) -> Document<'a> {
1201 let doc = "@"
1202 .to_doc()
1203 .append(Document::String(escape(string)).surround("\"", "\""));
1204 if string.contains('\n') {
1205 doc.force_break()
1206 } else {
1207 doc
1208 }
1209 }
1210
1211 pub fn trace_if_false<'a>(&mut self, value: &'a UntypedExpr) -> Document<'a> {
1212 docvec![self.wrap_unary_op(value), "?"]
1213 }
1214
1215 pub fn trace<'a>(
1216 &mut self,
1217 kind: &'a TraceKind,
1218 label: &'a UntypedExpr,
1219 arguments: &'a [UntypedExpr],
1220 then: &'a UntypedExpr,
1221 ) -> Document<'a> {
1222 let (keyword, default_label) = match kind {
1223 TraceKind::Trace => ("trace", None),
1224 TraceKind::Error => ("fail", Some(DEFAULT_ERROR_STR.to_string())),
1225 TraceKind::Todo => ("todo", Some(DEFAULT_TODO_STR.to_string())),
1226 };
1227
1228 let mut body = match label {
1229 UntypedExpr::String { value, .. } if Some(value) == default_label.as_ref() => {
1230 keyword.to_doc()
1231 }
1232 _ => keyword
1233 .to_doc()
1234 .append(" ")
1235 .append(self.wrap_expr(label))
1236 .group(),
1237 };
1238
1239 for (ix, arg) in arguments.iter().enumerate() {
1240 body = body
1241 .append(if ix == 0 { ": " } else { ", " })
1242 .append(self.wrap_expr(arg))
1243 .group();
1244 }
1245
1246 match kind {
1247 TraceKind::Error | TraceKind::Todo => body,
1248 TraceKind::Trace => body
1249 .append(if self.pop_empty_lines(then.start_byte_index()) {
1250 lines(2)
1251 } else {
1252 line()
1253 })
1254 .append(self.expr(then, true)),
1255 }
1256 }
1257
1258 pub fn pattern_constructor<'a>(
1259 &mut self,
1260 name: &'a str,
1261 args: &'a [CallArg<UntypedPattern>],
1262 module: &'a Option<Namespace>,
1263 spread_location: Option<Span>,
1264 is_record: bool,
1265 ) -> Document<'a> {
1266 fn is_breakable(expr: &UntypedPattern) -> bool {
1267 match expr {
1268 Pattern::Tuple { .. } | Pattern::List { .. } => true,
1269 Pattern::Constructor {
1270 arguments: args, ..
1271 } => !args.is_empty(),
1272 _ => false,
1273 }
1274 }
1275
1276 let name = match module {
1277 Some(Namespace::Module(m)) | Some(Namespace::Type(None, m)) => {
1278 m.to_doc().append(".").append(name)
1279 }
1280 Some(Namespace::Type(Some(m), c)) => m
1281 .to_doc()
1282 .append(".")
1283 .append(c.as_str())
1284 .append(".")
1285 .append(name),
1286 None => name.to_doc(),
1287 };
1288
1289 if args.is_empty() && spread_location.is_some() {
1290 if is_record {
1291 name.append(" { .. }")
1292 } else {
1293 name.append("(..)")
1294 }
1295 } else if args.is_empty() {
1296 name
1297 } else if let Some(spread_location) = spread_location {
1298 let args = args
1299 .iter()
1300 .map(|a| self.pattern_call_arg(a))
1301 .collect::<Vec<_>>();
1302
1303 let wrapped_args = if is_record {
1304 self.wrap_fields_with_spread(args, spread_location)
1305 } else {
1306 self.wrap_args_with_spread(args, spread_location)
1307 };
1308
1309 name.append(wrapped_args)
1310 } else {
1311 match args {
1312 [arg] if is_breakable(&arg.value) => name
1313 .append(if is_record { "{" } else { "(" })
1314 .append(self.pattern_call_arg(arg))
1315 .append(if is_record { "}" } else { ")" })
1316 .group(),
1317
1318 _ => name
1319 .append(wrap_args(
1320 args.iter().map(|a| (self.pattern_call_arg(a), is_record)),
1321 ))
1322 .group(),
1323 }
1324 }
1325 }
1326
1327 pub fn wrap_fields_with_spread<'a, I>(&mut self, args: I, spread_location: Span) -> Document<'a>
1328 where
1329 I: IntoIterator<Item = Document<'a>>,
1330 {
1331 let mut args = args.into_iter().peekable();
1332 if args.peek().is_none() {
1333 return "()".to_doc();
1334 }
1335
1336 let comments = self.pop_comments(spread_location.start);
1337
1338 break_(" {", " { ")
1339 .append(join(args, break_(",", ", ")))
1340 .append(break_(",", ", "))
1341 .append(commented("..".to_doc(), comments))
1342 .nest(INDENT)
1343 .append(break_("", " "))
1344 .append("}")
1345 .group()
1346 }
1347
1348 pub fn wrap_args_with_spread<'a, I>(&mut self, args: I, spread_location: Span) -> Document<'a>
1349 where
1350 I: IntoIterator<Item = Document<'a>>,
1351 {
1352 let mut args = args.into_iter().peekable();
1353 if args.peek().is_none() {
1354 return "()".to_doc();
1355 }
1356
1357 let comments = self.pop_comments(spread_location.start);
1358
1359 break_("(", "(")
1360 .append(join(args, break_(",", ", ")))
1361 .append(break_(",", ", "))
1362 .append(commented("..".to_doc(), comments))
1363 .nest(INDENT)
1364 .append(break_(",", ""))
1365 .append(")")
1366 .group()
1367 }
1368
1369 fn call<'a>(&mut self, fun: &'a UntypedExpr, args: &'a [CallArg<UntypedExpr>]) -> Document<'a> {
1370 let is_constr = match fun {
1371 UntypedExpr::Var { name, .. } => name[0..1].chars().all(|c| c.is_uppercase()),
1372 UntypedExpr::FieldAccess { label, .. } => label[0..1].chars().all(|c| c.is_uppercase()),
1373 _ => false,
1374 };
1375
1376 let needs_curly = if is_constr {
1377 args.iter().all(|arg| arg.label.is_some())
1378 } else {
1379 false
1380 };
1381
1382 self.expr(fun, false)
1383 .append(wrap_args(
1384 args.iter()
1385 .map(|a| (self.call_arg(a, needs_curly), needs_curly)),
1386 ))
1387 .group()
1388 }
1389
1390 pub fn if_expr<'a>(
1391 &mut self,
1392 branches: &'a Vec1<UntypedIfBranch>,
1393 final_else: &'a UntypedExpr,
1394 ) -> Document<'a> {
1395 let if_branches = self
1396 .if_branch(Document::Str("if "), branches.first())
1397 .append(join(
1398 branches[1..].iter().map(|branch| {
1399 self.if_branch(line().append(break_("} else if", "} else if ")), branch)
1400 }),
1401 nil(),
1402 ));
1403
1404 let else_begin = line().append("} else {");
1405
1406 let else_body = line().append(self.expr(final_else, true)).nest(INDENT);
1407
1408 let else_end = line().append("}");
1409
1410 if_branches
1411 .append(else_begin)
1412 .append(else_body)
1413 .append(else_end)
1414 .force_break()
1415 }
1416
1417 pub fn if_branch<'a>(
1418 &mut self,
1419 if_keyword: Document<'a>,
1420 branch: &'a UntypedIfBranch,
1421 ) -> Document<'a> {
1422 let if_begin = if_keyword
1423 .append(self.wrap_expr(&branch.condition))
1424 .append(match &branch.is {
1425 Some(AssignmentPattern {
1426 pattern,
1427 annotation,
1428 ..
1429 }) => {
1430 let is_sugar = matches!(
1431 (&pattern, &branch.condition),
1432 (
1433 Pattern::Var { name, .. },
1434 UntypedExpr::Var { name: var_name, .. }
1435 ) if name == var_name
1436 );
1437
1438 let Some(annotation) = &annotation else {
1439 unreachable!()
1440 };
1441
1442 let is = if is_sugar {
1443 self.annotation(annotation)
1444 } else {
1445 self.pattern(pattern)
1446 .append(": ")
1447 .append(self.annotation(annotation))
1448 .group()
1449 };
1450
1451 break_("", " ").append("is ").append(is)
1452 }
1453 None => nil(),
1454 })
1455 .append(Document::Str(" {"))
1456 .group();
1457
1458 let if_body = line().append(self.expr(&branch.body, true)).nest(INDENT);
1459
1460 if_begin.append(if_body)
1461 }
1462
1463 pub fn when<'a>(
1464 &mut self,
1465 subject: &'a UntypedExpr,
1466 clauses: &'a [UntypedClause],
1467 ) -> Document<'a> {
1468 let subjects_doc = break_("when", "when ")
1469 .append(self.wrap_expr(subject))
1470 .nest(INDENT)
1471 .append(break_("", " "))
1472 .append("is {")
1473 .group();
1474
1475 let clauses_doc = concat(
1476 clauses
1477 .iter()
1478 .enumerate()
1479 .map(|(i, c)| self.clause(c, i as u32)),
1480 );
1481
1482 subjects_doc
1483 .append(line().append(clauses_doc).nest(INDENT))
1484 .append(line())
1485 .append("}")
1486 .force_break()
1487 }
1488
1489 pub fn record_update<'a>(
1490 &mut self,
1491 constructor: &'a UntypedExpr,
1492 spread: &'a RecordUpdateSpread,
1493 args: &'a [UntypedRecordUpdateArg],
1494 ) -> Document<'a> {
1495 use std::iter::once;
1496 let constructor_doc = self.expr(constructor, false);
1497 let spread_doc = "..".to_doc().append(self.expr(&spread.base, false));
1498 let arg_docs = args.iter().map(|a| (self.record_update_arg(a), true));
1499 let all_arg_docs = once((spread_doc, true)).chain(arg_docs);
1500 constructor_doc.append(wrap_args(all_arg_docs)).group()
1501 }
1502
1503 pub fn bin_op<'a>(
1504 &mut self,
1505 name: &'a BinOp,
1506 left: &'a UntypedExpr,
1507 right: &'a UntypedExpr,
1508 ) -> Document<'a> {
1509 let precedence = name.precedence();
1510
1511 let left_precedence = left.binop_precedence();
1512 let right_precedence = right.binop_precedence();
1513
1514 let mut left = self.expr(left, false);
1515 if left.fits(MAX_COLUMNS) {
1516 left = left.force_unbroken()
1517 }
1518
1519 let mut right = self.expr(right, false);
1520 if right.fits(MAX_COLUMNS) {
1521 right = right.force_unbroken()
1522 }
1523
1524 self.operator_side(
1525 left,
1526 precedence,
1527 if matches!(name, BinOp::Or | BinOp::And) {
1528 left_precedence.saturating_sub(1)
1529 } else {
1530 left_precedence
1531 },
1532 )
1533 .append(" ")
1534 .append(name)
1535 .append(" ")
1536 .append(self.operator_side(
1537 right,
1538 precedence,
1539 if matches!(name, BinOp::Or | BinOp::And) {
1540 right_precedence
1541 } else {
1542 right_precedence.saturating_sub(1)
1543 },
1544 ))
1545 }
1546
1547 pub fn operator_side<'a>(&mut self, doc: Document<'a>, op: u8, side: u8) -> Document<'a> {
1548 if op > side {
1549 break_("(", "( ")
1550 .append(doc)
1551 .nest(INDENT)
1552 .append(break_("", " "))
1553 .append(")")
1554 .group()
1555 } else {
1556 doc
1557 }
1558 }
1559
1560 fn logical_op_chain<'a>(
1561 &mut self,
1562 kind: &'a LogicalOpChainKind,
1563 expressions: &'a [UntypedExpr],
1564 ) -> Document<'a> {
1565 kind.to_doc()
1566 .append(" {")
1567 .append(
1568 line()
1569 .append(join(
1570 expressions
1571 .iter()
1572 .map(|expression| self.expr(expression, false)),
1573 ",".to_doc().append(line()),
1574 ))
1575 .nest(INDENT)
1576 .group(),
1577 )
1578 .append(",")
1579 .append(line())
1580 .append("}")
1581 }
1582
1583 fn pipeline<'a>(
1584 &mut self,
1585 expressions: &'a Vec1<UntypedExpr>,
1586 one_liner: bool,
1587 ) -> Document<'a> {
1588 let mut docs = Vec::with_capacity(expressions.len() * 3);
1589
1590 let first = expressions.first();
1591
1592 let first_precedence = first.binop_precedence();
1593
1594 let first = self.wrap_expr(first);
1595
1596 docs.push(self.operator_side(first, 5, first_precedence));
1597
1598 for expr in expressions.iter().skip(1) {
1599 let comments = self.pop_comments(expr.location().start);
1600
1601 let doc = match expr {
1602 UntypedExpr::Fn {
1603 fn_style: FnStyle::Capture,
1604 body,
1605 ..
1606 } => self.pipe_capture_right_hand_side(body),
1607
1608 _ => self.wrap_expr(expr),
1609 };
1610
1611 let space = if one_liner { break_("", " ") } else { line() };
1612
1613 let pipe = space
1614 .append(commented("|> ".to_doc(), comments))
1615 .nest(INDENT);
1616
1617 docs.push(pipe);
1618
1619 let expr = self
1620 .operator_side(doc, 4, expr.binop_precedence())
1621 .nest(2 * INDENT);
1622
1623 docs.push(expr);
1624 }
1625
1626 if one_liner {
1627 docs.to_doc().group()
1628 } else {
1629 docs.to_doc().force_break()
1630 }
1631 }
1632
1633 fn pipe_capture_right_hand_side<'a>(&mut self, fun: &'a UntypedExpr) -> Document<'a> {
1634 let (fun, args) = match fun {
1635 UntypedExpr::Call {
1636 fun,
1637 arguments: args,
1638 ..
1639 } => (fun, args),
1640 _ => panic!("Function capture found not to have a function call body when formatting"),
1641 };
1642
1643 let hole_in_first_position = matches!(
1644 args.first(),
1645 Some(CallArg {
1646 value: UntypedExpr::Var { name, .. },
1647 ..
1648 }) if name.contains(CAPTURE_VARIABLE)
1649 );
1650
1651 if hole_in_first_position && args.len() == 1 {
1652 self.expr(fun, false)
1654 } else if hole_in_first_position {
1655 self.expr(fun, false).append(
1657 wrap_args(
1658 args.iter()
1659 .skip(1)
1660 .map(|a| (self.call_arg(a, false), false)),
1661 )
1662 .group(),
1663 )
1664 } else {
1665 self.expr(fun, false)
1667 .append(wrap_args(args.iter().map(|a| (self.call_arg(a, false), false))).group())
1668 }
1669 }
1670
1671 fn fn_capture<'a>(&mut self, call: &'a UntypedExpr) -> Document<'a> {
1672 match call {
1673 UntypedExpr::Call {
1674 fun,
1675 arguments: args,
1676 ..
1677 } => match args.as_slice() {
1678 [first, second] if is_breakable_expr(&second.value) && first.is_capture_hole() => {
1679 let discard_name = match first.value {
1680 UntypedExpr::Var { ref name, .. } => name.split("_").last().unwrap_or("_"),
1681 _ => "",
1682 };
1683 self.expr(fun, false)
1684 .append("(_")
1685 .append(discard_name)
1686 .append(", ")
1687 .append(self.call_arg(second, false))
1688 .append(")")
1689 .group()
1690 }
1691
1692 _ => self.expr(fun, false).append(
1693 wrap_args(args.iter().map(|a| (self.call_arg(a, false), false))).group(),
1694 ),
1695 },
1696
1697 _ => panic!("Function capture body found not to be a call in the formatter",),
1699 }
1700 }
1701
1702 pub fn record_constructor<'a, A>(
1703 &mut self,
1704 constructor: &'a RecordConstructor<A>,
1705 ) -> Document<'a> {
1706 let comments = self.pop_comments(constructor.location.start);
1707 let doc_comments = self.doc_comments(constructor.location.start);
1708
1709 let doc = if constructor.arguments.is_empty() {
1710 self.decorator(&constructor.decorators)
1711 .append(if constructor.decorators.is_empty() {
1712 nil()
1713 } else {
1714 line()
1715 })
1716 .append(constructor.name.as_str())
1717 } else if constructor.sugar {
1718 wrap_fields(constructor.arguments.iter().map(
1719 |RecordConstructorArg {
1720 label,
1721 annotation,
1722 location,
1723 ..
1724 }| {
1725 let arg_comments = self.pop_comments(location.start);
1726
1727 let arg = match label {
1728 Some(l) => l.to_doc().append(": ").append(self.annotation(annotation)),
1729 None => self.annotation(annotation),
1730 };
1731
1732 commented(
1733 self.doc_comments(location.start).append(arg).group(),
1734 arg_comments,
1735 )
1736 },
1737 ))
1738 .group()
1739 } else {
1740 self.decorator(&constructor.decorators)
1741 .append(if constructor.decorators.is_empty() {
1742 nil()
1743 } else {
1744 line()
1745 })
1746 .append(constructor.name.as_str())
1747 .append(wrap_args(constructor.arguments.iter().map(
1748 |RecordConstructorArg {
1749 label,
1750 annotation,
1751 location,
1752 ..
1753 }| {
1754 let arg_comments = self.pop_comments(location.start);
1755
1756 let arg = match label {
1757 Some(l) => l.to_doc().append(": ").append(self.annotation(annotation)),
1758 None => self.annotation(annotation),
1759 };
1760
1761 (
1762 commented(
1763 self.doc_comments(location.start).append(arg).group(),
1764 arg_comments,
1765 ),
1766 label.is_some(),
1767 )
1768 },
1769 )))
1770 .group()
1771 };
1772
1773 commented(doc_comments.append(doc).group(), comments)
1774 }
1775
1776 #[allow(clippy::too_many_arguments)]
1777 pub fn data_type<'a, A>(
1778 &mut self,
1779 public: bool,
1780 opaque: bool,
1781 name: &'a str,
1782 args: &'a [String],
1783 constructors: &'a [RecordConstructor<A>],
1784 decorators: &'a [Decorator],
1785 location: &'a Span,
1786 ) -> Document<'a> {
1787 self.pop_empty_lines(location.start);
1788
1789 let mut is_sugar = false;
1790
1791 self.decorator(decorators)
1792 .append(if decorators.is_empty() { nil() } else { line() })
1793 .append(pub_(public))
1794 .append(if opaque { "opaque type " } else { "type " })
1795 .append(if args.is_empty() {
1796 name.to_doc()
1797 } else {
1798 name.to_doc()
1799 .append(wrap_generics(args.iter().map(|e| e.to_doc())))
1800 .group()
1801 })
1802 .append(" {")
1803 .append(if constructors.len() == 1 && constructors[0].sugar {
1804 is_sugar = true;
1805
1806 self.record_constructor(&constructors[0])
1807 } else {
1808 concat(constructors.iter().map(|c| {
1809 if self.pop_empty_lines(c.location.start) {
1810 lines(2)
1811 } else {
1812 line()
1813 }
1814 .append(self.record_constructor(c))
1815 .nest(INDENT)
1816 .group()
1817 }))
1818 })
1819 .append(if is_sugar { nil() } else { line() })
1820 .append("}")
1821 }
1822
1823 pub fn decorator<'a>(&mut self, decorators: &'a [Decorator]) -> Document<'a> {
1824 join(
1825 decorators.iter().map(|d| match &d.kind {
1826 crate::ast::DecoratorKind::Tag { value, base } => {
1827 docvec!["@tag(", self.uint(value, base), ")"]
1828 }
1829 crate::ast::DecoratorKind::List => "@list".to_doc(),
1830 }),
1831 line(),
1832 )
1833 }
1834
1835 pub fn docs_data_type<'a, A>(
1836 &mut self,
1837 name: &'a str,
1838 args: &'a [String],
1839 constructors: &'a [RecordConstructor<A>],
1840 location: &'a Span,
1841 ) -> Document<'a> {
1842 self.pop_empty_lines(location.start);
1843
1844 let mut is_sugar = false;
1845
1846 (if args.is_empty() {
1847 name.to_doc()
1848 } else {
1849 name.to_doc()
1850 .append(wrap_generics(args.iter().map(|e| e.to_doc())))
1851 .group()
1852 })
1853 .append(" {")
1854 .append(if constructors.len() == 1 && constructors[0].sugar {
1855 is_sugar = true;
1856
1857 self.record_constructor(&constructors[0])
1858 } else {
1859 concat(constructors.iter().map(|c| {
1860 if self.pop_empty_lines(c.location.start) {
1861 lines(2)
1862 } else {
1863 line()
1864 }
1865 .append(self.record_constructor(c))
1866 .nest(INDENT)
1867 .group()
1868 }))
1869 })
1870 .append(if is_sugar { nil() } else { line() })
1871 .append("}")
1872 }
1873
1874 pub fn docs_opaque_data_type<'a>(
1875 &mut self,
1876 name: &'a str,
1877 args: &'a [String],
1878 location: &'a Span,
1879 ) -> Document<'a> {
1880 self.pop_empty_lines(location.start);
1881 if args.is_empty() {
1882 name.to_doc()
1883 } else {
1884 name.to_doc()
1885 .append(wrap_generics(args.iter().map(|e| e.to_doc())).group())
1886 }
1887 }
1888
1889 pub fn docs_type_alias<'a>(
1890 &mut self,
1891 name: &'a str,
1892 args: &'a [String],
1893 typ: &'a Annotation,
1894 ) -> Document<'a> {
1895 let head = name.to_doc();
1896
1897 let head = if args.is_empty() {
1898 head
1899 } else {
1900 head.append(wrap_generics(args.iter().map(|e| e.to_doc())).group())
1901 };
1902
1903 head.append(" = ")
1904 .append(self.annotation(typ).group().nest(INDENT))
1905 }
1906
1907 pub fn docs_record_constructor<'a, A>(
1908 &mut self,
1909 constructor: &'a RecordConstructor<A>,
1910 ) -> Document<'a> {
1911 if constructor.arguments.is_empty() {
1912 constructor.name.to_doc()
1913 } else {
1914 constructor
1915 .name
1916 .to_doc()
1917 .append(wrap_args(constructor.arguments.iter().map(|arg| {
1918 (
1919 (match &arg.label {
1920 Some(l) => l.to_doc().append(": "),
1921 None => "".to_doc(),
1922 })
1923 .append(self.annotation(&arg.annotation)),
1924 arg.label.is_some(),
1925 )
1926 })))
1927 .group()
1928 }
1929 }
1930
1931 pub fn docs_fn_signature<'a>(
1932 &mut self,
1933 name: &'a str,
1934 args: &'a [TypedArg],
1935 return_annotation: &'a Option<Annotation>,
1936 return_type: Rc<Type>,
1937 ) -> Document<'a> {
1938 let head = name.to_doc().append(self.docs_fn_args(args)).append(" -> ");
1939
1940 let tail = self.type_or_annotation(return_annotation, &return_type);
1941
1942 let doc = head.append(tail.clone()).group();
1943
1944 if doc.clone().to_pretty_string(MAX_COLUMNS).contains('\n') {
1946 let head = name
1947 .to_doc()
1948 .append(self.docs_fn_args(args).force_break())
1949 .append(" -> ");
1950 head.append(tail).group()
1951 } else {
1952 doc
1953 }
1954 }
1955
1956 pub fn docs_fn_args<'a>(&mut self, args: &'a [TypedArg]) -> Document<'a> {
1958 wrap_args(args.iter().map(|e| (self.docs_fn_arg(e), false)))
1959 }
1960
1961 fn docs_fn_arg<'a>(&mut self, arg: &'a TypedArg) -> Document<'a> {
1962 self.docs_fn_arg_name(&arg.arg_name)
1963 .append(self.type_or_annotation(&arg.annotation, &arg.tipo))
1964 .group()
1965 }
1966
1967 fn docs_fn_arg_name<'a>(&mut self, arg_name: &'a ArgName) -> Document<'a> {
1968 match arg_name {
1969 ArgName::Discarded { .. } => "".to_doc(),
1970 ArgName::Named { label, .. } => label.to_doc().append(": "),
1971 }
1972 }
1973
1974 fn type_or_annotation<'a>(
1976 &mut self,
1977 annotation: &'a Option<Annotation>,
1978 type_info: &Rc<Type>,
1979 ) -> Document<'a> {
1980 match annotation {
1981 Some(a) => self.annotation(a),
1982 None => tipo::pretty::Printer::new().print(type_info),
1983 }
1984 }
1985
1986 fn wrap_expr<'a>(&mut self, expr: &'a UntypedExpr) -> Document<'a> {
1987 match expr {
1988 UntypedExpr::Trace {
1989 kind: TraceKind::Trace,
1990 ..
1991 }
1992 | UntypedExpr::Sequence { .. }
1993 | UntypedExpr::Assignment { .. } => "{"
1994 .to_doc()
1995 .append(line().append(self.expr(expr, true)).nest(INDENT))
1996 .append(line())
1997 .append("}")
1998 .force_break(),
1999
2000 _ => self.expr(expr, false),
2001 }
2002 }
2003
2004 fn call_arg<'a>(&mut self, arg: &'a CallArg<UntypedExpr>, can_pun: bool) -> Document<'a> {
2005 match &arg.label {
2006 Some(s) => {
2007 if can_pun && matches!(&arg.value, UntypedExpr::Var { name, .. } if name == s) {
2008 nil()
2009 } else {
2010 commented(
2011 s.to_doc().append(": "),
2012 self.pop_comments(arg.location.start),
2013 )
2014 }
2015 }
2016 None => nil(),
2017 }
2018 .append(self.wrap_expr(&arg.value))
2019 }
2020
2021 fn record_update_arg<'a>(&mut self, arg: &'a UntypedRecordUpdateArg) -> Document<'a> {
2022 if matches!(&arg.value, UntypedExpr::Var { name, .. } if name == &arg.label) {
2023 nil()
2024 } else {
2025 commented(
2026 arg.label.to_doc().append(": "),
2027 self.pop_comments(arg.location.start),
2028 )
2029 }
2030 .append(self.wrap_expr(&arg.value))
2031 }
2032
2033 fn case_clause_value<'a>(&mut self, expr: &'a UntypedExpr) -> Document<'a> {
2034 match expr {
2035 UntypedExpr::Trace {
2036 kind: TraceKind::Trace,
2037 ..
2038 }
2039 | UntypedExpr::Sequence { .. }
2040 | UntypedExpr::Assignment { .. } => Document::Str(" {")
2041 .append(break_("", " ").nest(INDENT))
2042 .append(
2043 self.expr(expr, true)
2044 .nest(INDENT)
2045 .group()
2046 .append(line())
2047 .append("}")
2048 .force_break(),
2049 ),
2050
2051 UntypedExpr::Fn { .. } => line().append(self.expr(expr, false)).nest(INDENT).group(),
2052
2053 UntypedExpr::When { .. } => line().append(self.expr(expr, false)).nest(INDENT).group(),
2054
2055 _ => break_("", " ")
2056 .append(self.expr(expr, false))
2057 .nest(INDENT)
2058 .group(),
2059 }
2060 }
2061
2062 fn clause<'a>(&mut self, clause: &'a UntypedClause, index: u32) -> Document<'a> {
2063 let space_before = self.pop_empty_lines(clause.location.start);
2064 let clause_doc = join(
2065 clause.patterns.iter().map(|p| self.pattern(p)),
2066 break_(" |", " | "),
2067 )
2068 .group();
2069
2070 if index == 0 {
2071 clause_doc
2072 } else if space_before {
2073 lines(2).append(clause_doc)
2074 } else {
2075 lines(1).append(clause_doc)
2076 }
2077 .append(" ->")
2078 .append(self.case_clause_value(&clause.then))
2079 }
2080
2081 fn list<'a>(
2082 &mut self,
2083 elements: &'a [UntypedExpr],
2084 tail: Option<&'a UntypedExpr>,
2085 ) -> Document<'a> {
2086 let comma: fn() -> Document<'a> =
2087 if elements.iter().all(UntypedExpr::is_simple_expr_to_format) {
2088 || flex_break(",", ", ")
2089 } else {
2090 || break_(",", ", ")
2091 };
2092 let elements_document = join(elements.iter().map(|e| self.wrap_expr(e)), comma());
2093 let tail = tail.map(|e| self.expr(e, false));
2094 list(elements_document, elements.len(), tail)
2095 }
2096
2097 pub fn pattern<'a>(&mut self, pattern: &'a UntypedPattern) -> Document<'a> {
2098 let comments = self.pop_comments(pattern.location().start);
2099 let doc = match pattern {
2100 Pattern::Int { value, base, .. } => self.int(value, base),
2101
2102 Pattern::ByteArray {
2103 value,
2104 preferred_format,
2105 ..
2106 } => self.bytearray(value, None, preferred_format),
2107
2108 Pattern::Var { name, .. } => name.to_doc(),
2109
2110 Pattern::Assign { name, pattern, .. } => {
2111 self.pattern(pattern).append(" as ").append(name.as_str())
2112 }
2113
2114 Pattern::Discard { name, .. } => name.to_doc(),
2115
2116 Pattern::Tuple { elems, .. } => {
2117 wrap_args(elems.iter().map(|e| (self.pattern(e), false))).group()
2118 }
2119
2120 Pattern::Pair { fst, snd, .. } => "Pair"
2121 .to_doc()
2122 .append("(")
2123 .append(self.pattern(fst))
2124 .append(break_(",", ", "))
2125 .append(self.pattern(snd))
2126 .append(")")
2127 .group(),
2128
2129 Pattern::List { elements, tail, .. } => {
2130 let break_style: fn() -> Document<'a> =
2131 if elements.iter().all(Pattern::is_simple_pattern_to_format) {
2132 || flex_break(",", ", ")
2133 } else {
2134 || break_(",", ", ")
2135 };
2136
2137 let elements_document =
2138 join(elements.iter().map(|e| self.pattern(e)), break_style());
2139 let tail = tail.as_ref().map(|e| {
2140 if e.is_discard() {
2141 nil()
2142 } else {
2143 self.pattern(e)
2144 }
2145 });
2146 list(elements_document, elements.len(), tail)
2147 }
2148
2149 Pattern::Constructor {
2150 name,
2151 arguments: args,
2152 module,
2153 spread_location,
2154 is_record,
2155 ..
2156 } => self.pattern_constructor(name, args, module, *spread_location, *is_record),
2157 };
2158 commented(doc, comments)
2159 }
2160
2161 fn pattern_call_arg<'a>(&mut self, arg: &'a CallArg<UntypedPattern>) -> Document<'a> {
2162 let comments = self.pop_comments(arg.location.start);
2163
2164 if let (UntypedPattern::Var { name, .. }, Some(label)) = (&arg.value, &arg.label) {
2165 if name == label {
2166 return self.pattern(&arg.value);
2167 }
2168 }
2169
2170 let doc = arg
2171 .label
2172 .as_ref()
2173 .map(|s| s.to_doc().append(": "))
2174 .unwrap_or_else(nil)
2175 .append(self.pattern(&arg.value));
2176
2177 commented(doc, comments)
2178 }
2179
2180 fn un_op<'a>(&mut self, value: &'a UntypedExpr, op: &'a UnOp) -> Document<'a> {
2181 match op {
2182 UnOp::Not => docvec!["!", self.wrap_unary_op(value)],
2183 UnOp::Negate => docvec!["-", self.wrap_unary_op(value)],
2184 }
2185 }
2186
2187 fn wrap_unary_op<'a>(&mut self, expr: &'a UntypedExpr) -> Document<'a> {
2188 match expr {
2189 UntypedExpr::Trace {
2190 kind: TraceKind::Error,
2191 ..
2192 }
2193 | UntypedExpr::Trace {
2194 kind: TraceKind::Todo,
2195 ..
2196 }
2197 | UntypedExpr::PipeLine { .. }
2198 | UntypedExpr::BinOp { .. }
2199 | UntypedExpr::UnOp { .. } => "(".to_doc().append(self.expr(expr, false)).append(")"),
2200 _ => self.wrap_expr(expr),
2201 }
2202 }
2203}
2204
2205impl<'a> Documentable<'a> for &'a ArgName {
2206 fn to_doc(self) -> Document<'a> {
2207 match self {
2208 ArgName::Discarded { label, name, .. } | ArgName::Named { label, name, .. } => {
2209 if label == name {
2210 name.to_doc()
2211 } else {
2212 docvec![label, " ", name]
2213 }
2214 }
2215 }
2216 }
2217}
2218
2219fn pub_(public: bool) -> Document<'static> {
2220 if public { "pub ".to_doc() } else { nil() }
2221}
2222
2223impl<'a> Documentable<'a> for &'a UnqualifiedImport {
2224 fn to_doc(self) -> Document<'a> {
2225 self.name.to_doc().append(match &self.as_name {
2226 None => nil(),
2227 Some(s) => " as ".to_doc().append(s.as_str()),
2228 })
2229 }
2230}
2231
2232impl<'a> Documentable<'a> for &'a LogicalOpChainKind {
2233 fn to_doc(self) -> Document<'a> {
2234 match self {
2235 LogicalOpChainKind::And => "and",
2236 LogicalOpChainKind::Or => "or",
2237 }
2238 .to_doc()
2239 }
2240}
2241
2242impl<'a> Documentable<'a> for &'a BinOp {
2243 fn to_doc(self) -> Document<'a> {
2244 match self {
2245 BinOp::And => "&&",
2246 BinOp::Or => "||",
2247 BinOp::LtInt => "<",
2248 BinOp::LtEqInt => "<=",
2249 BinOp::Eq => "==",
2250 BinOp::NotEq => "!=",
2251 BinOp::GtEqInt => ">=",
2252 BinOp::GtInt => ">",
2253 BinOp::AddInt => "+",
2254 BinOp::SubInt => "-",
2255 BinOp::MultInt => "*",
2256 BinOp::DivInt => "/",
2257 BinOp::ModInt => "%",
2258 }
2259 .to_doc()
2260 }
2261}
2262
2263pub fn wrap_args<'a, I>(args: I) -> Document<'a>
2264where
2265 I: IntoIterator<Item = (Document<'a>, bool)>,
2266{
2267 let mut args = args.into_iter().peekable();
2268
2269 let curly = if let Some((_, uses_curly)) = args.peek() {
2270 *uses_curly
2271 } else {
2272 return "()".to_doc();
2273 };
2274
2275 let args = args.map(|a| a.0);
2276
2277 let (open_broken, open_unbroken, close) = if curly {
2278 (" {", " { ", "}")
2279 } else {
2280 ("(", "(", ")")
2281 };
2282
2283 break_(open_broken, open_unbroken)
2284 .append(join(args, break_(",", ", ")))
2285 .nest(INDENT)
2286 .append(break_(",", if curly { " " } else { "" }))
2287 .append(close)
2288}
2289
2290pub fn wrap_generics<'a, I>(args: I) -> Document<'a>
2291where
2292 I: IntoIterator<Item = Document<'a>>,
2293{
2294 break_("<", "<")
2295 .append(join(args, break_(",", ", ")))
2296 .nest(INDENT)
2297 .append(break_(",", ""))
2298 .append(">")
2299}
2300
2301pub fn wrap_fields<'a, I>(args: I) -> Document<'a>
2302where
2303 I: IntoIterator<Item = Document<'a>>,
2304{
2305 let mut args = args.into_iter().peekable();
2306 if args.peek().is_none() {
2307 return nil();
2308 }
2309
2310 line()
2311 .append(join(args, ",".to_doc().append(line())))
2312 .nest(INDENT)
2313 .append(",")
2314 .append(line())
2315}
2316
2317fn list<'a>(elements: Document<'a>, length: usize, tail: Option<Document<'a>>) -> Document<'a> {
2318 if length == 0 {
2319 return match tail {
2320 Some(tail) => tail,
2321 None => "[]".to_doc(),
2322 };
2323 }
2324
2325 let doc = break_("[", "[").append(elements);
2326
2327 match tail {
2328 None => doc.nest(INDENT).append(break_(",", "")),
2329
2330 Some(Document::String(t)) if t == *"_" => doc
2332 .append(break_(",", ", "))
2333 .append("..")
2334 .nest(INDENT)
2335 .append(break_("", "")),
2336
2337 Some(final_tail) => doc
2338 .append(break_(",", ", "))
2339 .append("..")
2340 .append(final_tail)
2341 .nest(INDENT)
2342 .append(break_("", "")),
2343 }
2344 .append("]")
2345 .group()
2346}
2347
2348fn printed_comments<'a, 'comments>(
2349 comments: impl IntoIterator<Item = Option<&'comments str>>,
2350 trailing_newline: bool,
2351) -> Option<Document<'a>> {
2352 let mut comments = comments.into_iter().peekable();
2353 comments.peek()?;
2354
2355 let mut doc = Vec::new();
2356 while let Some(c) = comments.next() {
2357 match c {
2358 None => continue,
2359 Some(c) => {
2360 doc.push("//".to_doc().append(Document::String(c.to_string())));
2363 match comments.peek() {
2364 Some(Some(_)) => doc.push(line()),
2366 Some(None) => {
2368 comments.next();
2369 match comments.peek() {
2370 Some(_) => doc.push(lines(2)),
2371 None => {
2372 if trailing_newline {
2373 doc.push(lines(2));
2374 }
2375 }
2376 }
2377 }
2378 None => {
2380 if trailing_newline {
2381 doc.push(line());
2382 }
2383 }
2384 }
2385 }
2386 }
2387 }
2388 let doc = concat(doc);
2389 if trailing_newline {
2390 Some(doc.force_break())
2391 } else {
2392 Some(doc)
2393 }
2394}
2395
2396fn commented<'a, 'comments>(
2397 doc: Document<'a>,
2398 comments: impl IntoIterator<Item = Option<&'comments str>>,
2399) -> Document<'a> {
2400 match printed_comments(comments, true) {
2401 Some(comments) => comments.append(doc.group()),
2402 None => doc,
2403 }
2404}
2405
2406pub fn comments_before<'a>(
2407 comments: &'a [Comment<'a>],
2408 empty_lines: &'a [usize],
2409 limit: usize,
2410 retain_empty_lines: bool,
2411) -> (
2412 impl Iterator<Item = Option<&'a str>>,
2413 &'a [Comment<'a>],
2414 &'a [usize],
2415) {
2416 let end_comments = comments
2417 .iter()
2418 .position(|c| c.start > limit)
2419 .unwrap_or(comments.len());
2420 let end_empty_lines = empty_lines
2421 .iter()
2422 .position(|l| *l > limit)
2423 .unwrap_or(empty_lines.len());
2424 let popped_comments = comments
2425 .get(0..end_comments)
2426 .expect("0..end_comments is guaranteed to be in bounds")
2427 .iter()
2428 .map(|c| (c.start, Some(c.content)));
2429 let popped_empty_lines = if retain_empty_lines { empty_lines } else { &[] }
2430 .get(0..end_empty_lines)
2431 .unwrap_or(&[])
2432 .iter()
2433 .map(|i| (i, i))
2434 .coalesce(|(a_start, a_end), (b_start, b_end)| {
2436 if *a_end + 1 == *b_start {
2437 Ok((a_start, b_end))
2438 } else {
2439 Err(((a_start, a_end), (b_start, b_end)))
2440 }
2441 })
2442 .map(|l| (*l.0, None));
2443 let popped = popped_comments
2444 .merge_by(popped_empty_lines, |(a, _), (b, _)| a < b)
2445 .skip_while(|(_, comment_or_line)| comment_or_line.is_none())
2446 .map(|(_, comment_or_line)| comment_or_line);
2447 (
2448 popped,
2449 comments.get(end_comments..).expect("in bounds"),
2450 empty_lines.get(end_empty_lines..).expect("in bounds"),
2451 )
2452}
2453
2454fn is_breakable_expr(expr: &UntypedExpr) -> bool {
2455 matches!(
2456 expr,
2457 UntypedExpr::Fn { .. }
2458 | UntypedExpr::Sequence { .. }
2459 | UntypedExpr::Assignment { .. }
2460 | UntypedExpr::Call { .. }
2461 | UntypedExpr::When { .. }
2462 | UntypedExpr::List { .. }
2463 | UntypedExpr::If { .. }
2464 )
2465}
2466
2467fn escape(string: &str) -> String {
2468 string
2469 .chars()
2470 .flat_map(|c| match c {
2471 '\n' => vec!['\\', 'n'],
2472 '\r' => vec!['\\', 'r'],
2473 '\t' => vec!['\\', 't'],
2474 '\0' => vec!['\\', '0'],
2475 '"' => vec!['\\', c],
2476 '\\' => vec!['\\', c],
2477 _ => vec![c],
2478 })
2479 .collect::<String>()
2480}