Skip to main content

qala_compiler/
ast.rs

1//! the untyped AST: what the parser produces and the type checker consumes.
2//! every node carries a [`Span`] (so Phase 3's diagnostics underline the source
3//! as written); the tree is boxed at recursive positions; nothing is desugared
4//! -- [`Expr::Pipeline`], [`Expr::Interpolation`], [`Stmt::For`], and
5//! [`Expr::Match`] are real nodes, not lowered to calls / `+` chains / `while`.
6//!
7//! `if`/`else` is a statement, not an expression: there is no `Expr::If`. only
8//! `match` and `{ ... }` blocks produce values in expression position. method
9//! calls get their own node ([`Expr::MethodCall`]) distinct from field access
10//! ([`Expr::FieldAccess`]) -- the parser decides based on whether `(` follows
11//! the `.name`; Phase 3 resolves a method call to the free function
12//! `fn Type.name(self, ...)`.
13//!
14//! the node enums derive `Debug, Clone, PartialEq` and no more: no `Eq`,
15//! because float literals reach the AST ([`Expr::Float`]) and `f64` is not
16//! `Eq`; no `serde`, because the type checker and codegen run in-process and
17//! never serialize the tree. spans are not merged here -- the parser builds
18//! each node *with* its computed span (first child to last child, via
19//! [`Span::to`]); `ast.rs` only stores and reads the field.
20
21use crate::span::Span;
22
23/// a whole program: the top-level items in source order.
24///
25/// a type alias rather than a wrapper struct -- a program's span is just the
26/// span of its source, which the caller already holds, so a node here would add
27/// nothing. an empty source parses to an empty `Vec`.
28pub type Ast = Vec<Item>;
29
30// ---- items -----------------------------------------------------------------
31
32/// a top-level declaration: a function, a struct, an enum, or an interface.
33/// `fn Type.method` definitions are an [`Item::Fn`] whose [`FnDecl`] carries an
34/// optional `type_name`.
35#[derive(Debug, Clone, PartialEq)]
36pub enum Item {
37    /// a `fn` declaration -- a free function, or, if `type_name` is set, a
38    /// `fn Type.method` method definition.
39    Fn(FnDecl),
40    /// a `struct` declaration with typed fields.
41    Struct(StructDecl),
42    /// an `enum` declaration with data-carrying variants.
43    Enum(EnumDecl),
44    /// an `interface` declaration -- a set of method signatures, no bodies.
45    Interface(InterfaceDecl),
46}
47
48impl Item {
49    /// the source span of this item, opening keyword to closing brace.
50    ///
51    /// each item kind wraps a struct that carries the span; this delegates to
52    /// it. exhaustive over every kind, so a new item kind forces an arm here.
53    pub fn span(&self) -> Span {
54        match self {
55            Item::Fn(d) => d.span,
56            Item::Struct(d) => d.span,
57            Item::Enum(d) => d.span,
58            Item::Interface(d) => d.span,
59        }
60    }
61}
62
63/// a `fn` declaration. `type_name` is `Some` for a `fn Type.method` definition
64/// (the receiver type's name) and `None` for a free function. `ret_ty` is the
65/// `-> T` return type, omitted for `void`. `effect` is the `is pure`/`io`/
66/// `alloc`/`panic` annotation, omitted when Phase 3 should infer it.
67#[derive(Debug, Clone, PartialEq)]
68pub struct FnDecl {
69    /// the receiver type's name for a `fn Type.method` definition, else `None`.
70    pub type_name: Option<String>,
71    /// the function (or method) name.
72    pub name: String,
73    /// the parameter list, in order; for a method, the first may be `self`.
74    pub params: Vec<Param>,
75    /// the `-> T` return type, or `None` for `void`.
76    pub ret_ty: Option<TypeExpr>,
77    /// the `is pure`/`io`/`alloc`/`panic` annotation, or `None` to infer.
78    pub effect: Option<Effect>,
79    /// the function body; an empty `{ }` body is a [`Block`] with no statements.
80    pub body: Block,
81    /// `fn` keyword to closing `}`.
82    pub span: Span,
83}
84
85/// one parameter of a function or method. `is_self` is true only for the `self`
86/// first parameter of a method, which has no declared `ty`; every other
87/// parameter requires a `ty`. `default` is the optional `= expr` default value.
88#[derive(Debug, Clone, PartialEq)]
89pub struct Param {
90    /// true if this is the untyped `self` first parameter of a method.
91    pub is_self: bool,
92    /// the parameter name (`"self"` when `is_self`).
93    pub name: String,
94    /// the declared type; `None` only when `is_self`.
95    pub ty: Option<TypeExpr>,
96    /// the `= expr` default value, or `None`.
97    pub default: Option<Expr>,
98    /// the parameter's source span (name to default, or name to type).
99    pub span: Span,
100}
101
102/// the four effect annotations. this is the parser's record of *which keyword
103/// appeared* (`is pure` etc.); the type checker's `effects` module (Phase 3)
104/// owns the real effect lattice and inference.
105#[derive(Debug, Clone, PartialEq)]
106pub enum Effect {
107    /// `is pure` -- only computes; no IO, no allocation, no panic.
108    Pure,
109    /// `is io` -- may perform input/output.
110    Io,
111    /// `is alloc` -- may allocate.
112    Alloc,
113    /// `is panic` -- may panic.
114    Panic,
115}
116
117/// a `struct` declaration: a name and its typed fields, in declaration order.
118#[derive(Debug, Clone, PartialEq)]
119pub struct StructDecl {
120    /// the struct's name.
121    pub name: String,
122    /// the fields, in declaration order.
123    pub fields: Vec<Field>,
124    /// `struct` keyword to closing `}`.
125    pub span: Span,
126}
127
128/// one field of a struct: a name and a type (struct fields are always typed --
129/// no inference at declaration sites).
130#[derive(Debug, Clone, PartialEq)]
131pub struct Field {
132    /// the field name.
133    pub name: String,
134    /// the field's declared type.
135    pub ty: TypeExpr,
136    /// the field's source span (name to type).
137    pub span: Span,
138}
139
140/// an `enum` declaration: a name and its variants, in declaration order. each
141/// variant may carry data (a tuple of types).
142#[derive(Debug, Clone, PartialEq)]
143pub struct EnumDecl {
144    /// the enum's name.
145    pub name: String,
146    /// the variants, in declaration order.
147    pub variants: Vec<Variant>,
148    /// `enum` keyword to closing `}`.
149    pub span: Span,
150}
151
152/// one variant of an enum: a name and zero or more field types. `North` has no
153/// fields, `Circle(f64)` one, `Rect(f64, f64)` two.
154#[derive(Debug, Clone, PartialEq)]
155pub struct Variant {
156    /// the variant name.
157    pub name: String,
158    /// the data the variant carries, as a list of field types (possibly empty).
159    pub fields: Vec<TypeExpr>,
160    /// the variant's source span (name, plus its `( ... )` if any).
161    pub span: Span,
162}
163
164/// an `interface` declaration: a name and a set of method signatures (no
165/// bodies). a type satisfies the interface structurally -- by having matching
166/// methods -- with no `implements` declaration; Phase 3 checks that.
167#[derive(Debug, Clone, PartialEq)]
168pub struct InterfaceDecl {
169    /// the interface's name.
170    pub name: String,
171    /// the required method signatures.
172    pub methods: Vec<MethodSig>,
173    /// `interface` keyword to closing `}`.
174    pub span: Span,
175}
176
177/// a method signature in an interface: like a [`FnDecl`] without a body and
178/// without a `type_name` (the receiver is whatever type satisfies the
179/// interface). the first parameter is typically `self`.
180#[derive(Debug, Clone, PartialEq)]
181pub struct MethodSig {
182    /// the method name.
183    pub name: String,
184    /// the parameter list; the first is typically `self`.
185    pub params: Vec<Param>,
186    /// the `-> T` return type, or `None` for `void`.
187    pub ret_ty: Option<TypeExpr>,
188    /// the `is ...` effect annotation, or `None`.
189    pub effect: Option<Effect>,
190    /// the signature's source span.
191    pub span: Span,
192}
193
194// ---- statements and blocks -------------------------------------------------
195
196/// a `{ ... }` block: a sequence of statements, optionally ending in a trailing
197/// expression with no `;`. `value` is that trailing expression; `None` means
198/// the block's value is `void` (it ended in a `;`, or it was empty). a block is
199/// also an expression in this language -- see [`Expr::Block`].
200#[derive(Debug, Clone, PartialEq)]
201pub struct Block {
202    /// the statements, in order.
203    pub stmts: Vec<Stmt>,
204    /// the trailing expression (no terminating `;`) that is the block's value,
205    /// or `None` for a `void` block.
206    pub value: Option<Box<Expr>>,
207    /// `{` to `}`.
208    pub span: Span,
209}
210
211/// a statement. `if`/`else`, `while`, `for ... in ...`, `return`, `break`,
212/// `continue`, and `defer expr` are all statements -- not expressions; there is
213/// no `Expr::If` in v1. an `if` chains through [`ElseBranch::If`] for
214/// `else if`.
215#[derive(Debug, Clone, PartialEq)]
216pub enum Stmt {
217    /// `let name = init` or `let mut name = init`, with an optional `: ty`. the
218    /// initializer is required (no uninitialized bindings); the type is inferred
219    /// from `init` when omitted.
220    Let {
221        is_mut: bool,
222        name: String,
223        ty: Option<TypeExpr>,
224        init: Expr,
225        span: Span,
226    },
227    /// `if cond { ... }` with an optional `else` (a block, or another `if` for
228    /// an `else if` chain). a statement, not an expression -- there is no
229    /// `let x = if c { a } else { b }` in v1.
230    If {
231        cond: Expr,
232        then_block: Block,
233        else_branch: Option<ElseBranch>,
234        span: Span,
235    },
236    /// `while cond { ... }`.
237    While { cond: Expr, body: Block, span: Span },
238    /// `for var in iter { ... }`. `iter` is any expression that yields an
239    /// iterable; a range is just one kind of iterable. kept as a `For` node, not
240    /// lowered to a `while`.
241    For {
242        var: String,
243        iter: Expr,
244        body: Block,
245        span: Span,
246    },
247    /// `return` or `return expr`. the value is optional -- a bare `return` in a
248    /// `void` function is legal.
249    Return { value: Option<Expr>, span: Span },
250    /// `break`. no labels, no value in v1.
251    Break { span: Span },
252    /// `continue`. no labels, no value in v1.
253    Continue { span: Span },
254    /// `defer expr` -- `expr` runs at scope exit, LIFO with other defers in the
255    /// same scope.
256    Defer { expr: Expr, span: Span },
257    /// an expression used as a statement (`expr ;`). its value is discarded.
258    Expr { expr: Expr, span: Span },
259}
260
261impl Stmt {
262    /// the source span of this statement.
263    ///
264    /// every variant carries its `span` field; this match is exhaustive, which
265    /// is the proof every statement has one. the parser builds the span from the
266    /// leading keyword (or the first sub-expression) to the last token.
267    pub fn span(&self) -> Span {
268        match self {
269            Stmt::Let { span, .. }
270            | Stmt::If { span, .. }
271            | Stmt::While { span, .. }
272            | Stmt::For { span, .. }
273            | Stmt::Return { span, .. }
274            | Stmt::Break { span }
275            | Stmt::Continue { span }
276            | Stmt::Defer { span, .. }
277            | Stmt::Expr { span, .. } => *span,
278        }
279    }
280}
281
282/// the `else` part of an [`Stmt::If`]: either a final `{ ... }` block, or
283/// another `if` (the boxed [`Stmt`] is always an [`Stmt::If`]) for an
284/// `else if` chain.
285#[derive(Debug, Clone, PartialEq)]
286pub enum ElseBranch {
287    /// `else { ... }`.
288    Block(Block),
289    /// `else if ...` -- the boxed statement is an [`Stmt::If`].
290    If(Box<Stmt>),
291}
292
293// ---- expressions -----------------------------------------------------------
294
295/// an expression. literals, identifiers, parenthesized and tuple expressions,
296/// array literals (including the `[v; n]` repeat form), struct literals, field
297/// access, method calls, calls, indexing, `?` propagation, unary and binary
298/// operators, ranges, the `|>` pipeline, `comptime`, block expressions, `match`,
299/// the `or` fallback, and string interpolation. there is deliberately no `If`
300/// variant -- `if`/`else` is a [`Stmt`] in v1.
301///
302/// nothing is desugared: [`Expr::Pipeline`] stays a pipeline (not `f(x, a)`),
303/// [`Expr::Interpolation`] stays a parts list (not a `+` chain), [`Expr::Match`]
304/// and [`Expr::Block`] produce values directly. that faithfulness is what lets
305/// Phase 3 diagnostics point at the source as written; lowering happens in
306/// Phase 4 codegen.
307#[derive(Debug, Clone, PartialEq)]
308pub enum Expr {
309    /// an integer literal (already decoded; a leading `-` is a separate
310    /// [`Expr::Unary`]).
311    Int { value: i64, span: Span },
312    /// a float literal (already decoded).
313    Float { value: f64, span: Span },
314    /// a byte literal `b'X'`, the byte it denotes.
315    Byte { value: u8, span: Span },
316    /// a string literal with no interpolation, escapes already decoded.
317    Str { value: String, span: Span },
318    /// a boolean literal, `true` or `false`.
319    Bool { value: bool, span: Span },
320    /// an identifier reference (a variable, a function name, a type name in
321    /// value position -- the parser does not distinguish; Phase 3 resolves it).
322    Ident { name: String, span: Span },
323    /// `( inner )` -- a parenthesized expression. kept as a node so the span and
324    /// the parentheses are visible to diagnostics; semantically transparent.
325    Paren { inner: Box<Expr>, span: Span },
326    /// `( e1, e2, ... )` -- a tuple. a single element with a trailing comma is a
327    /// one-tuple; without the comma it is just a [`Expr::Paren`].
328    Tuple { elems: Vec<Expr>, span: Span },
329    /// `[ e1, e2, ... ]` -- an array literal listing its elements.
330    ArrayLit { elems: Vec<Expr>, span: Span },
331    /// `[ value; count ]` -- an array literal repeating `value` `count` times.
332    ArrayRepeat {
333        value: Box<Expr>,
334        count: Box<Expr>,
335        span: Span,
336    },
337    /// `Name { field: e, ... }` -- a struct literal. `name` is the struct's
338    /// name; whether it names a real struct is Phase 3's call.
339    StructLit {
340        name: String,
341        fields: Vec<FieldInit>,
342        span: Span,
343    },
344    /// `obj.name` -- field access. distinct from [`Expr::MethodCall`]: this is
345    /// the form with no `( ... )` after the `.name`.
346    FieldAccess {
347        obj: Box<Expr>,
348        name: String,
349        span: Span,
350    },
351    /// `receiver.name(args)` -- a method call. distinct from a field access
352    /// followed by a call; Phase 3 resolves it to `fn Type.name(self, ...)`.
353    MethodCall {
354        receiver: Box<Expr>,
355        name: String,
356        args: Vec<Expr>,
357        span: Span,
358    },
359    /// `callee(args)` -- a call expression (`callee` is usually an
360    /// [`Expr::Ident`], but can be any expression that yields something
361    /// callable).
362    Call {
363        callee: Box<Expr>,
364        args: Vec<Expr>,
365        span: Span,
366    },
367    /// `obj[index]` -- an index expression.
368    Index {
369        obj: Box<Expr>,
370        index: Box<Expr>,
371        span: Span,
372    },
373    /// `expr?` -- error propagation: if `expr` is an error, return it from the
374    /// enclosing function; otherwise unwrap it. binds tightest (with `.`, call,
375    /// index), so `f()?.x` is `(f()?).x` and `a + b?` is `a + (b?)`.
376    Try { expr: Box<Expr>, span: Span },
377    /// a unary-operator application: `!operand` or `-operand`.
378    Unary {
379        op: UnaryOp,
380        operand: Box<Expr>,
381        span: Span,
382    },
383    /// a binary-operator application: `lhs op rhs`. covers arithmetic,
384    /// comparison, equality, and the boolean `&&` / `||`. the `or` fallback is
385    /// *not* here -- it is [`Expr::OrElse`].
386    Binary {
387        op: BinOp,
388        lhs: Box<Expr>,
389        rhs: Box<Expr>,
390        span: Span,
391    },
392    /// `start .. end` or `start ..= end` -- a range. `start` and `end` are each
393    /// optional to leave room for `..end` / `start..` forms; `inclusive` is true
394    /// for `..=`. ranges are ordinary expressions (an iterable for `for`).
395    Range {
396        start: Option<Box<Expr>>,
397        end: Option<Box<Expr>>,
398        inclusive: bool,
399        span: Span,
400    },
401    /// `lhs |> call` -- the pipeline operator. kept as a faithful node, NOT
402    /// lowered to `call(lhs, ...)`; that desugaring is Phase 4 codegen. `call`
403    /// is whatever expression followed `|>` (typically a call or a bare function
404    /// name); the parser does not rewrite it.
405    Pipeline {
406        lhs: Box<Expr>,
407        call: Box<Expr>,
408        span: Span,
409    },
410    /// `comptime body` -- evaluate `body` during compilation, embed the result
411    /// as a constant. a prefix operator at unary precedence, so `comptime a + b`
412    /// is `(comptime a) + b`; `body` may also be a block expression
413    /// (`comptime { ... }`).
414    Comptime { body: Box<Expr>, span: Span },
415    /// `{ ...; trailing }` used as an expression -- its value is the block's
416    /// trailing expression (or `void`). one of only two value-producing block
417    /// forms; the other is [`Expr::Match`].
418    Block { block: Block, span: Span },
419    /// `match scrutinee { arm, ... }` -- pattern matching with at least one arm.
420    /// kept as a faithful node. each arm has a pattern, an optional `if` guard,
421    /// and a body (an expression or a block); Phase 3 checks exhaustiveness.
422    Match {
423        scrutinee: Box<Expr>,
424        arms: Vec<MatchArm>,
425        span: Span,
426    },
427    /// `expr or fallback` -- inline fallback for a `Result`/`Option`: the value
428    /// of `expr` if it is `Ok`/`Some`, else `fallback`. modelled as its own
429    /// node (not a [`BinOp`]) because it is faithful surface syntax with the
430    /// loosest precedence, left-associative; `a or b or c` is `(a or b) or c`.
431    OrElse {
432        expr: Box<Expr>,
433        fallback: Box<Expr>,
434        span: Span,
435    },
436    /// a string with `{expr}` interpolations -- `parts` alternates literal text
437    /// and embedded expressions, in source order. NOT desugared to a `+` chain;
438    /// the conversion-and-concatenation happens in Phase 4 codegen.
439    Interpolation { parts: Vec<InterpPart>, span: Span },
440}
441
442impl Expr {
443    /// the source span of this expression.
444    ///
445    /// every variant carries its `span` field; this match is exhaustive over
446    /// all of them, which is the real guarantee that "every expression node has
447    /// a span". the parser computes each span from the first token to the last
448    /// (via [`Span::to`]) when it builds the node.
449    pub fn span(&self) -> Span {
450        match self {
451            Expr::Int { span, .. }
452            | Expr::Float { span, .. }
453            | Expr::Byte { span, .. }
454            | Expr::Str { span, .. }
455            | Expr::Bool { span, .. }
456            | Expr::Ident { span, .. }
457            | Expr::Paren { span, .. }
458            | Expr::Tuple { span, .. }
459            | Expr::ArrayLit { span, .. }
460            | Expr::ArrayRepeat { span, .. }
461            | Expr::StructLit { span, .. }
462            | Expr::FieldAccess { span, .. }
463            | Expr::MethodCall { span, .. }
464            | Expr::Call { span, .. }
465            | Expr::Index { span, .. }
466            | Expr::Try { span, .. }
467            | Expr::Unary { span, .. }
468            | Expr::Binary { span, .. }
469            | Expr::Range { span, .. }
470            | Expr::Pipeline { span, .. }
471            | Expr::Comptime { span, .. }
472            | Expr::Block { span, .. }
473            | Expr::Match { span, .. }
474            | Expr::OrElse { span, .. }
475            | Expr::Interpolation { span, .. } => *span,
476        }
477    }
478}
479
480/// one field initializer in a struct literal: `name: value`.
481#[derive(Debug, Clone, PartialEq)]
482pub struct FieldInit {
483    /// the field name.
484    pub name: String,
485    /// the value expression.
486    pub value: Expr,
487    /// the initializer's source span (name to value).
488    pub span: Span,
489}
490
491/// a unary prefix operator.
492#[derive(Debug, Clone, PartialEq)]
493pub enum UnaryOp {
494    /// `!` -- boolean negation.
495    Not,
496    /// `-` -- arithmetic negation.
497    Neg,
498}
499
500/// a binary operator. note `||` (boolean or) is [`BinOp::Or`]; the `or` fallback
501/// keyword is *not* a binary operator -- it is [`Expr::OrElse`].
502#[derive(Debug, Clone, PartialEq)]
503pub enum BinOp {
504    /// `+`
505    Add,
506    /// `-`
507    Sub,
508    /// `*`
509    Mul,
510    /// `/`
511    Div,
512    /// `%`
513    Rem,
514    /// `==`
515    Eq,
516    /// `!=`
517    Ne,
518    /// `<`
519    Lt,
520    /// `<=`
521    Le,
522    /// `>`
523    Gt,
524    /// `>=`
525    Ge,
526    /// `&&` -- boolean and.
527    And,
528    /// `||` -- boolean or (distinct from the `or` fallback keyword).
529    Or,
530}
531
532/// one piece of a string interpolation: either literal text, or an embedded
533/// expression. an [`Expr::Interpolation`]'s `parts` list alternates these,
534/// starting and ending with a (possibly empty) `Literal`.
535#[derive(Debug, Clone, PartialEq)]
536pub enum InterpPart {
537    /// literal text between interpolations (escapes already decoded; may be
538    /// empty).
539    Literal(String),
540    /// an embedded `{ expr }`.
541    Expr(Expr),
542}
543
544// ---- patterns --------------------------------------------------------------
545
546/// a pattern in a `match` arm. v1 has variant destructure (with sub-patterns),
547/// the `_` wildcard, a name binding, and literal patterns; no `or`-patterns, no
548/// struct patterns, no nested guards (guards live on the [`MatchArm`]).
549#[derive(Debug, Clone, PartialEq)]
550pub enum Pattern {
551    /// `Name(sub, ...)` -- destructure an enum variant; `sub` is the
552    /// sub-patterns for its fields (empty for a no-data variant matched as
553    /// `Name`). whether `Name` is a real variant is Phase 3's call.
554    Variant {
555        name: String,
556        sub: Vec<Pattern>,
557        span: Span,
558    },
559    /// `_` -- matches anything, binds nothing.
560    Wildcard { span: Span },
561    /// `name` -- matches anything, binds it to `name`.
562    Binding { name: String, span: Span },
563    /// an integer literal pattern.
564    Int { value: i64, span: Span },
565    /// a float literal pattern.
566    Float { value: f64, span: Span },
567    /// a byte literal pattern.
568    Byte { value: u8, span: Span },
569    /// a string literal pattern.
570    Str { value: String, span: Span },
571    /// a boolean literal pattern, `true` or `false`.
572    Bool { value: bool, span: Span },
573}
574
575impl Pattern {
576    /// the source span of this pattern.
577    ///
578    /// every variant carries its `span` field; the match is exhaustive, the
579    /// proof every pattern has one.
580    pub fn span(&self) -> Span {
581        match self {
582            Pattern::Variant { span, .. }
583            | Pattern::Wildcard { span }
584            | Pattern::Binding { span, .. }
585            | Pattern::Int { span, .. }
586            | Pattern::Float { span, .. }
587            | Pattern::Byte { span, .. }
588            | Pattern::Str { span, .. }
589            | Pattern::Bool { span, .. } => *span,
590        }
591    }
592}
593
594/// one arm of a `match`: a pattern, an optional `if` guard, and a body. the
595/// body is an expression or a block; arms are comma-separated (the last arm's
596/// comma is optional).
597#[derive(Debug, Clone, PartialEq)]
598pub struct MatchArm {
599    /// the pattern this arm matches.
600    pub pattern: Pattern,
601    /// the `if expr` guard, or `None`.
602    pub guard: Option<Expr>,
603    /// the arm body -- an expression or a block.
604    pub body: MatchArmBody,
605    /// the arm's source span (pattern to body).
606    pub span: Span,
607}
608
609/// a `match` arm body: a bare expression (`Pattern => expr`) or a block
610/// (`Pattern => { ... }`).
611#[derive(Debug, Clone, PartialEq)]
612pub enum MatchArmBody {
613    /// `=> expr`.
614    Expr(Box<Expr>),
615    /// `=> { ... }`.
616    Block(Block),
617}
618
619// ---- type expressions ------------------------------------------------------
620
621/// a type as written in source: a primitive name, a named type, a fixed or
622/// dynamic array, a tuple, a function type, or a generic application. type
623/// expressions appear in parameter and return positions, struct fields, enum
624/// variant data, and `let x: T = ...`.
625#[derive(Debug, Clone, PartialEq)]
626pub enum TypeExpr {
627    /// a primitive type name: `i64`, `f64`, `bool`, `str`, `byte`, `void`.
628    Primitive { kind: PrimType, span: Span },
629    /// a named type -- a struct, enum, or interface name (the parser does not
630    /// distinguish; Phase 3 resolves it).
631    Named { name: String, span: Span },
632    /// `[ elem; size ]` -- a fixed-size array. `size` is the literal length.
633    Array {
634        elem: Box<TypeExpr>,
635        size: u64,
636        span: Span,
637    },
638    /// `[ elem ]` -- a dynamic array.
639    DynArray { elem: Box<TypeExpr>, span: Span },
640    /// `( T1, T2, ... )` -- a tuple type.
641    Tuple { elems: Vec<TypeExpr>, span: Span },
642    /// `fn( T1, T2 ) -> T3` -- a function type.
643    Fn {
644        params: Vec<TypeExpr>,
645        ret: Box<TypeExpr>,
646        span: Span,
647    },
648    /// `Name< T1, T2 >` -- a generic application (used by `Result<T, E>` and
649    /// `Option<T>`). the parser allows it on any name; restricting which names
650    /// may be generic is Phase 3's job.
651    Generic {
652        name: String,
653        args: Vec<TypeExpr>,
654        span: Span,
655    },
656}
657
658impl TypeExpr {
659    /// the source span of this type expression.
660    ///
661    /// every variant carries its `span` field; the match is exhaustive, the
662    /// proof every type node has one.
663    pub fn span(&self) -> Span {
664        match self {
665            TypeExpr::Primitive { span, .. }
666            | TypeExpr::Named { span, .. }
667            | TypeExpr::Array { span, .. }
668            | TypeExpr::DynArray { span, .. }
669            | TypeExpr::Tuple { span, .. }
670            | TypeExpr::Fn { span, .. }
671            | TypeExpr::Generic { span, .. } => *span,
672        }
673    }
674}
675
676/// the six primitive type names.
677#[derive(Debug, Clone, PartialEq)]
678pub enum PrimType {
679    /// `i64`
680    I64,
681    /// `f64`
682    F64,
683    /// `bool`
684    Bool,
685    /// `str`
686    Str,
687    /// `byte`
688    Byte,
689    /// `void`
690    Void,
691}
692
693#[cfg(test)]
694mod tests {
695    use super::*;
696
697    // a distinct span per call, so a wrong span() arm or a clobbered field is
698    // caught: span(n) and span(m) never compare equal for n != m.
699    fn span(n: usize) -> Span {
700        Span::new(n, n + 1)
701    }
702
703    #[test]
704    fn nodes_are_debug_clone_and_partial_eq() {
705        // build a small expression tree, clone it, assert equal; build a
706        // different one, assert not equal. the precedence tests in the parser
707        // depend on PartialEq, so it must be derived on every node.
708        let a = Expr::Binary {
709            op: BinOp::Add,
710            lhs: Box::new(Expr::Int {
711                value: 1,
712                span: span(0),
713            }),
714            rhs: Box::new(Expr::Int {
715                value: 2,
716                span: span(2),
717            }),
718            span: span(0),
719        };
720        let b = a.clone();
721        assert_eq!(a, b);
722        // Debug must be derived too (assertion messages and parser tests use it).
723        let _ = format!("{a:?}");
724        let c = Expr::Binary {
725            op: BinOp::Mul, // different operator
726            lhs: Box::new(Expr::Int {
727                value: 1,
728                span: span(0),
729            }),
730            rhs: Box::new(Expr::Int {
731                value: 2,
732                span: span(2),
733            }),
734            span: span(0),
735        };
736        assert_ne!(a, c);
737        // a float literal node round-trips through clone -- this is why the
738        // node enums are PartialEq but not Eq.
739        let f = Expr::Float {
740            value: 1.5,
741            span: span(5),
742        };
743        assert_eq!(f.clone(), f);
744    }
745
746    #[test]
747    fn expr_span_returns_the_stored_span_for_a_sample_of_variants() {
748        let cases: Vec<(Expr, Span)> = vec![
749            (
750                Expr::Int {
751                    value: 0,
752                    span: span(1),
753                },
754                span(1),
755            ),
756            (
757                Expr::Float {
758                    value: 0.0,
759                    span: span(2),
760                },
761                span(2),
762            ),
763            (
764                Expr::Byte {
765                    value: 0,
766                    span: span(3),
767                },
768                span(3),
769            ),
770            (
771                Expr::Str {
772                    value: String::new(),
773                    span: span(4),
774                },
775                span(4),
776            ),
777            (
778                Expr::Bool {
779                    value: true,
780                    span: span(5),
781                },
782                span(5),
783            ),
784            (
785                Expr::Ident {
786                    name: "x".to_string(),
787                    span: span(6),
788                },
789                span(6),
790            ),
791            (
792                Expr::Unary {
793                    op: UnaryOp::Neg,
794                    operand: Box::new(Expr::Int {
795                        value: 1,
796                        span: span(8),
797                    }),
798                    span: span(7),
799                },
800                span(7),
801            ),
802            (
803                Expr::MethodCall {
804                    receiver: Box::new(Expr::Ident {
805                        name: "f".to_string(),
806                        span: span(10),
807                    }),
808                    name: "read_all".to_string(),
809                    args: vec![],
810                    span: span(9),
811                },
812                span(9),
813            ),
814            (
815                Expr::Pipeline {
816                    lhs: Box::new(Expr::Int {
817                        value: 5,
818                        span: span(12),
819                    }),
820                    call: Box::new(Expr::Ident {
821                        name: "double".to_string(),
822                        span: span(14),
823                    }),
824                    span: span(11),
825                },
826                span(11),
827            ),
828            (
829                Expr::OrElse {
830                    expr: Box::new(Expr::Ident {
831                        name: "a".to_string(),
832                        span: span(16),
833                    }),
834                    fallback: Box::new(Expr::Str {
835                        value: "no data".to_string(),
836                        span: span(18),
837                    }),
838                    span: span(15),
839                },
840                span(15),
841            ),
842            (
843                Expr::Interpolation {
844                    parts: vec![
845                        InterpPart::Literal("fib(".to_string()),
846                        InterpPart::Expr(Expr::Ident {
847                            name: "i".to_string(),
848                            span: span(20),
849                        }),
850                        InterpPart::Literal(")".to_string()),
851                    ],
852                    span: span(19),
853                },
854                span(19),
855            ),
856        ];
857        for (expr, expected) in cases {
858            assert_eq!(expr.span(), expected, "span() mismatch for {expr:?}");
859        }
860    }
861
862    #[test]
863    fn stmt_span_returns_the_stored_span_for_a_sample_of_variants() {
864        let cases: Vec<(Stmt, Span)> = vec![
865            (
866                Stmt::Let {
867                    is_mut: false,
868                    name: "x".to_string(),
869                    ty: None,
870                    init: Expr::Int {
871                        value: 1,
872                        span: span(1),
873                    },
874                    span: span(0),
875                },
876                span(0),
877            ),
878            (Stmt::Break { span: span(2) }, span(2)),
879            (Stmt::Continue { span: span(3) }, span(3)),
880            (
881                Stmt::Return {
882                    value: None,
883                    span: span(4),
884                },
885                span(4),
886            ),
887            (
888                Stmt::For {
889                    var: "i".to_string(),
890                    iter: Expr::Range {
891                        start: Some(Box::new(Expr::Int {
892                            value: 0,
893                            span: span(6),
894                        })),
895                        end: Some(Box::new(Expr::Int {
896                            value: 15,
897                            span: span(8),
898                        })),
899                        inclusive: false,
900                        span: span(6),
901                    },
902                    body: Block {
903                        stmts: vec![],
904                        value: None,
905                        span: span(9),
906                    },
907                    span: span(5),
908                },
909                span(5),
910            ),
911        ];
912        for (stmt, expected) in cases {
913            assert_eq!(stmt.span(), expected, "span() mismatch for {stmt:?}");
914        }
915    }
916
917    #[test]
918    fn item_span_delegates_to_the_wrapped_decl() {
919        let f = Item::Fn(FnDecl {
920            type_name: None,
921            name: "main".to_string(),
922            params: vec![],
923            ret_ty: None,
924            effect: Some(Effect::Io),
925            body: Block {
926                stmts: vec![],
927                value: None,
928                span: span(1),
929            },
930            span: span(0),
931        });
932        assert_eq!(f.span(), span(0));
933        let s = Item::Struct(StructDecl {
934            name: "S".to_string(),
935            fields: vec![],
936            span: span(2),
937        });
938        assert_eq!(s.span(), span(2));
939        let e = Item::Enum(EnumDecl {
940            name: "E".to_string(),
941            variants: vec![],
942            span: span(3),
943        });
944        assert_eq!(e.span(), span(3));
945        let i = Item::Interface(InterfaceDecl {
946            name: "I".to_string(),
947            methods: vec![],
948            span: span(4),
949        });
950        assert_eq!(i.span(), span(4));
951    }
952
953    #[test]
954    fn pattern_span_returns_the_stored_span_for_every_variant() {
955        let cases: Vec<(Pattern, Span)> = vec![
956            (
957                Pattern::Variant {
958                    name: "Circle".to_string(),
959                    sub: vec![Pattern::Binding {
960                        name: "r".to_string(),
961                        span: span(1),
962                    }],
963                    span: span(0),
964                },
965                span(0),
966            ),
967            (Pattern::Wildcard { span: span(2) }, span(2)),
968            (
969                Pattern::Binding {
970                    name: "v".to_string(),
971                    span: span(3),
972                },
973                span(3),
974            ),
975            (
976                Pattern::Int {
977                    value: 0,
978                    span: span(4),
979                },
980                span(4),
981            ),
982            (
983                Pattern::Float {
984                    value: 0.0,
985                    span: span(5),
986                },
987                span(5),
988            ),
989            (
990                Pattern::Byte {
991                    value: 0,
992                    span: span(6),
993                },
994                span(6),
995            ),
996            (
997                Pattern::Str {
998                    value: String::new(),
999                    span: span(7),
1000                },
1001                span(7),
1002            ),
1003            (
1004                Pattern::Bool {
1005                    value: false,
1006                    span: span(8),
1007                },
1008                span(8),
1009            ),
1010        ];
1011        for (pat, expected) in cases {
1012            assert_eq!(pat.span(), expected, "span() mismatch for {pat:?}");
1013        }
1014    }
1015
1016    #[test]
1017    fn type_expr_span_returns_the_stored_span_for_every_variant() {
1018        let cases: Vec<(TypeExpr, Span)> = vec![
1019            (
1020                TypeExpr::Primitive {
1021                    kind: PrimType::I64,
1022                    span: span(1),
1023                },
1024                span(1),
1025            ),
1026            (
1027                TypeExpr::Named {
1028                    name: "Shape".to_string(),
1029                    span: span(2),
1030                },
1031                span(2),
1032            ),
1033            (
1034                TypeExpr::Array {
1035                    elem: Box::new(TypeExpr::Primitive {
1036                        kind: PrimType::I64,
1037                        span: span(4),
1038                    }),
1039                    size: 5,
1040                    span: span(3),
1041                },
1042                span(3),
1043            ),
1044            (
1045                TypeExpr::DynArray {
1046                    elem: Box::new(TypeExpr::Primitive {
1047                        kind: PrimType::I64,
1048                        span: span(6),
1049                    }),
1050                    span: span(5),
1051                },
1052                span(5),
1053            ),
1054            (
1055                TypeExpr::Tuple {
1056                    elems: vec![
1057                        TypeExpr::Primitive {
1058                            kind: PrimType::I64,
1059                            span: span(8),
1060                        },
1061                        TypeExpr::Primitive {
1062                            kind: PrimType::Bool,
1063                            span: span(9),
1064                        },
1065                    ],
1066                    span: span(7),
1067                },
1068                span(7),
1069            ),
1070            (
1071                TypeExpr::Fn {
1072                    params: vec![TypeExpr::Primitive {
1073                        kind: PrimType::I64,
1074                        span: span(11),
1075                    }],
1076                    ret: Box::new(TypeExpr::Primitive {
1077                        kind: PrimType::I64,
1078                        span: span(12),
1079                    }),
1080                    span: span(10),
1081                },
1082                span(10),
1083            ),
1084            (
1085                TypeExpr::Generic {
1086                    name: "Result".to_string(),
1087                    args: vec![
1088                        TypeExpr::Primitive {
1089                            kind: PrimType::Str,
1090                            span: span(14),
1091                        },
1092                        TypeExpr::Primitive {
1093                            kind: PrimType::Str,
1094                            span: span(15),
1095                        },
1096                    ],
1097                    span: span(13),
1098                },
1099                span(13),
1100            ),
1101        ];
1102        for (ty, expected) in cases {
1103            assert_eq!(ty.span(), expected, "span() mismatch for {ty:?}");
1104        }
1105    }
1106
1107    #[test]
1108    fn a_match_arm_holds_a_pattern_an_optional_guard_and_a_body() {
1109        // an expression-body arm with a guard: `v if v > 0 => "positive"`.
1110        let arm = MatchArm {
1111            pattern: Pattern::Binding {
1112                name: "v".to_string(),
1113                span: span(0),
1114            },
1115            guard: Some(Expr::Binary {
1116                op: BinOp::Gt,
1117                lhs: Box::new(Expr::Ident {
1118                    name: "v".to_string(),
1119                    span: span(2),
1120                }),
1121                rhs: Box::new(Expr::Int {
1122                    value: 0,
1123                    span: span(4),
1124                }),
1125                span: span(2),
1126            }),
1127            body: MatchArmBody::Expr(Box::new(Expr::Str {
1128                value: "positive".to_string(),
1129                span: span(6),
1130            })),
1131            span: span(0),
1132        };
1133        assert!(arm.guard.is_some());
1134        // a block-body arm with no guard.
1135        let arm2 = MatchArm {
1136            pattern: Pattern::Wildcard { span: span(10) },
1137            guard: None,
1138            body: MatchArmBody::Block(Block {
1139                stmts: vec![],
1140                value: None,
1141                span: span(12),
1142            }),
1143            span: span(10),
1144        };
1145        assert!(arm2.guard.is_none());
1146        assert_ne!(arm, arm2);
1147    }
1148
1149    #[test]
1150    fn a_block_separates_statements_from_an_optional_trailing_value() {
1151        // `{ let x = 1; x }` -- one statement, a trailing-value expression.
1152        let block = Block {
1153            stmts: vec![Stmt::Let {
1154                is_mut: false,
1155                name: "x".to_string(),
1156                ty: None,
1157                init: Expr::Int {
1158                    value: 1,
1159                    span: span(1),
1160                },
1161                span: span(0),
1162            }],
1163            value: Some(Box::new(Expr::Ident {
1164                name: "x".to_string(),
1165                span: span(3),
1166            })),
1167            span: span(0),
1168        };
1169        assert_eq!(block.stmts.len(), 1);
1170        assert!(block.value.is_some());
1171        // an empty `{ }` block: no statements, value is void (None).
1172        let empty = Block {
1173            stmts: vec![],
1174            value: None,
1175            span: span(5),
1176        };
1177        assert!(empty.stmts.is_empty());
1178        assert!(empty.value.is_none());
1179    }
1180}