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//! Every node carries a source position and byte span. Downstream crates
5//! consume this tree directly: daml-fmt re-prints layout from the spans, and
6//! daml-lint lowers it onto its own rule-facing IR.
7
8pub use crate::lexer::Pos;
9
10/// Byte span of an AST node.
11///
12/// `[start, end)` into the original source, same basis as `Token::start`/
13/// `Token::end`. Covers every (non-virtual) token that belongs to the node —
14/// first token's `start` to last token's `end`.
15///
16/// Invariants the parser maintains (checked over the corpus by
17/// `render_from_ast`): a child's span is contained in its parent's span, and
18/// sibling spans are ordered and non-overlapping. Trivia (comments, blank
19/// lines) live *between* sibling spans.
20#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
21pub struct Span {
22    pub start: usize,
23    pub end: usize,
24}
25
26impl Span {
27    pub const fn new(start: usize, end: usize) -> Self {
28        Self { start, end }
29    }
30
31    pub const fn is_empty(&self) -> bool {
32        self.start >= self.end
33    }
34
35    /// `self` fully contains `other`.
36    pub const fn contains(&self, other: &Self) -> bool {
37        self.start <= other.start && other.end <= self.end
38    }
39}
40
41#[derive(Debug, Clone, PartialEq, Eq)]
42pub enum LitKind {
43    Int,
44    Decimal,
45    Text,
46    Char,
47}
48
49#[derive(Debug, Clone)]
50pub struct FieldAssign {
51    pub name: String,
52    /// None for record puns (`Foo with owner` meaning `owner = owner`)
53    /// and `..` wildcards.
54    pub value: Option<Expr>,
55    pub pos: Pos,
56    pub span: Span,
57}
58
59#[derive(Debug, Clone)]
60pub struct Alt {
61    pub pat: Pat,
62    pub body: Expr,
63    pub pos: Pos,
64    pub span: Span,
65}
66
67#[derive(Debug, Clone)]
68pub struct Binding {
69    /// Left-hand side: a variable with parameters, or a destructuring pattern.
70    pub pat: Pat,
71    /// Parameter patterns when the LHS is a function binding (`f x y = ...`).
72    pub params: Vec<Pat>,
73    pub expr: Expr,
74    pub pos: Pos,
75    pub span: Span,
76}
77
78#[derive(Debug, Clone)]
79pub enum Pat {
80    Var {
81        name: String,
82        pos: Pos,
83        span: Span,
84    },
85    Wild {
86        pos: Pos,
87        span: Span,
88    },
89    Con {
90        qualifier: Option<String>,
91        name: String,
92        args: Vec<Self>,
93        pos: Pos,
94        span: Span,
95    },
96    Tuple {
97        items: Vec<Self>,
98        pos: Pos,
99        span: Span,
100    },
101    List {
102        items: Vec<Self>,
103        pos: Pos,
104        span: Span,
105    },
106    Lit {
107        kind: LitKind,
108        text: String,
109        pos: Pos,
110        span: Span,
111    },
112    /// `name@pat`
113    As {
114        name: String,
115        pat: Box<Self>,
116        pos: Pos,
117        span: Span,
118    },
119    /// Anything the parser couldn't classify; raw text preserved.
120    Other {
121        raw: String,
122        pos: Pos,
123        span: Span,
124    },
125}
126
127#[derive(Debug, Clone)]
128pub enum Expr {
129    /// Lowercase variable reference, possibly qualified.
130    Var {
131        qualifier: Option<String>,
132        name: String,
133        pos: Pos,
134        span: Span,
135    },
136    /// Constructor / data-constructor reference, possibly qualified.
137    Con {
138        qualifier: Option<String>,
139        name: String,
140        pos: Pos,
141        span: Span,
142    },
143    Lit {
144        kind: LitKind,
145        text: String,
146        pos: Pos,
147        span: Span,
148    },
149    /// Application, flattened: `f a b c` is one App with three args.
150    App {
151        func: Box<Self>,
152        args: Vec<Self>,
153        pos: Pos,
154        span: Span,
155    },
156    /// Binary operator application with source-level operator text.
157    BinOp {
158        op: String,
159        lhs: Box<Self>,
160        rhs: Box<Self>,
161        pos: Pos,
162        span: Span,
163    },
164    /// Unary negation.
165    Neg {
166        expr: Box<Self>,
167        pos: Pos,
168        span: Span,
169    },
170    Lambda {
171        params: Vec<Pat>,
172        body: Box<Self>,
173        pos: Pos,
174        span: Span,
175    },
176    If {
177        cond: Box<Self>,
178        then_branch: Box<Self>,
179        else_branch: Box<Self>,
180        pos: Pos,
181        span: Span,
182    },
183    Case {
184        scrutinee: Box<Self>,
185        alts: Vec<Alt>,
186        pos: Pos,
187        span: Span,
188    },
189    Do {
190        stmts: Vec<DoStmt>,
191        pos: Pos,
192        span: Span,
193    },
194    LetIn {
195        bindings: Vec<Binding>,
196        body: Box<Self>,
197        pos: Pos,
198        span: Span,
199    },
200    /// `base with f = e, ...` — record construction when base is a Con,
201    /// record update otherwise.
202    Record {
203        base: Box<Self>,
204        fields: Vec<FieldAssign>,
205        pos: Pos,
206        span: Span,
207    },
208    Tuple {
209        items: Vec<Self>,
210        pos: Pos,
211        span: Span,
212    },
213    List {
214        items: Vec<Self>,
215        pos: Pos,
216        span: Span,
217    },
218    /// `try <body> catch <alts>`
219    Try {
220        body: Box<Self>,
221        handlers: Vec<Alt>,
222        pos: Pos,
223        span: Span,
224    },
225    /// Right operator section like `(+ 1)` / left section `(1 +)`.
226    Section {
227        op: String,
228        operand: Option<Box<Self>>,
229        left: bool,
230        pos: Pos,
231        span: Span,
232    },
233    /// Expression the parser could not understand; raw text preserved so
234    /// a parse failure degrades to the shim's behavior instead of dying.
235    Error { raw: String, pos: Pos, span: Span },
236}
237
238#[derive(Debug, Clone)]
239pub enum DoStmt {
240    /// `pat <- expr`
241    Bind {
242        pat: Pat,
243        expr: Expr,
244        pos: Pos,
245        span: Span,
246    },
247    /// `let x = e` (no `in`) inside a do block.
248    Let {
249        bindings: Vec<Binding>,
250        pos: Pos,
251        span: Span,
252    },
253    /// Bare expression statement.
254    Expr { expr: Expr, pos: Pos, span: Span },
255}
256
257/// Structured Daml type, parsed from the real token stream.
258///
259/// Scoped to the forms the corpus actually contains; it exists so downstream
260/// analysis can tell a type *application* from a *function arrow* from an
261/// atomic constructor — a distinction a string matcher structurally cannot make.
262/// Every node carries a byte span so consumers can render exact source text from
263/// `(source, span)`.
264#[derive(Debug, Clone)]
265pub enum Type {
266    /// Type constructor, possibly qualified: `Party`, `DA.Map.Map`.
267    Con {
268        qualifier: Option<String>,
269        name: String,
270        span: Span,
271    },
272    /// Type application, head applied to one or more args: `ContractId Foo`,
273    /// `Map Text Int`, `Script ()`. Type-level nat literals (the `10` in
274    /// `Numeric 10`) are NOT types, so they are dropped from the arg list — a
275    /// `Numeric 10` collapses to the bare head `Con "Numeric"`.
276    App(Box<Self>, Vec<Self>, Span),
277    /// List type `[T]`.
278    List(Box<Self>, Span),
279    /// Tuple type `(a, b, ...)`.
280    Tuple(Vec<Self>, Span),
281    /// Function type `a -> b` (right-associative).
282    Fun(Box<Self>, Box<Self>, Span),
283    /// Lowercase type variable: `a`, `n`.
284    Var(String, Span),
285    /// The unit type `()`.
286    Unit(Span),
287    /// A constrained type `C a => T`: the context is dropped (no detector
288    /// reasons about constraints), the body `T` is kept.
289    Constrained(Box<Self>, Span),
290}
291
292impl Type {
293    pub const fn span(&self) -> Span {
294        match self {
295            Self::Con { span, .. }
296            | Self::App(_, _, span)
297            | Self::List(_, span)
298            | Self::Tuple(_, span)
299            | Self::Fun(_, _, span)
300            | Self::Var(_, span)
301            | Self::Unit(span)
302            | Self::Constrained(_, span) => *span,
303        }
304    }
305
306    pub(crate) const fn with_span(mut self, span: Span) -> Self {
307        match &mut self {
308            Self::Con { span: s, .. }
309            | Self::App(_, _, s)
310            | Self::List(_, s)
311            | Self::Tuple(_, s)
312            | Self::Fun(_, _, s)
313            | Self::Var(_, s)
314            | Self::Unit(s)
315            | Self::Constrained(_, s) => *s = span,
316        }
317        self
318    }
319}
320
321impl PartialEq for Type {
322    fn eq(&self, other: &Self) -> bool {
323        match (self, other) {
324            (
325                Self::Con {
326                    qualifier: aq,
327                    name: an,
328                    ..
329                },
330                Self::Con {
331                    qualifier: bq,
332                    name: bn,
333                    ..
334                },
335            ) => aq == bq && an == bn,
336            (Self::App(ah, aa, _), Self::App(bh, ba, _)) => ah == bh && aa == ba,
337            (Self::List(a, _), Self::List(b, _)) => a == b,
338            (Self::Tuple(a, _), Self::Tuple(b, _)) => a == b,
339            (Self::Fun(al, ar, _), Self::Fun(bl, br, _)) => al == bl && ar == br,
340            (Self::Var(a, _), Self::Var(b, _)) => a == b,
341            (Self::Unit(_), Self::Unit(_)) => true,
342            (Self::Constrained(a, _), Self::Constrained(b, _)) => a == b,
343            _ => false,
344        }
345    }
346}
347
348#[derive(Debug, Clone)]
349pub struct FieldDecl {
350    pub name: String,
351    /// Structured field type parsed from the token stream. `None` when the type
352    /// could not be parsed cleanly (analysis treats it as unknown).
353    pub ty: Option<Type>,
354    pub pos: Pos,
355    pub span: Span,
356}
357
358#[derive(Debug, Clone, Copy, PartialEq, Eq)]
359pub enum Consuming {
360    Consuming,
361    NonConsuming,
362    PreConsuming,
363    PostConsuming,
364}
365
366#[derive(Debug, Clone)]
367pub struct ChoiceDecl {
368    pub name: String,
369    pub consuming: Consuming,
370    /// Structured return type. `None` if it could not be parsed cleanly or the
371    /// choice declared no return type.
372    pub return_ty: Option<Type>,
373    pub params: Vec<FieldDecl>,
374    /// Comma-separated controller expressions.
375    pub controllers: Vec<Expr>,
376    /// Choice observers, if any.
377    pub observers: Vec<Expr>,
378    pub body: Option<Expr>,
379    pub pos: Pos,
380    pub span: Span,
381}
382
383#[derive(Debug, Clone)]
384pub enum TemplateBodyDecl {
385    Signatory {
386        parties: Vec<Expr>,
387        pos: Pos,
388        span: Span,
389    },
390    Observer {
391        parties: Vec<Expr>,
392        pos: Pos,
393        span: Span,
394    },
395    Ensure {
396        expr: Expr,
397        pos: Pos,
398        span: Span,
399    },
400    Key {
401        expr: Expr,
402        /// Structured key type. `None` if absent or not cleanly parseable.
403        ty: Option<Type>,
404        pos: Pos,
405        span: Span,
406    },
407    Maintainer {
408        expr: Expr,
409        pos: Pos,
410        span: Span,
411    },
412    Choice(ChoiceDecl),
413    InterfaceInstance(InterfaceInstanceDecl),
414    /// `agreement`, `let` blocks, deprecated `controller ... can`, etc.
415    Other {
416        raw: String,
417        pos: Pos,
418        span: Span,
419    },
420}
421
422#[derive(Debug, Clone)]
423pub struct InterfaceInstanceDecl {
424    /// Interface being implemented (`Disclosure.I`).
425    pub interface_name: String,
426    /// Template it is for (from `for Foo`); the enclosing template when
427    /// declared inside one.
428    pub for_template: String,
429    /// Method implementations: name → bound expression.
430    pub methods: Vec<Binding>,
431    pub pos: Pos,
432    pub span: Span,
433}
434
435#[derive(Debug, Clone)]
436pub struct TemplateDecl {
437    pub name: String,
438    pub fields: Vec<FieldDecl>,
439    pub body: Vec<TemplateBodyDecl>,
440    pub pos: Pos,
441    pub span: Span,
442}
443
444#[derive(Debug, Clone)]
445pub struct InterfaceDecl {
446    pub name: String,
447    /// Interfaces this interface requires (`requires Lockable.I, ...`).
448    pub requires: Vec<String>,
449    pub viewtype: Option<String>,
450    /// Method signatures: name and type text.
451    pub methods: Vec<FieldDecl>,
452    pub choices: Vec<ChoiceDecl>,
453    pub pos: Pos,
454    pub span: Span,
455}
456
457#[derive(Debug, Clone)]
458pub struct Equation {
459    pub params: Vec<Pat>,
460    pub body: Expr,
461    /// Guarded equations keep their guards as (guard, body) pairs; `body`
462    /// then holds the first guarded body for convenience.
463    pub guards: Vec<(Expr, Expr)>,
464    /// `where` helper bindings attached to this equation.
465    pub where_bindings: Vec<Binding>,
466    pub pos: Pos,
467    pub span: Span,
468}
469
470#[derive(Debug, Clone)]
471pub struct FunctionDecl {
472    pub name: String,
473    pub ty: Option<Type>,
474    pub equations: Vec<Equation>,
475    pub pos: Pos,
476    /// Span of the function's first appearance (signature or first equation).
477    /// Convenience anchor; a multi-equation function's precise ranges are the
478    /// per-`Equation` spans, since equations need not be contiguous in source.
479    pub span: Span,
480    /// Span of the standalone type signature `name : Type`, if one was seen.
481    pub sig_span: Option<Span>,
482}
483
484#[derive(Debug, Clone)]
485pub struct ImportDecl {
486    pub module_name: String,
487    pub qualified: bool,
488    pub alias: Option<String>,
489    pub pos: Pos,
490    pub span: Span,
491}
492
493/// One constructor alternative in a `data`/`newtype` declaration.
494///
495/// Additive analysis truth alongside the decl's opaque `keyword`/`name`; like
496/// [`Type`] it is span-bearing but never rendered, so it stays invisible to
497/// daml-fmt (which lays out from byte spans).
498///
499/// The structured form models the common corpus shapes (record `with`/`{}`
500/// constructors, positional constructors, enum alternatives). It is partial by
501/// design: rather than record a half-truth, the parser leaves a whole decl's
502/// `constructors` empty (opaque) when it meets a shape it does not model —
503/// infix constructors (`data T = Int :+: Int`), strictness bangs
504/// (`data T = T !Int`), GADT (`data T where`), and existential/context
505/// constructors (`forall a. MkT a`). One additional accepted gap keeps the
506/// *first* constructor only: a single-line record sum where each alternative is
507/// a `with` block (`data T = A with x : Int | B with y : Int`); the multi-line
508/// corpus form parses fully.
509#[derive(Debug, Clone)]
510pub struct DataConstructor {
511    pub name: String,
512    /// Record fields when the constructor uses record syntax
513    /// (`Asset with amount : Decimal` or `Foo { x : Int }`). Empty for
514    /// positional and nullary constructors.
515    pub fields: Vec<FieldDecl>,
516    /// Positional argument types for non-record constructors
517    /// (`Node Int Text` → `[Int, Text]`, `Money Decimal` → `[Decimal]`). Empty
518    /// for record and nullary constructors, or when the arguments did not parse
519    /// cleanly (the constructor is then recorded name-only).
520    pub arg_types: Vec<Type>,
521    pub pos: Pos,
522    pub span: Span,
523}
524
525#[derive(Debug, Clone)]
526pub enum Decl {
527    Template(TemplateDecl),
528    Interface(InterfaceDecl),
529    Function(FunctionDecl),
530    /// data/type/class/instance/exception — recorded with name + span.
531    TypeDef {
532        keyword: String,
533        name: String,
534        /// Constructors of a `data`/`newtype` declaration, structured. Empty for
535        /// `type` synonyms, `class`/`instance`/`exception`, and any body that
536        /// did not parse cleanly (the decl stays opaque, as before).
537        constructors: Vec<DataConstructor>,
538        /// Aliased type of a `type` synonym (`type Name = T`), structured.
539        /// `None` for non-synonyms or an unparseable right-hand side.
540        synonym: Option<Type>,
541        /// Class names from a `deriving (...)` clause, flattened. Empty when
542        /// there is no deriving clause.
543        deriving: Vec<String>,
544        pos: Pos,
545        span: Span,
546    },
547    /// Anything unparseable at the top level (diagnostic already emitted).
548    Unknown {
549        raw: String,
550        pos: Pos,
551        span: Span,
552    },
553}
554
555#[derive(Debug, Clone)]
556pub struct Module {
557    pub name: String,
558    pub pos: Pos,
559    /// Whole-module extent: `[0, source.len())`. Container for all decls.
560    pub span: Span,
561    /// Span of the `module M (...) where` header clause; empty when the file
562    /// has no module header. Lets the span oracle treat header tokens as
563    /// covered without a dedicated header node.
564    pub header: Span,
565    pub imports: Vec<ImportDecl>,
566    pub decls: Vec<Decl>,
567}
568
569/// Why a [`ParseDiagnostic`] fired.
570///
571/// Lets a consumer separate syntax the parser deliberately does not model (still
572/// safe, just unanalyzed) from a genuine malformation, a recursion-limit
573/// degradation, or a lexical error.
574#[derive(Debug, Clone, Copy, PartialEq, Eq)]
575pub enum DiagnosticCategory {
576    /// A whole declaration could not be parsed and was skipped to the next item.
577    SkippedDecl,
578    /// A malformed expression, pattern, or expected-token error inside an
579    /// otherwise-recognized construct.
580    Malformed,
581    /// A construct the parser intentionally does not support, e.g. legacy
582    /// `controller ... can` choice syntax.
583    UnsupportedSyntax,
584    /// Expression/pattern nesting exceeded the recursion bound and was degraded
585    /// to raw text.
586    RecursionLimit,
587    /// A lexical error (unterminated string/comment, stray character).
588    Lex,
589}
590
591impl DiagnosticCategory {
592    /// Stable kebab-case tag for machine-readable output (JSON/SARIF) and logs.
593    pub const fn as_str(self) -> &'static str {
594        match self {
595            Self::SkippedDecl => "skipped-declaration",
596            Self::Malformed => "malformed",
597            Self::UnsupportedSyntax => "unsupported-syntax",
598            Self::RecursionLimit => "recursion-limit",
599            Self::Lex => "lexical-error",
600        }
601    }
602}
603
604/// Parse diagnostic — never fatal; the scan continues.
605#[derive(Debug, Clone)]
606pub struct ParseDiagnostic {
607    pub message: String,
608    pub pos: Pos,
609    /// Byte span of the offending region. The end is the actionable addition
610    /// over `pos`-alone; zero-width when only a point is known (lex errors,
611    /// EOF).
612    pub span: Span,
613    pub category: DiagnosticCategory,
614}
615
616impl Expr {
617    pub const fn pos(&self) -> Pos {
618        match self {
619            Self::Var { pos, .. }
620            | Self::Con { pos, .. }
621            | Self::Lit { pos, .. }
622            | Self::App { pos, .. }
623            | Self::BinOp { pos, .. }
624            | Self::Neg { pos, .. }
625            | Self::Lambda { pos, .. }
626            | Self::If { pos, .. }
627            | Self::Case { pos, .. }
628            | Self::Do { pos, .. }
629            | Self::LetIn { pos, .. }
630            | Self::Record { pos, .. }
631            | Self::Tuple { pos, .. }
632            | Self::List { pos, .. }
633            | Self::Try { pos, .. }
634            | Self::Section { pos, .. }
635            | Self::Error { pos, .. } => *pos,
636        }
637    }
638
639    /// Byte span covering the whole expression.
640    pub const fn span(&self) -> Span {
641        match self {
642            Self::Var { span, .. }
643            | Self::Con { span, .. }
644            | Self::Lit { span, .. }
645            | Self::App { span, .. }
646            | Self::BinOp { span, .. }
647            | Self::Neg { span, .. }
648            | Self::Lambda { span, .. }
649            | Self::If { span, .. }
650            | Self::Case { span, .. }
651            | Self::Do { span, .. }
652            | Self::LetIn { span, .. }
653            | Self::Record { span, .. }
654            | Self::Tuple { span, .. }
655            | Self::List { span, .. }
656            | Self::Try { span, .. }
657            | Self::Section { span, .. }
658            | Self::Error { span, .. } => *span,
659        }
660    }
661
662    /// Render back to compact source-like text (raw-field compatibility).
663    pub fn render(&self) -> String {
664        match self {
665            Self::Var {
666                qualifier, name, ..
667            }
668            | Self::Con {
669                qualifier, name, ..
670            } => qualifier
671                .as_ref()
672                .map_or_else(|| name.clone(), |q| format!("{}.{}", q, name)),
673            Self::Lit { kind, text, .. } => match kind {
674                LitKind::Text => format!("{:?}", text),
675                LitKind::Char => format!("'{}'", text),
676                _ => text.clone(),
677            },
678            Self::App { func, args, .. } => {
679                let mut s = func.render_atomic();
680                for a in args {
681                    s.push(' ');
682                    s.push_str(&a.render_atomic());
683                }
684                s
685            }
686            Self::BinOp { op, lhs, rhs, .. } => {
687                if op == "." {
688                    // Record projection / composition: `account.custodian`.
689                    format!("{}.{}", lhs.render_atomic(), rhs.render_atomic())
690                } else {
691                    format!("{} {} {}", lhs.render_atomic(), op, rhs.render_atomic())
692                }
693            }
694            Self::Neg { expr, .. } => format!("-{}", expr.render_atomic()),
695            Self::Lambda { params, body, .. } => {
696                let ps: Vec<String> = params.iter().map(|p| p.render()).collect();
697                format!("\\{} -> {}", ps.join(" "), body.render())
698            }
699            Self::If {
700                cond,
701                then_branch,
702                else_branch,
703                ..
704            } => format!(
705                "if {} then {} else {}",
706                cond.render(),
707                then_branch.render(),
708                else_branch.render()
709            ),
710            Self::Case {
711                scrutinee, alts, ..
712            } => {
713                let arms: Vec<String> = alts
714                    .iter()
715                    .map(|a| format!("{} -> {}", a.pat.render(), a.body.render()))
716                    .collect();
717                format!("case {} of {}", scrutinee.render(), arms.join("; "))
718            }
719            Self::Do { stmts, .. } => {
720                let body: Vec<String> = stmts.iter().map(render_do_stmt).collect();
721                format!("do {}", body.join("; "))
722            }
723            Self::LetIn { bindings, body, .. } => {
724                let bs: Vec<String> = bindings.iter().map(render_binding).collect();
725                format!("let {} in {}", bs.join("; "), body.render())
726            }
727            Self::Record { base, fields, .. } => {
728                let fs: Vec<String> = fields
729                    .iter()
730                    .map(|f| {
731                        f.value.as_ref().map_or_else(
732                            || f.name.clone(),
733                            |v| format!("{} = {}", f.name, v.render()),
734                        )
735                    })
736                    .collect();
737                format!("{} with {}", base.render_atomic(), fs.join("; "))
738            }
739            Self::Tuple { items, .. } => {
740                let xs: Vec<String> = items.iter().map(|e| e.render()).collect();
741                format!("({})", xs.join(", "))
742            }
743            Self::List { items, .. } => {
744                let xs: Vec<String> = items.iter().map(|e| e.render()).collect();
745                format!("[{}]", xs.join(", "))
746            }
747            Self::Try { body, handlers, .. } => {
748                let hs: Vec<String> = handlers
749                    .iter()
750                    .map(|a| format!("{} -> {}", a.pat.render(), a.body.render()))
751                    .collect();
752                format!("try {} catch {}", body.render(), hs.join("; "))
753            }
754            Self::Section {
755                op, operand, left, ..
756            } => match (operand, left) {
757                (Some(e), true) => format!("({} {})", e.render(), op),
758                (Some(e), false) => format!("({} {})", op, e.render()),
759                (None, _) => format!("({})", op),
760            },
761            Self::Error { raw, .. } => raw.clone(),
762        }
763    }
764
765    /// Render with parentheses if this expression wouldn't survive as an
766    /// application argument.
767    fn render_atomic(&self) -> String {
768        match self {
769            Self::Var { .. }
770            | Self::Con { .. }
771            | Self::Lit { .. }
772            | Self::Tuple { .. }
773            | Self::List { .. }
774            | Self::Section { .. }
775            | Self::Error { .. } => self.render(),
776            _ => format!("({})", self.render()),
777        }
778    }
779
780    /// The head of an application spine: for `Foo.exercise cid X`, the
781    /// `Foo.exercise` Var. For non-apps, the expression itself.
782    pub fn app_head(&self) -> &Self {
783        match self {
784            Self::App { func, .. } => func.app_head(),
785            _ => self,
786        }
787    }
788
789    /// Application arguments, empty for non-apps.
790    pub fn app_args(&self) -> &[Self] {
791        match self {
792            Self::App { args, .. } => args,
793            _ => &[],
794        }
795    }
796
797    /// Is the head an unqualified variable with this exact name?
798    pub fn head_is(&self, name: &str) -> bool {
799        matches!(
800            self.app_head(),
801            Self::Var { qualifier: None, name: n, .. } if n == name
802        )
803    }
804}
805
806pub fn render_do_stmt(s: &DoStmt) -> String {
807    match s {
808        DoStmt::Bind { pat, expr, .. } => format!("{} <- {}", pat.render(), expr.render()),
809        DoStmt::Let { bindings, .. } => {
810            let bs: Vec<String> = bindings.iter().map(render_binding).collect();
811            format!("let {}", bs.join("; "))
812        }
813        DoStmt::Expr { expr, .. } => expr.render(),
814    }
815}
816
817pub fn render_binding(b: &Binding) -> String {
818    let mut s = b.pat.render();
819    for p in &b.params {
820        s.push(' ');
821        s.push_str(&p.render());
822    }
823    format!("{} = {}", s, b.expr.render())
824}
825
826impl Pat {
827    pub const fn pos(&self) -> Pos {
828        match self {
829            Self::Var { pos, .. }
830            | Self::Wild { pos, .. }
831            | Self::Con { pos, .. }
832            | Self::Tuple { pos, .. }
833            | Self::List { pos, .. }
834            | Self::Lit { pos, .. }
835            | Self::As { pos, .. }
836            | Self::Other { pos, .. } => *pos,
837        }
838    }
839
840    /// Byte span covering the whole pattern.
841    pub const fn span(&self) -> Span {
842        match self {
843            Self::Var { span, .. }
844            | Self::Wild { span, .. }
845            | Self::Con { span, .. }
846            | Self::Tuple { span, .. }
847            | Self::List { span, .. }
848            | Self::Lit { span, .. }
849            | Self::As { span, .. }
850            | Self::Other { span, .. } => *span,
851        }
852    }
853
854    pub fn render(&self) -> String {
855        match self {
856            Self::Var { name, .. } => name.clone(),
857            Self::Wild { .. } => "_".to_string(),
858            Self::Con {
859                qualifier,
860                name,
861                args,
862                ..
863            } => {
864                let head = qualifier
865                    .as_ref()
866                    .map_or_else(|| name.clone(), |q| format!("{}.{}", q, name));
867                if args.is_empty() {
868                    head
869                } else {
870                    let parts: Vec<String> = args.iter().map(|p| p.render()).collect();
871                    format!("({} {})", head, parts.join(" "))
872                }
873            }
874            Self::Tuple { items, .. } => {
875                let xs: Vec<String> = items.iter().map(|p| p.render()).collect();
876                format!("({})", xs.join(", "))
877            }
878            Self::List { items, .. } => {
879                let xs: Vec<String> = items.iter().map(|p| p.render()).collect();
880                format!("[{}]", xs.join(", "))
881            }
882            Self::Lit { kind, text, .. } => match kind {
883                LitKind::Text => format!("{:?}", text),
884                LitKind::Char => format!("'{}'", text),
885                _ => text.clone(),
886            },
887            Self::As { name, pat, .. } => format!("{}@{}", name, pat.render()),
888            Self::Other { raw, .. } => raw.clone(),
889        }
890    }
891}