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#[derive(Debug, Clone, PartialEq, Eq)]
198pub struct Alt {
199 /// Pattern to match before `->`.
200 pub pat: Pat,
201 /// Alternative body after `->`.
202 pub body: Expr,
203 /// Position of the alternative's first token.
204 pub pos: Pos,
205 /// Span of the whole alternative.
206 pub span: Span,
207}
208
209#[derive(Debug, Clone, PartialEq, Eq)]
210pub struct Binding {
211 /// Left-hand side: a variable with parameters, or a destructuring pattern.
212 pub pat: Pat,
213 /// Parameter patterns when the LHS is a function binding (`f x y = ...`).
214 pub params: Vec<Pat>,
215 /// Right-hand-side expression.
216 pub expr: Expr,
217 /// Position of the binding's first token.
218 pub pos: Pos,
219 /// Span of the whole binding.
220 pub span: Span,
221}
222
223#[derive(Debug, Clone, PartialEq, Eq)]
224#[non_exhaustive]
225pub enum Pat {
226 /// Variable pattern.
227 Var {
228 /// Bound variable name.
229 name: Identifier,
230 /// Position of the variable token.
231 pos: Pos,
232 /// Span of the variable token.
233 span: Span,
234 },
235 /// Wildcard pattern (`_`).
236 Wild {
237 /// Position of the `_` token.
238 pos: Pos,
239 /// Span of the `_` token.
240 span: Span,
241 },
242 /// Constructor pattern with source-ordered arguments.
243 Con {
244 /// Optional module qualifier before the constructor name.
245 qualifier: Option<ModuleName>,
246 /// Constructor name.
247 name: Identifier,
248 /// Constructor arguments in source order.
249 args: Vec<Self>,
250 /// Position of the constructor token.
251 pos: Pos,
252 /// Span of the whole constructor pattern.
253 span: Span,
254 },
255 /// Tuple pattern with source-ordered items.
256 Tuple {
257 /// Tuple items in source order.
258 items: Vec<Self>,
259 /// Position of the opening parenthesis.
260 pos: Pos,
261 /// Span from `(` through `)`.
262 span: Span,
263 },
264 /// List pattern with source-ordered items.
265 List {
266 /// List items in source order.
267 items: Vec<Self>,
268 /// Position of the opening bracket.
269 pos: Pos,
270 /// Span from `[` through `]`.
271 span: Span,
272 },
273 /// Literal pattern; `text` is the parser's normalized literal text.
274 Lit {
275 /// Literal family.
276 kind: LitKind,
277 /// Normalized literal payload.
278 text: String,
279 /// Position of the literal token.
280 pos: Pos,
281 /// Span of the literal token in the source.
282 span: Span,
283 },
284 /// `name@pat`
285 As {
286 /// Name bound to the whole matched pattern.
287 name: Identifier,
288 /// Pattern being aliased.
289 pat: Box<Self>,
290 /// Position of the bound name.
291 pos: Pos,
292 /// Span of the whole `name@pat` pattern.
293 span: Span,
294 },
295 /// Anything the parser couldn't classify; raw text preserved.
296 Other {
297 /// Raw source text of the unclassified pattern.
298 raw: String,
299 /// Position of the raw pattern's first token.
300 pos: Pos,
301 /// Span of the preserved raw pattern text.
302 span: Span,
303 },
304}
305
306#[derive(Debug, Clone, PartialEq, Eq)]
307#[non_exhaustive]
308pub enum Expr {
309 /// Lowercase variable reference, possibly qualified.
310 Var {
311 /// Optional module qualifier before the variable name.
312 qualifier: Option<ModuleName>,
313 /// Variable name.
314 name: Identifier,
315 /// Position of the variable token.
316 pos: Pos,
317 /// Span of the variable token.
318 span: Span,
319 },
320 /// Constructor / data-constructor reference, possibly qualified.
321 Con {
322 /// Optional module qualifier before the constructor name.
323 qualifier: Option<ModuleName>,
324 /// Constructor name.
325 name: Identifier,
326 /// Position of the constructor token.
327 pos: Pos,
328 /// Span of the constructor token.
329 span: Span,
330 },
331 /// Literal expression; `text` is the parser's normalized literal text.
332 Lit {
333 /// Literal family.
334 kind: LitKind,
335 /// Normalized literal payload.
336 text: String,
337 /// Position of the literal token.
338 pos: Pos,
339 /// Span of the literal token in the source.
340 span: Span,
341 },
342 /// Application, flattened: `f a b c` is one App with three args.
343 App {
344 /// Function expression being applied.
345 func: Box<Self>,
346 /// Arguments in source order.
347 args: Vec<Self>,
348 /// Position of the application's first token.
349 pos: Pos,
350 /// Span of the whole application.
351 span: Span,
352 },
353 /// Binary operator application with source-level operator text.
354 BinOp {
355 /// Operator token text.
356 op: Operator,
357 /// Left operand.
358 lhs: Box<Self>,
359 /// Right operand.
360 rhs: Box<Self>,
361 /// Position of the left operand.
362 pos: Pos,
363 /// Span of the whole infix expression.
364 span: Span,
365 },
366 /// Unary negation.
367 Neg {
368 /// Negated expression.
369 expr: Box<Self>,
370 /// Position of the `-` token.
371 pos: Pos,
372 /// Span of the whole negated expression.
373 span: Span,
374 },
375 /// Lambda expression (`\params -> body`).
376 Lambda {
377 /// Parameter patterns in source order.
378 params: Vec<Pat>,
379 /// Lambda body.
380 body: Box<Self>,
381 /// Position of the lambda token.
382 pos: Pos,
383 /// Span of the whole lambda expression.
384 span: Span,
385 },
386 /// Conditional expression.
387 If {
388 /// Condition after `if`.
389 cond: Box<Self>,
390 /// Expression after `then`.
391 then_branch: Box<Self>,
392 /// Expression after `else`.
393 else_branch: Box<Self>,
394 /// Position of the `if` token.
395 pos: Pos,
396 /// Span of the whole conditional expression.
397 span: Span,
398 },
399 /// Case expression with source-ordered alternatives.
400 Case {
401 /// Scrutinee after `case`.
402 scrutinee: Box<Self>,
403 /// Alternatives in source order.
404 alts: Vec<Alt>,
405 /// Position of the `case` token.
406 pos: Pos,
407 /// Span of the whole case expression.
408 span: Span,
409 },
410 /// Do block.
411 Do {
412 /// Statements in source order.
413 stmts: Vec<DoStmt>,
414 /// Position of the `do` token.
415 pos: Pos,
416 /// Span of the whole do block.
417 span: Span,
418 },
419 /// Let/in expression.
420 LetIn {
421 /// Bindings in source order.
422 bindings: Vec<Binding>,
423 /// Body expression after `in`.
424 body: Box<Self>,
425 /// Position of the `let` token.
426 pos: Pos,
427 /// Span of the whole let/in expression.
428 span: Span,
429 },
430 /// `base with f = e, ...` — record construction when base is a Con,
431 /// record update otherwise.
432 Record {
433 /// Constructor or record value being constructed/updated.
434 base: Box<Self>,
435 /// Field assignments in source order.
436 fields: Vec<FieldAssign>,
437 /// Position of the base expression.
438 pos: Pos,
439 /// Span of the whole record expression.
440 span: Span,
441 },
442 /// Tuple expression with source-ordered items.
443 Tuple {
444 /// Tuple items in source order.
445 items: Vec<Self>,
446 /// Position of the opening parenthesis.
447 pos: Pos,
448 /// Span from `(` through `)`.
449 span: Span,
450 },
451 /// List expression with source-ordered items.
452 List {
453 /// List items in source order.
454 items: Vec<Self>,
455 /// Position of the opening bracket.
456 pos: Pos,
457 /// Span from `[` through `]`.
458 span: Span,
459 },
460 /// `try <body> catch <alts>`
461 Try {
462 /// Body after `try`.
463 body: Box<Self>,
464 /// Catch handlers in source order.
465 handlers: Vec<Alt>,
466 /// Position of the `try` token.
467 pos: Pos,
468 /// Span of the whole try/catch expression.
469 span: Span,
470 },
471 /// Parenthesized operator reference like `(+)`.
472 OperatorRef {
473 /// Referenced operator.
474 op: Operator,
475 /// Position of the opening parenthesis.
476 pos: Pos,
477 /// Span from `(` through `)`.
478 span: Span,
479 },
480 /// Left operator section like `(1 +)`.
481 LeftSection {
482 /// Section operator.
483 op: Operator,
484 /// Left operand before the operator.
485 operand: Box<Self>,
486 /// Position of the opening parenthesis.
487 pos: Pos,
488 /// Span from `(` through `)`.
489 span: Span,
490 },
491 /// Right operator section like `(+ 1)`.
492 RightSection {
493 /// Section operator.
494 op: Operator,
495 /// Right operand after the operator.
496 operand: Box<Self>,
497 /// Position of the opening parenthesis.
498 pos: Pos,
499 /// Span from `(` through `)`.
500 span: Span,
501 },
502 /// Expression the parser could not understand; raw text preserved so
503 /// a parse failure degrades to the shim's behavior instead of dying.
504 Error {
505 /// Raw source text preserved for the malformed expression.
506 raw: String,
507 /// Position of the malformed expression's first token.
508 pos: Pos,
509 /// Span of the preserved raw expression text.
510 span: Span,
511 },
512}
513
514#[derive(Debug, Clone, PartialEq, Eq)]
515#[non_exhaustive]
516pub enum DoStmt {
517 /// `pat <- expr`
518 Bind {
519 /// Pattern before `<-`.
520 pat: Pat,
521 /// Expression after `<-`.
522 expr: Expr,
523 /// Position of the statement's first token.
524 pos: Pos,
525 /// Span of the whole bind statement.
526 span: Span,
527 },
528 /// `let x = e` (no `in`) inside a do block.
529 Let {
530 /// Let bindings in source order.
531 bindings: Vec<Binding>,
532 /// Position of the `let` token.
533 pos: Pos,
534 /// Span of the whole let statement.
535 span: Span,
536 },
537 /// Bare expression statement.
538 Expr {
539 /// Statement expression.
540 expr: Expr,
541 /// Position of the expression's first token.
542 pos: Pos,
543 /// Span of the expression statement.
544 span: Span,
545 },
546}
547
548/// Structured Daml type, parsed from the real token stream.
549///
550/// Scoped to the forms the corpus actually contains; it exists so consumers can
551/// tell a type *application* from a *function arrow* from an
552/// atomic constructor — a distinction a string matcher structurally cannot make.
553/// Every node carries a byte span so consumers can render exact source text from
554/// `(source, span)`. Unlike declarations, expressions, and patterns, type nodes
555/// do not carry a separate [`Pos`]; use [`Type::span`] and source line mapping
556/// when a line/column anchor is required for a type fragment.
557#[derive(Debug, Clone)]
558#[non_exhaustive]
559pub enum Type {
560 /// Type constructor, possibly qualified: `Party`, `DA.Map.Map`.
561 Con {
562 /// Optional module qualifier before the constructor name.
563 qualifier: Option<ModuleName>,
564 /// Constructor name.
565 name: Identifier,
566 /// Span of the constructor token in the source.
567 span: Span,
568 },
569 /// Type application, head applied to one or more args: `ContractId Foo`,
570 /// `Map Text Int`, `Script ()`. Type-level nat literals (the `10` in
571 /// `Numeric 10`) are NOT types, so they are dropped from the arg list — a
572 /// `Numeric 10` collapses to the bare head `Con "Numeric"`.
573 App(Box<Self>, Vec<Self>, Span),
574 /// List type `[T]`.
575 List(Box<Self>, Span),
576 /// Tuple type `(a, b, ...)`.
577 Tuple(Vec<Self>, Span),
578 /// Function type `a -> b` (right-associative).
579 Fun(Box<Self>, Box<Self>, Span),
580 /// Lowercase type variable: `a`, `n`.
581 Var(Identifier, Span),
582 /// The unit type `()`.
583 Unit(Span),
584 /// A constrained type `C a => T`: the context is not modeled, the body `T`
585 /// is kept.
586 Constrained(Box<Self>, Span),
587 /// Type-level string or char literal, e.g. the `"observers"` in
588 /// `HasField "observers" t PartiesMap`.
589 Lit {
590 /// Literal family.
591 kind: LitKind,
592 /// Normalized literal payload.
593 text: String,
594 /// Span of the literal token in the source.
595 span: Span,
596 },
597}
598
599/// Type equality intentionally ignores source spans.
600///
601/// Spans describe where equivalent type syntax appeared in a source file; they
602/// are not part of structural type identity used by parser consumers.
603impl PartialEq for Type {
604 fn eq(&self, other: &Self) -> bool {
605 match (self, other) {
606 (
607 Self::Con {
608 qualifier: aq,
609 name: an,
610 ..
611 },
612 Self::Con {
613 qualifier: bq,
614 name: bn,
615 ..
616 },
617 ) => aq == bq && an == bn,
618 (Self::App(ah, aa, _), Self::App(bh, ba, _)) => ah == bh && aa == ba,
619 (Self::List(a, _), Self::List(b, _))
620 | (Self::Constrained(a, _), Self::Constrained(b, _)) => a == b,
621 (Self::Tuple(a, _), Self::Tuple(b, _)) => a == b,
622 (Self::Fun(al, ar, _), Self::Fun(bl, br, _)) => al == bl && ar == br,
623 (Self::Var(a, _), Self::Var(b, _)) => a == b,
624 (Self::Unit(_), Self::Unit(_)) => true,
625 (
626 Self::Lit {
627 kind: ak, text: at, ..
628 },
629 Self::Lit {
630 kind: bk, text: bt, ..
631 },
632 ) => ak == bk && at == bt,
633 _ => false,
634 }
635 }
636}
637
638impl Eq for Type {}
639
640impl Type {
641 #[must_use]
642 pub const fn span(&self) -> Span {
643 match self {
644 Self::Con { span, .. }
645 | Self::App(_, _, span)
646 | Self::List(_, span)
647 | Self::Tuple(_, span)
648 | Self::Fun(_, _, span)
649 | Self::Var(_, span)
650 | Self::Unit(span)
651 | Self::Constrained(_, span)
652 | Self::Lit { span, .. } => *span,
653 }
654 }
655
656 pub(crate) const fn with_span(mut self, span: Span) -> Self {
657 match &mut self {
658 Self::Con { span: s, .. }
659 | Self::App(_, _, s)
660 | Self::List(_, s)
661 | Self::Tuple(_, s)
662 | Self::Fun(_, _, s)
663 | Self::Var(_, s)
664 | Self::Unit(s)
665 | Self::Constrained(_, s)
666 | Self::Lit { span: s, .. } => *s = span,
667 }
668 self
669 }
670}
671
672#[derive(Debug, Clone, PartialEq, Eq)]
673#[non_exhaustive]
674pub enum TypeAnnotation {
675 /// The source construct did not include a type annotation.
676 Absent,
677 /// The source construct included a type annotation that parsed cleanly.
678 Present(Type),
679 /// The source construct included a type annotation, but the parser could
680 /// not model it as a [`Type`]. Diagnostics carry the detailed message.
681 Malformed { span: Span },
682}
683
684impl TypeAnnotation {
685 #[must_use]
686 pub const fn as_type(&self) -> Option<&Type> {
687 match self {
688 Self::Present(ty) => Some(ty),
689 Self::Absent | Self::Malformed { .. } => None,
690 }
691 }
692
693 #[must_use]
694 pub const fn is_absent(&self) -> bool {
695 matches!(self, Self::Absent)
696 }
697
698 #[must_use]
699 pub const fn is_malformed(&self) -> bool {
700 matches!(self, Self::Malformed { .. })
701 }
702}
703
704#[derive(Debug, Clone, PartialEq, Eq)]
705pub struct FieldDecl {
706 /// Field or method name.
707 pub name: Identifier,
708 /// Structured field type parse state.
709 pub ty: TypeAnnotation,
710 /// Position of the field/method name.
711 pub pos: Pos,
712 /// Span of the full field declaration (`name : Type` when present).
713 pub span: Span,
714}
715
716#[derive(Debug, Clone, Copy, PartialEq, Eq)]
717#[non_exhaustive]
718pub enum Consuming {
719 /// Daml `consuming` choice.
720 Consuming,
721 /// Daml `nonconsuming` choice.
722 NonConsuming,
723 /// Legacy/pre-Daml-3 `preconsuming` spelling.
724 PreConsuming,
725 /// Legacy/pre-Daml-3 `postconsuming` spelling.
726 PostConsuming,
727}
728
729#[derive(Debug, Clone, PartialEq, Eq)]
730pub struct ChoiceDecl {
731 /// Choice name.
732 pub name: Identifier,
733 /// Consuming mode parsed from the choice header.
734 pub consuming: Consuming,
735 /// Structured return type parse state.
736 pub return_ty: TypeAnnotation,
737 /// Choice parameter fields in source order.
738 pub params: Vec<FieldDecl>,
739 /// Comma-separated controller expressions.
740 pub controllers: Vec<Expr>,
741 /// Choice observers, if any.
742 pub observers: Vec<Expr>,
743 /// Choice body after `do`; `None` when the parser did not find one.
744 pub body: Option<Expr>,
745 /// Position of the `choice` token.
746 pub pos: Pos,
747 /// Span of the whole choice declaration.
748 pub span: Span,
749}
750
751#[derive(Debug, Clone, PartialEq, Eq)]
752#[non_exhaustive]
753pub enum TemplateBodyDecl {
754 /// `signatory` clause with source-ordered party expressions.
755 Signatory {
756 /// Party expressions in source order.
757 parties: Vec<Expr>,
758 /// Position of the `signatory` token.
759 pos: Pos,
760 /// Span of the whole clause.
761 span: Span,
762 },
763 /// `observer` clause with source-ordered party expressions.
764 Observer {
765 /// Party expressions in source order.
766 parties: Vec<Expr>,
767 /// Position of the `observer` token.
768 pos: Pos,
769 /// Span of the whole clause.
770 span: Span,
771 },
772 /// `ensure` clause.
773 Ensure {
774 /// Predicate expression after `ensure`.
775 expr: Expr,
776 /// Position of the `ensure` token.
777 pos: Pos,
778 /// Span of the whole clause.
779 span: Span,
780 },
781 /// Template `key` declaration.
782 Key {
783 /// Key expression.
784 expr: Expr,
785 /// Structured key type parse state.
786 ty: TypeAnnotation,
787 /// Position of the `key` token.
788 pos: Pos,
789 /// Span of the whole key declaration.
790 span: Span,
791 },
792 /// `maintainer` clause.
793 Maintainer {
794 /// Maintainer expression.
795 expr: Expr,
796 /// Position of the `maintainer` token.
797 pos: Pos,
798 /// Span of the whole clause.
799 span: Span,
800 },
801 /// `choice` declaration.
802 Choice(ChoiceDecl),
803 /// `interface instance` declaration nested in a template.
804 InterfaceInstance(InterfaceInstanceDecl),
805 /// `agreement`, `let` blocks, deprecated `controller ... can`, etc.
806 Other {
807 /// Raw source text preserved for unsupported/malformed body syntax.
808 raw: String,
809 /// Position of the raw body's first token.
810 pos: Pos,
811 /// Span of the preserved raw body text.
812 span: Span,
813 },
814}
815
816#[derive(Debug, Clone, PartialEq, Eq)]
817pub struct InterfaceInstanceDecl {
818 /// Interface being implemented (`Disclosure.I`).
819 pub interface_name: ModuleName,
820 /// Explicit template from `for Foo`; `None` when omitted (the enclosing
821 /// template when declared inside one).
822 pub for_template: Option<ModuleName>,
823 /// Method implementations: name → bound expression.
824 pub methods: Vec<Binding>,
825 /// Position of the `interface instance` clause.
826 pub pos: Pos,
827 /// Span of the whole interface instance declaration.
828 pub span: Span,
829}
830
831#[derive(Debug, Clone, PartialEq, Eq)]
832pub struct TemplateDecl {
833 /// Template name.
834 pub name: Identifier,
835 /// Template fields in source order.
836 pub fields: Vec<FieldDecl>,
837 /// Template body declarations in source order.
838 pub body: Vec<TemplateBodyDecl>,
839 /// Position of the `template` token.
840 pub pos: Pos,
841 /// Span of the whole template declaration.
842 pub span: Span,
843}
844
845#[derive(Debug, Clone, PartialEq, Eq)]
846pub struct InterfaceDecl {
847 /// Interface name.
848 pub name: Identifier,
849 /// Interfaces this interface requires (`requires Lockable.I, ...`).
850 pub requires: Vec<ModuleName>,
851 /// Optional view type name from `viewtype`.
852 pub viewtype: Option<ModuleName>,
853 /// Method signatures in source order.
854 pub methods: Vec<FieldDecl>,
855 /// Interface choices in source order.
856 pub choices: Vec<ChoiceDecl>,
857 /// Position of the `interface` token.
858 pub pos: Pos,
859 /// Span of the whole interface declaration.
860 pub span: Span,
861}
862
863#[derive(Debug, Clone, PartialEq, Eq)]
864pub struct Equation {
865 /// Parameter patterns in source order.
866 pub params: Vec<Pat>,
867 /// Unguarded body or first guarded body for convenience.
868 pub body: Expr,
869 /// Guarded equations keep their guards as (guard, body) pairs; `body`
870 /// then holds the first guarded body for convenience.
871 pub guards: Vec<(Expr, Expr)>,
872 /// `where` helper bindings attached to this equation.
873 pub where_bindings: Vec<Binding>,
874 /// Position of the equation's first token.
875 pub pos: Pos,
876 /// Span of this equation only.
877 pub span: Span,
878}
879
880#[derive(Debug, Clone, PartialEq, Eq)]
881pub struct FunctionDecl {
882 /// Function name.
883 pub name: Identifier,
884 /// Standalone signature type parse state.
885 pub ty: TypeAnnotation,
886 /// Equations for this function in source order.
887 pub equations: Vec<Equation>,
888 /// Position of the function's first appearance.
889 pub pos: Pos,
890 /// Span of the function's first appearance (signature or first equation).
891 /// Convenience anchor; a multi-equation function's precise ranges are the
892 /// per-`Equation` spans, since equations need not be contiguous in source.
893 pub span: Span,
894 /// Span of the standalone type signature `name : Type`, if one was seen.
895 pub sig_span: Option<Span>,
896}
897
898/// Fixity associativity keyword (`infix`, `infixl`, `infixr`).
899#[derive(Debug, Clone, Copy, PartialEq, Eq)]
900#[non_exhaustive]
901pub enum FixityAssoc {
902 Infix,
903 InfixL,
904 InfixR,
905}
906
907/// Operator or backtick-quoted name in a fixity declaration.
908#[derive(Debug, Clone, PartialEq, Eq)]
909#[non_exhaustive]
910pub enum FixityTarget {
911 /// Symbolic operator such as `===` or `>=>`.
912 Operator(Operator),
913 /// Backtick-quoted identifier such as `` `Pair` ``.
914 Backtick(Identifier),
915}
916
917/// Top-level fixity declaration (`infix[l|r]? n op [, op ...]`).
918#[derive(Debug, Clone, PartialEq, Eq)]
919pub struct FixityDecl {
920 pub assoc: FixityAssoc,
921 pub precedence: u8,
922 pub operators: Vec<FixityTarget>,
923 pub pos: Pos,
924 pub span: Span,
925}
926
927#[derive(Debug, Clone, PartialEq, Eq)]
928pub struct ImportDecl {
929 /// Imported module path.
930 pub module_name: ModuleName,
931 /// Whether the import is qualified.
932 pub style: ImportStyle,
933 /// Optional module alias from `as`.
934 pub alias: Option<ModuleName>,
935 /// Position of the `import` token.
936 pub pos: Pos,
937 /// Span of the whole import declaration.
938 pub span: Span,
939}
940
941#[derive(Debug, Clone, PartialEq, Eq)]
942#[non_exhaustive]
943pub enum Decl {
944 /// Template declaration.
945 Template(TemplateDecl),
946 /// Interface declaration.
947 Interface(InterfaceDecl),
948 /// Function signature/equations grouped by name.
949 Function(FunctionDecl),
950 /// data/type/class/instance/exception — recorded with name + span.
951 TypeDef {
952 /// Declaration keyword (`data`, `type`, `class`, ...).
953 keyword: String,
954 /// Declared type/class/instance name as parsed.
955 name: Identifier,
956 /// Position of the declaration keyword.
957 pos: Pos,
958 /// Span of the declaration header/body consumed by the parser.
959 span: Span,
960 },
961 /// Top-level fixity declaration.
962 Fixity(FixityDecl),
963 /// Top-level syntax the parser recognizes but intentionally does not model.
964 UnsupportedSyntax {
965 /// Why the declaration is unsupported.
966 kind: UnsupportedSyntaxKind,
967 /// Raw source text of the declaration.
968 raw: String,
969 /// Position of the declaration's first token.
970 pos: Pos,
971 /// Span of the declaration text.
972 span: Span,
973 },
974 /// Anything unparseable at the top level (diagnostic already emitted).
975 Unknown {
976 /// Raw source text of the skipped declaration.
977 raw: String,
978 /// Position of the skipped declaration's first token.
979 pos: Pos,
980 /// Span of the skipped declaration text.
981 span: Span,
982 },
983}
984
985#[derive(Debug, Clone, PartialEq, Eq)]
986pub struct Module {
987 /// Module name from the header, or `Unknown` when the source has no header.
988 pub name: ModuleName,
989 /// Position of the `module` keyword, or the start of the fallback module.
990 pub pos: Pos,
991 /// Whole-module extent: `[0, source.len())`. Container for all decls.
992 pub span: Span,
993 /// Span of the `module M (...) where` header clause; empty when the file
994 /// has no module header. Lets the span oracle treat header tokens as
995 /// covered without a dedicated header node.
996 pub header: Span,
997 /// Imports in source order.
998 pub imports: Vec<ImportDecl>,
999 /// Top-level declarations in source order.
1000 pub decls: Vec<Decl>,
1001}
1002
1003/// Why a [`ParseDiagnostic`] fired.
1004///
1005/// Lets a consumer separate syntax the parser deliberately does not model (still
1006/// safe, just unanalyzed) from a genuine malformation, a recursion-limit
1007/// degradation, or a lexical error.
1008#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1009#[non_exhaustive]
1010pub enum DiagnosticCategory {
1011 /// A whole declaration could not be parsed and was skipped to the next item.
1012 SkippedDecl,
1013 /// A malformed expression, pattern, or expected-token error inside an
1014 /// otherwise-recognized construct.
1015 Malformed,
1016 /// A construct the parser intentionally does not support, e.g. legacy
1017 /// `controller ... can` choice syntax.
1018 UnsupportedSyntax,
1019 /// Expression/pattern nesting exceeded the recursion bound and was degraded
1020 /// to raw text.
1021 RecursionLimit,
1022 /// A lexical error (unterminated string/comment, stray character).
1023 Lex,
1024}
1025
1026impl DiagnosticCategory {
1027 /// Stable kebab-case tag for machine-readable output (JSON/SARIF) and logs.
1028 #[must_use]
1029 pub const fn as_str(self) -> &'static str {
1030 match self {
1031 Self::SkippedDecl => "skipped-declaration",
1032 Self::Malformed => "malformed",
1033 Self::UnsupportedSyntax => "unsupported-syntax",
1034 Self::RecursionLimit => "recursion-limit",
1035 Self::Lex => "lexical-error",
1036 }
1037 }
1038}
1039
1040impl std::fmt::Display for DiagnosticCategory {
1041 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1042 f.write_str(self.as_str())
1043 }
1044}
1045
1046/// Machine-readable reason a [`ParseDiagnostic`] fired.
1047///
1048/// Keep [`ParseDiagnostic::message`] for presentation. Match on this enum when
1049/// downstream code needs stable behavior for recoverable parser failures.
1050#[derive(Debug, Clone, PartialEq, Eq)]
1051#[non_exhaustive]
1052pub enum ParseDiagnosticKind {
1053 /// The lexer reported malformed source; the original lexical kind is
1054 /// preserved so callers do not need to parse the human message.
1055 Lex(crate::lexer::LexErrorKind),
1056 /// The parser expected a specific token or token class and recovered.
1057 ExpectedToken(ExpectedToken),
1058 /// A type annotation was present but could not be parsed as a `Type`.
1059 MalformedTypeAnnotation(TypeAnnotationContext),
1060 /// A recognized construct was malformed in a way that is not only an
1061 /// expected-token miss.
1062 MalformedSyntax(MalformedSyntaxKind),
1063 /// A whole declaration could not be parsed and was skipped.
1064 SkippedDeclaration(SkippedDeclarationReason),
1065 /// The source used syntax this parser intentionally does not model.
1066 UnsupportedSyntax(UnsupportedSyntaxKind),
1067 /// Expression or pattern nesting exceeded the parser recursion bound.
1068 RecursionLimit { limit: u32 },
1069}
1070
1071impl ParseDiagnosticKind {
1072 /// Coarse diagnostic class retained for stable JSON/SARIF tags.
1073 #[must_use]
1074 pub const fn category(&self) -> DiagnosticCategory {
1075 match self {
1076 Self::Lex(_) => DiagnosticCategory::Lex,
1077 Self::ExpectedToken(_)
1078 | Self::MalformedTypeAnnotation(_)
1079 | Self::MalformedSyntax(_) => DiagnosticCategory::Malformed,
1080 Self::SkippedDeclaration(_) => DiagnosticCategory::SkippedDecl,
1081 Self::UnsupportedSyntax(_) => DiagnosticCategory::UnsupportedSyntax,
1082 Self::RecursionLimit { .. } => DiagnosticCategory::RecursionLimit,
1083 }
1084 }
1085}
1086
1087/// Expected token or token class for a recoverable parser diagnostic.
1088#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1089#[non_exhaustive]
1090pub enum ExpectedToken {
1091 WhereAfterModuleHeader,
1092 ModuleNameAfterImport,
1093 TemplateNameAfterInterfaceInstanceFor,
1094 FieldNameTypePair,
1095 EqualsAfterGuard,
1096 EqualsOrGuardedRightHandSide,
1097 ProjectionFieldAfterDot,
1098 ThenKeyword,
1099 ElseKeyword,
1100 OfKeywordInCaseExpression,
1101 ArrowInGuardedCaseAlternative,
1102 ArrowInCaseAlternative,
1103}
1104
1105/// The declaration context whose type annotation was malformed.
1106#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1107#[non_exhaustive]
1108pub enum TypeAnnotationContext {
1109 Field,
1110 Key,
1111 Choice,
1112 InterfaceMethod,
1113 Function,
1114}
1115
1116impl TypeAnnotationContext {
1117 #[must_use]
1118 pub const fn as_str(self) -> &'static str {
1119 match self {
1120 Self::Field => "field",
1121 Self::Key => "key",
1122 Self::Choice => "choice",
1123 Self::InterfaceMethod => "interface method",
1124 Self::Function => "function",
1125 }
1126 }
1127}
1128
1129/// Recoverable malformed syntax cases that are not just missing one token.
1130#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1131#[non_exhaustive]
1132pub enum MalformedSyntaxKind {
1133 FunctionEquation,
1134 FunctionParameterPattern,
1135 LambdaParameter,
1136}
1137
1138/// Why a whole declaration was skipped.
1139#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1140#[non_exhaustive]
1141pub enum SkippedDeclarationReason {
1142 TopLevelPatternBinding,
1143 UnrecognizedDeclaration,
1144}
1145
1146/// Unsupported syntax families surfaced by the parser.
1147#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1148#[non_exhaustive]
1149pub enum UnsupportedSyntaxKind {
1150 LegacyControllerCan,
1151 PatternSynonym,
1152}
1153
1154/// Parse diagnostic — never fatal under tolerant parsing.
1155///
1156/// Under [`crate::parse::parse_module`] the scan continues. Strict callers that
1157/// use [`crate::parse::parse_module_strict`] or
1158/// [`crate::parse::ParseModuleResult::into_result`] treat any diagnostic as
1159/// [`crate::parse::ParseModuleError`].
1160#[derive(Debug, Clone, PartialEq, Eq)]
1161pub struct ParseDiagnostic {
1162 /// Machine-readable reason for the diagnostic.
1163 pub kind: ParseDiagnosticKind,
1164 /// Human-readable presentation message. Use [`Self::kind`] for logic.
1165 pub message: String,
1166 pub pos: Pos,
1167 /// Byte span of the offending region. The end is the actionable addition
1168 /// over `pos`-alone; zero-width when only a point is known (lex errors,
1169 /// EOF).
1170 pub span: Span,
1171 pub category: DiagnosticCategory,
1172}
1173
1174impl ParseDiagnostic {
1175 #[must_use]
1176 pub fn new(
1177 kind: ParseDiagnosticKind,
1178 message: impl Into<String>,
1179 pos: Pos,
1180 span: Span,
1181 ) -> Self {
1182 let category = kind.category();
1183 Self {
1184 kind,
1185 message: message.into(),
1186 pos,
1187 span,
1188 category,
1189 }
1190 }
1191
1192 /// Human-readable presentation message.
1193 #[must_use]
1194 pub fn message(&self) -> &str {
1195 &self.message
1196 }
1197
1198 /// Machine-readable diagnostic reason.
1199 #[must_use]
1200 pub const fn kind(&self) -> &ParseDiagnosticKind {
1201 &self.kind
1202 }
1203
1204 /// Coarse recovery category.
1205 #[must_use]
1206 pub const fn category(&self) -> DiagnosticCategory {
1207 self.category
1208 }
1209}
1210
1211impl std::fmt::Display for ParseDiagnostic {
1212 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1213 self.message.fmt(f)
1214 }
1215}
1216
1217impl std::error::Error for ParseDiagnostic {}
1218
1219impl Expr {
1220 #[must_use]
1221 pub const fn pos(&self) -> Pos {
1222 match self {
1223 Self::Var { pos, .. }
1224 | Self::Con { pos, .. }
1225 | Self::Lit { pos, .. }
1226 | Self::App { pos, .. }
1227 | Self::BinOp { pos, .. }
1228 | Self::Neg { pos, .. }
1229 | Self::Lambda { pos, .. }
1230 | Self::If { pos, .. }
1231 | Self::Case { pos, .. }
1232 | Self::Do { pos, .. }
1233 | Self::LetIn { pos, .. }
1234 | Self::Record { pos, .. }
1235 | Self::Tuple { pos, .. }
1236 | Self::List { pos, .. }
1237 | Self::Try { pos, .. }
1238 | Self::OperatorRef { pos, .. }
1239 | Self::LeftSection { pos, .. }
1240 | Self::RightSection { pos, .. }
1241 | Self::Error { pos, .. } => *pos,
1242 }
1243 }
1244
1245 /// Byte span covering the whole expression.
1246 #[must_use]
1247 pub const fn span(&self) -> Span {
1248 match self {
1249 Self::Var { span, .. }
1250 | Self::Con { span, .. }
1251 | Self::Lit { span, .. }
1252 | Self::App { span, .. }
1253 | Self::BinOp { span, .. }
1254 | Self::Neg { span, .. }
1255 | Self::Lambda { span, .. }
1256 | Self::If { span, .. }
1257 | Self::Case { span, .. }
1258 | Self::Do { span, .. }
1259 | Self::LetIn { span, .. }
1260 | Self::Record { span, .. }
1261 | Self::Tuple { span, .. }
1262 | Self::List { span, .. }
1263 | Self::Try { span, .. }
1264 | Self::OperatorRef { span, .. }
1265 | Self::LeftSection { span, .. }
1266 | Self::RightSection { span, .. }
1267 | Self::Error { span, .. } => *span,
1268 }
1269 }
1270
1271 /// Render back to compact, source-*like* text for diagnostics and `raw`
1272 /// fields.
1273 ///
1274 /// This is **lossy and normalizing**, not byte-faithful: original layout is
1275 /// dropped (e.g. `do`/`let` statements are joined with `; `), operators and
1276 /// spacing are normalized, and comments/trivia are gone. Use it for a quick
1277 /// human-readable echo of an expression; for source-exact reconstruction use
1278 /// the node's [`span`](Self::span) into the original text (that is how
1279 /// `daml-fmt` and [`crate::ast_span::render_from_ast`] stay lossless).
1280 #[must_use]
1281 pub fn render(&self) -> String {
1282 match self {
1283 Self::Var {
1284 qualifier, name, ..
1285 }
1286 | Self::Con {
1287 qualifier, name, ..
1288 } => qualifier
1289 .as_ref()
1290 .map_or_else(|| name.to_string(), |q| format!("{q}.{name}")),
1291 Self::Lit { kind, text, .. } => match kind {
1292 LitKind::Text => format!("{text:?}"),
1293 LitKind::Char => format!("'{text}'"),
1294 _ => text.clone(),
1295 },
1296 Self::App { func, args, .. } => {
1297 let mut s = func.render_atomic();
1298 for a in args {
1299 s.push(' ');
1300 s.push_str(&a.render_atomic());
1301 }
1302 s
1303 }
1304 Self::BinOp { op, lhs, rhs, .. } => {
1305 if *op == "." {
1306 // Record projection / composition: `account.custodian`.
1307 format!("{}.{}", lhs.render_atomic(), rhs.render_atomic())
1308 } else {
1309 format!("{} {} {}", lhs.render_atomic(), op, rhs.render_atomic())
1310 }
1311 }
1312 Self::Neg { expr, .. } => format!("-{}", expr.render_atomic()),
1313 Self::Lambda { params, body, .. } => {
1314 let ps: Vec<String> = params.iter().map(|p| p.render()).collect();
1315 format!("\\{} -> {}", ps.join(" "), body.render())
1316 }
1317 Self::If {
1318 cond,
1319 then_branch,
1320 else_branch,
1321 ..
1322 } => format!(
1323 "if {} then {} else {}",
1324 cond.render(),
1325 then_branch.render(),
1326 else_branch.render()
1327 ),
1328 Self::Case {
1329 scrutinee, alts, ..
1330 } => {
1331 let arms: Vec<String> = alts
1332 .iter()
1333 .map(|a| format!("{} -> {}", a.pat.render(), a.body.render()))
1334 .collect();
1335 format!("case {} of {}", scrutinee.render(), arms.join("; "))
1336 }
1337 Self::Do { stmts, .. } => {
1338 let body: Vec<String> = stmts.iter().map(render_do_stmt).collect();
1339 format!("do {}", body.join("; "))
1340 }
1341 Self::LetIn { bindings, body, .. } => {
1342 let bs: Vec<String> = bindings.iter().map(render_binding).collect();
1343 format!("let {} in {}", bs.join("; "), body.render())
1344 }
1345 Self::Record { base, fields, .. } => {
1346 let fs: Vec<String> = fields
1347 .iter()
1348 .map(|f| match f {
1349 FieldAssign::Assign { name, value, .. } => {
1350 format!("{} = {}", name, value.render())
1351 }
1352 FieldAssign::Pun { name, .. } => name.to_string(),
1353 FieldAssign::Wildcard { .. } => "..".to_string(),
1354 })
1355 .collect();
1356 format!("{} with {}", base.render_atomic(), fs.join("; "))
1357 }
1358 Self::Tuple { items, .. } => {
1359 let xs: Vec<String> = items.iter().map(|e| e.render()).collect();
1360 format!("({})", xs.join(", "))
1361 }
1362 Self::List { items, .. } => {
1363 let xs: Vec<String> = items.iter().map(|e| e.render()).collect();
1364 format!("[{}]", xs.join(", "))
1365 }
1366 Self::Try { body, handlers, .. } => {
1367 let hs: Vec<String> = handlers
1368 .iter()
1369 .map(|a| format!("{} -> {}", a.pat.render(), a.body.render()))
1370 .collect();
1371 format!("try {} catch {}", body.render(), hs.join("; "))
1372 }
1373 Self::OperatorRef { op, .. } => format!("({op})"),
1374 Self::LeftSection { op, operand, .. } => format!("({} {})", operand.render(), op),
1375 Self::RightSection { op, operand, .. } => format!("({} {})", op, operand.render()),
1376 Self::Error { raw, .. } => raw.clone(),
1377 }
1378 }
1379
1380 /// Render with parentheses if this expression wouldn't survive as an
1381 /// application argument.
1382 fn render_atomic(&self) -> String {
1383 match self {
1384 Self::Var { .. }
1385 | Self::Con { .. }
1386 | Self::Lit { .. }
1387 | Self::Tuple { .. }
1388 | Self::List { .. }
1389 | Self::OperatorRef { .. }
1390 | Self::LeftSection { .. }
1391 | Self::RightSection { .. }
1392 | Self::Error { .. } => self.render(),
1393 _ => format!("({})", self.render()),
1394 }
1395 }
1396
1397 /// The head of an application spine: for `Foo.exercise cid X`, the
1398 /// `Foo.exercise` Var. For non-apps, the expression itself.
1399 #[must_use]
1400 pub fn application_head(&self) -> &Self {
1401 match self {
1402 Self::App { func, .. } => func.application_head(),
1403 _ => self,
1404 }
1405 }
1406
1407 /// Application arguments, empty for non-apps. The `App` spine is flattened
1408 /// (see the [`App`](Self::App) variant), so for `f a b c` this returns all
1409 /// three arguments `[a, b, c]`, not a single curried layer.
1410 #[must_use]
1411 pub fn application_args(&self) -> &[Self] {
1412 match self {
1413 Self::App { args, .. } => args,
1414 _ => &[],
1415 }
1416 }
1417}
1418
1419fn render_do_stmt(s: &DoStmt) -> String {
1420 match s {
1421 DoStmt::Bind { pat, expr, .. } => format!("{} <- {}", pat.render(), expr.render()),
1422 DoStmt::Let { bindings, .. } => {
1423 let bs: Vec<String> = bindings.iter().map(render_binding).collect();
1424 format!("let {}", bs.join("; "))
1425 }
1426 DoStmt::Expr { expr, .. } => expr.render(),
1427 }
1428}
1429
1430fn render_binding(b: &Binding) -> String {
1431 let mut s = b.pat.render();
1432 for p in &b.params {
1433 s.push(' ');
1434 s.push_str(&p.render());
1435 }
1436 format!("{} = {}", s, b.expr.render())
1437}
1438
1439impl Pat {
1440 #[must_use]
1441 pub const fn pos(&self) -> Pos {
1442 match self {
1443 Self::Var { pos, .. }
1444 | Self::Wild { pos, .. }
1445 | Self::Con { pos, .. }
1446 | Self::Tuple { pos, .. }
1447 | Self::List { pos, .. }
1448 | Self::Lit { pos, .. }
1449 | Self::As { pos, .. }
1450 | Self::Other { pos, .. } => *pos,
1451 }
1452 }
1453
1454 /// Byte span covering the whole pattern.
1455 #[must_use]
1456 pub const fn span(&self) -> Span {
1457 match self {
1458 Self::Var { span, .. }
1459 | Self::Wild { span, .. }
1460 | Self::Con { span, .. }
1461 | Self::Tuple { span, .. }
1462 | Self::List { span, .. }
1463 | Self::Lit { span, .. }
1464 | Self::As { span, .. }
1465 | Self::Other { span, .. } => *span,
1466 }
1467 }
1468
1469 /// Render back to compact, source-*like* text. Lossy and normalizing in the
1470 /// same way as [`Expr::render`]; use the node's [`span`](Self::span) for
1471 /// byte-faithful text.
1472 #[must_use]
1473 pub fn render(&self) -> String {
1474 match self {
1475 Self::Var { name, .. } => name.to_string(),
1476 Self::Wild { .. } => "_".to_string(),
1477 Self::Con {
1478 qualifier,
1479 name,
1480 args,
1481 ..
1482 } => {
1483 let head = qualifier
1484 .as_ref()
1485 .map_or_else(|| name.to_string(), |q| format!("{q}.{name}"));
1486 if args.is_empty() {
1487 head
1488 } else {
1489 let parts: Vec<String> = args.iter().map(|p| p.render()).collect();
1490 format!("({} {})", head, parts.join(" "))
1491 }
1492 }
1493 Self::Tuple { items, .. } => {
1494 let xs: Vec<String> = items.iter().map(|p| p.render()).collect();
1495 format!("({})", xs.join(", "))
1496 }
1497 Self::List { items, .. } => {
1498 let xs: Vec<String> = items.iter().map(|p| p.render()).collect();
1499 format!("[{}]", xs.join(", "))
1500 }
1501 Self::Lit { kind, text, .. } => match kind {
1502 LitKind::Text => format!("{text:?}"),
1503 LitKind::Char => format!("'{text}'"),
1504 _ => text.clone(),
1505 },
1506 Self::As { name, pat, .. } => format!("{}@{}", name, pat.render()),
1507 Self::Other { raw, .. } => raw.clone(),
1508 }
1509 }
1510}
1511
1512impl std::fmt::Display for Expr {
1513 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1514 f.write_str(&self.render())
1515 }
1516}
1517
1518impl std::fmt::Display for Pat {
1519 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1520 f.write_str(&self.render())
1521 }
1522}
1523
1524// Span invariants for the lossless AST tile layer; render-shape tests live in integration tests.
1525#[cfg(test)]
1526mod tests {
1527 use super::*;
1528
1529 fn span(start: usize, end: usize) -> Span {
1530 Span::from_usize(start, end)
1531 }
1532
1533 #[test]
1534 fn span_distinguishes_empty_from_invalid() {
1535 assert!(span(3, 3).is_valid());
1536 assert!(span(3, 3).is_empty());
1537
1538 assert!(!span(4, 3).is_valid());
1539 assert!(!span(4, 3).is_empty());
1540 }
1541
1542 #[test]
1543 fn contains_rejects_invalid_spans() {
1544 let parent = span(1, 10);
1545
1546 assert!(parent.contains(&span(3, 7)));
1547 assert!(!parent.contains(&span(7, 3)));
1548 assert!(!span(10, 1).contains(&span(3, 7)));
1549 }
1550
1551 #[test]
1552 fn span_range_and_get_share_source_bytes_safely() {
1553 let source = "foo: Int";
1554
1555 assert_eq!(span(0, 3).range(), 0..3);
1556 assert_eq!(span(0, 3).get(source), Some("foo"));
1557 assert_eq!(span(3, 7).get(source), Some(": In"));
1558 assert!(span(3, 100).get(source).is_none());
1559 }
1560}