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#[derive(Debug, Clone, PartialEq, Eq)]
899pub struct ImportDecl {
900 /// Imported module path.
901 pub module_name: ModuleName,
902 /// Whether the import is qualified.
903 pub style: ImportStyle,
904 /// Optional module alias from `as`.
905 pub alias: Option<ModuleName>,
906 /// Position of the `import` token.
907 pub pos: Pos,
908 /// Span of the whole import declaration.
909 pub span: Span,
910}
911
912#[derive(Debug, Clone, PartialEq, Eq)]
913#[non_exhaustive]
914pub enum Decl {
915 /// Template declaration.
916 Template(TemplateDecl),
917 /// Interface declaration.
918 Interface(InterfaceDecl),
919 /// Function signature/equations grouped by name.
920 Function(FunctionDecl),
921 /// data/type/class/instance/exception — recorded with name + span.
922 TypeDef {
923 /// Declaration keyword (`data`, `type`, `class`, ...).
924 keyword: String,
925 /// Declared type/class/instance name as parsed.
926 name: Identifier,
927 /// Position of the declaration keyword.
928 pos: Pos,
929 /// Span of the declaration header/body consumed by the parser.
930 span: Span,
931 },
932 /// Anything unparseable at the top level (diagnostic already emitted).
933 Unknown {
934 /// Raw source text of the skipped declaration.
935 raw: String,
936 /// Position of the skipped declaration's first token.
937 pos: Pos,
938 /// Span of the skipped declaration text.
939 span: Span,
940 },
941}
942
943#[derive(Debug, Clone, PartialEq, Eq)]
944pub struct Module {
945 /// Module name from the header, or `Unknown` when the source has no header.
946 pub name: ModuleName,
947 /// Position of the `module` keyword, or the start of the fallback module.
948 pub pos: Pos,
949 /// Whole-module extent: `[0, source.len())`. Container for all decls.
950 pub span: Span,
951 /// Span of the `module M (...) where` header clause; empty when the file
952 /// has no module header. Lets the span oracle treat header tokens as
953 /// covered without a dedicated header node.
954 pub header: Span,
955 /// Imports in source order.
956 pub imports: Vec<ImportDecl>,
957 /// Top-level declarations in source order.
958 pub decls: Vec<Decl>,
959}
960
961/// Why a [`ParseDiagnostic`] fired.
962///
963/// Lets a consumer separate syntax the parser deliberately does not model (still
964/// safe, just unanalyzed) from a genuine malformation, a recursion-limit
965/// degradation, or a lexical error.
966#[derive(Debug, Clone, Copy, PartialEq, Eq)]
967#[non_exhaustive]
968pub enum DiagnosticCategory {
969 /// A whole declaration could not be parsed and was skipped to the next item.
970 SkippedDecl,
971 /// A malformed expression, pattern, or expected-token error inside an
972 /// otherwise-recognized construct.
973 Malformed,
974 /// A construct the parser intentionally does not support, e.g. legacy
975 /// `controller ... can` choice syntax.
976 UnsupportedSyntax,
977 /// Expression/pattern nesting exceeded the recursion bound and was degraded
978 /// to raw text.
979 RecursionLimit,
980 /// A lexical error (unterminated string/comment, stray character).
981 Lex,
982}
983
984impl DiagnosticCategory {
985 /// Stable kebab-case tag for machine-readable output (JSON/SARIF) and logs.
986 #[must_use]
987 pub const fn as_str(self) -> &'static str {
988 match self {
989 Self::SkippedDecl => "skipped-declaration",
990 Self::Malformed => "malformed",
991 Self::UnsupportedSyntax => "unsupported-syntax",
992 Self::RecursionLimit => "recursion-limit",
993 Self::Lex => "lexical-error",
994 }
995 }
996}
997
998impl std::fmt::Display for DiagnosticCategory {
999 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1000 f.write_str(self.as_str())
1001 }
1002}
1003
1004/// Machine-readable reason a [`ParseDiagnostic`] fired.
1005///
1006/// Keep [`ParseDiagnostic::message`] for presentation. Match on this enum when
1007/// downstream code needs stable behavior for recoverable parser failures.
1008#[derive(Debug, Clone, PartialEq, Eq)]
1009#[non_exhaustive]
1010pub enum ParseDiagnosticKind {
1011 /// The lexer reported malformed source; the original lexical kind is
1012 /// preserved so callers do not need to parse the human message.
1013 Lex(crate::lexer::LexErrorKind),
1014 /// The parser expected a specific token or token class and recovered.
1015 ExpectedToken(ExpectedToken),
1016 /// A type annotation was present but could not be parsed as a `Type`.
1017 MalformedTypeAnnotation(TypeAnnotationContext),
1018 /// A recognized construct was malformed in a way that is not only an
1019 /// expected-token miss.
1020 MalformedSyntax(MalformedSyntaxKind),
1021 /// A whole declaration could not be parsed and was skipped.
1022 SkippedDeclaration(SkippedDeclarationReason),
1023 /// The source used syntax this parser intentionally does not model.
1024 UnsupportedSyntax(UnsupportedSyntaxKind),
1025 /// Expression or pattern nesting exceeded the parser recursion bound.
1026 RecursionLimit { limit: u32 },
1027}
1028
1029impl ParseDiagnosticKind {
1030 /// Coarse diagnostic class retained for stable JSON/SARIF tags.
1031 #[must_use]
1032 pub const fn category(&self) -> DiagnosticCategory {
1033 match self {
1034 Self::Lex(_) => DiagnosticCategory::Lex,
1035 Self::ExpectedToken(_)
1036 | Self::MalformedTypeAnnotation(_)
1037 | Self::MalformedSyntax(_) => DiagnosticCategory::Malformed,
1038 Self::SkippedDeclaration(_) => DiagnosticCategory::SkippedDecl,
1039 Self::UnsupportedSyntax(_) => DiagnosticCategory::UnsupportedSyntax,
1040 Self::RecursionLimit { .. } => DiagnosticCategory::RecursionLimit,
1041 }
1042 }
1043}
1044
1045/// Expected token or token class for a recoverable parser diagnostic.
1046#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1047#[non_exhaustive]
1048pub enum ExpectedToken {
1049 WhereAfterModuleHeader,
1050 ModuleNameAfterImport,
1051 TemplateNameAfterInterfaceInstanceFor,
1052 FieldNameTypePair,
1053 EqualsAfterGuard,
1054 EqualsOrGuardedRightHandSide,
1055 ProjectionFieldAfterDot,
1056 ThenKeyword,
1057 ElseKeyword,
1058 OfKeywordInCaseExpression,
1059 ArrowInGuardedCaseAlternative,
1060 ArrowInCaseAlternative,
1061}
1062
1063/// The declaration context whose type annotation was malformed.
1064#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1065#[non_exhaustive]
1066pub enum TypeAnnotationContext {
1067 Field,
1068 Key,
1069 Choice,
1070 InterfaceMethod,
1071 Function,
1072}
1073
1074impl TypeAnnotationContext {
1075 #[must_use]
1076 pub const fn as_str(self) -> &'static str {
1077 match self {
1078 Self::Field => "field",
1079 Self::Key => "key",
1080 Self::Choice => "choice",
1081 Self::InterfaceMethod => "interface method",
1082 Self::Function => "function",
1083 }
1084 }
1085}
1086
1087/// Recoverable malformed syntax cases that are not just missing one token.
1088#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1089#[non_exhaustive]
1090pub enum MalformedSyntaxKind {
1091 FunctionEquation,
1092 FunctionParameterPattern,
1093 LambdaParameter,
1094}
1095
1096/// Why a whole declaration was skipped.
1097#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1098#[non_exhaustive]
1099pub enum SkippedDeclarationReason {
1100 TopLevelPatternBinding,
1101 UnrecognizedDeclaration,
1102}
1103
1104/// Unsupported syntax families surfaced by the parser.
1105#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1106#[non_exhaustive]
1107pub enum UnsupportedSyntaxKind {
1108 LegacyControllerCan,
1109}
1110
1111/// Parse diagnostic — never fatal under tolerant parsing.
1112///
1113/// Under [`crate::parse::parse_module`] the scan continues. Strict callers that
1114/// use [`crate::parse::parse_module_strict`] or
1115/// [`crate::parse::ParseModuleResult::into_result`] treat any diagnostic as
1116/// [`crate::parse::ParseModuleError`].
1117#[derive(Debug, Clone, PartialEq, Eq)]
1118pub struct ParseDiagnostic {
1119 /// Machine-readable reason for the diagnostic.
1120 pub kind: ParseDiagnosticKind,
1121 /// Human-readable presentation message. Use [`Self::kind`] for logic.
1122 pub message: String,
1123 pub pos: Pos,
1124 /// Byte span of the offending region. The end is the actionable addition
1125 /// over `pos`-alone; zero-width when only a point is known (lex errors,
1126 /// EOF).
1127 pub span: Span,
1128 pub category: DiagnosticCategory,
1129}
1130
1131impl ParseDiagnostic {
1132 #[must_use]
1133 pub fn new(
1134 kind: ParseDiagnosticKind,
1135 message: impl Into<String>,
1136 pos: Pos,
1137 span: Span,
1138 ) -> Self {
1139 let category = kind.category();
1140 Self {
1141 kind,
1142 message: message.into(),
1143 pos,
1144 span,
1145 category,
1146 }
1147 }
1148
1149 /// Human-readable presentation message.
1150 #[must_use]
1151 pub fn message(&self) -> &str {
1152 &self.message
1153 }
1154
1155 /// Machine-readable diagnostic reason.
1156 #[must_use]
1157 pub const fn kind(&self) -> &ParseDiagnosticKind {
1158 &self.kind
1159 }
1160
1161 /// Coarse recovery category.
1162 #[must_use]
1163 pub const fn category(&self) -> DiagnosticCategory {
1164 self.category
1165 }
1166}
1167
1168impl std::fmt::Display for ParseDiagnostic {
1169 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1170 self.message.fmt(f)
1171 }
1172}
1173
1174impl std::error::Error for ParseDiagnostic {}
1175
1176impl Expr {
1177 #[must_use]
1178 pub const fn pos(&self) -> Pos {
1179 match self {
1180 Self::Var { pos, .. }
1181 | Self::Con { pos, .. }
1182 | Self::Lit { pos, .. }
1183 | Self::App { pos, .. }
1184 | Self::BinOp { pos, .. }
1185 | Self::Neg { pos, .. }
1186 | Self::Lambda { pos, .. }
1187 | Self::If { pos, .. }
1188 | Self::Case { pos, .. }
1189 | Self::Do { pos, .. }
1190 | Self::LetIn { pos, .. }
1191 | Self::Record { pos, .. }
1192 | Self::Tuple { pos, .. }
1193 | Self::List { pos, .. }
1194 | Self::Try { pos, .. }
1195 | Self::OperatorRef { pos, .. }
1196 | Self::LeftSection { pos, .. }
1197 | Self::RightSection { pos, .. }
1198 | Self::Error { pos, .. } => *pos,
1199 }
1200 }
1201
1202 /// Byte span covering the whole expression.
1203 #[must_use]
1204 pub const fn span(&self) -> Span {
1205 match self {
1206 Self::Var { span, .. }
1207 | Self::Con { span, .. }
1208 | Self::Lit { span, .. }
1209 | Self::App { span, .. }
1210 | Self::BinOp { span, .. }
1211 | Self::Neg { span, .. }
1212 | Self::Lambda { span, .. }
1213 | Self::If { span, .. }
1214 | Self::Case { span, .. }
1215 | Self::Do { span, .. }
1216 | Self::LetIn { span, .. }
1217 | Self::Record { span, .. }
1218 | Self::Tuple { span, .. }
1219 | Self::List { span, .. }
1220 | Self::Try { span, .. }
1221 | Self::OperatorRef { span, .. }
1222 | Self::LeftSection { span, .. }
1223 | Self::RightSection { span, .. }
1224 | Self::Error { span, .. } => *span,
1225 }
1226 }
1227
1228 /// Render back to compact, source-*like* text for diagnostics and `raw`
1229 /// fields.
1230 ///
1231 /// This is **lossy and normalizing**, not byte-faithful: original layout is
1232 /// dropped (e.g. `do`/`let` statements are joined with `; `), operators and
1233 /// spacing are normalized, and comments/trivia are gone. Use it for a quick
1234 /// human-readable echo of an expression; for source-exact reconstruction use
1235 /// the node's [`span`](Self::span) into the original text (that is how
1236 /// `daml-fmt` and [`crate::ast_span::render_from_ast`] stay lossless).
1237 #[must_use]
1238 pub fn render(&self) -> String {
1239 match self {
1240 Self::Var {
1241 qualifier, name, ..
1242 }
1243 | Self::Con {
1244 qualifier, name, ..
1245 } => qualifier
1246 .as_ref()
1247 .map_or_else(|| name.to_string(), |q| format!("{q}.{name}")),
1248 Self::Lit { kind, text, .. } => match kind {
1249 LitKind::Text => format!("{text:?}"),
1250 LitKind::Char => format!("'{text}'"),
1251 _ => text.clone(),
1252 },
1253 Self::App { func, args, .. } => {
1254 let mut s = func.render_atomic();
1255 for a in args {
1256 s.push(' ');
1257 s.push_str(&a.render_atomic());
1258 }
1259 s
1260 }
1261 Self::BinOp { op, lhs, rhs, .. } => {
1262 if *op == "." {
1263 // Record projection / composition: `account.custodian`.
1264 format!("{}.{}", lhs.render_atomic(), rhs.render_atomic())
1265 } else {
1266 format!("{} {} {}", lhs.render_atomic(), op, rhs.render_atomic())
1267 }
1268 }
1269 Self::Neg { expr, .. } => format!("-{}", expr.render_atomic()),
1270 Self::Lambda { params, body, .. } => {
1271 let ps: Vec<String> = params.iter().map(|p| p.render()).collect();
1272 format!("\\{} -> {}", ps.join(" "), body.render())
1273 }
1274 Self::If {
1275 cond,
1276 then_branch,
1277 else_branch,
1278 ..
1279 } => format!(
1280 "if {} then {} else {}",
1281 cond.render(),
1282 then_branch.render(),
1283 else_branch.render()
1284 ),
1285 Self::Case {
1286 scrutinee, alts, ..
1287 } => {
1288 let arms: Vec<String> = alts
1289 .iter()
1290 .map(|a| format!("{} -> {}", a.pat.render(), a.body.render()))
1291 .collect();
1292 format!("case {} of {}", scrutinee.render(), arms.join("; "))
1293 }
1294 Self::Do { stmts, .. } => {
1295 let body: Vec<String> = stmts.iter().map(render_do_stmt).collect();
1296 format!("do {}", body.join("; "))
1297 }
1298 Self::LetIn { bindings, body, .. } => {
1299 let bs: Vec<String> = bindings.iter().map(render_binding).collect();
1300 format!("let {} in {}", bs.join("; "), body.render())
1301 }
1302 Self::Record { base, fields, .. } => {
1303 let fs: Vec<String> = fields
1304 .iter()
1305 .map(|f| match f {
1306 FieldAssign::Assign { name, value, .. } => {
1307 format!("{} = {}", name, value.render())
1308 }
1309 FieldAssign::Pun { name, .. } => name.to_string(),
1310 FieldAssign::Wildcard { .. } => "..".to_string(),
1311 })
1312 .collect();
1313 format!("{} with {}", base.render_atomic(), fs.join("; "))
1314 }
1315 Self::Tuple { items, .. } => {
1316 let xs: Vec<String> = items.iter().map(|e| e.render()).collect();
1317 format!("({})", xs.join(", "))
1318 }
1319 Self::List { items, .. } => {
1320 let xs: Vec<String> = items.iter().map(|e| e.render()).collect();
1321 format!("[{}]", xs.join(", "))
1322 }
1323 Self::Try { body, handlers, .. } => {
1324 let hs: Vec<String> = handlers
1325 .iter()
1326 .map(|a| format!("{} -> {}", a.pat.render(), a.body.render()))
1327 .collect();
1328 format!("try {} catch {}", body.render(), hs.join("; "))
1329 }
1330 Self::OperatorRef { op, .. } => format!("({op})"),
1331 Self::LeftSection { op, operand, .. } => format!("({} {})", operand.render(), op),
1332 Self::RightSection { op, operand, .. } => format!("({} {})", op, operand.render()),
1333 Self::Error { raw, .. } => raw.clone(),
1334 }
1335 }
1336
1337 /// Render with parentheses if this expression wouldn't survive as an
1338 /// application argument.
1339 fn render_atomic(&self) -> String {
1340 match self {
1341 Self::Var { .. }
1342 | Self::Con { .. }
1343 | Self::Lit { .. }
1344 | Self::Tuple { .. }
1345 | Self::List { .. }
1346 | Self::OperatorRef { .. }
1347 | Self::LeftSection { .. }
1348 | Self::RightSection { .. }
1349 | Self::Error { .. } => self.render(),
1350 _ => format!("({})", self.render()),
1351 }
1352 }
1353
1354 /// The head of an application spine: for `Foo.exercise cid X`, the
1355 /// `Foo.exercise` Var. For non-apps, the expression itself.
1356 #[must_use]
1357 pub fn application_head(&self) -> &Self {
1358 match self {
1359 Self::App { func, .. } => func.application_head(),
1360 _ => self,
1361 }
1362 }
1363
1364 /// Application arguments, empty for non-apps. The `App` spine is flattened
1365 /// (see the [`App`](Self::App) variant), so for `f a b c` this returns all
1366 /// three arguments `[a, b, c]`, not a single curried layer.
1367 #[must_use]
1368 pub fn application_args(&self) -> &[Self] {
1369 match self {
1370 Self::App { args, .. } => args,
1371 _ => &[],
1372 }
1373 }
1374}
1375
1376fn render_do_stmt(s: &DoStmt) -> String {
1377 match s {
1378 DoStmt::Bind { pat, expr, .. } => format!("{} <- {}", pat.render(), expr.render()),
1379 DoStmt::Let { bindings, .. } => {
1380 let bs: Vec<String> = bindings.iter().map(render_binding).collect();
1381 format!("let {}", bs.join("; "))
1382 }
1383 DoStmt::Expr { expr, .. } => expr.render(),
1384 }
1385}
1386
1387fn render_binding(b: &Binding) -> String {
1388 let mut s = b.pat.render();
1389 for p in &b.params {
1390 s.push(' ');
1391 s.push_str(&p.render());
1392 }
1393 format!("{} = {}", s, b.expr.render())
1394}
1395
1396impl Pat {
1397 #[must_use]
1398 pub const fn pos(&self) -> Pos {
1399 match self {
1400 Self::Var { pos, .. }
1401 | Self::Wild { pos, .. }
1402 | Self::Con { pos, .. }
1403 | Self::Tuple { pos, .. }
1404 | Self::List { pos, .. }
1405 | Self::Lit { pos, .. }
1406 | Self::As { pos, .. }
1407 | Self::Other { pos, .. } => *pos,
1408 }
1409 }
1410
1411 /// Byte span covering the whole pattern.
1412 #[must_use]
1413 pub const fn span(&self) -> Span {
1414 match self {
1415 Self::Var { span, .. }
1416 | Self::Wild { span, .. }
1417 | Self::Con { span, .. }
1418 | Self::Tuple { span, .. }
1419 | Self::List { span, .. }
1420 | Self::Lit { span, .. }
1421 | Self::As { span, .. }
1422 | Self::Other { span, .. } => *span,
1423 }
1424 }
1425
1426 /// Render back to compact, source-*like* text. Lossy and normalizing in the
1427 /// same way as [`Expr::render`]; use the node's [`span`](Self::span) for
1428 /// byte-faithful text.
1429 #[must_use]
1430 pub fn render(&self) -> String {
1431 match self {
1432 Self::Var { name, .. } => name.to_string(),
1433 Self::Wild { .. } => "_".to_string(),
1434 Self::Con {
1435 qualifier,
1436 name,
1437 args,
1438 ..
1439 } => {
1440 let head = qualifier
1441 .as_ref()
1442 .map_or_else(|| name.to_string(), |q| format!("{q}.{name}"));
1443 if args.is_empty() {
1444 head
1445 } else {
1446 let parts: Vec<String> = args.iter().map(|p| p.render()).collect();
1447 format!("({} {})", head, parts.join(" "))
1448 }
1449 }
1450 Self::Tuple { items, .. } => {
1451 let xs: Vec<String> = items.iter().map(|p| p.render()).collect();
1452 format!("({})", xs.join(", "))
1453 }
1454 Self::List { items, .. } => {
1455 let xs: Vec<String> = items.iter().map(|p| p.render()).collect();
1456 format!("[{}]", xs.join(", "))
1457 }
1458 Self::Lit { kind, text, .. } => match kind {
1459 LitKind::Text => format!("{text:?}"),
1460 LitKind::Char => format!("'{text}'"),
1461 _ => text.clone(),
1462 },
1463 Self::As { name, pat, .. } => format!("{}@{}", name, pat.render()),
1464 Self::Other { raw, .. } => raw.clone(),
1465 }
1466 }
1467}
1468
1469impl std::fmt::Display for Expr {
1470 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1471 f.write_str(&self.render())
1472 }
1473}
1474
1475impl std::fmt::Display for Pat {
1476 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1477 f.write_str(&self.render())
1478 }
1479}
1480
1481// Span invariants for the lossless AST tile layer; render-shape tests live in integration tests.
1482#[cfg(test)]
1483mod tests {
1484 use super::*;
1485
1486 fn span(start: usize, end: usize) -> Span {
1487 Span::from_usize(start, end)
1488 }
1489
1490 #[test]
1491 fn span_distinguishes_empty_from_invalid() {
1492 assert!(span(3, 3).is_valid());
1493 assert!(span(3, 3).is_empty());
1494
1495 assert!(!span(4, 3).is_valid());
1496 assert!(!span(4, 3).is_empty());
1497 }
1498
1499 #[test]
1500 fn contains_rejects_invalid_spans() {
1501 let parent = span(1, 10);
1502
1503 assert!(parent.contains(&span(3, 7)));
1504 assert!(!parent.contains(&span(7, 3)));
1505 assert!(!span(10, 1).contains(&span(3, 7)));
1506 }
1507
1508 #[test]
1509 fn span_range_and_get_share_source_bytes_safely() {
1510 let source = "foo: Int";
1511
1512 assert_eq!(span(0, 3).range(), 0..3);
1513 assert_eq!(span(0, 3).get(source), Some("foo"));
1514 assert_eq!(span(3, 7).get(source), Some(": In"));
1515 assert!(span(3, 100).get(source).is_none());
1516 }
1517}