Skip to main content

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}