gruel_parser/ast.rs
1//! Abstract Syntax Tree types for Gruel.
2//!
3//! The AST represents the syntactic structure of the source code.
4//! It closely mirrors the source syntax and preserves all information
5//! needed for error reporting.
6//!
7//! ## SmallVec Usage
8//!
9//! Some non-recursive Vec fields use SmallVec to avoid heap allocation for
10//! common small sizes:
11//! - `Directives` (SmallVec<[Directive; 1]>) - most items have 0-1 directives
12//!
13//! ## Vec Usage (Cannot Use SmallVec)
14//!
15//! Vec fields containing recursive types (Expr) cannot use SmallVec because
16//! Expr's size cannot be determined at compile time. These include:
17//! - `Vec<CallArg>` - CallArg contains Expr
18//! - `Vec<MatchArm>` - contains Expr
19//! - `Vec<FieldInit>` - contains Box<Expr>
20//! - `Vec<IntrinsicArg>` - contains Expr
21//! - `Vec<Statement>` - Statement contains Expr
22//! - `Vec<Expr>` - directly recursive
23//!
24//! The IR layers (RIR, AIR, CFG) use index-based references which avoid
25//! this issue and are already efficiently allocated.
26
27use std::fmt;
28
29use gruel_builtins::Posture;
30use gruel_util::Span;
31use lasso::{Key, Spur};
32use smallvec::SmallVec;
33
34/// Type alias for a small vector of directives.
35/// Most items have 0-1 directives, so we inline capacity for 1.
36pub type Directives = SmallVec<[Directive; 1]>;
37
38/// A doc comment block attached to a nameable item (ADR-0089).
39///
40/// The body is the raw Markdown content with the `///` marker and at most
41/// one shared leading space stripped per line; consecutive `///` lines are
42/// joined with `\n`. The span covers the entire block in source.
43#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
44pub struct Doc {
45 /// Raw Markdown body (lines joined with `\n`).
46 pub body: String,
47 /// Span covering the doc-comment block.
48 pub span: Span,
49}
50
51/// A complete source file (list of items).
52#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
53pub struct Ast {
54 /// ADR-0089: module-level docstring, if the first doc block in the file
55 /// was separated from the first item by a blank line.
56 #[serde(default)]
57 pub module_doc: Option<Doc>,
58 pub items: Vec<Item>,
59}
60
61/// A directive that modifies compiler behavior for the following item or statement.
62///
63/// Directives use the `@name(args)` syntax and appear before items or statements.
64/// For example: `@allow(unused_variable)`
65#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
66pub struct Directive {
67 /// The directive name (without the @)
68 pub name: Ident,
69 /// Arguments to the directive
70 pub args: Vec<DirectiveArg>,
71 /// Span covering the entire directive
72 pub span: Span,
73}
74
75/// An argument to a directive.
76#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
77pub enum DirectiveArg {
78 /// An identifier argument (e.g., `unused_variable` in `@allow(unused_variable)`)
79 Ident(Ident),
80 /// A string-literal argument (e.g., `"drop"` in `@lang("drop")`).
81 /// ADR-0079.
82 String(StringLit),
83}
84
85/// A top-level item in a source file.
86#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
87pub enum Item {
88 Function(Function),
89 Struct(StructDecl),
90 Enum(EnumDecl),
91 /// Interface declaration (ADR-0056) - a structurally typed set of method
92 /// requirements.
93 Interface(InterfaceDecl),
94 /// Derive declaration (ADR-0058) - a set of method declarations attached
95 /// to a host type via `@derive(...)`.
96 Derive(DeriveDecl),
97 /// Constant declaration (e.g., `const math = @import("math");`)
98 Const(ConstDecl),
99 /// ADR-0085: `link_extern("libname") { … }` — a block of extern C
100 /// function declarations linked against the named library.
101 LinkExtern(LinkExternBlock),
102 /// Error node for recovered parse errors at item level.
103 /// Used by error recovery to continue parsing after a syntax error.
104 Error(Span),
105}
106
107/// ADR-0086: dynamic vs static link mode for a `link_extern` block.
108#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
109pub enum LinkMode {
110 /// `link_extern("name") { … }` — dynamic linkage (`-l<name>`).
111 Dynamic,
112 /// `static_link_extern("name") { … }` — static linkage. On ELF
113 /// the linker emits `-Wl,-Bstatic -l<name> -Wl,-Bdynamic`; on
114 /// Mach-O it emits `-Wl,-search_paths_first -l<name>` and falls
115 /// back to dynamic if no `.a` is on the search path.
116 Static,
117}
118
119/// ADR-0085: a `link_extern("libname") { … }` block (or, per ADR-0086,
120/// `static_link_extern("libname") { … }`).
121///
122/// Each item inside the block is an extern fn declaration (body-less,
123/// implicit `@mark(c)`). The library name contributes `-l<libname>` to
124/// the link line.
125#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
126pub struct LinkExternBlock {
127 /// ADR-0089: docstring attached to the block.
128 #[serde(default)]
129 pub doc: Option<Doc>,
130 /// Library name as written in source (the contents of the string
131 /// literal between the parentheses).
132 pub library: StringLit,
133 /// Body-less fn declarations inside the block.
134 pub items: Vec<ExternFn>,
135 /// ADR-0086: dynamic (`link_extern`) or static (`static_link_extern`).
136 #[serde(default = "default_link_mode")]
137 pub link_mode: LinkMode,
138 /// Span covering the entire `link_extern(...) { ... }` form.
139 pub span: Span,
140}
141
142fn default_link_mode() -> LinkMode {
143 LinkMode::Dynamic
144}
145
146/// ADR-0085: a body-less fn declaration that appears inside a
147/// `link_extern` block.
148///
149/// Bodies are forbidden here (the symbol is resolved at link time, not
150/// emitted from this compilation unit). Directives like `@link_name`
151/// still attach per-declaration.
152#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
153pub struct ExternFn {
154 /// ADR-0089: docstring attached to this declaration.
155 #[serde(default)]
156 pub doc: Option<Doc>,
157 /// Directives attached to this declaration (e.g. `@link_name(...)`).
158 pub directives: Directives,
159 /// Function name as it appears in Gruel source.
160 pub name: Ident,
161 /// Parameters; comptime params are rejected by sema.
162 pub params: Vec<Param>,
163 /// Return type (`None` means implicit unit `()`).
164 pub return_type: Option<TypeExpr>,
165 /// Span covering the whole `fn …;` declaration.
166 pub span: Span,
167}
168
169/// A constant declaration.
170///
171/// Constants are compile-time values. In the context of the module system,
172/// they're used for re-exports:
173/// ```gruel
174/// // _utils.gruel (directory module root)
175/// pub const strings = @import("utils/strings.gruel");
176/// pub const helper = @import("utils/internal.gruel").helper;
177/// ```
178#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
179pub struct ConstDecl {
180 /// ADR-0089: docstring attached to this const.
181 #[serde(default)]
182 pub doc: Option<Doc>,
183 /// Directives applied to this const
184 pub directives: Directives,
185 /// Visibility of this constant
186 pub visibility: Visibility,
187 /// Constant name
188 pub name: Ident,
189 /// Optional type annotation (usually inferred)
190 pub ty: Option<TypeExpr>,
191 /// Initializer expression
192 pub init: Box<Expr>,
193 /// Span covering the entire const declaration
194 pub span: Span,
195}
196
197/// A struct declaration.
198///
199/// Structs can contain both fields and methods. Methods are defined inline
200/// within the struct block, not in separate impl blocks.
201///
202/// ```gruel
203/// struct Point {
204/// x: i32,
205/// y: i32,
206///
207/// fn distance(self) -> i32 {
208/// self.x + self.y
209/// }
210/// }
211/// ```
212#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
213pub struct StructDecl {
214 /// ADR-0089: docstring attached to this struct.
215 #[serde(default)]
216 pub doc: Option<Doc>,
217 /// Directives applied to this struct (e.g., `@mark(copy)`, `@derive(...)`)
218 pub directives: Directives,
219 /// Visibility of this struct
220 pub visibility: Visibility,
221 /// Declared ownership posture (ADR-0080). `Affine` when neither
222 /// `@mark(copy)` nor `@mark(linear)` appears in the directive list.
223 pub posture: Posture,
224 /// Struct name
225 pub name: Ident,
226 /// Struct fields
227 pub fields: Vec<FieldDecl>,
228 /// Methods defined on this struct
229 pub methods: Vec<Method>,
230 /// Span covering the entire struct declaration
231 pub span: Span,
232}
233
234/// A field declaration in a struct.
235#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
236pub struct FieldDecl {
237 /// ADR-0089: docstring attached to this field.
238 #[serde(default)]
239 pub doc: Option<Doc>,
240 /// Visibility (ADR-0073). Defaults to `Private` when `pub` is absent.
241 pub visibility: Visibility,
242 /// Field name
243 pub name: Ident,
244 /// Field type
245 pub ty: TypeExpr,
246 /// Span covering the entire field declaration
247 pub span: Span,
248}
249
250/// An enum declaration.
251///
252/// Like structs, enums may declare inline methods after their variants.
253///
254/// ```gruel
255/// enum Option {
256/// Some(i32),
257/// None,
258///
259/// fn is_some(self) -> bool {
260/// match self { Self::Some(_) => true, Self::None => false }
261/// }
262/// }
263/// ```
264#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
265pub struct EnumDecl {
266 /// ADR-0089: docstring attached to this enum.
267 #[serde(default)]
268 pub doc: Option<Doc>,
269 /// Directives applied to this enum (e.g., `@derive(...)`, `@lang("ordering")`).
270 pub directives: Directives,
271 /// Visibility of this enum
272 pub visibility: Visibility,
273 /// Declared ownership posture (ADR-0080). `Affine` when neither
274 /// `@mark(copy)` nor `@mark(linear)` appears in the directive list.
275 pub posture: Posture,
276 /// Enum name
277 pub name: Ident,
278 /// Enum variants
279 pub variants: Vec<EnumVariant>,
280 /// Methods defined on this enum
281 pub methods: Vec<Method>,
282 /// Span covering the entire enum declaration
283 pub span: Span,
284}
285
286/// A variant in an enum declaration.
287#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
288pub struct EnumVariant {
289 /// ADR-0089: docstring attached to this variant.
290 #[serde(default)]
291 pub doc: Option<Doc>,
292 /// Variant name
293 pub name: Ident,
294 /// The kind of variant (unit, tuple, or struct).
295 pub kind: EnumVariantKind,
296 /// Span covering the variant
297 pub span: Span,
298}
299
300/// The kind of an enum variant.
301#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
302pub enum EnumVariantKind {
303 /// Unit variant: `Red`
304 Unit,
305 /// Tuple variant: `Some(i32, i32)`
306 Tuple(Vec<TypeExpr>),
307 /// Struct variant: `Circle { radius: i32 }`
308 Struct(Vec<EnumVariantField>),
309}
310
311/// A named field in a struct-style enum variant.
312#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
313pub struct EnumVariantField {
314 /// ADR-0089: docstring attached to this field.
315 #[serde(default)]
316 pub doc: Option<Doc>,
317 /// Visibility (ADR-0073). Defaults to `Private` when `pub` is absent.
318 pub visibility: Visibility,
319 /// Field name
320 pub name: Ident,
321 /// Field type
322 pub ty: TypeExpr,
323 /// Span covering the field
324 pub span: Span,
325}
326
327/// An interface declaration (ADR-0056).
328///
329/// Interfaces are structurally typed sets of method requirements. A type
330/// conforms to an interface iff its method set covers every method signature
331/// required here.
332///
333/// ```gruel
334/// interface Drop {
335/// fn __drop(self);
336/// }
337/// ```
338#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
339pub struct InterfaceDecl {
340 /// ADR-0089: docstring attached to this interface.
341 #[serde(default)]
342 pub doc: Option<Doc>,
343 /// Directives applied to this interface (e.g., `@lang("drop")`).
344 pub directives: Directives,
345 /// Visibility (currently always private; module-system support is future
346 /// work).
347 pub visibility: Visibility,
348 /// Interface name
349 pub name: Ident,
350 /// Required method signatures, in declaration order. The order is
351 /// significant: it determines vtable slot indices in the runtime
352 /// dispatch path (Phase 4).
353 pub methods: Vec<MethodSig>,
354 /// Span covering the entire interface declaration
355 pub span: Span,
356}
357
358/// A method signature inside an interface declaration.
359///
360/// No body and no associated functions (no-`self`) are allowed in MVP.
361#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
362pub struct MethodSig {
363 /// ADR-0089: docstring attached to this signature.
364 #[serde(default)]
365 pub doc: Option<Doc>,
366 /// Directives applied to this signature (e.g. `@mark(unchecked)`).
367 pub directives: Directives,
368 /// ADR-0088: whether this signature is marked `@mark(unchecked)`.
369 /// Implementors must match this flag exactly (conformance is
370 /// strict — checked impls must match a checked sig, unchecked
371 /// impls must match an unchecked sig).
372 pub is_unchecked: bool,
373 /// Method name
374 pub name: Ident,
375 /// The receiver. Required (associated functions are not yet allowed in
376 /// interfaces).
377 pub receiver: SelfParam,
378 /// Method parameters (excluding self)
379 pub params: Vec<Param>,
380 /// Return type (None means implicit unit `()`)
381 pub return_type: Option<TypeExpr>,
382 /// Span covering the entire signature (`fn name(...) -> R;`)
383 pub span: Span,
384}
385
386/// A derive declaration (ADR-0058).
387///
388/// Holds a list of method declarations whose body refers to the host type
389/// as `Self`. Methods are spliced into a host type's method list when a
390/// `@derive(Name)` directive on a struct or enum names this derive.
391///
392/// ```gruel
393/// derive Drop {
394/// fn __drop(self) {
395/// comptime_unroll for f in @type_info(Self).fields {
396/// drop(@field(self, f.name));
397/// }
398/// }
399/// }
400/// ```
401#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
402pub struct DeriveDecl {
403 /// ADR-0089: docstring attached to this derive.
404 #[serde(default)]
405 pub doc: Option<Doc>,
406 /// Derive name (e.g., `Drop`)
407 pub name: Ident,
408 /// Method declarations inside the derive body
409 pub methods: Vec<Method>,
410 /// Span covering the entire derive item
411 pub span: Span,
412}
413
414/// A method definition in an impl block.
415#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
416pub struct Method {
417 /// ADR-0089: docstring attached to this method.
418 #[serde(default)]
419 pub doc: Option<Doc>,
420 /// Directives applied to this method
421 pub directives: Directives,
422 /// Visibility (ADR-0073). Defaults to `Private` when `pub` is absent.
423 pub visibility: Visibility,
424 /// ADR-0088: whether this method is marked `@mark(unchecked)` —
425 /// caller must wrap every call in a `checked { }` block.
426 pub is_unchecked: bool,
427 /// Method name
428 pub name: Ident,
429 /// Whether this method takes self (None = associated function, Some = method with receiver)
430 pub receiver: Option<SelfParam>,
431 /// Method parameters (excluding self)
432 pub params: Vec<Param>,
433 /// Return type (None means implicit unit `()`)
434 pub return_type: Option<TypeExpr>,
435 /// Method body
436 pub body: Expr,
437 /// Span covering the entire method
438 pub span: Span,
439}
440
441/// A self parameter in a method.
442///
443/// ADR-0076 made `self : T` the sole spelling for receivers; the legacy
444/// `inout self` / `borrow self` keyword forms are gone. The receiver kind
445/// (by-value, `Ref(Self)`, `MutRef(Self)`) is determined at parse time and
446/// recorded here directly to avoid downstream stages having to re-classify
447/// the annotation.
448#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
449pub struct SelfParam {
450 /// Receiver kind, derived from the optional `: T` annotation.
451 pub kind: SelfReceiverKind,
452 /// Span covering the receiver.
453 pub span: Span,
454}
455
456/// Classification of a [`SelfParam`] based on the parsed annotation.
457#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, serde::Serialize, serde::Deserialize)]
458pub enum SelfReceiverKind {
459 /// `self`, `self : Self`, or any annotation not matching `Ref(Self)` /
460 /// `MutRef(Self)`. Lowered as a by-value receiver.
461 #[default]
462 ByValue,
463 /// `self : Ref(Self)` — shared borrow receiver.
464 Ref,
465 /// `self : MutRef(Self)` — exclusive mutable borrow receiver.
466 MutRef,
467}
468
469/// Visibility of an item (function, struct, enum, etc.)
470#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, serde::Serialize, serde::Deserialize)]
471pub enum Visibility {
472 /// Private to the current file (default)
473 #[default]
474 Private,
475 /// Public - visible to importers
476 Public,
477}
478
479/// A function definition.
480#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
481pub struct Function {
482 /// ADR-0089: docstring attached to this function.
483 #[serde(default)]
484 pub doc: Option<Doc>,
485 /// Directives applied to this function
486 pub directives: Directives,
487 /// Visibility of this function
488 pub visibility: Visibility,
489 /// Whether this function is marked `unchecked` (can only be called from checked blocks)
490 pub is_unchecked: bool,
491 /// Function name
492 pub name: Ident,
493 /// Function parameters
494 pub params: Vec<Param>,
495 /// Return type (None means implicit unit `()`)
496 pub return_type: Option<TypeExpr>,
497 /// Function body
498 pub body: Expr,
499 /// Span covering the entire function
500 pub span: Span,
501}
502
503/// Parameter passing mode.
504///
505/// ADR-0076 retired the `inout` / `borrow` keyword forms; ref-ness is now
506/// encoded in the parameter type (`Ref(T)` / `MutRef(T)`).
507#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, serde::Serialize, serde::Deserialize)]
508pub enum ParamMode {
509 /// Normal pass-by-value parameter
510 #[default]
511 Normal,
512 /// Comptime parameter - evaluated at compile time (used for type parameters)
513 Comptime,
514}
515
516/// A function parameter.
517#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
518pub struct Param {
519 /// Whether this parameter is evaluated at compile time
520 pub is_comptime: bool,
521 /// Parameter passing mode (normal or inout)
522 pub mode: ParamMode,
523 /// Parameter name
524 pub name: Ident,
525 /// Parameter type
526 pub ty: TypeExpr,
527 /// Span covering the entire parameter
528 pub span: Span,
529}
530
531/// An identifier.
532#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
533pub struct Ident {
534 pub name: Spur,
535 pub span: Span,
536}
537
538/// A type expression in the AST.
539#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
540pub enum TypeExpr {
541 /// A simple named type (e.g., i32, bool, MyStruct)
542 Named(Ident),
543 /// Unit type: ()
544 Unit(Span),
545 /// Never type: !
546 Never(Span),
547 /// Array type: [T; N] where T is the element type and N is the length
548 Array {
549 element: Box<TypeExpr>,
550 length: u64,
551 span: Span,
552 },
553 /// Anonymous struct type: struct { field: Type, fn method(...) { ... }, ... }
554 /// Used in comptime type construction (e.g., `fn Pair(comptime T: type) -> type { struct { first: T, second: T } }`)
555 /// Methods can be included inside the struct definition (Zig-style).
556 AnonymousStruct {
557 /// Directives applied to the struct expression (e.g., `@derive(...)`,
558 /// ADR-0058). Parsed before the `struct` keyword.
559 directives: Directives,
560 /// Declared ownership posture (ADR-0080). `Affine` when neither
561 /// `@mark(copy)` nor `@mark(linear)` appears in the directive list.
562 posture: Posture,
563 /// Field declarations (name and type)
564 fields: Vec<AnonStructField>,
565 /// Method definitions inside the anonymous struct
566 methods: Vec<Method>,
567 span: Span,
568 },
569 /// Anonymous enum type: enum { Variant, Variant(T), Variant { field: T }, fn method(...) { ... }, ... }
570 /// Used in comptime type construction (e.g., `fn Option(comptime T: type) -> type { enum { Some(T), None } }`)
571 /// Methods can be included inside the enum definition (Zig-style).
572 AnonymousEnum {
573 /// Directives applied to the enum expression (ADR-0058).
574 directives: Directives,
575 /// Declared ownership posture (ADR-0080). `Affine` when neither
576 /// `@mark(copy)` nor `@mark(linear)` appears in the directive list.
577 posture: Posture,
578 /// Enum variants
579 variants: Vec<EnumVariant>,
580 /// Method definitions inside the anonymous enum
581 methods: Vec<Method>,
582 span: Span,
583 },
584 /// Anonymous interface type: interface { fn name(self) [-> T]; ... }
585 /// (ADR-0057) — comptime-constructed interface type, parallel to
586 /// `AnonymousStruct` and `AnonymousEnum`. Used inside `fn ... -> type`
587 /// bodies to build parameterized interfaces.
588 AnonymousInterface {
589 /// Required method signatures (no bodies, no associated functions).
590 methods: Vec<MethodSig>,
591 span: Span,
592 },
593 /// Parameterized type call (ADR-0057): `Name(arg1, arg2, ...)` used in
594 /// type position to invoke a comptime function returning `type`.
595 /// Resolves at sema time by evaluating the call at comptime.
596 TypeCall {
597 /// The function being called (e.g. `Sized`).
598 callee: Ident,
599 /// Type-or-expression arguments. Currently restricted to type
600 /// expressions; expressions like integer literals can be added when
601 /// comptime value parameters become common at type positions.
602 args: Vec<TypeExpr>,
603 span: Span,
604 },
605 /// Tuple type: `(T,)`, `(T, U)`, `(T, U, V)`, ... (ADR-0048)
606 ///
607 /// `()` remains the unit type. A 1-tuple requires a trailing comma (`(T,)`)
608 /// to distinguish it from a parenthesised type.
609 Tuple { elems: Vec<TypeExpr>, span: Span },
610}
611
612/// A field in an anonymous struct type expression.
613#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
614pub struct AnonStructField {
615 /// ADR-0089: docstring attached to this field.
616 #[serde(default)]
617 pub doc: Option<Doc>,
618 /// Field name
619 pub name: Ident,
620 /// Field type
621 pub ty: TypeExpr,
622 /// Span covering the entire field declaration
623 pub span: Span,
624}
625
626impl TypeExpr {
627 /// Get the span of this type expression.
628 pub fn span(&self) -> Span {
629 match self {
630 TypeExpr::Named(ident) => ident.span,
631 TypeExpr::Unit(span) => *span,
632 TypeExpr::Never(span) => *span,
633 TypeExpr::Array { span, .. } => *span,
634 TypeExpr::AnonymousStruct { span, .. } => *span,
635 TypeExpr::AnonymousEnum { span, .. } => *span,
636 TypeExpr::AnonymousInterface { span, .. } => *span,
637 TypeExpr::TypeCall { span, .. } => *span,
638 TypeExpr::Tuple { span, .. } => *span,
639 }
640 }
641}
642
643impl fmt::Display for TypeExpr {
644 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
645 match self {
646 TypeExpr::Named(ident) => write!(f, "sym:{}", ident.name.into_usize()),
647 TypeExpr::Unit(_) => write!(f, "()"),
648 TypeExpr::Never(_) => write!(f, "!"),
649 TypeExpr::Array {
650 element, length, ..
651 } => write!(f, "[{}; {}]", element, length),
652 TypeExpr::AnonymousStruct {
653 fields, methods, ..
654 } => {
655 write!(f, "struct {{ ")?;
656 for (i, field) in fields.iter().enumerate() {
657 if i > 0 {
658 write!(f, ", ")?;
659 }
660 write!(f, "sym:{}: {}", field.name.name.into_usize(), field.ty)?;
661 }
662 for (i, method) in methods.iter().enumerate() {
663 if !fields.is_empty() || i > 0 {
664 write!(f, ", ")?;
665 }
666 write!(f, "fn sym:{}", method.name.name.into_usize())?;
667 }
668 write!(f, " }}")
669 }
670 TypeExpr::AnonymousEnum {
671 variants, methods, ..
672 } => {
673 write!(f, "enum {{ ")?;
674 for (i, variant) in variants.iter().enumerate() {
675 if i > 0 {
676 write!(f, ", ")?;
677 }
678 write!(f, "sym:{}", variant.name.name.into_usize())?;
679 }
680 for (i, method) in methods.iter().enumerate() {
681 if !variants.is_empty() || i > 0 {
682 write!(f, ", ")?;
683 }
684 write!(f, "fn sym:{}", method.name.name.into_usize())?;
685 }
686 write!(f, " }}")
687 }
688 TypeExpr::AnonymousInterface { methods, .. } => {
689 write!(f, "interface {{ ")?;
690 for (i, m) in methods.iter().enumerate() {
691 if i > 0 {
692 write!(f, ", ")?;
693 }
694 write!(f, "fn sym:{}", m.name.name.into_usize())?;
695 }
696 write!(f, " }}")
697 }
698 TypeExpr::TypeCall { callee, args, .. } => {
699 write!(f, "sym:{}(", callee.name.into_usize())?;
700 for (i, a) in args.iter().enumerate() {
701 if i > 0 {
702 write!(f, ", ")?;
703 }
704 write!(f, "{}", a)?;
705 }
706 write!(f, ")")
707 }
708 TypeExpr::Tuple { elems, .. } => {
709 write!(f, "(")?;
710 for (i, elem) in elems.iter().enumerate() {
711 if i > 0 {
712 write!(f, ", ")?;
713 }
714 write!(f, "{}", elem)?;
715 }
716 if elems.len() == 1 {
717 write!(f, ",")?;
718 }
719 write!(f, ")")
720 }
721 }
722 }
723}
724
725/// A unit literal expression - represents `()` or implicit unit.
726#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
727pub struct UnitLit {
728 pub span: Span,
729}
730
731/// An expression.
732#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
733pub enum Expr {
734 /// Integer literal
735 Int(IntLit),
736 /// Floating-point literal
737 Float(FloatLit),
738 /// String literal
739 String(StringLit),
740 /// Character literal — Unicode scalar value (ADR-0071)
741 Char(CharLit),
742 /// Boolean literal
743 Bool(BoolLit),
744 /// Unit literal (explicit `()` or implicit unit for blocks without final expression)
745 Unit(UnitLit),
746 /// Identifier reference (variable)
747 Ident(Ident),
748 /// Binary operation (e.g., `a + b`)
749 Binary(BinaryExpr),
750 /// Unary operation (e.g., `-x`)
751 Unary(UnaryExpr),
752 /// Parenthesized expression (e.g., `(a + b)`)
753 Paren(ParenExpr),
754 /// Block with statements and final expression
755 Block(BlockExpr),
756 /// If expression (e.g., `if cond { a } else { b }`)
757 If(IfExpr),
758 /// Match expression (e.g., `match x { 1 => a, _ => b }`)
759 Match(MatchExpr),
760 /// While expression (e.g., `while cond { body }`)
761 While(WhileExpr),
762 /// For-in expression (e.g., `for x in arr { body }`)
763 For(ForExpr),
764 /// Loop expression - infinite loop (e.g., `loop { body }`)
765 Loop(LoopExpr),
766 /// Function call (e.g., `foo(1, 2)`)
767 Call(CallExpr),
768 /// Break statement (exits the innermost loop)
769 Break(BreakExpr),
770 /// Continue statement (skips to the next iteration of the innermost loop)
771 Continue(ContinueExpr),
772 /// Return statement (returns a value from the current function)
773 Return(ReturnExpr),
774 /// Struct literal (e.g., `Point { x: 1, y: 2 }`)
775 StructLit(StructLitExpr),
776 /// Field access (e.g., `point.x`)
777 Field(FieldExpr),
778 /// Method call (e.g., `point.distance()`)
779 MethodCall(MethodCallExpr),
780 /// Intrinsic call (e.g., `@dbg(42)`)
781 IntrinsicCall(IntrinsicCallExpr),
782 /// Array literal (e.g., `[1, 2, 3]`)
783 ArrayLit(ArrayLitExpr),
784 /// Array indexing (e.g., `arr[0]`)
785 Index(IndexExpr),
786 /// Path expression (e.g., `Color::Red`)
787 Path(PathExpr),
788 /// Enum struct variant literal (e.g., `Shape::Circle { radius: 5 }`)
789 EnumStructLit(EnumStructLitExpr),
790 /// Associated function call (e.g., `Point::origin()`)
791 AssocFnCall(AssocFnCallExpr),
792 /// Self expression (e.g., `self` in method bodies)
793 SelfExpr(SelfExpr),
794 /// Comptime block expression (e.g., `comptime { 1 + 2 }`)
795 Comptime(ComptimeBlockExpr),
796 /// Comptime unroll for expression (e.g., `comptime_unroll for field in info.fields { ... }`)
797 ComptimeUnrollFor(ComptimeUnrollForExpr),
798 /// Checked block expression (e.g., `checked { @ptr_read(p) }`)
799 Checked(CheckedBlockExpr),
800 /// Type literal expression (e.g., `i32` used as a value in generic function calls)
801 TypeLit(TypeLitExpr),
802 /// Tuple literal expression (e.g., `(1, true)`, `(42,)`) (ADR-0048)
803 Tuple(TupleExpr),
804 /// Tuple index expression (e.g., `t.0`, `t.1`) (ADR-0048)
805 TupleIndex(TupleIndexExpr),
806 /// Range expression for slice subscripts (ADR-0064): `..`, `a..`, `..b`,
807 /// `a..b`, `a..b :s`. Only legal as the index of an `IndexExpr`; sema
808 /// rejects bare ranges in other positions.
809 Range(RangeExpr),
810 /// Anonymous function expression (e.g., `fn(x: i32) -> i32 { x + 1 }`) (ADR-0055)
811 AnonFn(AnonFnExpr),
812 /// Error node for recovered parse errors.
813 /// Used by error recovery to continue parsing after a syntax error.
814 Error(Span),
815}
816
817/// An integer literal.
818#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
819pub struct IntLit {
820 pub value: u64,
821 pub span: Span,
822}
823
824/// A floating-point literal, stored as f64 bits for Eq compatibility.
825#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
826pub struct FloatLit {
827 /// The f64 value stored as bits via `f64::to_bits()`.
828 pub bits: u64,
829 pub span: Span,
830}
831
832/// A string literal.
833#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
834pub struct StringLit {
835 pub value: Spur,
836 pub span: Span,
837}
838
839/// A character literal — Unicode scalar value (ADR-0071).
840#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
841pub struct CharLit {
842 pub value: u32,
843 pub span: Span,
844}
845
846/// A boolean literal.
847#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
848pub struct BoolLit {
849 pub value: bool,
850 pub span: Span,
851}
852
853/// A binary expression.
854#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
855pub struct BinaryExpr {
856 pub left: Box<Expr>,
857 pub op: BinaryOp,
858 pub right: Box<Expr>,
859 pub span: Span,
860}
861
862/// Binary operators.
863#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
864pub enum BinaryOp {
865 // Arithmetic
866 Add, // +
867 Sub, // -
868 Mul, // *
869 Div, // /
870 Mod, // %
871 // Comparison
872 Eq, // ==
873 Ne, // !=
874 Lt, // <
875 Gt, // >
876 Le, // <=
877 Ge, // >=
878 // Logical
879 And, // &&
880 Or, // ||
881 // Bitwise
882 BitAnd, // &
883 BitOr, // |
884 BitXor, // ^
885 Shl, // <<
886 Shr, // >>
887}
888
889/// A unary expression.
890#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
891pub struct UnaryExpr {
892 pub op: UnaryOp,
893 pub operand: Box<Expr>,
894 pub span: Span,
895}
896
897/// Unary operators.
898#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
899pub enum UnaryOp {
900 Neg, // -
901 Not, // !
902 BitNot, // ~
903 /// Immutable reference construction: `&x` (ADR-0062). Operand must be an lvalue.
904 Ref,
905 /// Mutable reference construction: `&mut x` (ADR-0062). Operand must be an lvalue.
906 MutRef,
907}
908
909/// A parenthesized expression.
910#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
911pub struct ParenExpr {
912 pub inner: Box<Expr>,
913 pub span: Span,
914}
915
916/// A block expression containing statements and a final expression.
917#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
918pub struct BlockExpr {
919 /// Statements in the block
920 pub statements: Vec<Statement>,
921 /// Final expression (the value of the block)
922 pub expr: Box<Expr>,
923 pub span: Span,
924}
925
926/// An if expression.
927#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
928pub struct IfExpr {
929 /// Condition (must be bool)
930 pub cond: Box<Expr>,
931 /// Then branch
932 pub then_block: BlockExpr,
933 /// Optional else branch
934 pub else_block: Option<BlockExpr>,
935 pub span: Span,
936 /// ADR-0079 follow-up: `comptime if cond { … } else { … }` — sema
937 /// evaluates `cond` at comptime and emits *only* the chosen
938 /// branch's runtime AIR, never analyzing the discarded one.
939 /// Lets the prelude `derive Clone` / `derive Copy` dispatch on
940 /// `@type_info(Self).kind` without forcing the unused branch to
941 /// type-check against the host type.
942 pub is_comptime: bool,
943}
944
945/// A match expression.
946#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
947pub struct MatchExpr {
948 /// The value being matched (scrutinee)
949 pub scrutinee: Box<Expr>,
950 /// Match arms
951 pub arms: Vec<MatchArm>,
952 pub span: Span,
953}
954
955/// A single arm in a match expression.
956#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
957pub struct MatchArm {
958 /// The pattern to match
959 pub pattern: Pattern,
960 /// The body expression
961 pub body: Box<Expr>,
962 pub span: Span,
963}
964
965/// A pattern in a let binding or match arm (ADR-0049).
966///
967/// A single recursive `Pattern` type is used in both contexts. Sema enforces
968/// refutability per context (let requires irrefutable patterns, match accepts any).
969#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
970pub enum Pattern {
971 /// Wildcard pattern `_` - matches anything, irrefutable
972 Wildcard(Span),
973 /// Named binding (`x` or `mut x`), irrefutable
974 Ident {
975 is_mut: bool,
976 name: Ident,
977 span: Span,
978 },
979 /// Integer literal pattern (positive or zero), refutable
980 Int(IntLit),
981 /// Negative integer literal pattern (e.g., `-1`, `-42`), refutable
982 NegInt(NegIntLit),
983 /// Boolean literal pattern, refutable
984 Bool(BoolLit),
985 /// Path pattern (e.g., `Color::Red` for a unit enum variant), refutable
986 Path(PathPattern),
987 /// Data variant pattern with positional sub-patterns (e.g., `Option::Some(x)`)
988 DataVariant {
989 /// Optional module prefix
990 base: Option<Box<Expr>>,
991 /// Enum type name
992 type_name: Ident,
993 /// Variant name
994 variant: Ident,
995 /// Sub-patterns for each field (may include `..` via `TupleElemPattern::Rest`)
996 fields: Vec<TupleElemPattern>,
997 span: Span,
998 },
999 /// Struct variant pattern with named field sub-patterns (e.g., `Shape::Circle { radius }`)
1000 StructVariant {
1001 /// Optional module prefix
1002 base: Option<Box<Expr>>,
1003 /// Enum type name
1004 type_name: Ident,
1005 /// Variant name
1006 variant: Ident,
1007 /// Named field sub-patterns (may include `..` via a field with `field_name: None`)
1008 fields: Vec<FieldPattern>,
1009 span: Span,
1010 },
1011 /// Struct destructure pattern (e.g., `Point { x, y }`), irrefutable when all sub-patterns are.
1012 Struct {
1013 /// The struct type name (for anonymous structs, resolved via a local type alias)
1014 type_name: Ident,
1015 /// Named field sub-patterns (may include `..`)
1016 fields: Vec<FieldPattern>,
1017 span: Span,
1018 },
1019 /// Tuple destructure pattern (e.g., `(a, b)` or `(a, .., c)`), irrefutable when all sub-patterns are.
1020 Tuple {
1021 elems: Vec<TupleElemPattern>,
1022 span: Span,
1023 },
1024 /// ADR-0079 Phase 3: a `comptime_unroll for` arm template. Only
1025 /// valid at the top level of a match arm — sema rejects it
1026 /// elsewhere. The arm fires once per element of `iterable`; the
1027 /// compiler synthesizes a variant-specific concrete pattern per
1028 /// iteration and substitutes `binding` as a comptime value in
1029 /// the arm body.
1030 ComptimeUnrollArm {
1031 binding: Ident,
1032 iterable: Box<Expr>,
1033 span: Span,
1034 },
1035}
1036
1037/// One position in a tuple-like sequence (tuple pattern or data-variant fields):
1038/// either a sub-pattern or the rest-pattern `..` (ADR-0049 Phase 6).
1039#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
1040pub enum TupleElemPattern {
1041 Pattern(Pattern),
1042 /// `..` at this position; matches zero or more remaining positions.
1043 Rest(Span),
1044}
1045
1046/// A named field binding in a struct / struct-variant pattern.
1047///
1048/// When `field_name` is `None`, this is the `..` sentinel (ADR-0049 Phase 6).
1049/// Otherwise, `sub` may be:
1050/// - `None` — shorthand: `field` (binds field to a same-named local).
1051/// - `Some(Pattern::Wildcard(..))` — `field: _` drops the field.
1052/// - `Some(Pattern::Ident { ... })` — `field: name` renames.
1053/// - `Some(other)` — recursive destructure of the field's value.
1054#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
1055pub struct FieldPattern {
1056 pub field_name: Option<Ident>,
1057 pub sub: Option<Pattern>,
1058 /// Only meaningful for shorthand or when `sub` is `Pattern::Ident`.
1059 pub is_mut: bool,
1060 pub span: Span,
1061}
1062
1063impl TupleElemPattern {
1064 pub fn span(&self) -> Span {
1065 match self {
1066 TupleElemPattern::Pattern(p) => p.span(),
1067 TupleElemPattern::Rest(s) => *s,
1068 }
1069 }
1070}
1071
1072/// A negative integer literal pattern.
1073#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
1074pub struct NegIntLit {
1075 /// The absolute value of the negative integer
1076 pub value: u64,
1077 /// Span covering the entire pattern (minus sign and literal)
1078 pub span: Span,
1079}
1080
1081/// A path pattern (e.g., `Color::Red` or `module.Color::Red` for enum variant matching).
1082#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
1083pub struct PathPattern {
1084 /// Optional module/namespace prefix (e.g., `utils` in `utils.Color::Red`)
1085 pub base: Option<Box<Expr>>,
1086 /// The type name (e.g., `Color`)
1087 pub type_name: Ident,
1088 /// The variant name (e.g., `Red`)
1089 pub variant: Ident,
1090 pub span: Span,
1091}
1092
1093impl Pattern {
1094 /// Get the span of this pattern.
1095 pub fn span(&self) -> Span {
1096 match self {
1097 Pattern::Wildcard(span) => *span,
1098 Pattern::Ident { span, .. } => *span,
1099 Pattern::Int(lit) => lit.span,
1100 Pattern::NegInt(lit) => lit.span,
1101 Pattern::Bool(lit) => lit.span,
1102 Pattern::Path(path) => path.span,
1103 Pattern::DataVariant { span, .. } => *span,
1104 Pattern::StructVariant { span, .. } => *span,
1105 Pattern::Struct { span, .. } => *span,
1106 Pattern::Tuple { span, .. } => *span,
1107 Pattern::ComptimeUnrollArm { span, .. } => *span,
1108 }
1109 }
1110}
1111
1112/// Argument passing mode.
1113///
1114/// ADR-0076 retired the `inout` / `borrow` argument prefixes; references are
1115/// now constructed with `&x` / `&mut x` and pass as `Normal` arguments whose
1116/// type carries the ref-ness.
1117#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, serde::Serialize, serde::Deserialize)]
1118pub enum ArgMode {
1119 /// Normal pass-by-value argument
1120 #[default]
1121 Normal,
1122}
1123
1124/// An argument in a function call.
1125#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
1126pub struct CallArg {
1127 /// The passing mode for this argument
1128 pub mode: ArgMode,
1129 /// The argument expression
1130 pub expr: Expr,
1131 /// Span covering the entire argument
1132 pub span: Span,
1133}
1134
1135/// A function call expression.
1136#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
1137pub struct CallExpr {
1138 /// Function name
1139 pub name: Ident,
1140 /// Arguments
1141 pub args: Vec<CallArg>,
1142 pub span: Span,
1143}
1144
1145/// An argument to an intrinsic call (can be an expression or a type).
1146#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
1147pub enum IntrinsicArg {
1148 /// An expression argument (e.g., `@dbg(42)`)
1149 Expr(Expr),
1150 /// A type argument (e.g., `@size_of(i32)`)
1151 Type(TypeExpr),
1152}
1153
1154/// An intrinsic call expression (e.g., `@dbg(42)` or `@size_of(i32)`).
1155#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
1156pub struct IntrinsicCallExpr {
1157 /// Intrinsic name (without the @)
1158 pub name: Ident,
1159 /// Arguments (can be expressions or types)
1160 pub args: Vec<IntrinsicArg>,
1161 pub span: Span,
1162}
1163
1164/// A struct literal expression (e.g., `Point { x: 1, y: 2 }` or `module.Point { x: 1, y: 2 }`).
1165#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
1166pub struct StructLitExpr {
1167 /// Optional module/namespace prefix (e.g., `utils` in `utils.Point { ... }`)
1168 pub base: Option<Box<Expr>>,
1169 /// Struct type name
1170 pub name: Ident,
1171 /// Field initializers
1172 pub fields: Vec<FieldInit>,
1173 pub span: Span,
1174}
1175
1176/// A field initializer in a struct literal.
1177#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
1178pub struct FieldInit {
1179 /// Field name
1180 pub name: Ident,
1181 /// Field value
1182 pub value: Box<Expr>,
1183 pub span: Span,
1184}
1185
1186/// A tuple literal expression (e.g., `(1, true)`, `(42,)`).
1187#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
1188pub struct TupleExpr {
1189 /// Element expressions
1190 pub elems: Vec<Expr>,
1191 pub span: Span,
1192}
1193
1194/// An anonymous function expression (e.g., `fn(x: i32) -> i32 { x + 1 }`).
1195///
1196/// ADR-0055: desugars to an anonymous struct with a single `__call` method
1197/// and is instantiated as an empty struct literal. Each `AnonFn` site produces
1198/// a distinct type.
1199#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
1200pub struct AnonFnExpr {
1201 /// Parameters (all require type annotations, like named functions).
1202 pub params: Vec<Param>,
1203 /// Return type (None means implicit unit `()`, same as named functions).
1204 pub return_type: Option<TypeExpr>,
1205 /// Function body (always a block).
1206 pub body: BlockExpr,
1207 /// Span covering the entire `fn(...) { ... }` expression.
1208 pub span: Span,
1209}
1210
1211/// A tuple index expression (e.g., `t.0`, `t.1`).
1212#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
1213pub struct TupleIndexExpr {
1214 /// Base expression (the tuple value)
1215 pub base: Box<Expr>,
1216 /// Numeric index (0-based)
1217 pub index: u32,
1218 /// Span of the whole expression (base through the index token)
1219 pub span: Span,
1220 /// Span of just the index token (for diagnostics)
1221 pub index_span: Span,
1222}
1223
1224/// A field access expression (e.g., `point.x`).
1225#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
1226pub struct FieldExpr {
1227 /// Base expression (the struct value)
1228 pub base: Box<Expr>,
1229 /// Field name
1230 pub field: Ident,
1231 pub span: Span,
1232}
1233
1234/// A method call expression (e.g., `point.distance()`).
1235#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
1236pub struct MethodCallExpr {
1237 /// Base expression (the receiver)
1238 pub receiver: Box<Expr>,
1239 /// Method name
1240 pub method: Ident,
1241 /// Arguments (excluding self)
1242 pub args: Vec<CallArg>,
1243 pub span: Span,
1244}
1245
1246/// An array literal expression (e.g., `[1, 2, 3]`).
1247#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
1248pub struct ArrayLitExpr {
1249 /// Array elements
1250 pub elements: Vec<Expr>,
1251 pub span: Span,
1252}
1253
1254/// An array index expression (e.g., `arr[0]`).
1255#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
1256pub struct IndexExpr {
1257 /// The array being indexed
1258 pub base: Box<Expr>,
1259 /// The index expression. May be `Expr::Range(_)` for slice subscripts
1260 /// (ADR-0064); sema enforces that range subscripts appear only as the
1261 /// place under `&` / `&mut`.
1262 pub index: Box<Expr>,
1263 pub span: Span,
1264}
1265
1266/// A range expression used as a slice subscript (ADR-0064).
1267///
1268/// Ranges are recognized only inside `[ … ]`. `lo` and `hi` are optional
1269/// (defaulting to 0 and `arr.len()` respectively).
1270#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
1271pub struct RangeExpr {
1272 pub lo: Option<Box<Expr>>,
1273 pub hi: Option<Box<Expr>>,
1274 pub span: Span,
1275}
1276
1277/// A path expression (e.g., `Color::Red` or `module.Color::Red` for enum variant).
1278#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
1279pub struct PathExpr {
1280 /// Optional module/namespace prefix (e.g., `utils` in `utils.Color::Red`)
1281 pub base: Option<Box<Expr>>,
1282 /// The type name (e.g., `Color`)
1283 pub type_name: Ident,
1284 /// The variant name (e.g., `Red`)
1285 pub variant: Ident,
1286 pub span: Span,
1287}
1288
1289/// An enum struct variant literal expression (e.g., `Shape::Circle { radius: 5 }`).
1290#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
1291pub struct EnumStructLitExpr {
1292 /// Optional module/namespace prefix
1293 pub base: Option<Box<Expr>>,
1294 /// The enum type name (e.g., `Shape`)
1295 pub type_name: Ident,
1296 /// The variant name (e.g., `Circle`)
1297 pub variant: Ident,
1298 /// Field initializers
1299 pub fields: Vec<FieldInit>,
1300 pub span: Span,
1301}
1302
1303/// An associated function call expression (e.g., `Point::origin()` or `module.Point::origin()`).
1304///
1305/// ADR-0063 also accepts a type-call LHS: `Ptr(i32)::null()`. The
1306/// parenthesised arguments after `type_name` are stored in `type_args`. They
1307/// are empty for the legacy form (`Point::origin()`) and non-empty for the
1308/// type-call form. Sema interprets each `type_args` entry as a type
1309/// expression.
1310#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
1311pub struct AssocFnCallExpr {
1312 /// Optional module/namespace prefix (e.g., `utils` in `utils.Point::origin()`)
1313 pub base: Option<Box<Expr>>,
1314 /// The type name (e.g., `Point` or `Ptr`)
1315 pub type_name: Ident,
1316 /// Type arguments for a type-call LHS (e.g., `[i32]` in `Ptr(i32)::null()`).
1317 /// Empty for the plain `Type::function()` form.
1318 pub type_args: Vec<Expr>,
1319 /// The function name (e.g., `origin`)
1320 pub function: Ident,
1321 /// Arguments
1322 pub args: Vec<CallArg>,
1323 pub span: Span,
1324}
1325
1326/// A statement (does not produce a value).
1327#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
1328pub enum Statement {
1329 /// Let binding: `let x = expr;` or `let mut x = expr;`
1330 Let(LetStatement),
1331 /// Assignment: `x = expr;`
1332 Assign(AssignStatement),
1333 /// Expression statement: `expr;`
1334 Expr(Expr),
1335}
1336
1337/// A let binding statement.
1338#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
1339pub struct LetStatement {
1340 /// Directives applied to this let binding
1341 pub directives: Directives,
1342 /// Whether the binding is mutable.
1343 ///
1344 /// For ident patterns this is the binding's mutability. For destructuring
1345 /// patterns, per-binding mutability is stored on each sub-pattern; this
1346 /// top-level flag is ignored.
1347 pub is_mut: bool,
1348 /// The binding pattern (ADR-0049). Must be irrefutable in a let statement.
1349 pub pattern: Pattern,
1350 /// Optional type annotation
1351 pub ty: Option<TypeExpr>,
1352 /// Initializer expression
1353 pub init: Box<Expr>,
1354 pub span: Span,
1355}
1356
1357/// An assignment statement.
1358#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
1359pub struct AssignStatement {
1360 /// Assignment target (variable or field)
1361 pub target: AssignTarget,
1362 /// Value expression
1363 pub value: Box<Expr>,
1364 pub span: Span,
1365}
1366
1367/// An assignment target.
1368#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
1369pub enum AssignTarget {
1370 /// Variable assignment (e.g., `x = 5`)
1371 Var(Ident),
1372 /// Field assignment (e.g., `point.x = 5`)
1373 Field(FieldExpr),
1374 /// Index assignment (e.g., `arr[0] = 5`)
1375 Index(IndexExpr),
1376}
1377
1378/// A while loop expression.
1379#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
1380pub struct WhileExpr {
1381 /// Condition (must be bool)
1382 pub cond: Box<Expr>,
1383 /// Loop body
1384 pub body: BlockExpr,
1385 pub span: Span,
1386}
1387
1388/// A for-in loop expression (e.g., `for x in expr { body }`).
1389#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
1390pub struct ForExpr {
1391 /// Loop variable name
1392 pub binding: Ident,
1393 /// Whether the loop variable is mutable (`for mut x in ...`)
1394 pub is_mut: bool,
1395 /// The iterable expression (array or Range)
1396 pub iterable: Box<Expr>,
1397 /// Loop body
1398 pub body: BlockExpr,
1399 pub span: Span,
1400}
1401
1402/// An infinite loop expression.
1403#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
1404pub struct LoopExpr {
1405 /// Loop body
1406 pub body: BlockExpr,
1407 pub span: Span,
1408}
1409
1410/// A break expression (exits the innermost loop).
1411#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
1412pub struct BreakExpr {
1413 pub span: Span,
1414}
1415
1416/// A continue expression (skips to the next iteration of the innermost loop).
1417#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
1418pub struct ContinueExpr {
1419 pub span: Span,
1420}
1421
1422/// A return expression (returns a value from the current function).
1423#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
1424pub struct ReturnExpr {
1425 /// The value to return (None for `return;` in unit-returning functions)
1426 pub value: Option<Box<Expr>>,
1427 pub span: Span,
1428}
1429
1430/// A self expression (the `self` keyword in method bodies).
1431#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
1432pub struct SelfExpr {
1433 pub span: Span,
1434}
1435
1436/// A comptime block expression (e.g., `comptime { 1 + 2 }`).
1437/// The expression inside must be evaluable at compile time.
1438#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
1439pub struct ComptimeBlockExpr {
1440 /// The expression to evaluate at compile time
1441 pub expr: Box<Expr>,
1442 pub span: Span,
1443}
1444
1445/// A comptime_unroll for expression.
1446/// The collection is evaluated at compile time, then the body is unrolled once per element.
1447#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
1448pub struct ComptimeUnrollForExpr {
1449 /// Loop variable name
1450 pub binding: Ident,
1451 /// The iterable expression (must be comptime-known)
1452 pub iterable: Box<Expr>,
1453 /// Loop body
1454 pub body: BlockExpr,
1455 pub span: Span,
1456}
1457
1458/// A checked block expression (e.g., `checked { @ptr_read(p) }`).
1459/// Unchecked operations (raw pointer manipulation, calling unchecked functions)
1460/// are only allowed inside checked blocks.
1461#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
1462pub struct CheckedBlockExpr {
1463 /// The expression inside the checked block
1464 pub expr: Box<Expr>,
1465 pub span: Span,
1466}
1467
1468/// A type literal expression (e.g., `i32` used as a value).
1469/// This represents a type used as a value in expression context, typically
1470/// as an argument to a generic function with comptime parameters.
1471#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
1472pub struct TypeLitExpr {
1473 /// The type being used as a value
1474 pub type_expr: TypeExpr,
1475 pub span: Span,
1476}
1477
1478impl Expr {
1479 /// Get the span of this expression.
1480 pub fn span(&self) -> Span {
1481 match self {
1482 Expr::Int(lit) => lit.span,
1483 Expr::Float(lit) => lit.span,
1484 Expr::String(lit) => lit.span,
1485 Expr::Char(lit) => lit.span,
1486 Expr::Bool(lit) => lit.span,
1487 Expr::Unit(lit) => lit.span,
1488 Expr::Ident(ident) => ident.span,
1489 Expr::Binary(bin) => bin.span,
1490 Expr::Unary(un) => un.span,
1491 Expr::Paren(paren) => paren.span,
1492 Expr::Block(block) => block.span,
1493 Expr::If(if_expr) => if_expr.span,
1494 Expr::Match(match_expr) => match_expr.span,
1495 Expr::While(while_expr) => while_expr.span,
1496 Expr::For(for_expr) => for_expr.span,
1497 Expr::Loop(loop_expr) => loop_expr.span,
1498 Expr::Call(call) => call.span,
1499 Expr::Break(break_expr) => break_expr.span,
1500 Expr::Continue(continue_expr) => continue_expr.span,
1501 Expr::Return(return_expr) => return_expr.span,
1502 Expr::StructLit(struct_lit) => struct_lit.span,
1503 Expr::Field(field_expr) => field_expr.span,
1504 Expr::MethodCall(method_call) => method_call.span,
1505 Expr::IntrinsicCall(intrinsic) => intrinsic.span,
1506 Expr::ArrayLit(array_lit) => array_lit.span,
1507 Expr::Index(index_expr) => index_expr.span,
1508 Expr::Path(path_expr) => path_expr.span,
1509 Expr::EnumStructLit(lit) => lit.span,
1510 Expr::AssocFnCall(assoc_fn_call) => assoc_fn_call.span,
1511 Expr::SelfExpr(self_expr) => self_expr.span,
1512 Expr::Comptime(comptime_expr) => comptime_expr.span,
1513 Expr::ComptimeUnrollFor(e) => e.span,
1514 Expr::Checked(checked_expr) => checked_expr.span,
1515 Expr::TypeLit(type_lit) => type_lit.span,
1516 Expr::Tuple(tuple) => tuple.span,
1517 Expr::TupleIndex(ti) => ti.span,
1518 Expr::Range(range_expr) => range_expr.span,
1519 Expr::AnonFn(anon_fn) => anon_fn.span,
1520 Expr::Error(span) => *span,
1521 }
1522 }
1523}
1524
1525// Display implementations for AST pretty-printing
1526
1527impl fmt::Display for Ast {
1528 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1529 for item in &self.items {
1530 match item {
1531 Item::Function(func) => fmt_function(f, func, 0)?,
1532 Item::Struct(s) => fmt_struct(f, s, 0)?,
1533 Item::Enum(e) => fmt_enum(f, e, 0)?,
1534 Item::Interface(i) => fmt_interface(f, i, 0)?,
1535 Item::Derive(d) => fmt_derive(f, d, 0)?,
1536 Item::Const(c) => fmt_const(f, c, 0)?,
1537 Item::LinkExtern(b) => fmt_link_extern(f, b, 0)?,
1538 Item::Error(span) => writeln!(f, "Error({:?})", span)?,
1539 }
1540 }
1541 Ok(())
1542 }
1543}
1544
1545fn indent(f: &mut fmt::Formatter<'_>, level: usize) -> fmt::Result {
1546 for _ in 0..level {
1547 write!(f, " ")?;
1548 }
1549 Ok(())
1550}
1551
1552fn fmt_struct(f: &mut fmt::Formatter<'_>, s: &StructDecl, level: usize) -> fmt::Result {
1553 indent(f, level)?;
1554 for directive in &s.directives {
1555 write!(f, "@sym:{} ", directive.name.name.into_usize())?;
1556 }
1557 match s.posture {
1558 Posture::Copy => write!(f, "@mark(copy) ")?,
1559 Posture::Linear => write!(f, "@mark(linear) ")?,
1560 Posture::Affine => {}
1561 }
1562 writeln!(f, "Struct sym:{}", s.name.name.into_usize())?;
1563 for field in &s.fields {
1564 indent(f, level + 1)?;
1565 writeln!(
1566 f,
1567 "Field sym:{} : {}",
1568 field.name.name.into_usize(),
1569 field.ty
1570 )?;
1571 }
1572 for method in &s.methods {
1573 fmt_method(f, method, level + 1)?;
1574 }
1575 Ok(())
1576}
1577
1578fn fmt_enum(f: &mut fmt::Formatter<'_>, e: &EnumDecl, level: usize) -> fmt::Result {
1579 indent(f, level)?;
1580 writeln!(f, "Enum sym:{}", e.name.name.into_usize())?;
1581 for variant in &e.variants {
1582 indent(f, level + 1)?;
1583 writeln!(f, "Variant sym:{}", variant.name.name.into_usize())?;
1584 }
1585 for method in &e.methods {
1586 fmt_method(f, method, level + 1)?;
1587 }
1588 Ok(())
1589}
1590
1591fn fmt_const(f: &mut fmt::Formatter<'_>, c: &ConstDecl, level: usize) -> fmt::Result {
1592 indent(f, level)?;
1593 for directive in &c.directives {
1594 write!(f, "@sym:{} ", directive.name.name.into_usize())?;
1595 }
1596 if c.visibility == Visibility::Public {
1597 write!(f, "pub ")?;
1598 }
1599 write!(f, "Const sym:{}", c.name.name.into_usize())?;
1600 if let Some(ref ty) = c.ty {
1601 write!(f, ": {}", ty)?;
1602 }
1603 writeln!(f)?;
1604 fmt_expr(f, &c.init, level + 1)?;
1605 Ok(())
1606}
1607
1608fn fmt_interface(f: &mut fmt::Formatter<'_>, iface: &InterfaceDecl, level: usize) -> fmt::Result {
1609 indent(f, level)?;
1610 if iface.visibility == Visibility::Public {
1611 write!(f, "pub ")?;
1612 }
1613 writeln!(f, "Interface sym:{}", iface.name.name.into_usize())?;
1614 for sig in &iface.methods {
1615 indent(f, level + 1)?;
1616 write!(f, "MethodSig sym:{}(self", sig.name.name.into_usize())?;
1617 for param in &sig.params {
1618 write!(f, ", ")?;
1619 fmt_param(f, param)?;
1620 }
1621 write!(f, ")")?;
1622 if let Some(ref ret) = sig.return_type {
1623 write!(f, " -> {}", ret)?;
1624 }
1625 writeln!(f)?;
1626 }
1627 Ok(())
1628}
1629
1630fn fmt_derive(f: &mut fmt::Formatter<'_>, d: &DeriveDecl, level: usize) -> fmt::Result {
1631 indent(f, level)?;
1632 writeln!(f, "Derive sym:{}", d.name.name.into_usize())?;
1633 for method in &d.methods {
1634 fmt_method(f, method, level + 1)?;
1635 }
1636 Ok(())
1637}
1638
1639fn fmt_link_extern(
1640 f: &mut fmt::Formatter<'_>,
1641 block: &LinkExternBlock,
1642 level: usize,
1643) -> fmt::Result {
1644 indent(f, level)?;
1645 writeln!(f, "LinkExtern sym:{}", block.library.value.into_usize())?;
1646 for item in &block.items {
1647 indent(f, level + 1)?;
1648 for directive in &item.directives {
1649 write!(f, "@sym:{} ", directive.name.name.into_usize())?;
1650 }
1651 write!(f, "ExternFn sym:{}(", item.name.name.into_usize())?;
1652 for (i, param) in item.params.iter().enumerate() {
1653 if i > 0 {
1654 write!(f, ", ")?;
1655 }
1656 fmt_param(f, param)?;
1657 }
1658 write!(f, ")")?;
1659 if let Some(ref ret) = item.return_type {
1660 write!(f, " -> {}", ret)?;
1661 }
1662 writeln!(f)?;
1663 }
1664 Ok(())
1665}
1666
1667fn fmt_method(f: &mut fmt::Formatter<'_>, method: &Method, level: usize) -> fmt::Result {
1668 indent(f, level)?;
1669 if method.is_unchecked {
1670 write!(f, "unchecked ")?;
1671 }
1672 write!(f, "Method sym:{}", method.name.name.into_usize())?;
1673 write!(f, "(")?;
1674 if method.receiver.is_some() {
1675 write!(f, "self")?;
1676 if !method.params.is_empty() {
1677 write!(f, ", ")?;
1678 }
1679 }
1680 for (i, param) in method.params.iter().enumerate() {
1681 if i > 0 {
1682 write!(f, ", ")?;
1683 }
1684 fmt_param(f, param)?;
1685 }
1686 write!(f, ")")?;
1687 if let Some(ref ret) = method.return_type {
1688 write!(f, " -> {}", ret)?;
1689 }
1690 writeln!(f)?;
1691 fmt_expr(f, &method.body, level + 1)?;
1692 Ok(())
1693}
1694
1695fn fmt_param(f: &mut fmt::Formatter<'_>, param: &Param) -> fmt::Result {
1696 match param.mode {
1697 ParamMode::Comptime => write!(f, "comptime ")?,
1698 ParamMode::Normal => {}
1699 }
1700 write!(f, "sym:{}: {}", param.name.name.into_usize(), param.ty)
1701}
1702
1703fn fmt_call_arg(f: &mut fmt::Formatter<'_>, arg: &CallArg, level: usize) -> fmt::Result {
1704 match arg.mode {
1705 ArgMode::Normal => fmt_expr(f, &arg.expr, level),
1706 }
1707}
1708
1709fn fmt_function(f: &mut fmt::Formatter<'_>, func: &Function, level: usize) -> fmt::Result {
1710 indent(f, level)?;
1711 if func.is_unchecked {
1712 write!(f, "unchecked ")?;
1713 }
1714 write!(f, "Function sym:{}", func.name.name.into_usize())?;
1715 if !func.params.is_empty() {
1716 write!(f, "(")?;
1717 for (i, param) in func.params.iter().enumerate() {
1718 if i > 0 {
1719 write!(f, ", ")?;
1720 }
1721 fmt_param(f, param)?;
1722 }
1723 write!(f, ")")?;
1724 }
1725 if let Some(ref ret) = func.return_type {
1726 write!(f, " -> {}", ret)?;
1727 }
1728 writeln!(f)?;
1729 fmt_expr(f, &func.body, level + 1)?;
1730 Ok(())
1731}
1732
1733fn fmt_expr(f: &mut fmt::Formatter<'_>, expr: &Expr, level: usize) -> fmt::Result {
1734 indent(f, level)?;
1735 match expr {
1736 Expr::Int(lit) => writeln!(f, "Int({})", lit.value),
1737 Expr::Float(lit) => writeln!(f, "Float({})", f64::from_bits(lit.bits)),
1738 Expr::String(lit) => writeln!(f, "String(sym:{})", lit.value.into_usize()),
1739 Expr::Char(lit) => writeln!(f, "Char(U+{:04X})", lit.value),
1740 Expr::Bool(lit) => writeln!(f, "Bool({})", lit.value),
1741 Expr::Unit(_) => writeln!(f, "Unit"),
1742 Expr::Ident(ident) => writeln!(f, "Ident(sym:{})", ident.name.into_usize()),
1743 Expr::Binary(bin) => {
1744 writeln!(f, "Binary {:?}", bin.op)?;
1745 fmt_expr(f, &bin.left, level + 1)?;
1746 fmt_expr(f, &bin.right, level + 1)
1747 }
1748 Expr::Unary(un) => {
1749 writeln!(f, "Unary {:?}", un.op)?;
1750 fmt_expr(f, &un.operand, level + 1)
1751 }
1752 Expr::Paren(paren) => {
1753 writeln!(f, "Paren")?;
1754 fmt_expr(f, &paren.inner, level + 1)
1755 }
1756 Expr::Block(block) => {
1757 writeln!(f, "Block")?;
1758 for stmt in &block.statements {
1759 fmt_stmt(f, stmt, level + 1)?;
1760 }
1761 fmt_expr(f, &block.expr, level + 1)
1762 }
1763 Expr::If(if_expr) => {
1764 writeln!(f, "If")?;
1765 indent(f, level + 1)?;
1766 writeln!(f, "Cond:")?;
1767 fmt_expr(f, &if_expr.cond, level + 2)?;
1768 indent(f, level + 1)?;
1769 writeln!(f, "Then:")?;
1770 fmt_block_expr(f, &if_expr.then_block, level + 2)?;
1771 if let Some(ref else_block) = if_expr.else_block {
1772 indent(f, level + 1)?;
1773 writeln!(f, "Else:")?;
1774 fmt_block_expr(f, else_block, level + 2)?;
1775 }
1776 Ok(())
1777 }
1778 Expr::Match(match_expr) => {
1779 writeln!(f, "Match")?;
1780 indent(f, level + 1)?;
1781 writeln!(f, "Scrutinee:")?;
1782 fmt_expr(f, &match_expr.scrutinee, level + 2)?;
1783 for arm in &match_expr.arms {
1784 indent(f, level + 1)?;
1785 writeln!(f, "Arm {:?} =>", arm.pattern)?;
1786 fmt_expr(f, &arm.body, level + 2)?;
1787 }
1788 Ok(())
1789 }
1790 Expr::While(while_expr) => {
1791 writeln!(f, "While")?;
1792 indent(f, level + 1)?;
1793 writeln!(f, "Cond:")?;
1794 fmt_expr(f, &while_expr.cond, level + 2)?;
1795 indent(f, level + 1)?;
1796 writeln!(f, "Body:")?;
1797 fmt_block_expr(f, &while_expr.body, level + 2)
1798 }
1799 Expr::For(for_expr) => {
1800 writeln!(
1801 f,
1802 "For {}sym:{}",
1803 if for_expr.is_mut { "mut " } else { "" },
1804 for_expr.binding.name.into_usize()
1805 )?;
1806 indent(f, level + 1)?;
1807 writeln!(f, "Iterable:")?;
1808 fmt_expr(f, &for_expr.iterable, level + 2)?;
1809 indent(f, level + 1)?;
1810 writeln!(f, "Body:")?;
1811 fmt_block_expr(f, &for_expr.body, level + 2)
1812 }
1813 Expr::Loop(loop_expr) => {
1814 writeln!(f, "Loop")?;
1815 fmt_block_expr(f, &loop_expr.body, level + 1)
1816 }
1817 Expr::Call(call) => {
1818 writeln!(f, "Call sym:{}", call.name.name.into_usize())?;
1819 for arg in &call.args {
1820 fmt_call_arg(f, arg, level + 1)?;
1821 }
1822 Ok(())
1823 }
1824 Expr::IntrinsicCall(intrinsic) => {
1825 writeln!(f, "Intrinsic @sym:{}", intrinsic.name.name.into_usize())?;
1826 for arg in &intrinsic.args {
1827 match arg {
1828 IntrinsicArg::Expr(expr) => fmt_expr(f, expr, level + 1)?,
1829 IntrinsicArg::Type(ty) => {
1830 indent(f, level + 1)?;
1831 writeln!(f, "Type {:?}", ty)?;
1832 }
1833 }
1834 }
1835 Ok(())
1836 }
1837 Expr::Break(_) => writeln!(f, "Break"),
1838 Expr::Continue(_) => writeln!(f, "Continue"),
1839 Expr::Return(ret) => {
1840 if let Some(ref value) = ret.value {
1841 writeln!(f, "Return")?;
1842 fmt_expr(f, value, level + 1)
1843 } else {
1844 writeln!(f, "Return (unit)")
1845 }
1846 }
1847 Expr::StructLit(lit) => {
1848 writeln!(f, "StructLit sym:{}", lit.name.name.into_usize())?;
1849 for field in &lit.fields {
1850 indent(f, level + 1)?;
1851 writeln!(f, "sym:{} =", field.name.name.into_usize())?;
1852 fmt_expr(f, &field.value, level + 2)?;
1853 }
1854 Ok(())
1855 }
1856 Expr::Field(field) => {
1857 writeln!(f, "Field .sym:{}", field.field.name.into_usize())?;
1858 fmt_expr(f, &field.base, level + 1)
1859 }
1860 Expr::MethodCall(method_call) => {
1861 writeln!(
1862 f,
1863 "MethodCall .sym:{}",
1864 method_call.method.name.into_usize()
1865 )?;
1866 indent(f, level + 1)?;
1867 writeln!(f, "Receiver:")?;
1868 fmt_expr(f, &method_call.receiver, level + 2)?;
1869 if !method_call.args.is_empty() {
1870 indent(f, level + 1)?;
1871 writeln!(f, "Args:")?;
1872 for arg in &method_call.args {
1873 fmt_call_arg(f, arg, level + 2)?;
1874 }
1875 }
1876 Ok(())
1877 }
1878 Expr::ArrayLit(array) => {
1879 writeln!(f, "ArrayLit")?;
1880 for elem in &array.elements {
1881 fmt_expr(f, elem, level + 1)?;
1882 }
1883 Ok(())
1884 }
1885 Expr::Index(index) => {
1886 writeln!(f, "Index")?;
1887 indent(f, level + 1)?;
1888 writeln!(f, "Base:")?;
1889 fmt_expr(f, &index.base, level + 2)?;
1890 indent(f, level + 1)?;
1891 writeln!(f, "Index:")?;
1892 fmt_expr(f, &index.index, level + 2)
1893 }
1894 Expr::Path(path) => writeln!(
1895 f,
1896 "Path sym:{}::sym:{}",
1897 path.type_name.name.into_usize(),
1898 path.variant.name.into_usize()
1899 ),
1900 Expr::EnumStructLit(lit) => {
1901 writeln!(
1902 f,
1903 "EnumStructLit sym:{}::sym:{}",
1904 lit.type_name.name.into_usize(),
1905 lit.variant.name.into_usize()
1906 )?;
1907 for field in &lit.fields {
1908 indent(f, level + 1)?;
1909 writeln!(f, "field sym:{}:", field.name.name.into_usize())?;
1910 fmt_expr(f, &field.value, level + 2)?;
1911 }
1912 Ok(())
1913 }
1914 Expr::AssocFnCall(assoc_fn_call) => {
1915 writeln!(
1916 f,
1917 "AssocFnCall sym:{}::sym:{}",
1918 assoc_fn_call.type_name.name.into_usize(),
1919 assoc_fn_call.function.name.into_usize()
1920 )?;
1921 for arg in &assoc_fn_call.args {
1922 fmt_call_arg(f, arg, level + 1)?;
1923 }
1924 Ok(())
1925 }
1926 Expr::SelfExpr(_) => {
1927 writeln!(f, "SelfExpr")
1928 }
1929 Expr::Comptime(comptime) => {
1930 writeln!(f, "Comptime")?;
1931 fmt_expr(f, &comptime.expr, level + 1)
1932 }
1933 Expr::ComptimeUnrollFor(unroll) => {
1934 writeln!(
1935 f,
1936 "ComptimeUnrollFor sym:{}",
1937 unroll.binding.name.into_usize()
1938 )?;
1939 indent(f, level + 1)?;
1940 writeln!(f, "Iterable:")?;
1941 fmt_expr(f, &unroll.iterable, level + 2)?;
1942 indent(f, level + 1)?;
1943 writeln!(f, "Body:")?;
1944 fmt_block_expr(f, &unroll.body, level + 2)
1945 }
1946 Expr::Checked(checked) => {
1947 writeln!(f, "Checked")?;
1948 fmt_expr(f, &checked.expr, level + 1)
1949 }
1950 Expr::TypeLit(type_lit) => {
1951 writeln!(f, "TypeLit({})", type_lit.type_expr)
1952 }
1953 Expr::Tuple(tuple) => {
1954 writeln!(f, "Tuple[{}]", tuple.elems.len())?;
1955 for elem in &tuple.elems {
1956 fmt_expr(f, elem, level + 1)?;
1957 }
1958 Ok(())
1959 }
1960 Expr::TupleIndex(ti) => {
1961 writeln!(f, "TupleIndex .{}", ti.index)?;
1962 fmt_expr(f, &ti.base, level + 1)
1963 }
1964 Expr::Range(range_expr) => {
1965 writeln!(f, "Range")?;
1966 if let Some(lo) = &range_expr.lo {
1967 indent(f, level + 1)?;
1968 writeln!(f, "Lo:")?;
1969 fmt_expr(f, lo, level + 2)?;
1970 }
1971 if let Some(hi) = &range_expr.hi {
1972 indent(f, level + 1)?;
1973 writeln!(f, "Hi:")?;
1974 fmt_expr(f, hi, level + 2)?;
1975 }
1976 Ok(())
1977 }
1978 Expr::AnonFn(anon_fn) => {
1979 writeln!(f, "AnonFn (params: {})", anon_fn.params.len())?;
1980 for param in &anon_fn.params {
1981 indent(f, level + 1)?;
1982 writeln!(f, "Param(sym:{})", param.name.name.into_usize())?;
1983 }
1984 if let Some(ret) = &anon_fn.return_type {
1985 indent(f, level + 1)?;
1986 writeln!(f, "Return -> {:?}", ret)?;
1987 }
1988 indent(f, level + 1)?;
1989 writeln!(f, "Body")?;
1990 fmt_block_expr(f, &anon_fn.body, level + 2)
1991 }
1992 Expr::Error(span) => {
1993 writeln!(f, "Error({:?})", span)
1994 }
1995 }
1996}
1997
1998fn fmt_block_expr(f: &mut fmt::Formatter<'_>, block: &BlockExpr, level: usize) -> fmt::Result {
1999 for stmt in &block.statements {
2000 fmt_stmt(f, stmt, level)?;
2001 }
2002 fmt_expr(f, &block.expr, level)
2003}
2004
2005/// Render a `Pattern` into the single-line bind-site form used by `Statement::Let`
2006/// display. This is a compact diagnostic-style format, not a parse-roundtrip form.
2007fn fmt_pattern(f: &mut fmt::Formatter<'_>, pat: &Pattern) -> fmt::Result {
2008 match pat {
2009 Pattern::Wildcard(_) => write!(f, " _"),
2010 Pattern::Ident {
2011 is_mut: true, name, ..
2012 } => write!(f, " mut sym:{}", name.name.into_usize()),
2013 Pattern::Ident { name, .. } => write!(f, " sym:{}", name.name.into_usize()),
2014 Pattern::Int(lit) => write!(f, " {}", lit.value),
2015 Pattern::NegInt(lit) => write!(f, " -{}", lit.value),
2016 Pattern::Bool(lit) => write!(f, " {}", lit.value),
2017 Pattern::Path(path) => write!(
2018 f,
2019 " sym:{}::sym:{}",
2020 path.type_name.name.into_usize(),
2021 path.variant.name.into_usize()
2022 ),
2023 Pattern::DataVariant {
2024 type_name,
2025 variant,
2026 fields,
2027 ..
2028 } => {
2029 write!(
2030 f,
2031 " sym:{}::sym:{}(",
2032 type_name.name.into_usize(),
2033 variant.name.into_usize()
2034 )?;
2035 for (i, elem) in fields.iter().enumerate() {
2036 if i > 0 {
2037 write!(f, ",")?;
2038 }
2039 fmt_tuple_elem(f, elem)?;
2040 }
2041 write!(f, " )")
2042 }
2043 Pattern::StructVariant {
2044 type_name,
2045 variant,
2046 fields,
2047 ..
2048 } => {
2049 write!(
2050 f,
2051 " sym:{}::sym:{} {{",
2052 type_name.name.into_usize(),
2053 variant.name.into_usize()
2054 )?;
2055 for (i, fld) in fields.iter().enumerate() {
2056 if i > 0 {
2057 write!(f, ",")?;
2058 }
2059 fmt_field_pattern(f, fld)?;
2060 }
2061 write!(f, " }}")
2062 }
2063 Pattern::Struct {
2064 type_name, fields, ..
2065 } => {
2066 write!(f, " sym:{} {{", type_name.name.into_usize())?;
2067 for (i, fld) in fields.iter().enumerate() {
2068 if i > 0 {
2069 write!(f, ",")?;
2070 }
2071 fmt_field_pattern(f, fld)?;
2072 }
2073 write!(f, " }}")
2074 }
2075 Pattern::Tuple { elems, .. } => {
2076 write!(f, " (")?;
2077 for (i, elem) in elems.iter().enumerate() {
2078 if i > 0 {
2079 write!(f, ",")?;
2080 }
2081 fmt_tuple_elem(f, elem)?;
2082 }
2083 if elems.len() == 1 {
2084 write!(f, ",")?;
2085 }
2086 write!(f, " )")
2087 }
2088 Pattern::ComptimeUnrollArm { binding, .. } => {
2089 write!(f, " comptime_unroll for sym:{}", binding.name.into_usize())
2090 }
2091 }
2092}
2093
2094fn fmt_tuple_elem(f: &mut fmt::Formatter<'_>, elem: &TupleElemPattern) -> fmt::Result {
2095 match elem {
2096 TupleElemPattern::Pattern(p) => fmt_pattern(f, p),
2097 TupleElemPattern::Rest(_) => write!(f, " .."),
2098 }
2099}
2100
2101fn fmt_field_pattern(f: &mut fmt::Formatter<'_>, fld: &FieldPattern) -> fmt::Result {
2102 match &fld.field_name {
2103 None => write!(f, " .."),
2104 Some(name) => {
2105 if fld.is_mut {
2106 write!(f, " mut")?;
2107 }
2108 write!(f, " sym:{}", name.name.into_usize())?;
2109 if let Some(sub) = &fld.sub {
2110 write!(f, ":")?;
2111 fmt_pattern(f, sub)?;
2112 }
2113 Ok(())
2114 }
2115 }
2116}
2117
2118fn fmt_stmt(f: &mut fmt::Formatter<'_>, stmt: &Statement, level: usize) -> fmt::Result {
2119 indent(f, level)?;
2120 match stmt {
2121 Statement::Let(let_stmt) => {
2122 write!(f, "Let")?;
2123 if let_stmt.is_mut {
2124 write!(f, " mut")?;
2125 }
2126 fmt_pattern(f, &let_stmt.pattern)?;
2127 if let Some(ref ty) = let_stmt.ty {
2128 write!(f, ": {}", ty)?;
2129 }
2130 writeln!(f)?;
2131 fmt_expr(f, &let_stmt.init, level + 1)
2132 }
2133 Statement::Assign(assign) => {
2134 match &assign.target {
2135 AssignTarget::Var(ident) => writeln!(f, "Assign sym:{}", ident.name.into_usize())?,
2136 AssignTarget::Field(field) => {
2137 writeln!(f, "Assign field .sym:{}", field.field.name.into_usize())?;
2138 fmt_expr(f, &field.base, level + 1)?;
2139 }
2140 AssignTarget::Index(index) => {
2141 writeln!(f, "Assign index")?;
2142 indent(f, level + 1)?;
2143 writeln!(f, "Base:")?;
2144 fmt_expr(f, &index.base, level + 2)?;
2145 indent(f, level + 1)?;
2146 writeln!(f, "Index:")?;
2147 fmt_expr(f, &index.index, level + 2)?;
2148 }
2149 }
2150 fmt_expr(f, &assign.value, level + 1)
2151 }
2152 Statement::Expr(expr) => {
2153 writeln!(f, "ExprStmt")?;
2154 fmt_expr(f, expr, level + 1)
2155 }
2156 }
2157}
2158
2159// ============================================================================
2160// Struct-of-Arrays (SOA) AST Layout
2161// ============================================================================
2162//
2163// This section implements Zig-style SOA layout for the AST.
2164// See docs/designs/soa-ast-layout.md for full design rationale.
2165//
2166// Key characteristics:
2167// - Fixed-size nodes (tag + main_token + lhs + rhs)
2168// - Index-based references (no lifetimes)
2169// - Extra data array for nodes with >2 children
2170// - Single allocation for entire AST
2171// - Better cache locality than tree-based approach
2172//
2173// Migration: This will eventually replace the tree-based Ast above.
2174// For now, both representations coexist during Phase 2-3 migration.
2175
2176/// Node index - references a node in the SOA AST.
2177///
2178/// Nodes are stored in parallel arrays (tags, data, extra) and referenced
2179/// by their index. This is similar to how RIR uses InstRef.
2180#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
2181pub struct NodeIndex(pub u32);
2182
2183impl NodeIndex {
2184 /// Create a new node index.
2185 pub const fn new(idx: u32) -> Self {
2186 NodeIndex(idx)
2187 }
2188
2189 /// Get the raw index value.
2190 pub const fn as_u32(self) -> u32 {
2191 self.0
2192 }
2193
2194 /// Get the index as usize for array indexing.
2195 pub const fn as_usize(self) -> usize {
2196 self.0 as usize
2197 }
2198}
2199
2200/// Sentinel value representing "no node" or "null node".
2201/// Used for optional children (e.g., else block in if expression).
2202pub const NULL_NODE: NodeIndex = NodeIndex(u32::MAX);
2203
2204/// Encode a UnaryOp into a u32 for storage in NodeData.
2205pub fn encode_unary_op(op: UnaryOp) -> u32 {
2206 match op {
2207 UnaryOp::Neg => 0,
2208 UnaryOp::Not => 1,
2209 UnaryOp::BitNot => 2,
2210 UnaryOp::Ref => 3,
2211 UnaryOp::MutRef => 4,
2212 }
2213}
2214
2215/// Node tag - identifies what kind of node this is.
2216///
2217/// The tag determines how to interpret the lhs/rhs fields in NodeData.
2218/// See docs/designs/soa-ast-layout.md for encoding details.
2219#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
2220#[repr(u8)]
2221pub enum NodeTag {
2222 // ===== Items (top-level declarations) =====
2223 /// Function declaration: fn name(params) -> ret { body }
2224 /// - lhs: index into extra (param count + param nodes)
2225 /// - rhs: body expression node
2226 Function,
2227
2228 /// Struct declaration: struct Name { fields... methods... }
2229 /// - lhs: index into extra (field count + field nodes)
2230 /// - rhs: index into extra (method count + method nodes)
2231 StructDecl,
2232
2233 /// Enum declaration: enum Name { variants... }
2234 /// - lhs: index into extra (variant count + variant nodes)
2235 /// - rhs: 0 (unused)
2236 EnumDecl,
2237
2238 /// Constant declaration: const name: type = init;
2239 /// - lhs: type expression node (or NULL_NODE if inferred)
2240 /// - rhs: initializer expression node
2241 ConstDecl,
2242
2243 // ===== Expressions - Literals =====
2244 /// Integer literal: 42
2245 /// - lhs: low 32 bits of u64 value
2246 /// - rhs: high 32 bits of u64 value
2247 IntLit,
2248
2249 /// String literal: "hello"
2250 /// - lhs: Spur index (u32) for interned string
2251 /// - rhs: 0 (unused)
2252 StringLit,
2253
2254 /// Boolean literal: true, false
2255 /// - lhs: 0 (false) or 1 (true)
2256 /// - rhs: 0 (unused)
2257 BoolLit,
2258
2259 /// Unit literal: ()
2260 /// - lhs: 0 (unused)
2261 /// - rhs: 0 (unused)
2262 UnitLit,
2263
2264 // ===== Expressions - Identifiers and Paths =====
2265 /// Identifier: variable_name
2266 /// - lhs: Spur index (u32) for identifier name
2267 /// - rhs: 0 (unused)
2268 Ident,
2269
2270 /// Path expression: Color::Red
2271 /// - lhs: type name identifier node
2272 /// - rhs: variant name identifier node
2273 Path,
2274
2275 // ===== Expressions - Operations =====
2276 /// Unary expression: -x, !x, ~x
2277 /// - lhs: operand expression node
2278 /// - rhs: operator kind (u32 from UnaryOp enum)
2279 UnaryExpr,
2280
2281 /// Parenthesized expression: (expr)
2282 /// - lhs: inner expression node
2283 /// - rhs: 0 (unused)
2284 ParenExpr,
2285
2286 /// Binary expression: a + b, a == b, etc.
2287 /// - main_token: the operator token
2288 /// - lhs: left operand expression node
2289 /// - rhs: right operand expression node
2290 BinaryExpr,
2291
2292 // ===== Expressions - Control Flow =====
2293 /// If expression: if cond { then } else { else_block }
2294 /// - lhs: condition expression node
2295 /// - rhs: index into extra (then_block, else_block or NULL_NODE)
2296 IfExpr,
2297
2298 /// Match expression: match x { arms... }
2299 /// - lhs: scrutinee expression node
2300 /// - rhs: index into extra (arm count + arm nodes)
2301 MatchExpr,
2302
2303 /// While loop: while cond { body }
2304 /// - lhs: condition expression node
2305 /// - rhs: body block expression node
2306 WhileExpr,
2307
2308 /// For-in loop: for [mut] x in expr { body }
2309 /// - lhs: iterable expression node
2310 /// - rhs: body block expression node
2311 /// - extra: binding name (Spur index), is_mut flag
2312 ForExpr,
2313
2314 /// Infinite loop: loop { body }
2315 /// - lhs: body block expression node
2316 /// - rhs: 0 (unused)
2317 LoopExpr,
2318
2319 /// Break statement: break
2320 /// - lhs: 0 (unused)
2321 /// - rhs: 0 (unused)
2322 BreakExpr,
2323
2324 /// Continue statement: continue
2325 /// - lhs: 0 (unused)
2326 /// - rhs: 0 (unused)
2327 ContinueExpr,
2328
2329 /// Return statement: return expr
2330 /// - lhs: value expression node (or NULL_NODE for implicit unit return)
2331 /// - rhs: 0 (unused)
2332 ReturnExpr,
2333
2334 // ===== Expressions - Blocks and Statements =====
2335 /// Block expression: { stmts...; final_expr }
2336 /// - lhs: index into extra (stmt count + stmt nodes)
2337 /// - rhs: final expression node
2338 BlockExpr,
2339
2340 /// Let statement: let x: type = init;
2341 /// - lhs: pattern node (identifier or wildcard)
2342 /// - rhs: index into extra (flags, type_expr or NULL_NODE, init_expr)
2343 LetStmt,
2344
2345 /// Assignment statement: x = value;
2346 /// - lhs: target node (Ident, FieldExpr, or IndexExpr)
2347 /// - rhs: value expression node
2348 AssignStmt,
2349
2350 /// Expression statement: expr;
2351 /// - lhs: expression node
2352 /// - rhs: 0 (unused)
2353 ExprStmt,
2354
2355 // ===== Expressions - Function Calls =====
2356 /// Function call: func(args...)
2357 /// - lhs: callee identifier node
2358 /// - rhs: index into extra (arg count + arg nodes)
2359 Call,
2360
2361 /// Method call: receiver.method(args...)
2362 /// - lhs: receiver expression node
2363 /// - rhs: index into extra (method name, arg count, arg nodes)
2364 MethodCall,
2365
2366 /// Intrinsic call: @intrinsic(args...)
2367 /// - lhs: intrinsic name identifier node
2368 /// - rhs: index into extra (arg count + arg nodes)
2369 IntrinsicCall,
2370
2371 /// Associated function call: Type::func(args...)
2372 /// - lhs: type name identifier node
2373 /// - rhs: index into extra (fn name, arg count, arg nodes)
2374 AssocFnCall,
2375
2376 // ===== Expressions - Struct Operations =====
2377 /// Struct literal: Point { x: 1, y: 2 }
2378 /// - lhs: struct name identifier node
2379 /// - rhs: index into extra (field init count + field init nodes)
2380 StructLit,
2381
2382 /// Field access: obj.field
2383 /// - lhs: base expression node
2384 /// - rhs: field name identifier node
2385 FieldExpr,
2386
2387 /// Field initializer in struct literal: field_name: value
2388 /// - lhs: field name identifier node
2389 /// - rhs: value expression node
2390 FieldInit,
2391
2392 // ===== Expressions - Array Operations =====
2393 /// Array literal: [1, 2, 3]
2394 /// - lhs: index into extra (element count + element nodes)
2395 /// - rhs: 0 (unused, count stored in extra)
2396 ArrayLit,
2397
2398 /// Array index: arr[index]
2399 /// - lhs: base expression node
2400 /// - rhs: index expression node
2401 IndexExpr,
2402
2403 // ===== Expressions - Special =====
2404 /// Self expression: self
2405 /// - lhs: 0 (unused)
2406 /// - rhs: 0 (unused)
2407 SelfExpr,
2408
2409 /// Comptime block: comptime { expr }
2410 /// - lhs: inner expression node
2411 /// - rhs: 0 (unused)
2412 ComptimeBlockExpr,
2413
2414 /// Checked block: checked { expr }
2415 /// - lhs: inner expression node
2416 /// - rhs: 0 (unused)
2417 CheckedBlockExpr,
2418
2419 /// Type literal: i32 (used as value)
2420 /// - lhs: type expression node
2421 /// - rhs: 0 (unused)
2422 TypeLit,
2423
2424 // ===== Type Expressions =====
2425 /// Named type: i32, MyStruct
2426 /// - lhs: name identifier node
2427 /// - rhs: 0 (unused)
2428 TypeNamed,
2429
2430 /// Unit type: ()
2431 /// - lhs: 0 (unused)
2432 /// - rhs: 0 (unused)
2433 TypeUnit,
2434
2435 /// Never type: !
2436 /// - lhs: 0 (unused)
2437 /// - rhs: 0 (unused)
2438 TypeNever,
2439
2440 /// Array type: [T; N]
2441 /// - lhs: element type expression node
2442 /// - rhs: length (u32, stored directly)
2443 TypeArray,
2444
2445 /// Anonymous struct type: struct { fields... methods... }
2446 /// - lhs: index into extra (field count + field nodes)
2447 /// - rhs: index into extra (method count + method nodes)
2448 TypeAnonStruct,
2449
2450 // ===== Patterns =====
2451 /// Wildcard pattern: _
2452 /// - lhs: 0 (unused)
2453 /// - rhs: 0 (unused)
2454 PatternWildcard,
2455
2456 /// Integer literal pattern: 42, -1
2457 /// - lhs: low 32 bits of value
2458 /// - rhs: high 32 bits of value
2459 PatternInt,
2460
2461 /// Boolean literal pattern: true, false
2462 /// - lhs: 0 (false) or 1 (true)
2463 /// - rhs: 0 (unused)
2464 PatternBool,
2465
2466 /// Path pattern: Color::Red
2467 /// - lhs: type name identifier node
2468 /// - rhs: variant name identifier node
2469 PatternPath,
2470
2471 // ===== Other Nodes =====
2472 /// Function parameter: name: type
2473 /// - lhs: name identifier node
2474 /// - rhs: type expression node
2475 /// - extra: flags (is_comptime, mode)
2476 Param,
2477
2478 /// Method definition
2479 /// - lhs: index into extra (name, receiver?, param count, params)
2480 /// - rhs: index into extra (return_type or NULL_NODE, body_expr)
2481 Method,
2482
2483 /// Match arm: pattern => body
2484 /// - lhs: pattern node
2485 /// - rhs: body expression node
2486 MatchArm,
2487
2488 /// Call argument (wraps expression with mode flags)
2489 /// - lhs: expression node
2490 /// - rhs: flags (normal=0, inout=1, borrow=2)
2491 CallArg,
2492
2493 /// Field declaration in struct
2494 /// - lhs: name identifier node
2495 /// - rhs: type expression node
2496 FieldDecl,
2497
2498 /// Enum variant
2499 /// - lhs: name identifier node
2500 /// - rhs: 0 (unused, payload support future)
2501 EnumVariant,
2502
2503 /// Directive: @name(args...)
2504 /// - lhs: name identifier node
2505 /// - rhs: index into extra (arg count + arg nodes)
2506 Directive,
2507
2508 /// Directive argument (currently just identifiers)
2509 /// - lhs: identifier node
2510 /// - rhs: 0 (unused)
2511 DirectiveArg,
2512
2513 // ===== Error Recovery =====
2514 /// Error node (parse error recovery)
2515 /// - lhs: 0 (unused)
2516 /// - rhs: 0 (unused)
2517 ErrorNode,
2518}
2519
2520/// Fixed-size node data (12 bytes total).
2521///
2522/// Each node in the SOA AST has:
2523/// - A tag (stored in separate tags array)
2524/// - A main_token (for span information)
2525/// - Two u32 slots (lhs and rhs) whose meaning depends on the tag
2526///
2527/// This matches Zig's design: compact, cache-friendly, uniform size.
2528#[derive(Debug, Clone, Copy)]
2529pub struct NodeData {
2530 /// Primary token for this node.
2531 ///
2532 /// Used for:
2533 /// - Span information in error messages
2534 /// - Operator tokens (for BinaryExpr, UnaryExpr)
2535 /// - Keyword tokens (for if, while, etc.)
2536 pub main_token: u32,
2537
2538 /// Left child or first data slot.
2539 ///
2540 /// Interpretation depends on NodeTag - see NodeTag documentation.
2541 /// Common uses:
2542 /// - Left operand in binary expressions
2543 /// - Single child in unary expressions
2544 /// - Index into extra_data for multi-child nodes
2545 /// - Direct data storage (e.g., low 32 bits of u64)
2546 pub lhs: u32,
2547
2548 /// Right child or second data slot.
2549 ///
2550 /// Interpretation depends on NodeTag - see NodeTag documentation.
2551 /// Common uses:
2552 /// - Right operand in binary expressions
2553 /// - Index into extra_data for multi-child nodes
2554 /// - Direct data storage (e.g., high 32 bits of u64)
2555 /// - Flags and small enums
2556 pub rhs: u32,
2557}
2558
2559/// Struct-of-Arrays AST representation.
2560///
2561/// This is the new SOA-based AST that will replace the tree-based `Ast`.
2562/// During migration (Phases 2-3), both representations will coexist.
2563///
2564/// Design principles:
2565/// - All nodes stored in parallel arrays (tags, data, extra)
2566/// - Nodes reference children by index, not pointers
2567/// - Single allocation for entire AST (better cache locality)
2568/// - Cheap cloning (just clone Arc, not deep copy)
2569///
2570/// See docs/designs/soa-ast-layout.md for full design.
2571#[derive(Debug, Clone)]
2572pub struct SoaAst {
2573 /// Node tags (what kind of node is at each index).
2574 ///
2575 /// Index i contains the tag for node NodeIndex(i).
2576 /// Length of this vec == number of nodes in the AST.
2577 pub tags: Vec<NodeTag>,
2578
2579 /// Node data (main_token + lhs + rhs for each node).
2580 ///
2581 /// Parallel array to tags - tags[i] and data[i] together describe node i.
2582 pub data: Vec<NodeData>,
2583
2584 /// Extra data storage for nodes with >2 children.
2585 ///
2586 /// Nodes that can't fit their data in lhs+rhs store additional
2587 /// data here. The lhs or rhs field contains an index into this array.
2588 ///
2589 /// Layout is node-type specific - see NodeTag documentation.
2590 pub extra: Vec<u32>,
2591
2592 /// Root nodes (top-level items in the source file).
2593 ///
2594 /// These are the entry points for traversing the AST.
2595 /// Each element is a NodeIndex pointing to a Function, StructDecl, etc.
2596 pub items: Vec<NodeIndex>,
2597}
2598
2599impl SoaAst {
2600 /// Create a new empty SOA AST.
2601 pub fn new() -> Self {
2602 SoaAst {
2603 tags: Vec::new(),
2604 data: Vec::new(),
2605 extra: Vec::new(),
2606 items: Vec::new(),
2607 }
2608 }
2609
2610 /// Create a new SOA AST with pre-allocated capacity.
2611 pub fn with_capacity(nodes: usize, extra: usize) -> Self {
2612 SoaAst {
2613 tags: Vec::with_capacity(nodes),
2614 data: Vec::with_capacity(nodes),
2615 extra: Vec::with_capacity(extra),
2616 items: Vec::new(),
2617 }
2618 }
2619
2620 /// Get the tag for a node.
2621 pub fn node_tag(&self, idx: NodeIndex) -> NodeTag {
2622 self.tags[idx.as_usize()]
2623 }
2624
2625 /// Get the data for a node.
2626 pub fn node_data(&self, idx: NodeIndex) -> NodeData {
2627 self.data[idx.as_usize()]
2628 }
2629
2630 /// Get the main token for a node.
2631 pub fn main_token(&self, idx: NodeIndex) -> u32 {
2632 self.data[idx.as_usize()].main_token
2633 }
2634
2635 /// Get the number of nodes in the AST.
2636 pub fn node_count(&self) -> usize {
2637 self.tags.len()
2638 }
2639
2640 /// Get a slice of the extra data array.
2641 pub fn extra_slice(&self, start: usize, len: usize) -> &[u32] {
2642 &self.extra[start..start + len]
2643 }
2644
2645 // ===== Typed Accessors =====
2646 // These provide type-safe access to specific node types.
2647
2648 /// Get the value of an integer literal.
2649 pub fn int_value(&self, idx: NodeIndex) -> u64 {
2650 debug_assert_eq!(self.node_tag(idx), NodeTag::IntLit);
2651 let data = self.node_data(idx);
2652 (data.lhs as u64) | ((data.rhs as u64) << 32)
2653 }
2654
2655 /// Get the boolean value of a boolean literal.
2656 pub fn bool_value(&self, idx: NodeIndex) -> bool {
2657 debug_assert_eq!(self.node_tag(idx), NodeTag::BoolLit);
2658 let data = self.node_data(idx);
2659 data.lhs != 0
2660 }
2661
2662 /// Get the string spur of a string literal.
2663 pub fn string_spur(&self, idx: NodeIndex) -> Spur {
2664 debug_assert_eq!(self.node_tag(idx), NodeTag::StringLit);
2665 let data = self.node_data(idx);
2666 Spur::try_from_usize(data.lhs as usize).expect("invalid spur")
2667 }
2668
2669 /// Get the identifier spur.
2670 pub fn ident_spur(&self, idx: NodeIndex) -> Spur {
2671 debug_assert_eq!(self.node_tag(idx), NodeTag::Ident);
2672 let data = self.node_data(idx);
2673 Spur::try_from_usize(data.lhs as usize).expect("invalid spur")
2674 }
2675
2676 /// Get the operands of a binary expression.
2677 pub fn binary_operands(&self, idx: NodeIndex) -> (NodeIndex, NodeIndex) {
2678 debug_assert_eq!(self.node_tag(idx), NodeTag::BinaryExpr);
2679 let data = self.node_data(idx);
2680 (NodeIndex(data.lhs), NodeIndex(data.rhs))
2681 }
2682
2683 /// Get the operand of a unary expression.
2684 pub fn unary_operand(&self, idx: NodeIndex) -> NodeIndex {
2685 debug_assert_eq!(self.node_tag(idx), NodeTag::UnaryExpr);
2686 let data = self.node_data(idx);
2687 NodeIndex(data.lhs)
2688 }
2689
2690 /// Get the operator kind of a unary expression.
2691 pub fn unary_op(&self, idx: NodeIndex) -> UnaryOp {
2692 debug_assert_eq!(self.node_tag(idx), NodeTag::UnaryExpr);
2693 let data = self.node_data(idx);
2694 match data.rhs {
2695 0 => UnaryOp::Neg,
2696 1 => UnaryOp::Not,
2697 2 => UnaryOp::BitNot,
2698 3 => UnaryOp::Ref,
2699 4 => UnaryOp::MutRef,
2700 _ => panic!("invalid UnaryOp encoding: {}", data.rhs),
2701 }
2702 }
2703}
2704
2705impl Default for SoaAst {
2706 fn default() -> Self {
2707 Self::new()
2708 }
2709}
2710
2711#[cfg(test)]
2712mod soa_tests {
2713 use super::*;
2714
2715 #[test]
2716 fn test_node_index() {
2717 let idx = NodeIndex::new(42);
2718 assert_eq!(idx.as_u32(), 42);
2719 assert_eq!(idx.as_usize(), 42);
2720 }
2721
2722 #[test]
2723 fn test_null_node() {
2724 assert_eq!(NULL_NODE.as_u32(), u32::MAX);
2725 }
2726
2727 #[test]
2728 fn test_soa_ast_creation() {
2729 let ast = SoaAst::new();
2730 assert_eq!(ast.node_count(), 0);
2731 assert_eq!(ast.tags.len(), 0);
2732 assert_eq!(ast.data.len(), 0);
2733 assert_eq!(ast.extra.len(), 0);
2734 }
2735
2736 #[test]
2737 fn test_soa_ast_with_capacity() {
2738 let ast = SoaAst::with_capacity(100, 50);
2739 assert!(ast.tags.capacity() >= 100);
2740 assert!(ast.data.capacity() >= 100);
2741 assert!(ast.extra.capacity() >= 50);
2742 }
2743
2744 #[test]
2745 fn test_int_literal_encoding() {
2746 let mut ast = SoaAst::new();
2747
2748 // Add an integer literal node
2749 let value = 0x123456789ABCDEF0u64;
2750 ast.tags.push(NodeTag::IntLit);
2751 ast.data.push(NodeData {
2752 main_token: 0,
2753 lhs: (value & 0xFFFFFFFF) as u32, // low 32 bits
2754 rhs: ((value >> 32) & 0xFFFFFFFF) as u32, // high 32 bits
2755 });
2756
2757 let idx = NodeIndex(0);
2758 assert_eq!(ast.node_tag(idx), NodeTag::IntLit);
2759 assert_eq!(ast.int_value(idx), value);
2760 }
2761
2762 #[test]
2763 fn test_bool_literal_encoding() {
2764 let mut ast = SoaAst::new();
2765
2766 // Add true
2767 ast.tags.push(NodeTag::BoolLit);
2768 ast.data.push(NodeData {
2769 main_token: 0,
2770 lhs: 1,
2771 rhs: 0,
2772 });
2773
2774 // Add false
2775 ast.tags.push(NodeTag::BoolLit);
2776 ast.data.push(NodeData {
2777 main_token: 1,
2778 lhs: 0,
2779 rhs: 0,
2780 });
2781
2782 assert!(ast.bool_value(NodeIndex(0)));
2783 assert!(!ast.bool_value(NodeIndex(1)));
2784 }
2785
2786 #[test]
2787 fn test_binary_expr_encoding() {
2788 let mut ast = SoaAst::new();
2789
2790 // Create: 1 + 2
2791 // First add the literals
2792 ast.tags.push(NodeTag::IntLit);
2793 ast.data.push(NodeData {
2794 main_token: 0,
2795 lhs: 1,
2796 rhs: 0,
2797 });
2798
2799 ast.tags.push(NodeTag::IntLit);
2800 ast.data.push(NodeData {
2801 main_token: 1,
2802 lhs: 2,
2803 rhs: 0,
2804 });
2805
2806 // Then add the binary expression
2807 ast.tags.push(NodeTag::BinaryExpr);
2808 ast.data.push(NodeData {
2809 main_token: 2, // the '+' token
2810 lhs: 0, // left operand (node 0)
2811 rhs: 1, // right operand (node 1)
2812 });
2813
2814 let binop_idx = NodeIndex(2);
2815 assert_eq!(ast.node_tag(binop_idx), NodeTag::BinaryExpr);
2816
2817 let (left, right) = ast.binary_operands(binop_idx);
2818 assert_eq!(left, NodeIndex(0));
2819 assert_eq!(right, NodeIndex(1));
2820 assert_eq!(ast.int_value(left), 1);
2821 assert_eq!(ast.int_value(right), 2);
2822 }
2823
2824 #[test]
2825 fn test_unary_expr_encoding() {
2826 let mut ast = SoaAst::new();
2827
2828 // Create: -42
2829 // First add the literal
2830 ast.tags.push(NodeTag::IntLit);
2831 ast.data.push(NodeData {
2832 main_token: 0,
2833 lhs: 42,
2834 rhs: 0,
2835 });
2836
2837 // Then add the unary expression
2838 ast.tags.push(NodeTag::UnaryExpr);
2839 ast.data.push(NodeData {
2840 main_token: 1, // the '-' token
2841 lhs: 0, // operand (node 0)
2842 rhs: encode_unary_op(UnaryOp::Neg), // operator kind
2843 });
2844
2845 let unary_idx = NodeIndex(1);
2846 assert_eq!(ast.node_tag(unary_idx), NodeTag::UnaryExpr);
2847
2848 let operand = ast.unary_operand(unary_idx);
2849 assert_eq!(operand, NodeIndex(0));
2850 assert_eq!(ast.int_value(operand), 42);
2851 assert_eq!(ast.unary_op(unary_idx), UnaryOp::Neg);
2852 }
2853
2854 #[test]
2855 fn test_ident_encoding() {
2856 let mut ast = SoaAst::new();
2857
2858 // Mock identifier with spur index 123
2859 ast.tags.push(NodeTag::Ident);
2860 ast.data.push(NodeData {
2861 main_token: 0,
2862 lhs: 123, // spur index
2863 rhs: 0,
2864 });
2865
2866 let idx = NodeIndex(0);
2867 assert_eq!(ast.node_tag(idx), NodeTag::Ident);
2868 assert_eq!(ast.node_data(idx).lhs, 123);
2869 }
2870
2871 #[test]
2872 fn test_extra_data_slice() {
2873 let mut ast = SoaAst::new();
2874 ast.extra = vec![10, 20, 30, 40, 50];
2875
2876 let slice = ast.extra_slice(1, 3);
2877 assert_eq!(slice, &[20, 30, 40]);
2878 }
2879
2880 #[test]
2881 fn test_items() {
2882 let mut ast = SoaAst::new();
2883
2884 // Add two function nodes
2885 ast.tags.push(NodeTag::Function);
2886 ast.data.push(NodeData {
2887 main_token: 0,
2888 lhs: 0,
2889 rhs: 0,
2890 });
2891
2892 ast.tags.push(NodeTag::Function);
2893 ast.data.push(NodeData {
2894 main_token: 1,
2895 lhs: 0,
2896 rhs: 0,
2897 });
2898
2899 ast.items = vec![NodeIndex(0), NodeIndex(1)];
2900
2901 assert_eq!(ast.items.len(), 2);
2902 assert_eq!(ast.node_tag(ast.items[0]), NodeTag::Function);
2903 assert_eq!(ast.node_tag(ast.items[1]), NodeTag::Function);
2904 }
2905}