Skip to main content

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}