daml_parser/ast.rs
1//! Typed, lossless parse tree produced by the recursive-descent parser
2//! (src/parse.rs).
3//!
4//! Declarations, expressions, patterns, and most parser DTOs carry both a
5//! 1-based source `Pos` for their first token and a byte `Span` for their
6//! full source extent. `Type` nodes carry spans only because downstream
7//! consumers slice type source text but do not currently need line/column
8//! anchors per type fragment. Downstream crates consume this tree directly:
9//! daml-fmt re-prints layout from the spans, and daml-lint lowers it onto its
10//! own rule-facing IR.
11//!
12//! Parser-created trees are the supported construction path. Public fields are
13//! exposed so tools can match the tree directly; vectors preserve source order,
14//! `pos` is the first token's position, and `span` is the half-open byte range
15//! covering the node's real source tokens.
16
17pub use crate::lexer::{ByteOffset, Identifier, ModuleName, Operator, Pos};
18
19/// Byte span of an AST node.
20///
21/// `[start, end)` into the original source, same basis as `Token::start`/
22/// `Token::end`. Covers every (non-virtual) token that belongs to the node —
23/// first token's `start` to last token's `end`.
24///
25/// Invariants the parser maintains (checked over the corpus by
26/// `render_from_ast`): a child's span is contained in its parent's span, and
27/// sibling spans are ordered and non-overlapping. Trivia (comments, blank
28/// lines) live *between* sibling spans.
29#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
30pub struct Span {
31 /// Inclusive byte offset at which the node starts.
32 pub start: ByteOffset,
33 /// Exclusive byte offset at which the node ends.
34 pub end: ByteOffset,
35}
36
37impl Span {
38 /// Create a span from already-typed byte offsets.
39 ///
40 /// Use [`Self::from_usize`] only at parser/source-slicing boundaries where
41 /// raw byte offsets are being converted deliberately.
42 ///
43 /// ```compile_fail
44 /// use daml_parser::ast::Span;
45 ///
46 /// let _ = Span::new(1usize, 2usize);
47 /// ```
48 #[must_use]
49 pub const fn new(start: ByteOffset, end: ByteOffset) -> Self {
50 Self { start, end }
51 }
52
53 /// Convert raw byte offsets into a typed parser span.
54 #[must_use]
55 pub const fn from_usize(start: usize, end: usize) -> Self {
56 Self {
57 start: ByteOffset::new(start),
58 end: ByteOffset::new(end),
59 }
60 }
61
62 /// Raw start byte offset for source slicing and external interop.
63 #[must_use]
64 pub const fn start_usize(self) -> usize {
65 self.start.get()
66 }
67
68 /// Raw exclusive end byte offset for source slicing and external interop.
69 #[must_use]
70 pub const fn end_usize(self) -> usize {
71 self.end.get()
72 }
73
74 /// True when the span is well-formed (`start <= end`).
75 #[must_use]
76 pub const fn is_valid(&self) -> bool {
77 self.start.get() <= self.end.get()
78 }
79
80 /// True for a zero-width but still valid span.
81 #[must_use]
82 pub const fn is_empty(&self) -> bool {
83 self.start.get() == self.end.get()
84 }
85
86 #[must_use]
87 pub const fn range(&self) -> std::ops::Range<usize> {
88 self.start.get()..self.end.get()
89 }
90
91 #[must_use]
92 pub fn get<'a>(&self, source: &'a str) -> Option<&'a str> {
93 let start = self.start.get();
94 let end = self.end.get();
95 if start <= end
96 && end <= source.len()
97 && source.is_char_boundary(start)
98 && source.is_char_boundary(end)
99 {
100 Some(&source[start..end])
101 } else {
102 None
103 }
104 }
105
106 /// `self` fully contains `other`.
107 #[must_use]
108 pub const fn contains(&self, other: &Self) -> bool {
109 self.is_valid()
110 && other.is_valid()
111 && self.start.get() <= other.start.get()
112 && other.end.get() <= self.end.get()
113 }
114}
115
116#[derive(Debug, Clone, PartialEq, Eq)]
117#[non_exhaustive]
118pub enum LitKind {
119 /// Integer literal.
120 Int,
121 /// Decimal literal.
122 Decimal,
123 /// Text/string literal.
124 Text,
125 /// Character literal.
126 Char,
127}
128
129/// Import syntax style.
130#[derive(Debug, Clone, Copy, PartialEq, Eq)]
131#[non_exhaustive]
132pub enum ImportStyle {
133 /// Qualified import (`import qualified Foo.Bar`, `import Foo.Bar qualified`).
134 Qualified,
135 /// Unqualified import (`import Foo.Bar`, `import Foo.Bar as Baz`).
136 Unqualified,
137}
138
139#[derive(Debug, Clone, PartialEq, Eq)]
140#[non_exhaustive]
141pub enum FieldAssign {
142 /// Explicit record assignment: `field = expression`.
143 Assign {
144 /// Field being assigned.
145 name: Identifier,
146 /// Right-hand-side expression after `=`.
147 value: Expr,
148 /// Position of the field name.
149 pos: Pos,
150 /// Span of the whole `field = expression` assignment.
151 span: Span,
152 },
153 /// Record pun: `field`, meaning `field = field`.
154 Pun {
155 /// Punned field name.
156 name: Identifier,
157 /// Position of the field name.
158 pos: Pos,
159 /// Span of the field name.
160 span: Span,
161 },
162 /// Record wildcard: `..`.
163 Wildcard {
164 /// Position of the `..` token.
165 pos: Pos,
166 /// Span of the `..` token.
167 span: Span,
168 },
169}
170
171impl FieldAssign {
172 #[must_use]
173 pub const fn pos(&self) -> Pos {
174 match self {
175 Self::Assign { pos, .. } | Self::Pun { pos, .. } | Self::Wildcard { pos, .. } => *pos,
176 }
177 }
178
179 #[must_use]
180 pub const fn span(&self) -> Span {
181 match self {
182 Self::Assign { span, .. } | Self::Pun { span, .. } | Self::Wildcard { span, .. } => {
183 *span
184 }
185 }
186 }
187
188 #[must_use]
189 pub const fn name(&self) -> Option<&Identifier> {
190 match self {
191 Self::Assign { name, .. } | Self::Pun { name, .. } => Some(name),
192 Self::Wildcard { .. } => None,
193 }
194 }
195}
196
197/// Boolean or pattern guard qualifier in a guarded case alternative branch.
198#[derive(Debug, Clone, PartialEq, Eq)]
199#[non_exhaustive]
200pub enum GuardQualifier {
201 /// Boolean guard expression.
202 Bool {
203 /// Guard expression.
204 expr: Expr,
205 /// Position of the guard's first token.
206 pos: Pos,
207 /// Span of the guard qualifier.
208 span: Span,
209 },
210 /// Pattern guard `pat <- expr`.
211 Pattern {
212 /// Pattern bound by the guard.
213 pat: Pat,
214 /// Source expression on the right of `<-`.
215 expr: Expr,
216 /// Position of the pattern guard's first token.
217 pos: Pos,
218 /// Span of the pattern guard qualifier.
219 span: Span,
220 },
221}
222
223impl GuardQualifier {
224 #[must_use]
225 pub const fn span(&self) -> Span {
226 match self {
227 Self::Bool { span, .. } | Self::Pattern { span, .. } => *span,
228 }
229 }
230
231 #[must_use]
232 pub const fn pos(&self) -> Pos {
233 match self {
234 Self::Bool { pos, .. } | Self::Pattern { pos, .. } => *pos,
235 }
236 }
237}
238
239/// One guarded or unguarded branch of a case/`try` alternative.
240#[derive(Debug, Clone, PartialEq, Eq)]
241pub struct AltBranch {
242 /// Comma-separated guard qualifiers before `->`; empty for unguarded branches.
243 pub guards: Vec<GuardQualifier>,
244 /// Branch body after `->`.
245 pub body: Expr,
246 /// Position of the branch's first token (`|` or `->`).
247 pub pos: Pos,
248 /// Span of the whole branch.
249 pub span: Span,
250}
251
252#[derive(Debug, Clone, PartialEq, Eq)]
253pub struct Alt {
254 /// Pattern to match before `->` or the first `|`.
255 pub pat: Pat,
256 /// First branch body for convenience; mirrors `branches[0].body`.
257 pub body: Expr,
258 /// Source-ordered guarded/unguarded branches for this alternative.
259 pub branches: Vec<AltBranch>,
260 /// `where` helper bindings attached to this alternative.
261 pub where_bindings: Vec<Binding>,
262 /// Position of the alternative's first token.
263 pub pos: Pos,
264 /// Span of the whole alternative.
265 pub span: Span,
266}
267
268#[derive(Debug, Clone, PartialEq, Eq)]
269pub struct Binding {
270 /// Left-hand side: a variable with parameters, or a destructuring pattern.
271 pub pat: Pat,
272 /// Parameter patterns when the LHS is a function binding (`f x y = ...`).
273 pub params: Vec<Pat>,
274 /// Right-hand-side expression.
275 pub expr: Expr,
276 /// Position of the binding's first token.
277 pub pos: Pos,
278 /// Span of the whole binding.
279 pub span: Span,
280}
281
282/// Record-pattern field syntax: explicit braces or layout `with`.
283#[derive(Debug, Clone, Copy, PartialEq, Eq)]
284#[non_exhaustive]
285pub enum RecordPatternSyntax {
286 /// `Foo { field = pat; .. }`.
287 Braces,
288 /// `Foo with field; nested = pat`.
289 With,
290}
291
292#[derive(Debug, Clone, PartialEq, Eq)]
293#[non_exhaustive]
294pub enum PatFieldAssign {
295 /// Explicit record-pattern assignment: `field = pattern`.
296 Assign {
297 /// Field being matched.
298 name: Identifier,
299 /// Pattern bound to the field.
300 pat: Pat,
301 /// Position of the field name.
302 pos: Pos,
303 /// Span of the whole `field = pattern` assignment.
304 span: Span,
305 },
306 /// Record-pattern pun: `field`, meaning `field = field`.
307 Pun {
308 /// Punned field name.
309 name: Identifier,
310 /// Position of the field name.
311 pos: Pos,
312 /// Span of the field name.
313 span: Span,
314 },
315 /// Record-pattern wildcard: `..`.
316 Wildcard {
317 /// Position of the `..` token.
318 pos: Pos,
319 /// Span of the `..` token.
320 span: Span,
321 },
322}
323
324impl PatFieldAssign {
325 #[must_use]
326 pub const fn pos(&self) -> Pos {
327 match self {
328 Self::Assign { pos, .. } | Self::Pun { pos, .. } | Self::Wildcard { pos, .. } => *pos,
329 }
330 }
331
332 #[must_use]
333 pub const fn span(&self) -> Span {
334 match self {
335 Self::Assign { span, .. } | Self::Pun { span, .. } | Self::Wildcard { span, .. } => {
336 *span
337 }
338 }
339 }
340
341 #[must_use]
342 pub const fn name(&self) -> Option<&Identifier> {
343 match self {
344 Self::Assign { name, .. } | Self::Pun { name, .. } => Some(name),
345 Self::Wildcard { .. } => None,
346 }
347 }
348}
349
350#[derive(Debug, Clone, PartialEq, Eq)]
351#[non_exhaustive]
352pub enum Pat {
353 /// Variable pattern.
354 Var {
355 /// Bound variable name.
356 name: Identifier,
357 /// Position of the variable token.
358 pos: Pos,
359 /// Span of the variable token.
360 span: Span,
361 },
362 /// Wildcard pattern (`_`).
363 Wild {
364 /// Position of the `_` token.
365 pos: Pos,
366 /// Span of the `_` token.
367 span: Span,
368 },
369 /// Constructor pattern with source-ordered arguments.
370 Con {
371 /// Optional module qualifier before the constructor name.
372 qualifier: Option<ModuleName>,
373 /// Constructor name.
374 name: Identifier,
375 /// Constructor arguments in source order.
376 args: Vec<Self>,
377 /// Position of the constructor token.
378 pos: Pos,
379 /// Span of the whole constructor pattern.
380 span: Span,
381 },
382 /// Constructor record pattern with source-ordered fields.
383 Record {
384 /// Optional module qualifier before the constructor name.
385 qualifier: Option<ModuleName>,
386 /// Constructor name.
387 name: Identifier,
388 /// Whether fields used `{..}` or `with`.
389 syntax: RecordPatternSyntax,
390 /// Field patterns in source order.
391 fields: Vec<PatFieldAssign>,
392 /// Position of the constructor token.
393 pos: Pos,
394 /// Span of the whole record pattern.
395 span: Span,
396 },
397 /// Tuple pattern with source-ordered items.
398 Tuple {
399 /// Tuple items in source order.
400 items: Vec<Self>,
401 /// Position of the opening parenthesis.
402 pos: Pos,
403 /// Span from `(` through `)`.
404 span: Span,
405 },
406 /// List pattern with source-ordered items.
407 List {
408 /// List items in source order.
409 items: Vec<Self>,
410 /// Position of the opening bracket.
411 pos: Pos,
412 /// Span from `[` through `]`.
413 span: Span,
414 },
415 /// Literal pattern; `text` is the parser's normalized literal text.
416 Lit {
417 /// Literal family.
418 kind: LitKind,
419 /// Normalized literal payload.
420 text: String,
421 /// Position of the literal token.
422 pos: Pos,
423 /// Span of the literal token in the source.
424 span: Span,
425 },
426 /// `name@pat`
427 As {
428 /// Name bound to the whole matched pattern.
429 name: Identifier,
430 /// Pattern being aliased.
431 pat: Box<Self>,
432 /// Position of the bound name.
433 pos: Pos,
434 /// Span of the whole `name@pat` pattern.
435 span: Span,
436 },
437 /// Anything the parser couldn't classify; raw text preserved.
438 Other {
439 /// Raw source text of the unclassified pattern.
440 raw: String,
441 /// Position of the raw pattern's first token.
442 pos: Pos,
443 /// Span of the preserved raw pattern text.
444 span: Span,
445 },
446}
447
448#[derive(Debug, Clone, PartialEq, Eq)]
449#[non_exhaustive]
450pub enum Expr {
451 /// Lowercase variable reference, possibly qualified.
452 Var {
453 /// Optional module qualifier before the variable name.
454 qualifier: Option<ModuleName>,
455 /// Variable name.
456 name: Identifier,
457 /// Position of the variable token.
458 pos: Pos,
459 /// Span of the variable token.
460 span: Span,
461 },
462 /// Constructor / data-constructor reference, possibly qualified.
463 Con {
464 /// Optional module qualifier before the constructor name.
465 qualifier: Option<ModuleName>,
466 /// Constructor name.
467 name: Identifier,
468 /// Position of the constructor token.
469 pos: Pos,
470 /// Span of the constructor token.
471 span: Span,
472 },
473 /// Literal expression; `text` is the parser's normalized literal text.
474 Lit {
475 /// Literal family.
476 kind: LitKind,
477 /// Normalized literal payload.
478 text: String,
479 /// Position of the literal token.
480 pos: Pos,
481 /// Span of the literal token in the source.
482 span: Span,
483 },
484 /// Application, flattened: `f a b c` is one App with three args.
485 App {
486 /// Function expression being applied.
487 func: Box<Self>,
488 /// Arguments in source order.
489 args: Vec<Self>,
490 /// Position of the application's first token.
491 pos: Pos,
492 /// Span of the whole application.
493 span: Span,
494 },
495 /// Binary operator application with source-level operator text.
496 BinOp {
497 /// Operator token text.
498 op: Operator,
499 /// Left operand.
500 lhs: Box<Self>,
501 /// Right operand.
502 rhs: Box<Self>,
503 /// Position of the left operand.
504 pos: Pos,
505 /// Span of the whole infix expression.
506 span: Span,
507 },
508 /// Unary negation.
509 Neg {
510 /// Negated expression.
511 expr: Box<Self>,
512 /// Position of the `-` token.
513 pos: Pos,
514 /// Span of the whole negated expression.
515 span: Span,
516 },
517 /// Lambda expression (`\params -> body`).
518 Lambda {
519 /// Parameter patterns in source order.
520 params: Vec<Pat>,
521 /// Lambda body.
522 body: Box<Self>,
523 /// Position of the lambda token.
524 pos: Pos,
525 /// Span of the whole lambda expression.
526 span: Span,
527 },
528 /// Conditional expression.
529 If {
530 /// Condition after `if`.
531 cond: Box<Self>,
532 /// Expression after `then`.
533 then_branch: Box<Self>,
534 /// Expression after `else`.
535 else_branch: Box<Self>,
536 /// Position of the `if` token.
537 pos: Pos,
538 /// Span of the whole conditional expression.
539 span: Span,
540 },
541 /// Case expression with source-ordered alternatives.
542 Case {
543 /// Scrutinee after `case`.
544 scrutinee: Box<Self>,
545 /// Alternatives in source order.
546 alts: Vec<Alt>,
547 /// Position of the `case` token.
548 pos: Pos,
549 /// Span of the whole case expression.
550 span: Span,
551 },
552 /// Do block.
553 Do {
554 /// Statements in source order.
555 stmts: Vec<DoStmt>,
556 /// Position of the `do` token.
557 pos: Pos,
558 /// Span of the whole do block.
559 span: Span,
560 },
561 /// Let/in expression.
562 LetIn {
563 /// Bindings in source order.
564 bindings: Vec<Binding>,
565 /// Body expression after `in`.
566 body: Box<Self>,
567 /// Position of the `let` token.
568 pos: Pos,
569 /// Span of the whole let/in expression.
570 span: Span,
571 },
572 /// `base with f = e, ...` — record construction when base is a Con,
573 /// record update otherwise.
574 Record {
575 /// Constructor or record value being constructed/updated.
576 base: Box<Self>,
577 /// Field assignments in source order.
578 fields: Vec<FieldAssign>,
579 /// Position of the base expression.
580 pos: Pos,
581 /// Span of the whole record expression.
582 span: Span,
583 },
584 /// Tuple expression with source-ordered items.
585 Tuple {
586 /// Tuple items in source order.
587 items: Vec<Self>,
588 /// Position of the opening parenthesis.
589 pos: Pos,
590 /// Span from `(` through `)`.
591 span: Span,
592 },
593 /// List expression with source-ordered items.
594 List {
595 /// List items in source order.
596 items: Vec<Self>,
597 /// Position of the opening bracket.
598 pos: Pos,
599 /// Span from `[` through `]`.
600 span: Span,
601 },
602 /// `try <body> catch <alts>`
603 Try {
604 /// Body after `try`.
605 body: Box<Self>,
606 /// Catch handlers in source order.
607 handlers: Vec<Alt>,
608 /// Position of the `try` token.
609 pos: Pos,
610 /// Span of the whole try/catch expression.
611 span: Span,
612 },
613 /// Parenthesized operator reference like `(+)`.
614 OperatorRef {
615 /// Referenced operator.
616 op: Operator,
617 /// Position of the opening parenthesis.
618 pos: Pos,
619 /// Span from `(` through `)`.
620 span: Span,
621 },
622 /// Left operator section like `(1 +)`.
623 LeftSection {
624 /// Section operator.
625 op: Operator,
626 /// Left operand before the operator.
627 operand: Box<Self>,
628 /// Position of the opening parenthesis.
629 pos: Pos,
630 /// Span from `(` through `)`.
631 span: Span,
632 },
633 /// Right operator section like `(+ 1)`.
634 RightSection {
635 /// Section operator.
636 op: Operator,
637 /// Right operand after the operator.
638 operand: Box<Self>,
639 /// Position of the opening parenthesis.
640 pos: Pos,
641 /// Span from `(` through `)`.
642 span: Span,
643 },
644 /// Expression the parser could not understand; raw text preserved so
645 /// a parse failure degrades to the shim's behavior instead of dying.
646 Error {
647 /// Raw source text preserved for the malformed expression.
648 raw: String,
649 /// Position of the malformed expression's first token.
650 pos: Pos,
651 /// Span of the preserved raw expression text.
652 span: Span,
653 },
654}
655
656#[derive(Debug, Clone, PartialEq, Eq)]
657#[non_exhaustive]
658pub enum DoStmt {
659 /// `pat <- expr`
660 Bind {
661 /// Pattern before `<-`.
662 pat: Pat,
663 /// Expression after `<-`.
664 expr: Expr,
665 /// Position of the statement's first token.
666 pos: Pos,
667 /// Span of the whole bind statement.
668 span: Span,
669 },
670 /// `let x = e` (no `in`) inside a do block.
671 Let {
672 /// Let bindings in source order.
673 bindings: Vec<Binding>,
674 /// Position of the `let` token.
675 pos: Pos,
676 /// Span of the whole let statement.
677 span: Span,
678 },
679 /// Bare expression statement.
680 Expr {
681 /// Statement expression.
682 expr: Expr,
683 /// Position of the expression's first token.
684 pos: Pos,
685 /// Span of the expression statement.
686 span: Span,
687 },
688}
689
690/// Structured Daml type, parsed from the real token stream.
691///
692/// Scoped to the forms the corpus actually contains; it exists so consumers can
693/// tell a type *application* from a *function arrow* from an
694/// atomic constructor — a distinction a string matcher structurally cannot make.
695/// Every node carries a byte span so consumers can render exact source text from
696/// `(source, span)`. Unlike declarations, expressions, and patterns, type nodes
697/// do not carry a separate [`Pos`]; use [`Type::span`] and source line mapping
698/// when a line/column anchor is required for a type fragment.
699#[derive(Debug, Clone)]
700#[non_exhaustive]
701pub enum Type {
702 /// Type constructor, possibly qualified: `Party`, `DA.Map.Map`.
703 Con {
704 /// Optional module qualifier before the constructor name.
705 qualifier: Option<ModuleName>,
706 /// Constructor name.
707 name: Identifier,
708 /// Span of the constructor token in the source.
709 span: Span,
710 },
711 /// Type application, head applied to one or more args: `ContractId Foo`,
712 /// `Map Text Int`, `Script ()`. Type-level nat literals (the `10` in
713 /// `Numeric 10`) are NOT types, so they are dropped from the arg list — a
714 /// `Numeric 10` collapses to the bare head `Con "Numeric"`.
715 App(Box<Self>, Vec<Self>, Span),
716 /// List type `[T]`.
717 List(Box<Self>, Span),
718 /// Tuple type `(a, b, ...)`.
719 Tuple(Vec<Self>, Span),
720 /// Function type `a -> b` (right-associative).
721 Fun(Box<Self>, Box<Self>, Span),
722 /// Lowercase type variable: `a`, `n`.
723 Var(Identifier, Span),
724 /// The unit type `()`.
725 Unit(Span),
726 /// A constrained type `C a => T`: the context is not modeled, the body `T`
727 /// is kept.
728 Constrained(Box<Self>, Span),
729 /// Type-level string or char literal, e.g. the `"observers"` in
730 /// `HasField "observers" t PartiesMap`.
731 Lit {
732 /// Literal family.
733 kind: LitKind,
734 /// Normalized literal payload.
735 text: String,
736 /// Span of the literal token in the source.
737 span: Span,
738 },
739}
740
741/// Type equality intentionally ignores source spans.
742///
743/// Spans describe where equivalent type syntax appeared in a source file; they
744/// are not part of structural type identity used by parser consumers.
745impl PartialEq for Type {
746 fn eq(&self, other: &Self) -> bool {
747 match (self, other) {
748 (
749 Self::Con {
750 qualifier: aq,
751 name: an,
752 ..
753 },
754 Self::Con {
755 qualifier: bq,
756 name: bn,
757 ..
758 },
759 ) => aq == bq && an == bn,
760 (Self::App(ah, aa, _), Self::App(bh, ba, _)) => ah == bh && aa == ba,
761 (Self::List(a, _), Self::List(b, _))
762 | (Self::Constrained(a, _), Self::Constrained(b, _)) => a == b,
763 (Self::Tuple(a, _), Self::Tuple(b, _)) => a == b,
764 (Self::Fun(al, ar, _), Self::Fun(bl, br, _)) => al == bl && ar == br,
765 (Self::Var(a, _), Self::Var(b, _)) => a == b,
766 (Self::Unit(_), Self::Unit(_)) => true,
767 (
768 Self::Lit {
769 kind: ak, text: at, ..
770 },
771 Self::Lit {
772 kind: bk, text: bt, ..
773 },
774 ) => ak == bk && at == bt,
775 _ => false,
776 }
777 }
778}
779
780impl Eq for Type {}
781
782impl Type {
783 #[must_use]
784 pub const fn span(&self) -> Span {
785 match self {
786 Self::Con { span, .. }
787 | Self::App(_, _, span)
788 | Self::List(_, span)
789 | Self::Tuple(_, span)
790 | Self::Fun(_, _, span)
791 | Self::Var(_, span)
792 | Self::Unit(span)
793 | Self::Constrained(_, span)
794 | Self::Lit { span, .. } => *span,
795 }
796 }
797
798 pub(crate) const fn with_span(mut self, span: Span) -> Self {
799 match &mut self {
800 Self::Con { span: s, .. }
801 | Self::App(_, _, s)
802 | Self::List(_, s)
803 | Self::Tuple(_, s)
804 | Self::Fun(_, _, s)
805 | Self::Var(_, s)
806 | Self::Unit(s)
807 | Self::Constrained(_, s)
808 | Self::Lit { span: s, .. } => *s = span,
809 }
810 self
811 }
812}
813
814#[derive(Debug, Clone, PartialEq, Eq)]
815#[non_exhaustive]
816pub enum TypeAnnotation {
817 /// The source construct did not include a type annotation.
818 Absent,
819 /// The source construct included a type annotation that parsed cleanly.
820 Present(Type),
821 /// The source construct included a type annotation, but the parser could
822 /// not model it as a [`Type`]. Diagnostics carry the detailed message.
823 Malformed { span: Span },
824}
825
826impl TypeAnnotation {
827 #[must_use]
828 pub const fn as_type(&self) -> Option<&Type> {
829 match self {
830 Self::Present(ty) => Some(ty),
831 Self::Absent | Self::Malformed { .. } => None,
832 }
833 }
834
835 #[must_use]
836 pub const fn is_absent(&self) -> bool {
837 matches!(self, Self::Absent)
838 }
839
840 #[must_use]
841 pub const fn is_malformed(&self) -> bool {
842 matches!(self, Self::Malformed { .. })
843 }
844}
845
846#[derive(Debug, Clone, PartialEq, Eq)]
847pub struct FieldDecl {
848 /// Field or method name.
849 pub name: Identifier,
850 /// Structured field type parse state.
851 pub ty: TypeAnnotation,
852 /// Position of the field/method name.
853 pub pos: Pos,
854 /// Span of the full field declaration (`name : Type` when present).
855 pub span: Span,
856}
857
858#[derive(Debug, Clone, Copy, PartialEq, Eq)]
859#[non_exhaustive]
860pub enum Consuming {
861 /// Daml `consuming` choice.
862 Consuming,
863 /// Daml `nonconsuming` choice.
864 NonConsuming,
865 /// Legacy/pre-Daml-3 `preconsuming` spelling.
866 PreConsuming,
867 /// Legacy/pre-Daml-3 `postconsuming` spelling.
868 PostConsuming,
869}
870
871#[derive(Debug, Clone, PartialEq, Eq)]
872pub struct ChoiceDecl {
873 /// Choice name.
874 pub name: Identifier,
875 /// Consuming mode parsed from the choice header.
876 pub consuming: Consuming,
877 /// Structured return type parse state.
878 pub return_ty: TypeAnnotation,
879 /// Choice parameter fields in source order.
880 pub params: Vec<FieldDecl>,
881 /// Comma-separated controller expressions.
882 pub controllers: Vec<Expr>,
883 /// Choice observers, if any.
884 pub observers: Vec<Expr>,
885 /// Choice authority expressions from `authority` metadata clauses.
886 pub authority_exprs: Vec<Expr>,
887 /// Choice body after `do`; `None` when the parser did not find one.
888 pub body: Option<Expr>,
889 /// Position of the `choice` token.
890 pub pos: Pos,
891 /// Span of the whole choice declaration.
892 pub span: Span,
893}
894
895#[derive(Debug, Clone, PartialEq, Eq)]
896#[non_exhaustive]
897pub enum TemplateBodyDecl {
898 /// `signatory` clause with source-ordered party expressions.
899 Signatory {
900 /// Party expressions in source order.
901 parties: Vec<Expr>,
902 /// Position of the `signatory` token.
903 pos: Pos,
904 /// Span of the whole clause.
905 span: Span,
906 },
907 /// `observer` clause with source-ordered party expressions.
908 Observer {
909 /// Party expressions in source order.
910 parties: Vec<Expr>,
911 /// Position of the `observer` token.
912 pos: Pos,
913 /// Span of the whole clause.
914 span: Span,
915 },
916 /// `ensure` clause.
917 Ensure {
918 /// Predicate expression after `ensure`.
919 expr: Expr,
920 /// Position of the `ensure` token.
921 pos: Pos,
922 /// Span of the whole clause.
923 span: Span,
924 },
925 /// Template `key` declaration.
926 Key {
927 /// Key expression.
928 expr: Expr,
929 /// Structured key type parse state.
930 ty: TypeAnnotation,
931 /// Position of the `key` token.
932 pos: Pos,
933 /// Span of the whole key declaration.
934 span: Span,
935 },
936 /// `maintainer` clause.
937 Maintainer {
938 /// Maintainer expression.
939 expr: Expr,
940 /// Position of the `maintainer` token.
941 pos: Pos,
942 /// Span of the whole clause.
943 span: Span,
944 },
945 /// `choice` declaration.
946 Choice(ChoiceDecl),
947 /// `interface instance` declaration nested in a template.
948 InterfaceInstance(InterfaceInstanceDecl),
949 /// `agreement`, `let` blocks, deprecated `controller ... can`, etc.
950 Other {
951 /// Raw source text preserved for unsupported/malformed body syntax.
952 raw: String,
953 /// Position of the raw body's first token.
954 pos: Pos,
955 /// Span of the preserved raw body text.
956 span: Span,
957 },
958}
959
960/// One item in an `interface instance ... where` body, in source order.
961#[derive(Debug, Clone, PartialEq, Eq)]
962#[non_exhaustive]
963pub enum InterfaceInstanceBodyItem {
964 /// `view = <expr>` binding for the interface view implementation.
965 View {
966 /// View expression.
967 expr: Expr,
968 /// Position of the `view` token.
969 pos: Pos,
970 /// Span of the whole `view = ...` item.
971 span: Span,
972 },
973 /// An ordinary interface method implementation.
974 Method(Binding),
975}
976
977#[derive(Debug, Clone, PartialEq, Eq)]
978pub struct InterfaceInstanceDecl {
979 /// Interface being implemented (`Disclosure.I`).
980 pub interface_name: ModuleName,
981 /// Explicit template from `for Foo`; `None` when omitted (the enclosing
982 /// template when declared inside one).
983 pub for_template: Option<ModuleName>,
984 /// View and method implementations in source order.
985 pub items: Vec<InterfaceInstanceBodyItem>,
986 /// Position of the `interface instance` clause.
987 pub pos: Pos,
988 /// Span of the whole interface instance declaration.
989 pub span: Span,
990}
991
992#[derive(Debug, Clone, PartialEq, Eq)]
993pub struct TemplateDecl {
994 /// Template name.
995 pub name: Identifier,
996 /// Template fields in source order.
997 pub fields: Vec<FieldDecl>,
998 /// Template body declarations in source order.
999 pub body: Vec<TemplateBodyDecl>,
1000 /// Position of the `template` token.
1001 pub pos: Pos,
1002 /// Span of the whole template declaration.
1003 pub span: Span,
1004}
1005
1006#[derive(Debug, Clone, PartialEq, Eq)]
1007pub struct InterfaceDecl {
1008 /// Interface name.
1009 pub name: Identifier,
1010 /// Interfaces this interface requires (`requires Lockable.I, ...`).
1011 pub requires: Vec<ModuleName>,
1012 /// Optional view type name from `viewtype`.
1013 pub viewtype: Option<ModuleName>,
1014 /// Method signatures in source order.
1015 pub methods: Vec<FieldDecl>,
1016 /// Interface choices in source order.
1017 pub choices: Vec<ChoiceDecl>,
1018 /// Position of the `interface` token.
1019 pub pos: Pos,
1020 /// Span of the whole interface declaration.
1021 pub span: Span,
1022}
1023
1024#[derive(Debug, Clone, PartialEq, Eq)]
1025pub struct Equation {
1026 /// Parameter patterns in source order.
1027 pub params: Vec<Pat>,
1028 /// Unguarded body or first guarded body for convenience.
1029 pub body: Expr,
1030 /// Guarded equations keep their guards as (guard, body) pairs; `body`
1031 /// then holds the first guarded body for convenience.
1032 pub guards: Vec<(Expr, Expr)>,
1033 /// `where` helper bindings attached to this equation.
1034 pub where_bindings: Vec<Binding>,
1035 /// Position of the equation's first token.
1036 pub pos: Pos,
1037 /// Span of this equation only.
1038 pub span: Span,
1039}
1040
1041#[derive(Debug, Clone, PartialEq, Eq)]
1042pub struct FunctionDecl {
1043 /// Function name.
1044 pub name: Identifier,
1045 /// Standalone signature type parse state.
1046 pub ty: TypeAnnotation,
1047 /// Equations for this function in source order.
1048 pub equations: Vec<Equation>,
1049 /// Position of the function's first appearance.
1050 pub pos: Pos,
1051 /// Span of the function's first appearance (signature or first equation).
1052 /// Convenience anchor; a multi-equation function's precise ranges are the
1053 /// per-`Equation` spans, since equations need not be contiguous in source.
1054 pub span: Span,
1055 /// Span of the standalone type signature `name : Type`, if one was seen.
1056 pub sig_span: Option<Span>,
1057}
1058
1059/// Fixity associativity keyword (`infix`, `infixl`, `infixr`).
1060#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1061#[non_exhaustive]
1062pub enum FixityAssoc {
1063 Infix,
1064 InfixL,
1065 InfixR,
1066}
1067
1068/// Operator or backtick-quoted name in a fixity declaration.
1069#[derive(Debug, Clone, PartialEq, Eq)]
1070#[non_exhaustive]
1071pub enum FixityTarget {
1072 /// Symbolic operator such as `===` or `>=>`.
1073 Operator(Operator),
1074 /// Backtick-quoted identifier such as `` `Pair` ``.
1075 Backtick(Identifier),
1076}
1077
1078/// Top-level fixity declaration (`infix[l|r]? n op [, op ...]`).
1079#[derive(Debug, Clone, PartialEq, Eq)]
1080pub struct FixityDecl {
1081 pub assoc: FixityAssoc,
1082 pub precedence: u8,
1083 pub operators: Vec<FixityTarget>,
1084 pub pos: Pos,
1085 pub span: Span,
1086}
1087
1088/// Source package label on a package-qualified import (`import "pkg" Module`).
1089///
1090/// Holds the decoded string literal value and its source span. This is source
1091/// syntax only; it is not resolved to an LF `PackageId`.
1092#[derive(Debug, Clone, PartialEq, Eq)]
1093pub struct ImportPackageLabel {
1094 /// Decoded package label text from the string literal.
1095 pub value: String,
1096 /// Span of the string literal token, including quotes.
1097 pub span: Span,
1098}
1099
1100#[derive(Debug, Clone, PartialEq, Eq)]
1101pub struct ImportDecl {
1102 /// Imported module path.
1103 pub module_name: ModuleName,
1104 /// Whether the import is qualified.
1105 pub style: ImportStyle,
1106 /// Optional module alias from `as`.
1107 pub alias: Option<ModuleName>,
1108 /// Optional package label from `import "pkg" Module` source syntax.
1109 pub package_label: Option<ImportPackageLabel>,
1110 /// Position of the `import` token.
1111 pub pos: Pos,
1112 /// Span of the whole import declaration.
1113 pub span: Span,
1114}
1115
1116#[derive(Debug, Clone, PartialEq, Eq)]
1117#[non_exhaustive]
1118pub enum Decl {
1119 /// Template declaration.
1120 Template(TemplateDecl),
1121 /// Interface declaration.
1122 Interface(InterfaceDecl),
1123 /// Function signature/equations grouped by name.
1124 Function(FunctionDecl),
1125 /// data/type/class/instance/exception — recorded with name + span.
1126 TypeDef {
1127 /// Declaration keyword (`data`, `type`, `class`, ...).
1128 keyword: String,
1129 /// Declared type/class/instance name as parsed.
1130 name: Identifier,
1131 /// Position of the declaration keyword.
1132 pos: Pos,
1133 /// Span of the declaration header/body consumed by the parser.
1134 span: Span,
1135 },
1136 /// Top-level fixity declaration.
1137 Fixity(FixityDecl),
1138 /// Top-level syntax the parser recognizes but intentionally does not model.
1139 UnsupportedSyntax {
1140 /// Why the declaration is unsupported.
1141 kind: UnsupportedSyntaxKind,
1142 /// Raw source text of the declaration.
1143 raw: String,
1144 /// Position of the declaration's first token.
1145 pos: Pos,
1146 /// Span of the declaration text.
1147 span: Span,
1148 },
1149 /// Anything unparseable at the top level (diagnostic already emitted).
1150 Unknown {
1151 /// Raw source text of the skipped declaration.
1152 raw: String,
1153 /// Position of the skipped declaration's first token.
1154 pos: Pos,
1155 /// Span of the skipped declaration text.
1156 span: Span,
1157 },
1158}
1159
1160#[derive(Debug, Clone, PartialEq, Eq)]
1161pub struct Module {
1162 /// Module name from the header, or `Unknown` when the source has no header.
1163 pub name: ModuleName,
1164 /// Position of the `module` keyword, or the start of the fallback module.
1165 pub pos: Pos,
1166 /// Whole-module extent: `[0, source.len())`. Container for all decls.
1167 pub span: Span,
1168 /// Span of the `module M (...) where` header clause; empty when the file
1169 /// has no module header. Lets the span oracle treat header tokens as
1170 /// covered without a dedicated header node.
1171 pub header: Span,
1172 /// Imports in source order.
1173 pub imports: Vec<ImportDecl>,
1174 /// Top-level declarations in source order.
1175 pub decls: Vec<Decl>,
1176}
1177
1178/// Why a [`ParseDiagnostic`] fired.
1179///
1180/// Lets a consumer separate syntax the parser deliberately does not model (still
1181/// safe, just unanalyzed) from a genuine malformation, a recursion-limit
1182/// degradation, or a lexical error.
1183#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1184#[non_exhaustive]
1185pub enum DiagnosticCategory {
1186 /// A whole declaration could not be parsed and was skipped to the next item.
1187 SkippedDecl,
1188 /// A malformed expression, pattern, or expected-token error inside an
1189 /// otherwise-recognized construct.
1190 Malformed,
1191 /// A construct the parser intentionally does not support, e.g. legacy
1192 /// `controller ... can` choice syntax.
1193 UnsupportedSyntax,
1194 /// Expression/pattern nesting exceeded the recursion bound and was degraded
1195 /// to raw text.
1196 RecursionLimit,
1197 /// A lexical error (unterminated string/comment, stray character).
1198 Lex,
1199}
1200
1201impl DiagnosticCategory {
1202 /// Stable kebab-case tag for machine-readable output (JSON/SARIF) and logs.
1203 #[must_use]
1204 pub const fn as_str(self) -> &'static str {
1205 match self {
1206 Self::SkippedDecl => "skipped-declaration",
1207 Self::Malformed => "malformed",
1208 Self::UnsupportedSyntax => "unsupported-syntax",
1209 Self::RecursionLimit => "recursion-limit",
1210 Self::Lex => "lexical-error",
1211 }
1212 }
1213}
1214
1215impl std::fmt::Display for DiagnosticCategory {
1216 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1217 f.write_str(self.as_str())
1218 }
1219}
1220
1221/// Machine-readable reason a [`ParseDiagnostic`] fired.
1222///
1223/// Keep [`ParseDiagnostic::message`] for presentation. Match on this enum when
1224/// downstream code needs stable behavior for recoverable parser failures.
1225#[derive(Debug, Clone, PartialEq, Eq)]
1226#[non_exhaustive]
1227pub enum ParseDiagnosticKind {
1228 /// The lexer reported malformed source; the original lexical kind is
1229 /// preserved so callers do not need to parse the human message.
1230 Lex(crate::lexer::LexErrorKind),
1231 /// The parser expected a specific token or token class and recovered.
1232 ExpectedToken(ExpectedToken),
1233 /// A type annotation was present but could not be parsed as a `Type`.
1234 MalformedTypeAnnotation(TypeAnnotationContext),
1235 /// A recognized construct was malformed in a way that is not only an
1236 /// expected-token miss.
1237 MalformedSyntax(MalformedSyntaxKind),
1238 /// A whole declaration could not be parsed and was skipped.
1239 SkippedDeclaration(SkippedDeclarationReason),
1240 /// The source used syntax this parser intentionally does not model.
1241 UnsupportedSyntax(UnsupportedSyntaxKind),
1242 /// Expression or pattern nesting exceeded the parser recursion bound.
1243 RecursionLimit { limit: u32 },
1244}
1245
1246impl ParseDiagnosticKind {
1247 /// Coarse diagnostic class retained for stable JSON/SARIF tags.
1248 #[must_use]
1249 pub const fn category(&self) -> DiagnosticCategory {
1250 match self {
1251 Self::Lex(_) => DiagnosticCategory::Lex,
1252 Self::ExpectedToken(_)
1253 | Self::MalformedTypeAnnotation(_)
1254 | Self::MalformedSyntax(_) => DiagnosticCategory::Malformed,
1255 Self::SkippedDeclaration(_) => DiagnosticCategory::SkippedDecl,
1256 Self::UnsupportedSyntax(_) => DiagnosticCategory::UnsupportedSyntax,
1257 Self::RecursionLimit { .. } => DiagnosticCategory::RecursionLimit,
1258 }
1259 }
1260}
1261
1262/// Expected token or token class for a recoverable parser diagnostic.
1263#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1264#[non_exhaustive]
1265pub enum ExpectedToken {
1266 WhereAfterModuleHeader,
1267 ModuleNameAfterImport,
1268 TemplateNameAfterInterfaceInstanceFor,
1269 FieldNameTypePair,
1270 EqualsAfterGuard,
1271 EqualsOrGuardedRightHandSide,
1272 ProjectionFieldAfterDot,
1273 ThenKeyword,
1274 ElseKeyword,
1275 OfKeywordInCaseExpression,
1276 ArrowInGuardedCaseAlternative,
1277 ArrowInCaseAlternative,
1278}
1279
1280/// The declaration context whose type annotation was malformed.
1281#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1282#[non_exhaustive]
1283pub enum TypeAnnotationContext {
1284 Field,
1285 Key,
1286 Choice,
1287 InterfaceMethod,
1288 Function,
1289}
1290
1291impl TypeAnnotationContext {
1292 #[must_use]
1293 pub const fn as_str(self) -> &'static str {
1294 match self {
1295 Self::Field => "field",
1296 Self::Key => "key",
1297 Self::Choice => "choice",
1298 Self::InterfaceMethod => "interface method",
1299 Self::Function => "function",
1300 }
1301 }
1302}
1303
1304/// Recoverable malformed syntax cases that are not just missing one token.
1305#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1306#[non_exhaustive]
1307pub enum MalformedSyntaxKind {
1308 FunctionEquation,
1309 FunctionParameterPattern,
1310 LambdaParameter,
1311}
1312
1313/// Why a whole declaration was skipped.
1314#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1315#[non_exhaustive]
1316pub enum SkippedDeclarationReason {
1317 TopLevelPatternBinding,
1318 UnrecognizedDeclaration,
1319}
1320
1321/// Unsupported syntax families surfaced by the parser.
1322#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1323#[non_exhaustive]
1324pub enum UnsupportedSyntaxKind {
1325 LegacyControllerCan,
1326 PatternSynonym,
1327}
1328
1329/// Parse diagnostic — never fatal under tolerant parsing.
1330///
1331/// Under [`crate::parse::parse_module`] the scan continues. Strict callers that
1332/// use [`crate::parse::parse_module_strict`] or
1333/// [`crate::parse::ParseModuleResult::into_result`] treat any diagnostic as
1334/// [`crate::parse::ParseModuleError`].
1335#[derive(Debug, Clone, PartialEq, Eq)]
1336pub struct ParseDiagnostic {
1337 /// Machine-readable reason for the diagnostic.
1338 pub kind: ParseDiagnosticKind,
1339 /// Human-readable presentation message. Use [`Self::kind`] for logic.
1340 pub message: String,
1341 pub pos: Pos,
1342 /// Byte span of the offending region. The end is the actionable addition
1343 /// over `pos`-alone; zero-width when only a point is known (lex errors,
1344 /// EOF).
1345 pub span: Span,
1346 pub category: DiagnosticCategory,
1347}
1348
1349impl ParseDiagnostic {
1350 #[must_use]
1351 pub fn new(
1352 kind: ParseDiagnosticKind,
1353 message: impl Into<String>,
1354 pos: Pos,
1355 span: Span,
1356 ) -> Self {
1357 let category = kind.category();
1358 Self {
1359 kind,
1360 message: message.into(),
1361 pos,
1362 span,
1363 category,
1364 }
1365 }
1366
1367 /// Human-readable presentation message.
1368 #[must_use]
1369 pub fn message(&self) -> &str {
1370 &self.message
1371 }
1372
1373 /// Machine-readable diagnostic reason.
1374 #[must_use]
1375 pub const fn kind(&self) -> &ParseDiagnosticKind {
1376 &self.kind
1377 }
1378
1379 /// Coarse recovery category.
1380 #[must_use]
1381 pub const fn category(&self) -> DiagnosticCategory {
1382 self.category
1383 }
1384}
1385
1386impl std::fmt::Display for ParseDiagnostic {
1387 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1388 self.message.fmt(f)
1389 }
1390}
1391
1392impl std::error::Error for ParseDiagnostic {}
1393
1394impl Expr {
1395 #[must_use]
1396 pub const fn pos(&self) -> Pos {
1397 match self {
1398 Self::Var { pos, .. }
1399 | Self::Con { pos, .. }
1400 | Self::Lit { pos, .. }
1401 | Self::App { pos, .. }
1402 | Self::BinOp { pos, .. }
1403 | Self::Neg { pos, .. }
1404 | Self::Lambda { pos, .. }
1405 | Self::If { pos, .. }
1406 | Self::Case { pos, .. }
1407 | Self::Do { pos, .. }
1408 | Self::LetIn { pos, .. }
1409 | Self::Record { pos, .. }
1410 | Self::Tuple { pos, .. }
1411 | Self::List { pos, .. }
1412 | Self::Try { pos, .. }
1413 | Self::OperatorRef { pos, .. }
1414 | Self::LeftSection { pos, .. }
1415 | Self::RightSection { pos, .. }
1416 | Self::Error { pos, .. } => *pos,
1417 }
1418 }
1419
1420 /// Byte span covering the whole expression.
1421 #[must_use]
1422 pub const fn span(&self) -> Span {
1423 match self {
1424 Self::Var { span, .. }
1425 | Self::Con { span, .. }
1426 | Self::Lit { span, .. }
1427 | Self::App { span, .. }
1428 | Self::BinOp { span, .. }
1429 | Self::Neg { span, .. }
1430 | Self::Lambda { span, .. }
1431 | Self::If { span, .. }
1432 | Self::Case { span, .. }
1433 | Self::Do { span, .. }
1434 | Self::LetIn { span, .. }
1435 | Self::Record { span, .. }
1436 | Self::Tuple { span, .. }
1437 | Self::List { span, .. }
1438 | Self::Try { span, .. }
1439 | Self::OperatorRef { span, .. }
1440 | Self::LeftSection { span, .. }
1441 | Self::RightSection { span, .. }
1442 | Self::Error { span, .. } => *span,
1443 }
1444 }
1445
1446 /// Render back to compact, source-*like* text for diagnostics and `raw`
1447 /// fields.
1448 ///
1449 /// This is **lossy and normalizing**, not byte-faithful: original layout is
1450 /// dropped (e.g. `do`/`let` statements are joined with `; `), operators and
1451 /// spacing are normalized, and comments/trivia are gone. Use it for a quick
1452 /// human-readable echo of an expression; for source-exact reconstruction use
1453 /// the node's [`span`](Self::span) into the original text (that is how
1454 /// `daml-fmt` and [`crate::ast_span::render_from_ast`] stay lossless).
1455 #[must_use]
1456 pub fn render(&self) -> String {
1457 match self {
1458 Self::Var {
1459 qualifier, name, ..
1460 }
1461 | Self::Con {
1462 qualifier, name, ..
1463 } => qualifier
1464 .as_ref()
1465 .map_or_else(|| name.to_string(), |q| format!("{q}.{name}")),
1466 Self::Lit { kind, text, .. } => match kind {
1467 LitKind::Text => format!("{text:?}"),
1468 LitKind::Char => format!("'{text}'"),
1469 _ => text.clone(),
1470 },
1471 Self::App { func, args, .. } => {
1472 let mut s = func.render_atomic();
1473 for a in args {
1474 s.push(' ');
1475 s.push_str(&a.render_atomic());
1476 }
1477 s
1478 }
1479 Self::BinOp { op, lhs, rhs, .. } => {
1480 if *op == "." {
1481 // Record projection / composition: `account.custodian`.
1482 format!("{}.{}", lhs.render_atomic(), rhs.render_atomic())
1483 } else {
1484 format!("{} {} {}", lhs.render_atomic(), op, rhs.render_atomic())
1485 }
1486 }
1487 Self::Neg { expr, .. } => format!("-{}", expr.render_atomic()),
1488 Self::Lambda { params, body, .. } => {
1489 let ps: Vec<String> = params.iter().map(|p| p.render()).collect();
1490 format!("\\{} -> {}", ps.join(" "), body.render())
1491 }
1492 Self::If {
1493 cond,
1494 then_branch,
1495 else_branch,
1496 ..
1497 } => format!(
1498 "if {} then {} else {}",
1499 cond.render(),
1500 then_branch.render(),
1501 else_branch.render()
1502 ),
1503 Self::Case {
1504 scrutinee, alts, ..
1505 } => {
1506 let arms: Vec<String> = alts.iter().map(render_alt).collect();
1507 format!("case {} of {}", scrutinee.render(), arms.join("; "))
1508 }
1509 Self::Do { stmts, .. } => {
1510 let body: Vec<String> = stmts.iter().map(render_do_stmt).collect();
1511 format!("do {}", body.join("; "))
1512 }
1513 Self::LetIn { bindings, body, .. } => {
1514 let bs: Vec<String> = bindings.iter().map(render_binding).collect();
1515 format!("let {} in {}", bs.join("; "), body.render())
1516 }
1517 Self::Record { base, fields, .. } => {
1518 let fs: Vec<String> = fields
1519 .iter()
1520 .map(|f| match f {
1521 FieldAssign::Assign { name, value, .. } => {
1522 format!("{} = {}", name, value.render())
1523 }
1524 FieldAssign::Pun { name, .. } => name.to_string(),
1525 FieldAssign::Wildcard { .. } => "..".to_string(),
1526 })
1527 .collect();
1528 format!("{} with {}", base.render_atomic(), fs.join("; "))
1529 }
1530 Self::Tuple { items, .. } => {
1531 let xs: Vec<String> = items.iter().map(|e| e.render()).collect();
1532 format!("({})", xs.join(", "))
1533 }
1534 Self::List { items, .. } => {
1535 let xs: Vec<String> = items.iter().map(|e| e.render()).collect();
1536 format!("[{}]", xs.join(", "))
1537 }
1538 Self::Try { body, handlers, .. } => {
1539 let hs: Vec<String> = handlers.iter().map(render_alt).collect();
1540 format!("try {} catch {}", body.render(), hs.join("; "))
1541 }
1542 Self::OperatorRef { op, .. } => format!("({op})"),
1543 Self::LeftSection { op, operand, .. } => format!("({} {})", operand.render(), op),
1544 Self::RightSection { op, operand, .. } => format!("({} {})", op, operand.render()),
1545 Self::Error { raw, .. } => raw.clone(),
1546 }
1547 }
1548
1549 /// Render with parentheses if this expression wouldn't survive as an
1550 /// application argument.
1551 fn render_atomic(&self) -> String {
1552 match self {
1553 Self::Var { .. }
1554 | Self::Con { .. }
1555 | Self::Lit { .. }
1556 | Self::Tuple { .. }
1557 | Self::List { .. }
1558 | Self::OperatorRef { .. }
1559 | Self::LeftSection { .. }
1560 | Self::RightSection { .. }
1561 | Self::Error { .. } => self.render(),
1562 _ => format!("({})", self.render()),
1563 }
1564 }
1565
1566 /// The head of an application spine: for `Foo.exercise cid X`, the
1567 /// `Foo.exercise` Var. For non-apps, the expression itself.
1568 #[must_use]
1569 pub fn application_head(&self) -> &Self {
1570 match self {
1571 Self::App { func, .. } => func.application_head(),
1572 _ => self,
1573 }
1574 }
1575
1576 /// Application arguments, empty for non-apps. The `App` spine is flattened
1577 /// (see the [`App`](Self::App) variant), so for `f a b c` this returns all
1578 /// three arguments `[a, b, c]`, not a single curried layer.
1579 #[must_use]
1580 pub fn application_args(&self) -> &[Self] {
1581 match self {
1582 Self::App { args, .. } => args,
1583 _ => &[],
1584 }
1585 }
1586}
1587
1588fn render_guard_qualifier(guard: &GuardQualifier) -> String {
1589 match guard {
1590 GuardQualifier::Bool { expr, .. } => expr.render(),
1591 GuardQualifier::Pattern { pat, expr, .. } => {
1592 format!("{} <- {}", pat.render(), expr.render())
1593 }
1594 }
1595}
1596
1597fn render_alt(alt: &Alt) -> String {
1598 let mut rendered = if alt.branches.len() == 1 && alt.branches[0].guards.is_empty() {
1599 format!("{} -> {}", alt.pat.render(), alt.branches[0].body.render())
1600 } else {
1601 let mut parts = vec![alt.pat.render()];
1602 for branch in &alt.branches {
1603 let guards: Vec<String> = branch.guards.iter().map(render_guard_qualifier).collect();
1604 parts.push(format!(
1605 "| {} -> {}",
1606 guards.join(", "),
1607 branch.body.render()
1608 ));
1609 }
1610 parts.join(" ")
1611 };
1612 if !alt.where_bindings.is_empty() {
1613 use std::fmt::Write;
1614 let bindings: Vec<String> = alt.where_bindings.iter().map(render_binding).collect();
1615 let _ = write!(rendered, " where {}", bindings.join("; "));
1616 }
1617 rendered
1618}
1619
1620fn render_do_stmt(s: &DoStmt) -> String {
1621 match s {
1622 DoStmt::Bind { pat, expr, .. } => format!("{} <- {}", pat.render(), expr.render()),
1623 DoStmt::Let { bindings, .. } => {
1624 let bs: Vec<String> = bindings.iter().map(render_binding).collect();
1625 format!("let {}", bs.join("; "))
1626 }
1627 DoStmt::Expr { expr, .. } => expr.render(),
1628 }
1629}
1630
1631fn render_binding(b: &Binding) -> String {
1632 let mut s = b.pat.render();
1633 for p in &b.params {
1634 s.push(' ');
1635 s.push_str(&p.render());
1636 }
1637 format!("{} = {}", s, b.expr.render())
1638}
1639
1640impl Pat {
1641 #[must_use]
1642 pub const fn pos(&self) -> Pos {
1643 match self {
1644 Self::Var { pos, .. }
1645 | Self::Wild { pos, .. }
1646 | Self::Con { pos, .. }
1647 | Self::Record { pos, .. }
1648 | Self::Tuple { pos, .. }
1649 | Self::List { pos, .. }
1650 | Self::Lit { pos, .. }
1651 | Self::As { pos, .. }
1652 | Self::Other { pos, .. } => *pos,
1653 }
1654 }
1655
1656 /// Byte span covering the whole pattern.
1657 #[must_use]
1658 pub const fn span(&self) -> Span {
1659 match self {
1660 Self::Var { span, .. }
1661 | Self::Wild { span, .. }
1662 | Self::Con { span, .. }
1663 | Self::Record { span, .. }
1664 | Self::Tuple { span, .. }
1665 | Self::List { span, .. }
1666 | Self::Lit { span, .. }
1667 | Self::As { span, .. }
1668 | Self::Other { span, .. } => *span,
1669 }
1670 }
1671
1672 /// Render back to compact, source-*like* text. Lossy and normalizing in the
1673 /// same way as [`Expr::render`]; use the node's [`span`](Self::span) for
1674 /// byte-faithful text.
1675 #[must_use]
1676 pub fn render(&self) -> String {
1677 match self {
1678 Self::Var { name, .. } => name.to_string(),
1679 Self::Wild { .. } => "_".to_string(),
1680 Self::Con {
1681 qualifier,
1682 name,
1683 args,
1684 ..
1685 } => {
1686 let head = qualifier
1687 .as_ref()
1688 .map_or_else(|| name.to_string(), |q| format!("{q}.{name}"));
1689 if args.is_empty() {
1690 head
1691 } else {
1692 let parts: Vec<String> = args.iter().map(|p| p.render()).collect();
1693 format!("({} {})", head, parts.join(" "))
1694 }
1695 }
1696 Self::Record {
1697 qualifier,
1698 name,
1699 syntax,
1700 fields,
1701 ..
1702 } => {
1703 let head = qualifier
1704 .as_ref()
1705 .map_or_else(|| name.to_string(), |q| format!("{q}.{name}"));
1706 let fs: Vec<String> = fields
1707 .iter()
1708 .map(|f| match f {
1709 PatFieldAssign::Assign { name, pat, .. } => {
1710 format!("{} = {}", name, pat.render())
1711 }
1712 PatFieldAssign::Pun { name, .. } => name.to_string(),
1713 PatFieldAssign::Wildcard { .. } => "..".to_string(),
1714 })
1715 .collect();
1716 match syntax {
1717 RecordPatternSyntax::Braces => format!("{} {{ {} }}", head, fs.join(", ")),
1718 RecordPatternSyntax::With => format!("{} with {}", head, fs.join("; ")),
1719 }
1720 }
1721 Self::Tuple { items, .. } => {
1722 let xs: Vec<String> = items.iter().map(|p| p.render()).collect();
1723 format!("({})", xs.join(", "))
1724 }
1725 Self::List { items, .. } => {
1726 let xs: Vec<String> = items.iter().map(|p| p.render()).collect();
1727 format!("[{}]", xs.join(", "))
1728 }
1729 Self::Lit { kind, text, .. } => match kind {
1730 LitKind::Text => format!("{text:?}"),
1731 LitKind::Char => format!("'{text}'"),
1732 _ => text.clone(),
1733 },
1734 Self::As { name, pat, .. } => format!("{}@{}", name, pat.render()),
1735 Self::Other { raw, .. } => raw.clone(),
1736 }
1737 }
1738}
1739
1740impl std::fmt::Display for Expr {
1741 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1742 f.write_str(&self.render())
1743 }
1744}
1745
1746impl std::fmt::Display for Pat {
1747 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1748 f.write_str(&self.render())
1749 }
1750}
1751
1752// Span invariants for the lossless AST tile layer; render-shape tests live in integration tests.
1753#[cfg(test)]
1754mod tests {
1755 use super::*;
1756
1757 fn span(start: usize, end: usize) -> Span {
1758 Span::from_usize(start, end)
1759 }
1760
1761 #[test]
1762 fn span_distinguishes_empty_from_invalid() {
1763 assert!(span(3, 3).is_valid());
1764 assert!(span(3, 3).is_empty());
1765
1766 assert!(!span(4, 3).is_valid());
1767 assert!(!span(4, 3).is_empty());
1768 }
1769
1770 #[test]
1771 fn contains_rejects_invalid_spans() {
1772 let parent = span(1, 10);
1773
1774 assert!(parent.contains(&span(3, 7)));
1775 assert!(!parent.contains(&span(7, 3)));
1776 assert!(!span(10, 1).contains(&span(3, 7)));
1777 }
1778
1779 #[test]
1780 fn span_range_and_get_share_source_bytes_safely() {
1781 let source = "foo: Int";
1782
1783 assert_eq!(span(0, 3).range(), 0..3);
1784 assert_eq!(span(0, 3).get(source), Some("foo"));
1785 assert_eq!(span(3, 7).get(source), Some(": In"));
1786 assert!(span(3, 100).get(source).is_none());
1787 }
1788}