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}