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_span::Span;
30use lasso::{Key, Spur};
31use smallvec::SmallVec;
32
33/// Type alias for a small vector of directives.
34/// Most items have 0-1 directives, so we inline capacity for 1.
35pub type Directives = SmallVec<[Directive; 1]>;
36
37/// A complete source file (list of items).
38#[derive(Debug, Clone, PartialEq, Eq)]
39pub struct Ast {
40    pub items: Vec<Item>,
41}
42
43/// A directive that modifies compiler behavior for the following item or statement.
44///
45/// Directives use the `@name(args)` syntax and appear before items or statements.
46/// For example: `@allow(unused_variable)`
47#[derive(Debug, Clone, PartialEq, Eq)]
48pub struct Directive {
49    /// The directive name (without the @)
50    pub name: Ident,
51    /// Arguments to the directive
52    pub args: Vec<DirectiveArg>,
53    /// Span covering the entire directive
54    pub span: Span,
55}
56
57/// An argument to a directive.
58#[derive(Debug, Clone, PartialEq, Eq)]
59pub enum DirectiveArg {
60    /// An identifier argument (e.g., `unused_variable` in `@allow(unused_variable)`)
61    Ident(Ident),
62}
63
64/// A top-level item in a source file.
65#[derive(Debug, Clone, PartialEq, Eq)]
66pub enum Item {
67    Function(Function),
68    Struct(StructDecl),
69    Enum(EnumDecl),
70    DropFn(DropFn),
71    /// Constant declaration (e.g., `const math = @import("math");`)
72    Const(ConstDecl),
73    /// Error node for recovered parse errors at item level.
74    /// Used by error recovery to continue parsing after a syntax error.
75    Error(Span),
76}
77
78/// A constant declaration.
79///
80/// Constants are compile-time values. In the context of the module system,
81/// they're used for re-exports:
82/// ```gruel
83/// // _utils.gruel (directory module root)
84/// pub const strings = @import("utils/strings.gruel");
85/// pub const helper = @import("utils/internal.gruel").helper;
86/// ```
87#[derive(Debug, Clone, PartialEq, Eq)]
88pub struct ConstDecl {
89    /// Directives applied to this const
90    pub directives: Directives,
91    /// Visibility of this constant
92    pub visibility: Visibility,
93    /// Constant name
94    pub name: Ident,
95    /// Optional type annotation (usually inferred)
96    pub ty: Option<TypeExpr>,
97    /// Initializer expression
98    pub init: Box<Expr>,
99    /// Span covering the entire const declaration
100    pub span: Span,
101}
102
103/// A struct declaration.
104///
105/// Structs can contain both fields and methods. Methods are defined inline
106/// within the struct block, not in separate impl blocks.
107///
108/// ```gruel
109/// struct Point {
110///     x: i32,
111///     y: i32,
112///
113///     fn distance(self) -> i32 {
114///         self.x + self.y
115///     }
116/// }
117/// ```
118#[derive(Debug, Clone, PartialEq, Eq)]
119pub struct StructDecl {
120    /// Directives applied to this struct (e.g., @copy)
121    pub directives: Directives,
122    /// Visibility of this struct
123    pub visibility: Visibility,
124    /// Whether this struct is a linear type (must be consumed, cannot be dropped)
125    pub is_linear: bool,
126    /// Struct name
127    pub name: Ident,
128    /// Struct fields
129    pub fields: Vec<FieldDecl>,
130    /// Methods defined on this struct
131    pub methods: Vec<Method>,
132    /// Span covering the entire struct declaration
133    pub span: Span,
134}
135
136/// A field declaration in a struct.
137#[derive(Debug, Clone, PartialEq, Eq)]
138pub struct FieldDecl {
139    /// Field name
140    pub name: Ident,
141    /// Field type
142    pub ty: TypeExpr,
143    /// Span covering the entire field declaration
144    pub span: Span,
145}
146
147/// An enum declaration.
148#[derive(Debug, Clone, PartialEq, Eq)]
149pub struct EnumDecl {
150    /// Visibility of this enum
151    pub visibility: Visibility,
152    /// Enum name
153    pub name: Ident,
154    /// Enum variants
155    pub variants: Vec<EnumVariant>,
156    /// Span covering the entire enum declaration
157    pub span: Span,
158}
159
160/// A variant in an enum declaration.
161#[derive(Debug, Clone, PartialEq, Eq)]
162pub struct EnumVariant {
163    /// Variant name
164    pub name: Ident,
165    /// The kind of variant (unit, tuple, or struct).
166    pub kind: EnumVariantKind,
167    /// Span covering the variant
168    pub span: Span,
169}
170
171/// The kind of an enum variant.
172#[derive(Debug, Clone, PartialEq, Eq)]
173pub enum EnumVariantKind {
174    /// Unit variant: `Red`
175    Unit,
176    /// Tuple variant: `Some(i32, i32)`
177    Tuple(Vec<TypeExpr>),
178    /// Struct variant: `Circle { radius: i32 }`
179    Struct(Vec<EnumVariantField>),
180}
181
182/// A named field in a struct-style enum variant.
183#[derive(Debug, Clone, PartialEq, Eq)]
184pub struct EnumVariantField {
185    /// Field name
186    pub name: Ident,
187    /// Field type
188    pub ty: TypeExpr,
189    /// Span covering the field
190    pub span: Span,
191}
192
193/// A user-defined destructor declaration.
194///
195/// Syntax: `drop fn TypeName(self) { body }`
196#[derive(Debug, Clone, PartialEq, Eq)]
197pub struct DropFn {
198    /// The struct type this destructor is for
199    pub type_name: Ident,
200    /// The self parameter
201    pub self_param: SelfParam,
202    /// Destructor body
203    pub body: Expr,
204    /// Span covering the entire drop fn
205    pub span: Span,
206}
207
208/// A method definition in an impl block.
209#[derive(Debug, Clone, PartialEq, Eq)]
210pub struct Method {
211    /// Directives applied to this method
212    pub directives: Directives,
213    /// Method name
214    pub name: Ident,
215    /// Whether this method takes self (None = associated function, Some = method with receiver)
216    pub receiver: Option<SelfParam>,
217    /// Method parameters (excluding self)
218    pub params: Vec<Param>,
219    /// Return type (None means implicit unit `()`)
220    pub return_type: Option<TypeExpr>,
221    /// Method body
222    pub body: Expr,
223    /// Span covering the entire method
224    pub span: Span,
225}
226
227/// A self parameter in a method.
228#[derive(Debug, Clone, PartialEq, Eq)]
229pub struct SelfParam {
230    /// Span covering the `self` keyword
231    pub span: Span,
232}
233
234/// Visibility of an item (function, struct, enum, etc.)
235#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
236pub enum Visibility {
237    /// Private to the current file (default)
238    #[default]
239    Private,
240    /// Public - visible to importers
241    Public,
242}
243
244/// A function definition.
245#[derive(Debug, Clone, PartialEq, Eq)]
246pub struct Function {
247    /// Directives applied to this function
248    pub directives: Directives,
249    /// Visibility of this function
250    pub visibility: Visibility,
251    /// Whether this function is marked `unchecked` (can only be called from checked blocks)
252    pub is_unchecked: bool,
253    /// Function name
254    pub name: Ident,
255    /// Function parameters
256    pub params: Vec<Param>,
257    /// Return type (None means implicit unit `()`)
258    pub return_type: Option<TypeExpr>,
259    /// Function body
260    pub body: Expr,
261    /// Span covering the entire function
262    pub span: Span,
263}
264
265/// Parameter passing mode.
266#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
267pub enum ParamMode {
268    /// Normal pass-by-value parameter
269    #[default]
270    Normal,
271    /// Inout parameter - mutated in place and returned to caller
272    Inout,
273    /// Borrow parameter - immutable borrow without ownership transfer
274    Borrow,
275    /// Comptime parameter - evaluated at compile time (used for type parameters)
276    Comptime,
277}
278
279/// A function parameter.
280#[derive(Debug, Clone, PartialEq, Eq)]
281pub struct Param {
282    /// Whether this parameter is evaluated at compile time
283    pub is_comptime: bool,
284    /// Parameter passing mode (normal or inout)
285    pub mode: ParamMode,
286    /// Parameter name
287    pub name: Ident,
288    /// Parameter type
289    pub ty: TypeExpr,
290    /// Span covering the entire parameter
291    pub span: Span,
292}
293
294/// An identifier.
295#[derive(Debug, Clone, Copy, PartialEq, Eq)]
296pub struct Ident {
297    pub name: Spur,
298    pub span: Span,
299}
300
301/// A type expression in the AST.
302#[derive(Debug, Clone, PartialEq, Eq)]
303pub enum TypeExpr {
304    /// A simple named type (e.g., i32, bool, MyStruct)
305    Named(Ident),
306    /// Unit type: ()
307    Unit(Span),
308    /// Never type: !
309    Never(Span),
310    /// Array type: [T; N] where T is the element type and N is the length
311    Array {
312        element: Box<TypeExpr>,
313        length: u64,
314        span: Span,
315    },
316    /// Anonymous struct type: struct { field: Type, fn method(...) { ... }, ... }
317    /// Used in comptime type construction (e.g., `fn Pair(comptime T: type) -> type { struct { first: T, second: T } }`)
318    /// Methods can be included inside the struct definition (Zig-style).
319    AnonymousStruct {
320        /// Field declarations (name and type)
321        fields: Vec<AnonStructField>,
322        /// Method definitions inside the anonymous struct
323        methods: Vec<Method>,
324        span: Span,
325    },
326    /// Anonymous enum type: enum { Variant, Variant(T), Variant { field: T }, fn method(...) { ... }, ... }
327    /// Used in comptime type construction (e.g., `fn Option(comptime T: type) -> type { enum { Some(T), None } }`)
328    /// Methods can be included inside the enum definition (Zig-style).
329    AnonymousEnum {
330        /// Enum variants
331        variants: Vec<EnumVariant>,
332        /// Method definitions inside the anonymous enum
333        methods: Vec<Method>,
334        span: Span,
335    },
336    /// Raw pointer to immutable data: ptr const T
337    PointerConst { pointee: Box<TypeExpr>, span: Span },
338    /// Raw pointer to mutable data: ptr mut T
339    PointerMut { pointee: Box<TypeExpr>, span: Span },
340}
341
342/// A field in an anonymous struct type expression.
343#[derive(Debug, Clone, PartialEq, Eq)]
344pub struct AnonStructField {
345    /// Field name
346    pub name: Ident,
347    /// Field type
348    pub ty: TypeExpr,
349    /// Span covering the entire field declaration
350    pub span: Span,
351}
352
353impl TypeExpr {
354    /// Get the span of this type expression.
355    pub fn span(&self) -> Span {
356        match self {
357            TypeExpr::Named(ident) => ident.span,
358            TypeExpr::Unit(span) => *span,
359            TypeExpr::Never(span) => *span,
360            TypeExpr::Array { span, .. } => *span,
361            TypeExpr::AnonymousStruct { span, .. } => *span,
362            TypeExpr::AnonymousEnum { span, .. } => *span,
363            TypeExpr::PointerConst { span, .. } => *span,
364            TypeExpr::PointerMut { span, .. } => *span,
365        }
366    }
367}
368
369impl fmt::Display for TypeExpr {
370    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
371        match self {
372            TypeExpr::Named(ident) => write!(f, "sym:{}", ident.name.into_usize()),
373            TypeExpr::Unit(_) => write!(f, "()"),
374            TypeExpr::Never(_) => write!(f, "!"),
375            TypeExpr::Array {
376                element, length, ..
377            } => write!(f, "[{}; {}]", element, length),
378            TypeExpr::AnonymousStruct {
379                fields, methods, ..
380            } => {
381                write!(f, "struct {{ ")?;
382                for (i, field) in fields.iter().enumerate() {
383                    if i > 0 {
384                        write!(f, ", ")?;
385                    }
386                    write!(f, "sym:{}: {}", field.name.name.into_usize(), field.ty)?;
387                }
388                for (i, method) in methods.iter().enumerate() {
389                    if !fields.is_empty() || i > 0 {
390                        write!(f, ", ")?;
391                    }
392                    write!(f, "fn sym:{}", method.name.name.into_usize())?;
393                }
394                write!(f, " }}")
395            }
396            TypeExpr::AnonymousEnum {
397                variants, methods, ..
398            } => {
399                write!(f, "enum {{ ")?;
400                for (i, variant) in variants.iter().enumerate() {
401                    if i > 0 {
402                        write!(f, ", ")?;
403                    }
404                    write!(f, "sym:{}", variant.name.name.into_usize())?;
405                }
406                for (i, method) in methods.iter().enumerate() {
407                    if !variants.is_empty() || i > 0 {
408                        write!(f, ", ")?;
409                    }
410                    write!(f, "fn sym:{}", method.name.name.into_usize())?;
411                }
412                write!(f, " }}")
413            }
414            TypeExpr::PointerConst { pointee, .. } => write!(f, "ptr const {}", pointee),
415            TypeExpr::PointerMut { pointee, .. } => write!(f, "ptr mut {}", pointee),
416        }
417    }
418}
419
420/// A unit literal expression - represents `()` or implicit unit.
421#[derive(Debug, Clone, PartialEq, Eq)]
422pub struct UnitLit {
423    pub span: Span,
424}
425
426/// An expression.
427#[derive(Debug, Clone, PartialEq, Eq)]
428pub enum Expr {
429    /// Integer literal
430    Int(IntLit),
431    /// Floating-point literal
432    Float(FloatLit),
433    /// String literal
434    String(StringLit),
435    /// Boolean literal
436    Bool(BoolLit),
437    /// Unit literal (explicit `()` or implicit unit for blocks without final expression)
438    Unit(UnitLit),
439    /// Identifier reference (variable)
440    Ident(Ident),
441    /// Binary operation (e.g., `a + b`)
442    Binary(BinaryExpr),
443    /// Unary operation (e.g., `-x`)
444    Unary(UnaryExpr),
445    /// Parenthesized expression (e.g., `(a + b)`)
446    Paren(ParenExpr),
447    /// Block with statements and final expression
448    Block(BlockExpr),
449    /// If expression (e.g., `if cond { a } else { b }`)
450    If(IfExpr),
451    /// Match expression (e.g., `match x { 1 => a, _ => b }`)
452    Match(MatchExpr),
453    /// While expression (e.g., `while cond { body }`)
454    While(WhileExpr),
455    /// For-in expression (e.g., `for x in arr { body }`)
456    For(ForExpr),
457    /// Loop expression - infinite loop (e.g., `loop { body }`)
458    Loop(LoopExpr),
459    /// Function call (e.g., `foo(1, 2)`)
460    Call(CallExpr),
461    /// Break statement (exits the innermost loop)
462    Break(BreakExpr),
463    /// Continue statement (skips to the next iteration of the innermost loop)
464    Continue(ContinueExpr),
465    /// Return statement (returns a value from the current function)
466    Return(ReturnExpr),
467    /// Struct literal (e.g., `Point { x: 1, y: 2 }`)
468    StructLit(StructLitExpr),
469    /// Field access (e.g., `point.x`)
470    Field(FieldExpr),
471    /// Method call (e.g., `point.distance()`)
472    MethodCall(MethodCallExpr),
473    /// Intrinsic call (e.g., `@dbg(42)`)
474    IntrinsicCall(IntrinsicCallExpr),
475    /// Array literal (e.g., `[1, 2, 3]`)
476    ArrayLit(ArrayLitExpr),
477    /// Array indexing (e.g., `arr[0]`)
478    Index(IndexExpr),
479    /// Path expression (e.g., `Color::Red`)
480    Path(PathExpr),
481    /// Enum struct variant literal (e.g., `Shape::Circle { radius: 5 }`)
482    EnumStructLit(EnumStructLitExpr),
483    /// Associated function call (e.g., `Point::origin()`)
484    AssocFnCall(AssocFnCallExpr),
485    /// Self expression (e.g., `self` in method bodies)
486    SelfExpr(SelfExpr),
487    /// Comptime block expression (e.g., `comptime { 1 + 2 }`)
488    Comptime(ComptimeBlockExpr),
489    /// Comptime unroll for expression (e.g., `comptime_unroll for field in info.fields { ... }`)
490    ComptimeUnrollFor(ComptimeUnrollForExpr),
491    /// Checked block expression (e.g., `checked { @ptr_read(p) }`)
492    Checked(CheckedBlockExpr),
493    /// Type literal expression (e.g., `i32` used as a value in generic function calls)
494    TypeLit(TypeLitExpr),
495    /// Error node for recovered parse errors.
496    /// Used by error recovery to continue parsing after a syntax error.
497    Error(Span),
498}
499
500/// An integer literal.
501#[derive(Debug, Clone, PartialEq, Eq)]
502pub struct IntLit {
503    pub value: u64,
504    pub span: Span,
505}
506
507/// A floating-point literal, stored as f64 bits for Eq compatibility.
508#[derive(Debug, Clone, PartialEq, Eq)]
509pub struct FloatLit {
510    /// The f64 value stored as bits via `f64::to_bits()`.
511    pub bits: u64,
512    pub span: Span,
513}
514
515/// A string literal.
516#[derive(Debug, Clone, Copy, PartialEq, Eq)]
517pub struct StringLit {
518    pub value: Spur,
519    pub span: Span,
520}
521
522/// A boolean literal.
523#[derive(Debug, Clone, PartialEq, Eq)]
524pub struct BoolLit {
525    pub value: bool,
526    pub span: Span,
527}
528
529/// A binary expression.
530#[derive(Debug, Clone, PartialEq, Eq)]
531pub struct BinaryExpr {
532    pub left: Box<Expr>,
533    pub op: BinaryOp,
534    pub right: Box<Expr>,
535    pub span: Span,
536}
537
538/// Binary operators.
539#[derive(Debug, Clone, Copy, PartialEq, Eq)]
540pub enum BinaryOp {
541    // Arithmetic
542    Add, // +
543    Sub, // -
544    Mul, // *
545    Div, // /
546    Mod, // %
547    // Comparison
548    Eq, // ==
549    Ne, // !=
550    Lt, // <
551    Gt, // >
552    Le, // <=
553    Ge, // >=
554    // Logical
555    And, // &&
556    Or,  // ||
557    // Bitwise
558    BitAnd, // &
559    BitOr,  // |
560    BitXor, // ^
561    Shl,    // <<
562    Shr,    // >>
563}
564
565/// A unary expression.
566#[derive(Debug, Clone, PartialEq, Eq)]
567pub struct UnaryExpr {
568    pub op: UnaryOp,
569    pub operand: Box<Expr>,
570    pub span: Span,
571}
572
573/// Unary operators.
574#[derive(Debug, Clone, Copy, PartialEq, Eq)]
575pub enum UnaryOp {
576    Neg,    // -
577    Not,    // !
578    BitNot, // ~
579}
580
581/// A parenthesized expression.
582#[derive(Debug, Clone, PartialEq, Eq)]
583pub struct ParenExpr {
584    pub inner: Box<Expr>,
585    pub span: Span,
586}
587
588/// A block expression containing statements and a final expression.
589#[derive(Debug, Clone, PartialEq, Eq)]
590pub struct BlockExpr {
591    /// Statements in the block
592    pub statements: Vec<Statement>,
593    /// Final expression (the value of the block)
594    pub expr: Box<Expr>,
595    pub span: Span,
596}
597
598/// An if expression.
599#[derive(Debug, Clone, PartialEq, Eq)]
600pub struct IfExpr {
601    /// Condition (must be bool)
602    pub cond: Box<Expr>,
603    /// Then branch
604    pub then_block: BlockExpr,
605    /// Optional else branch
606    pub else_block: Option<BlockExpr>,
607    pub span: Span,
608}
609
610/// A match expression.
611#[derive(Debug, Clone, PartialEq, Eq)]
612pub struct MatchExpr {
613    /// The value being matched (scrutinee)
614    pub scrutinee: Box<Expr>,
615    /// Match arms
616    pub arms: Vec<MatchArm>,
617    pub span: Span,
618}
619
620/// A single arm in a match expression.
621#[derive(Debug, Clone, PartialEq, Eq)]
622pub struct MatchArm {
623    /// The pattern to match
624    pub pattern: Pattern,
625    /// The body expression
626    pub body: Box<Expr>,
627    pub span: Span,
628}
629
630/// A pattern in a match arm.
631#[derive(Debug, Clone, PartialEq, Eq)]
632pub enum Pattern {
633    /// Wildcard pattern `_` - matches anything
634    Wildcard(Span),
635    /// Integer literal pattern (positive or zero)
636    Int(IntLit),
637    /// Negative integer literal pattern (e.g., `-1`, `-42`)
638    NegInt(NegIntLit),
639    /// Boolean literal pattern
640    Bool(BoolLit),
641    /// Path pattern (e.g., `Color::Red` for enum variant)
642    Path(PathPattern),
643    /// Data variant pattern with bindings (e.g., `Option::Some(x)`)
644    DataVariant {
645        /// Optional module prefix
646        base: Option<Box<Expr>>,
647        /// Enum type name
648        type_name: Ident,
649        /// Variant name
650        variant: Ident,
651        /// Bindings for each field
652        bindings: Vec<PatternBinding>,
653        span: Span,
654    },
655    /// Struct variant pattern with named field bindings (e.g., `Shape::Circle { radius }`)
656    StructVariant {
657        /// Optional module prefix
658        base: Option<Box<Expr>>,
659        /// Enum type name
660        type_name: Ident,
661        /// Variant name
662        variant: Ident,
663        /// Named field bindings
664        fields: Vec<PatternFieldBinding>,
665        span: Span,
666    },
667}
668
669/// A named field binding in a struct variant pattern.
670#[derive(Debug, Clone, PartialEq, Eq)]
671pub struct PatternFieldBinding {
672    /// The field name being matched
673    pub field_name: Ident,
674    /// The binding for this field
675    pub binding: PatternBinding,
676}
677
678/// A binding in a data variant pattern.
679#[derive(Debug, Clone, PartialEq, Eq)]
680pub enum PatternBinding {
681    /// Wildcard binding (`_`)
682    Wildcard(Span),
683    /// Named binding (`x` or `mut x`)
684    Ident { is_mut: bool, name: Ident },
685}
686
687/// A negative integer literal pattern.
688#[derive(Debug, Clone, PartialEq, Eq)]
689pub struct NegIntLit {
690    /// The absolute value of the negative integer
691    pub value: u64,
692    /// Span covering the entire pattern (minus sign and literal)
693    pub span: Span,
694}
695
696/// A path pattern (e.g., `Color::Red` or `module.Color::Red` for enum variant matching).
697#[derive(Debug, Clone, PartialEq, Eq)]
698pub struct PathPattern {
699    /// Optional module/namespace prefix (e.g., `utils` in `utils.Color::Red`)
700    pub base: Option<Box<Expr>>,
701    /// The type name (e.g., `Color`)
702    pub type_name: Ident,
703    /// The variant name (e.g., `Red`)
704    pub variant: Ident,
705    pub span: Span,
706}
707
708impl Pattern {
709    /// Get the span of this pattern.
710    pub fn span(&self) -> Span {
711        match self {
712            Pattern::Wildcard(span) => *span,
713            Pattern::Int(lit) => lit.span,
714            Pattern::NegInt(lit) => lit.span,
715            Pattern::Bool(lit) => lit.span,
716            Pattern::Path(path) => path.span,
717            Pattern::DataVariant { span, .. } => *span,
718            Pattern::StructVariant { span, .. } => *span,
719        }
720    }
721}
722
723/// Argument passing mode.
724#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
725pub enum ArgMode {
726    /// Normal pass-by-value argument
727    #[default]
728    Normal,
729    /// Inout argument - mutated in place
730    Inout,
731    /// Borrow argument - immutable borrow
732    Borrow,
733}
734
735/// An argument in a function call.
736#[derive(Debug, Clone, PartialEq, Eq)]
737pub struct CallArg {
738    /// The passing mode for this argument
739    pub mode: ArgMode,
740    /// The argument expression
741    pub expr: Expr,
742    /// Span covering the entire argument (including inout/borrow keyword if present)
743    pub span: Span,
744}
745
746impl CallArg {
747    /// Returns true if this argument is passed as inout.
748    /// This is a convenience method for backwards compatibility.
749    pub fn is_inout(&self) -> bool {
750        self.mode == ArgMode::Inout
751    }
752
753    /// Returns true if this argument is passed as borrow.
754    pub fn is_borrow(&self) -> bool {
755        self.mode == ArgMode::Borrow
756    }
757}
758
759/// A function call expression.
760#[derive(Debug, Clone, PartialEq, Eq)]
761pub struct CallExpr {
762    /// Function name
763    pub name: Ident,
764    /// Arguments
765    pub args: Vec<CallArg>,
766    pub span: Span,
767}
768
769/// An argument to an intrinsic call (can be an expression or a type).
770#[derive(Debug, Clone, PartialEq, Eq)]
771pub enum IntrinsicArg {
772    /// An expression argument (e.g., `@dbg(42)`)
773    Expr(Expr),
774    /// A type argument (e.g., `@size_of(i32)`)
775    Type(TypeExpr),
776}
777
778/// An intrinsic call expression (e.g., `@dbg(42)` or `@size_of(i32)`).
779#[derive(Debug, Clone, PartialEq, Eq)]
780pub struct IntrinsicCallExpr {
781    /// Intrinsic name (without the @)
782    pub name: Ident,
783    /// Arguments (can be expressions or types)
784    pub args: Vec<IntrinsicArg>,
785    pub span: Span,
786}
787
788/// A struct literal expression (e.g., `Point { x: 1, y: 2 }` or `module.Point { x: 1, y: 2 }`).
789#[derive(Debug, Clone, PartialEq, Eq)]
790pub struct StructLitExpr {
791    /// Optional module/namespace prefix (e.g., `utils` in `utils.Point { ... }`)
792    pub base: Option<Box<Expr>>,
793    /// Struct type name
794    pub name: Ident,
795    /// Field initializers
796    pub fields: Vec<FieldInit>,
797    pub span: Span,
798}
799
800/// A field initializer in a struct literal.
801#[derive(Debug, Clone, PartialEq, Eq)]
802pub struct FieldInit {
803    /// Field name
804    pub name: Ident,
805    /// Field value
806    pub value: Box<Expr>,
807    pub span: Span,
808}
809
810/// A field access expression (e.g., `point.x`).
811#[derive(Debug, Clone, PartialEq, Eq)]
812pub struct FieldExpr {
813    /// Base expression (the struct value)
814    pub base: Box<Expr>,
815    /// Field name
816    pub field: Ident,
817    pub span: Span,
818}
819
820/// A method call expression (e.g., `point.distance()`).
821#[derive(Debug, Clone, PartialEq, Eq)]
822pub struct MethodCallExpr {
823    /// Base expression (the receiver)
824    pub receiver: Box<Expr>,
825    /// Method name
826    pub method: Ident,
827    /// Arguments (excluding self)
828    pub args: Vec<CallArg>,
829    pub span: Span,
830}
831
832/// An array literal expression (e.g., `[1, 2, 3]`).
833#[derive(Debug, Clone, PartialEq, Eq)]
834pub struct ArrayLitExpr {
835    /// Array elements
836    pub elements: Vec<Expr>,
837    pub span: Span,
838}
839
840/// An array index expression (e.g., `arr[0]`).
841#[derive(Debug, Clone, PartialEq, Eq)]
842pub struct IndexExpr {
843    /// The array being indexed
844    pub base: Box<Expr>,
845    /// The index expression
846    pub index: Box<Expr>,
847    pub span: Span,
848}
849
850/// A path expression (e.g., `Color::Red` or `module.Color::Red` for enum variant).
851#[derive(Debug, Clone, PartialEq, Eq)]
852pub struct PathExpr {
853    /// Optional module/namespace prefix (e.g., `utils` in `utils.Color::Red`)
854    pub base: Option<Box<Expr>>,
855    /// The type name (e.g., `Color`)
856    pub type_name: Ident,
857    /// The variant name (e.g., `Red`)
858    pub variant: Ident,
859    pub span: Span,
860}
861
862/// An enum struct variant literal expression (e.g., `Shape::Circle { radius: 5 }`).
863#[derive(Debug, Clone, PartialEq, Eq)]
864pub struct EnumStructLitExpr {
865    /// Optional module/namespace prefix
866    pub base: Option<Box<Expr>>,
867    /// The enum type name (e.g., `Shape`)
868    pub type_name: Ident,
869    /// The variant name (e.g., `Circle`)
870    pub variant: Ident,
871    /// Field initializers
872    pub fields: Vec<FieldInit>,
873    pub span: Span,
874}
875
876/// An associated function call expression (e.g., `Point::origin()` or `module.Point::origin()`).
877#[derive(Debug, Clone, PartialEq, Eq)]
878pub struct AssocFnCallExpr {
879    /// Optional module/namespace prefix (e.g., `utils` in `utils.Point::origin()`)
880    pub base: Option<Box<Expr>>,
881    /// The type name (e.g., `Point`)
882    pub type_name: Ident,
883    /// The function name (e.g., `origin`)
884    pub function: Ident,
885    /// Arguments
886    pub args: Vec<CallArg>,
887    pub span: Span,
888}
889
890/// A statement (does not produce a value).
891#[derive(Debug, Clone, PartialEq, Eq)]
892pub enum Statement {
893    /// Let binding: `let x = expr;` or `let mut x = expr;`
894    Let(LetStatement),
895    /// Assignment: `x = expr;`
896    Assign(AssignStatement),
897    /// Expression statement: `expr;`
898    Expr(Expr),
899}
900
901/// A pattern in a let binding.
902#[derive(Debug, Clone, PartialEq, Eq)]
903pub enum LetPattern {
904    /// Named binding (e.g., `x`, `_unused`)
905    Ident(Ident),
906    /// Wildcard pattern `_` - discards the value without creating a binding
907    Wildcard(Span),
908    /// Struct destructuring (e.g., `Point { x, y }`)
909    Struct {
910        type_name: Ident,
911        fields: Vec<DestructureField>,
912        span: Span,
913    },
914}
915
916/// A field binding in a struct destructure pattern.
917#[derive(Debug, Clone, PartialEq, Eq)]
918pub struct DestructureField {
919    /// The struct field being bound
920    pub field_name: Ident,
921    /// How the field is bound
922    pub binding: DestructureBinding,
923    /// Whether the binding is mutable
924    pub is_mut: bool,
925}
926
927/// How a field is bound in a struct destructure.
928#[derive(Debug, Clone, PartialEq, Eq)]
929pub enum DestructureBinding {
930    /// `field` — bind to same name
931    Shorthand,
932    /// `field: new_name`
933    Renamed(Ident),
934    /// `field: _`
935    Wildcard(Span),
936}
937
938impl LetPattern {
939    /// Get the span of this pattern.
940    pub fn span(&self) -> Span {
941        match self {
942            LetPattern::Ident(ident) => ident.span,
943            LetPattern::Wildcard(span) => *span,
944            LetPattern::Struct { span, .. } => *span,
945        }
946    }
947}
948
949/// A let binding statement.
950#[derive(Debug, Clone, PartialEq, Eq)]
951pub struct LetStatement {
952    /// Directives applied to this let binding
953    pub directives: Directives,
954    /// Whether the binding is mutable
955    pub is_mut: bool,
956    /// The binding pattern (identifier or wildcard)
957    pub pattern: LetPattern,
958    /// Optional type annotation
959    pub ty: Option<TypeExpr>,
960    /// Initializer expression
961    pub init: Box<Expr>,
962    pub span: Span,
963}
964
965/// An assignment statement.
966#[derive(Debug, Clone, PartialEq, Eq)]
967pub struct AssignStatement {
968    /// Assignment target (variable or field)
969    pub target: AssignTarget,
970    /// Value expression
971    pub value: Box<Expr>,
972    pub span: Span,
973}
974
975/// An assignment target.
976#[derive(Debug, Clone, PartialEq, Eq)]
977pub enum AssignTarget {
978    /// Variable assignment (e.g., `x = 5`)
979    Var(Ident),
980    /// Field assignment (e.g., `point.x = 5`)
981    Field(FieldExpr),
982    /// Index assignment (e.g., `arr[0] = 5`)
983    Index(IndexExpr),
984}
985
986/// A while loop expression.
987#[derive(Debug, Clone, PartialEq, Eq)]
988pub struct WhileExpr {
989    /// Condition (must be bool)
990    pub cond: Box<Expr>,
991    /// Loop body
992    pub body: BlockExpr,
993    pub span: Span,
994}
995
996/// A for-in loop expression (e.g., `for x in expr { body }`).
997#[derive(Debug, Clone, PartialEq, Eq)]
998pub struct ForExpr {
999    /// Loop variable name
1000    pub binding: Ident,
1001    /// Whether the loop variable is mutable (`for mut x in ...`)
1002    pub is_mut: bool,
1003    /// The iterable expression (array or Range)
1004    pub iterable: Box<Expr>,
1005    /// Loop body
1006    pub body: BlockExpr,
1007    pub span: Span,
1008}
1009
1010/// An infinite loop expression.
1011#[derive(Debug, Clone, PartialEq, Eq)]
1012pub struct LoopExpr {
1013    /// Loop body
1014    pub body: BlockExpr,
1015    pub span: Span,
1016}
1017
1018/// A break expression (exits the innermost loop).
1019#[derive(Debug, Clone, PartialEq, Eq)]
1020pub struct BreakExpr {
1021    pub span: Span,
1022}
1023
1024/// A continue expression (skips to the next iteration of the innermost loop).
1025#[derive(Debug, Clone, PartialEq, Eq)]
1026pub struct ContinueExpr {
1027    pub span: Span,
1028}
1029
1030/// A return expression (returns a value from the current function).
1031#[derive(Debug, Clone, PartialEq, Eq)]
1032pub struct ReturnExpr {
1033    /// The value to return (None for `return;` in unit-returning functions)
1034    pub value: Option<Box<Expr>>,
1035    pub span: Span,
1036}
1037
1038/// A self expression (the `self` keyword in method bodies).
1039#[derive(Debug, Clone, PartialEq, Eq)]
1040pub struct SelfExpr {
1041    pub span: Span,
1042}
1043
1044/// A comptime block expression (e.g., `comptime { 1 + 2 }`).
1045/// The expression inside must be evaluable at compile time.
1046#[derive(Debug, Clone, PartialEq, Eq)]
1047pub struct ComptimeBlockExpr {
1048    /// The expression to evaluate at compile time
1049    pub expr: Box<Expr>,
1050    pub span: Span,
1051}
1052
1053/// A comptime_unroll for expression.
1054/// The collection is evaluated at compile time, then the body is unrolled once per element.
1055#[derive(Debug, Clone, PartialEq, Eq)]
1056pub struct ComptimeUnrollForExpr {
1057    /// Loop variable name
1058    pub binding: Ident,
1059    /// The iterable expression (must be comptime-known)
1060    pub iterable: Box<Expr>,
1061    /// Loop body
1062    pub body: BlockExpr,
1063    pub span: Span,
1064}
1065
1066/// A checked block expression (e.g., `checked { @ptr_read(p) }`).
1067/// Unchecked operations (raw pointer manipulation, calling unchecked functions)
1068/// are only allowed inside checked blocks.
1069#[derive(Debug, Clone, PartialEq, Eq)]
1070pub struct CheckedBlockExpr {
1071    /// The expression inside the checked block
1072    pub expr: Box<Expr>,
1073    pub span: Span,
1074}
1075
1076/// A type literal expression (e.g., `i32` used as a value).
1077/// This represents a type used as a value in expression context, typically
1078/// as an argument to a generic function with comptime parameters.
1079#[derive(Debug, Clone, PartialEq, Eq)]
1080pub struct TypeLitExpr {
1081    /// The type being used as a value
1082    pub type_expr: TypeExpr,
1083    pub span: Span,
1084}
1085
1086impl Expr {
1087    /// Get the span of this expression.
1088    pub fn span(&self) -> Span {
1089        match self {
1090            Expr::Int(lit) => lit.span,
1091            Expr::Float(lit) => lit.span,
1092            Expr::String(lit) => lit.span,
1093            Expr::Bool(lit) => lit.span,
1094            Expr::Unit(lit) => lit.span,
1095            Expr::Ident(ident) => ident.span,
1096            Expr::Binary(bin) => bin.span,
1097            Expr::Unary(un) => un.span,
1098            Expr::Paren(paren) => paren.span,
1099            Expr::Block(block) => block.span,
1100            Expr::If(if_expr) => if_expr.span,
1101            Expr::Match(match_expr) => match_expr.span,
1102            Expr::While(while_expr) => while_expr.span,
1103            Expr::For(for_expr) => for_expr.span,
1104            Expr::Loop(loop_expr) => loop_expr.span,
1105            Expr::Call(call) => call.span,
1106            Expr::Break(break_expr) => break_expr.span,
1107            Expr::Continue(continue_expr) => continue_expr.span,
1108            Expr::Return(return_expr) => return_expr.span,
1109            Expr::StructLit(struct_lit) => struct_lit.span,
1110            Expr::Field(field_expr) => field_expr.span,
1111            Expr::MethodCall(method_call) => method_call.span,
1112            Expr::IntrinsicCall(intrinsic) => intrinsic.span,
1113            Expr::ArrayLit(array_lit) => array_lit.span,
1114            Expr::Index(index_expr) => index_expr.span,
1115            Expr::Path(path_expr) => path_expr.span,
1116            Expr::EnumStructLit(lit) => lit.span,
1117            Expr::AssocFnCall(assoc_fn_call) => assoc_fn_call.span,
1118            Expr::SelfExpr(self_expr) => self_expr.span,
1119            Expr::Comptime(comptime_expr) => comptime_expr.span,
1120            Expr::ComptimeUnrollFor(e) => e.span,
1121            Expr::Checked(checked_expr) => checked_expr.span,
1122            Expr::TypeLit(type_lit) => type_lit.span,
1123            Expr::Error(span) => *span,
1124        }
1125    }
1126}
1127
1128// Display implementations for AST pretty-printing
1129
1130impl fmt::Display for Ast {
1131    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1132        for item in &self.items {
1133            match item {
1134                Item::Function(func) => fmt_function(f, func, 0)?,
1135                Item::Struct(s) => fmt_struct(f, s, 0)?,
1136                Item::Enum(e) => fmt_enum(f, e, 0)?,
1137                Item::DropFn(drop_fn) => fmt_drop_fn(f, drop_fn, 0)?,
1138                Item::Const(c) => fmt_const(f, c, 0)?,
1139                Item::Error(span) => writeln!(f, "Error({:?})", span)?,
1140            }
1141        }
1142        Ok(())
1143    }
1144}
1145
1146fn indent(f: &mut fmt::Formatter<'_>, level: usize) -> fmt::Result {
1147    for _ in 0..level {
1148        write!(f, "  ")?;
1149    }
1150    Ok(())
1151}
1152
1153fn fmt_struct(f: &mut fmt::Formatter<'_>, s: &StructDecl, level: usize) -> fmt::Result {
1154    indent(f, level)?;
1155    for directive in &s.directives {
1156        write!(f, "@sym:{} ", directive.name.name.into_usize())?;
1157    }
1158    if s.is_linear {
1159        write!(f, "linear ")?;
1160    }
1161    writeln!(f, "Struct sym:{}", s.name.name.into_usize())?;
1162    for field in &s.fields {
1163        indent(f, level + 1)?;
1164        writeln!(
1165            f,
1166            "Field sym:{} : {}",
1167            field.name.name.into_usize(),
1168            field.ty
1169        )?;
1170    }
1171    for method in &s.methods {
1172        fmt_method(f, method, level + 1)?;
1173    }
1174    Ok(())
1175}
1176
1177fn fmt_enum(f: &mut fmt::Formatter<'_>, e: &EnumDecl, level: usize) -> fmt::Result {
1178    indent(f, level)?;
1179    writeln!(f, "Enum sym:{}", e.name.name.into_usize())?;
1180    for variant in &e.variants {
1181        indent(f, level + 1)?;
1182        writeln!(f, "Variant sym:{}", variant.name.name.into_usize())?;
1183    }
1184    Ok(())
1185}
1186
1187fn fmt_const(f: &mut fmt::Formatter<'_>, c: &ConstDecl, level: usize) -> fmt::Result {
1188    indent(f, level)?;
1189    for directive in &c.directives {
1190        write!(f, "@sym:{} ", directive.name.name.into_usize())?;
1191    }
1192    if c.visibility == Visibility::Public {
1193        write!(f, "pub ")?;
1194    }
1195    write!(f, "Const sym:{}", c.name.name.into_usize())?;
1196    if let Some(ref ty) = c.ty {
1197        write!(f, ": {}", ty)?;
1198    }
1199    writeln!(f)?;
1200    fmt_expr(f, &c.init, level + 1)?;
1201    Ok(())
1202}
1203
1204fn fmt_drop_fn(f: &mut fmt::Formatter<'_>, drop_fn: &DropFn, level: usize) -> fmt::Result {
1205    indent(f, level)?;
1206    writeln!(
1207        f,
1208        "DropFn sym:{}(self)",
1209        drop_fn.type_name.name.into_usize()
1210    )?;
1211    fmt_expr(f, &drop_fn.body, level + 1)?;
1212    Ok(())
1213}
1214
1215fn fmt_method(f: &mut fmt::Formatter<'_>, method: &Method, level: usize) -> fmt::Result {
1216    indent(f, level)?;
1217    write!(f, "Method sym:{}", method.name.name.into_usize())?;
1218    write!(f, "(")?;
1219    if method.receiver.is_some() {
1220        write!(f, "self")?;
1221        if !method.params.is_empty() {
1222            write!(f, ", ")?;
1223        }
1224    }
1225    for (i, param) in method.params.iter().enumerate() {
1226        if i > 0 {
1227            write!(f, ", ")?;
1228        }
1229        fmt_param(f, param)?;
1230    }
1231    write!(f, ")")?;
1232    if let Some(ref ret) = method.return_type {
1233        write!(f, " -> {}", ret)?;
1234    }
1235    writeln!(f)?;
1236    fmt_expr(f, &method.body, level + 1)?;
1237    Ok(())
1238}
1239
1240fn fmt_param(f: &mut fmt::Formatter<'_>, param: &Param) -> fmt::Result {
1241    match param.mode {
1242        ParamMode::Inout => write!(f, "inout ")?,
1243        ParamMode::Borrow => write!(f, "borrow ")?,
1244        ParamMode::Comptime => write!(f, "comptime ")?,
1245        ParamMode::Normal => {}
1246    }
1247    write!(f, "sym:{}: {}", param.name.name.into_usize(), param.ty)
1248}
1249
1250fn fmt_call_arg(f: &mut fmt::Formatter<'_>, arg: &CallArg, level: usize) -> fmt::Result {
1251    match arg.mode {
1252        ArgMode::Inout => {
1253            indent(f, level)?;
1254            writeln!(f, "inout:")?;
1255            fmt_expr(f, &arg.expr, level + 1)
1256        }
1257        ArgMode::Borrow => {
1258            indent(f, level)?;
1259            writeln!(f, "borrow:")?;
1260            fmt_expr(f, &arg.expr, level + 1)
1261        }
1262        ArgMode::Normal => fmt_expr(f, &arg.expr, level),
1263    }
1264}
1265
1266fn fmt_function(f: &mut fmt::Formatter<'_>, func: &Function, level: usize) -> fmt::Result {
1267    indent(f, level)?;
1268    if func.is_unchecked {
1269        write!(f, "unchecked ")?;
1270    }
1271    write!(f, "Function sym:{}", func.name.name.into_usize())?;
1272    if !func.params.is_empty() {
1273        write!(f, "(")?;
1274        for (i, param) in func.params.iter().enumerate() {
1275            if i > 0 {
1276                write!(f, ", ")?;
1277            }
1278            fmt_param(f, param)?;
1279        }
1280        write!(f, ")")?;
1281    }
1282    if let Some(ref ret) = func.return_type {
1283        write!(f, " -> {}", ret)?;
1284    }
1285    writeln!(f)?;
1286    fmt_expr(f, &func.body, level + 1)?;
1287    Ok(())
1288}
1289
1290fn fmt_expr(f: &mut fmt::Formatter<'_>, expr: &Expr, level: usize) -> fmt::Result {
1291    indent(f, level)?;
1292    match expr {
1293        Expr::Int(lit) => writeln!(f, "Int({})", lit.value),
1294        Expr::Float(lit) => writeln!(f, "Float({})", f64::from_bits(lit.bits)),
1295        Expr::String(lit) => writeln!(f, "String(sym:{})", lit.value.into_usize()),
1296        Expr::Bool(lit) => writeln!(f, "Bool({})", lit.value),
1297        Expr::Unit(_) => writeln!(f, "Unit"),
1298        Expr::Ident(ident) => writeln!(f, "Ident(sym:{})", ident.name.into_usize()),
1299        Expr::Binary(bin) => {
1300            writeln!(f, "Binary {:?}", bin.op)?;
1301            fmt_expr(f, &bin.left, level + 1)?;
1302            fmt_expr(f, &bin.right, level + 1)
1303        }
1304        Expr::Unary(un) => {
1305            writeln!(f, "Unary {:?}", un.op)?;
1306            fmt_expr(f, &un.operand, level + 1)
1307        }
1308        Expr::Paren(paren) => {
1309            writeln!(f, "Paren")?;
1310            fmt_expr(f, &paren.inner, level + 1)
1311        }
1312        Expr::Block(block) => {
1313            writeln!(f, "Block")?;
1314            for stmt in &block.statements {
1315                fmt_stmt(f, stmt, level + 1)?;
1316            }
1317            fmt_expr(f, &block.expr, level + 1)
1318        }
1319        Expr::If(if_expr) => {
1320            writeln!(f, "If")?;
1321            indent(f, level + 1)?;
1322            writeln!(f, "Cond:")?;
1323            fmt_expr(f, &if_expr.cond, level + 2)?;
1324            indent(f, level + 1)?;
1325            writeln!(f, "Then:")?;
1326            fmt_block_expr(f, &if_expr.then_block, level + 2)?;
1327            if let Some(ref else_block) = if_expr.else_block {
1328                indent(f, level + 1)?;
1329                writeln!(f, "Else:")?;
1330                fmt_block_expr(f, else_block, level + 2)?;
1331            }
1332            Ok(())
1333        }
1334        Expr::Match(match_expr) => {
1335            writeln!(f, "Match")?;
1336            indent(f, level + 1)?;
1337            writeln!(f, "Scrutinee:")?;
1338            fmt_expr(f, &match_expr.scrutinee, level + 2)?;
1339            for arm in &match_expr.arms {
1340                indent(f, level + 1)?;
1341                writeln!(f, "Arm {:?} =>", arm.pattern)?;
1342                fmt_expr(f, &arm.body, level + 2)?;
1343            }
1344            Ok(())
1345        }
1346        Expr::While(while_expr) => {
1347            writeln!(f, "While")?;
1348            indent(f, level + 1)?;
1349            writeln!(f, "Cond:")?;
1350            fmt_expr(f, &while_expr.cond, level + 2)?;
1351            indent(f, level + 1)?;
1352            writeln!(f, "Body:")?;
1353            fmt_block_expr(f, &while_expr.body, level + 2)
1354        }
1355        Expr::For(for_expr) => {
1356            writeln!(
1357                f,
1358                "For {}sym:{}",
1359                if for_expr.is_mut { "mut " } else { "" },
1360                for_expr.binding.name.into_usize()
1361            )?;
1362            indent(f, level + 1)?;
1363            writeln!(f, "Iterable:")?;
1364            fmt_expr(f, &for_expr.iterable, level + 2)?;
1365            indent(f, level + 1)?;
1366            writeln!(f, "Body:")?;
1367            fmt_block_expr(f, &for_expr.body, level + 2)
1368        }
1369        Expr::Loop(loop_expr) => {
1370            writeln!(f, "Loop")?;
1371            fmt_block_expr(f, &loop_expr.body, level + 1)
1372        }
1373        Expr::Call(call) => {
1374            writeln!(f, "Call sym:{}", call.name.name.into_usize())?;
1375            for arg in &call.args {
1376                fmt_call_arg(f, arg, level + 1)?;
1377            }
1378            Ok(())
1379        }
1380        Expr::IntrinsicCall(intrinsic) => {
1381            writeln!(f, "Intrinsic @sym:{}", intrinsic.name.name.into_usize())?;
1382            for arg in &intrinsic.args {
1383                match arg {
1384                    IntrinsicArg::Expr(expr) => fmt_expr(f, expr, level + 1)?,
1385                    IntrinsicArg::Type(ty) => {
1386                        indent(f, level + 1)?;
1387                        writeln!(f, "Type {:?}", ty)?;
1388                    }
1389                }
1390            }
1391            Ok(())
1392        }
1393        Expr::Break(_) => writeln!(f, "Break"),
1394        Expr::Continue(_) => writeln!(f, "Continue"),
1395        Expr::Return(ret) => {
1396            if let Some(ref value) = ret.value {
1397                writeln!(f, "Return")?;
1398                fmt_expr(f, value, level + 1)
1399            } else {
1400                writeln!(f, "Return (unit)")
1401            }
1402        }
1403        Expr::StructLit(lit) => {
1404            writeln!(f, "StructLit sym:{}", lit.name.name.into_usize())?;
1405            for field in &lit.fields {
1406                indent(f, level + 1)?;
1407                writeln!(f, "sym:{} =", field.name.name.into_usize())?;
1408                fmt_expr(f, &field.value, level + 2)?;
1409            }
1410            Ok(())
1411        }
1412        Expr::Field(field) => {
1413            writeln!(f, "Field .sym:{}", field.field.name.into_usize())?;
1414            fmt_expr(f, &field.base, level + 1)
1415        }
1416        Expr::MethodCall(method_call) => {
1417            writeln!(
1418                f,
1419                "MethodCall .sym:{}",
1420                method_call.method.name.into_usize()
1421            )?;
1422            indent(f, level + 1)?;
1423            writeln!(f, "Receiver:")?;
1424            fmt_expr(f, &method_call.receiver, level + 2)?;
1425            if !method_call.args.is_empty() {
1426                indent(f, level + 1)?;
1427                writeln!(f, "Args:")?;
1428                for arg in &method_call.args {
1429                    fmt_call_arg(f, arg, level + 2)?;
1430                }
1431            }
1432            Ok(())
1433        }
1434        Expr::ArrayLit(array) => {
1435            writeln!(f, "ArrayLit")?;
1436            for elem in &array.elements {
1437                fmt_expr(f, elem, level + 1)?;
1438            }
1439            Ok(())
1440        }
1441        Expr::Index(index) => {
1442            writeln!(f, "Index")?;
1443            indent(f, level + 1)?;
1444            writeln!(f, "Base:")?;
1445            fmt_expr(f, &index.base, level + 2)?;
1446            indent(f, level + 1)?;
1447            writeln!(f, "Index:")?;
1448            fmt_expr(f, &index.index, level + 2)
1449        }
1450        Expr::Path(path) => writeln!(
1451            f,
1452            "Path sym:{}::sym:{}",
1453            path.type_name.name.into_usize(),
1454            path.variant.name.into_usize()
1455        ),
1456        Expr::EnumStructLit(lit) => {
1457            writeln!(
1458                f,
1459                "EnumStructLit sym:{}::sym:{}",
1460                lit.type_name.name.into_usize(),
1461                lit.variant.name.into_usize()
1462            )?;
1463            for field in &lit.fields {
1464                indent(f, level + 1)?;
1465                writeln!(f, "field sym:{}:", field.name.name.into_usize())?;
1466                fmt_expr(f, &field.value, level + 2)?;
1467            }
1468            Ok(())
1469        }
1470        Expr::AssocFnCall(assoc_fn_call) => {
1471            writeln!(
1472                f,
1473                "AssocFnCall sym:{}::sym:{}",
1474                assoc_fn_call.type_name.name.into_usize(),
1475                assoc_fn_call.function.name.into_usize()
1476            )?;
1477            for arg in &assoc_fn_call.args {
1478                fmt_call_arg(f, arg, level + 1)?;
1479            }
1480            Ok(())
1481        }
1482        Expr::SelfExpr(_) => {
1483            writeln!(f, "SelfExpr")
1484        }
1485        Expr::Comptime(comptime) => {
1486            writeln!(f, "Comptime")?;
1487            fmt_expr(f, &comptime.expr, level + 1)
1488        }
1489        Expr::ComptimeUnrollFor(unroll) => {
1490            writeln!(
1491                f,
1492                "ComptimeUnrollFor sym:{}",
1493                unroll.binding.name.into_usize()
1494            )?;
1495            indent(f, level + 1)?;
1496            writeln!(f, "Iterable:")?;
1497            fmt_expr(f, &unroll.iterable, level + 2)?;
1498            indent(f, level + 1)?;
1499            writeln!(f, "Body:")?;
1500            fmt_block_expr(f, &unroll.body, level + 2)
1501        }
1502        Expr::Checked(checked) => {
1503            writeln!(f, "Checked")?;
1504            fmt_expr(f, &checked.expr, level + 1)
1505        }
1506        Expr::TypeLit(type_lit) => {
1507            writeln!(f, "TypeLit({})", type_lit.type_expr)
1508        }
1509        Expr::Error(span) => {
1510            writeln!(f, "Error({:?})", span)
1511        }
1512    }
1513}
1514
1515fn fmt_block_expr(f: &mut fmt::Formatter<'_>, block: &BlockExpr, level: usize) -> fmt::Result {
1516    for stmt in &block.statements {
1517        fmt_stmt(f, stmt, level)?;
1518    }
1519    fmt_expr(f, &block.expr, level)
1520}
1521
1522fn fmt_stmt(f: &mut fmt::Formatter<'_>, stmt: &Statement, level: usize) -> fmt::Result {
1523    indent(f, level)?;
1524    match stmt {
1525        Statement::Let(let_stmt) => {
1526            write!(f, "Let")?;
1527            if let_stmt.is_mut {
1528                write!(f, " mut")?;
1529            }
1530            match &let_stmt.pattern {
1531                LetPattern::Ident(ident) => write!(f, " sym:{}", ident.name.into_usize())?,
1532                LetPattern::Wildcard(_) => write!(f, " _")?,
1533                LetPattern::Struct {
1534                    type_name, fields, ..
1535                } => {
1536                    write!(f, " sym:{} {{", type_name.name.into_usize())?;
1537                    for (i, field) in fields.iter().enumerate() {
1538                        if i > 0 {
1539                            write!(f, ",")?;
1540                        }
1541                        if field.is_mut {
1542                            write!(f, " mut")?;
1543                        }
1544                        write!(f, " sym:{}", field.field_name.name.into_usize())?;
1545                        match &field.binding {
1546                            DestructureBinding::Shorthand => {}
1547                            DestructureBinding::Renamed(name) => {
1548                                write!(f, ": sym:{}", name.name.into_usize())?;
1549                            }
1550                            DestructureBinding::Wildcard(_) => write!(f, ": _")?,
1551                        }
1552                    }
1553                    write!(f, " }}")?;
1554                }
1555            }
1556            if let Some(ref ty) = let_stmt.ty {
1557                write!(f, ": {}", ty)?;
1558            }
1559            writeln!(f)?;
1560            fmt_expr(f, &let_stmt.init, level + 1)
1561        }
1562        Statement::Assign(assign) => {
1563            match &assign.target {
1564                AssignTarget::Var(ident) => writeln!(f, "Assign sym:{}", ident.name.into_usize())?,
1565                AssignTarget::Field(field) => {
1566                    writeln!(f, "Assign field .sym:{}", field.field.name.into_usize())?;
1567                    fmt_expr(f, &field.base, level + 1)?;
1568                }
1569                AssignTarget::Index(index) => {
1570                    writeln!(f, "Assign index")?;
1571                    indent(f, level + 1)?;
1572                    writeln!(f, "Base:")?;
1573                    fmt_expr(f, &index.base, level + 2)?;
1574                    indent(f, level + 1)?;
1575                    writeln!(f, "Index:")?;
1576                    fmt_expr(f, &index.index, level + 2)?;
1577                }
1578            }
1579            fmt_expr(f, &assign.value, level + 1)
1580        }
1581        Statement::Expr(expr) => {
1582            writeln!(f, "ExprStmt")?;
1583            fmt_expr(f, expr, level + 1)
1584        }
1585    }
1586}
1587
1588// ============================================================================
1589// Struct-of-Arrays (SOA) AST Layout
1590// ============================================================================
1591//
1592// This section implements Zig-style SOA layout for the AST.
1593// See docs/designs/soa-ast-layout.md for full design rationale.
1594//
1595// Key characteristics:
1596// - Fixed-size nodes (tag + main_token + lhs + rhs)
1597// - Index-based references (no lifetimes)
1598// - Extra data array for nodes with >2 children
1599// - Single allocation for entire AST
1600// - Better cache locality than tree-based approach
1601//
1602// Migration: This will eventually replace the tree-based Ast above.
1603// For now, both representations coexist during Phase 2-3 migration.
1604
1605/// Node index - references a node in the SOA AST.
1606///
1607/// Nodes are stored in parallel arrays (tags, data, extra) and referenced
1608/// by their index. This is similar to how RIR uses InstRef.
1609#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1610pub struct NodeIndex(pub u32);
1611
1612impl NodeIndex {
1613    /// Create a new node index.
1614    pub const fn new(idx: u32) -> Self {
1615        NodeIndex(idx)
1616    }
1617
1618    /// Get the raw index value.
1619    pub const fn as_u32(self) -> u32 {
1620        self.0
1621    }
1622
1623    /// Get the index as usize for array indexing.
1624    pub const fn as_usize(self) -> usize {
1625        self.0 as usize
1626    }
1627}
1628
1629/// Sentinel value representing "no node" or "null node".
1630/// Used for optional children (e.g., else block in if expression).
1631pub const NULL_NODE: NodeIndex = NodeIndex(u32::MAX);
1632
1633/// Encode a UnaryOp into a u32 for storage in NodeData.
1634pub fn encode_unary_op(op: UnaryOp) -> u32 {
1635    match op {
1636        UnaryOp::Neg => 0,
1637        UnaryOp::Not => 1,
1638        UnaryOp::BitNot => 2,
1639    }
1640}
1641
1642/// Node tag - identifies what kind of node this is.
1643///
1644/// The tag determines how to interpret the lhs/rhs fields in NodeData.
1645/// See docs/designs/soa-ast-layout.md for encoding details.
1646#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1647#[repr(u8)]
1648pub enum NodeTag {
1649    // ===== Items (top-level declarations) =====
1650    /// Function declaration: fn name(params) -> ret { body }
1651    /// - lhs: index into extra (param count + param nodes)
1652    /// - rhs: body expression node
1653    Function,
1654
1655    /// Struct declaration: struct Name { fields... methods... }
1656    /// - lhs: index into extra (field count + field nodes)
1657    /// - rhs: index into extra (method count + method nodes)
1658    StructDecl,
1659
1660    /// Enum declaration: enum Name { variants... }
1661    /// - lhs: index into extra (variant count + variant nodes)
1662    /// - rhs: 0 (unused)
1663    EnumDecl,
1664
1665    /// Drop function: drop fn TypeName(self) { body }
1666    /// - lhs: type name identifier
1667    /// - rhs: body expression node
1668    DropFn,
1669
1670    /// Constant declaration: const name: type = init;
1671    /// - lhs: type expression node (or NULL_NODE if inferred)
1672    /// - rhs: initializer expression node
1673    ConstDecl,
1674
1675    // ===== Expressions - Literals =====
1676    /// Integer literal: 42
1677    /// - lhs: low 32 bits of u64 value
1678    /// - rhs: high 32 bits of u64 value
1679    IntLit,
1680
1681    /// String literal: "hello"
1682    /// - lhs: Spur index (u32) for interned string
1683    /// - rhs: 0 (unused)
1684    StringLit,
1685
1686    /// Boolean literal: true, false
1687    /// - lhs: 0 (false) or 1 (true)
1688    /// - rhs: 0 (unused)
1689    BoolLit,
1690
1691    /// Unit literal: ()
1692    /// - lhs: 0 (unused)
1693    /// - rhs: 0 (unused)
1694    UnitLit,
1695
1696    // ===== Expressions - Identifiers and Paths =====
1697    /// Identifier: variable_name
1698    /// - lhs: Spur index (u32) for identifier name
1699    /// - rhs: 0 (unused)
1700    Ident,
1701
1702    /// Path expression: Color::Red
1703    /// - lhs: type name identifier node
1704    /// - rhs: variant name identifier node
1705    Path,
1706
1707    // ===== Expressions - Operations =====
1708    /// Unary expression: -x, !x, ~x
1709    /// - lhs: operand expression node
1710    /// - rhs: operator kind (u32 from UnaryOp enum)
1711    UnaryExpr,
1712
1713    /// Parenthesized expression: (expr)
1714    /// - lhs: inner expression node
1715    /// - rhs: 0 (unused)
1716    ParenExpr,
1717
1718    /// Binary expression: a + b, a == b, etc.
1719    /// - main_token: the operator token
1720    /// - lhs: left operand expression node
1721    /// - rhs: right operand expression node
1722    BinaryExpr,
1723
1724    // ===== Expressions - Control Flow =====
1725    /// If expression: if cond { then } else { else_block }
1726    /// - lhs: condition expression node
1727    /// - rhs: index into extra (then_block, else_block or NULL_NODE)
1728    IfExpr,
1729
1730    /// Match expression: match x { arms... }
1731    /// - lhs: scrutinee expression node
1732    /// - rhs: index into extra (arm count + arm nodes)
1733    MatchExpr,
1734
1735    /// While loop: while cond { body }
1736    /// - lhs: condition expression node
1737    /// - rhs: body block expression node
1738    WhileExpr,
1739
1740    /// For-in loop: for [mut] x in expr { body }
1741    /// - lhs: iterable expression node
1742    /// - rhs: body block expression node
1743    /// - extra: binding name (Spur index), is_mut flag
1744    ForExpr,
1745
1746    /// Infinite loop: loop { body }
1747    /// - lhs: body block expression node
1748    /// - rhs: 0 (unused)
1749    LoopExpr,
1750
1751    /// Break statement: break
1752    /// - lhs: 0 (unused)
1753    /// - rhs: 0 (unused)
1754    BreakExpr,
1755
1756    /// Continue statement: continue
1757    /// - lhs: 0 (unused)
1758    /// - rhs: 0 (unused)
1759    ContinueExpr,
1760
1761    /// Return statement: return expr
1762    /// - lhs: value expression node (or NULL_NODE for implicit unit return)
1763    /// - rhs: 0 (unused)
1764    ReturnExpr,
1765
1766    // ===== Expressions - Blocks and Statements =====
1767    /// Block expression: { stmts...; final_expr }
1768    /// - lhs: index into extra (stmt count + stmt nodes)
1769    /// - rhs: final expression node
1770    BlockExpr,
1771
1772    /// Let statement: let x: type = init;
1773    /// - lhs: pattern node (identifier or wildcard)
1774    /// - rhs: index into extra (flags, type_expr or NULL_NODE, init_expr)
1775    LetStmt,
1776
1777    /// Assignment statement: x = value;
1778    /// - lhs: target node (Ident, FieldExpr, or IndexExpr)
1779    /// - rhs: value expression node
1780    AssignStmt,
1781
1782    /// Expression statement: expr;
1783    /// - lhs: expression node
1784    /// - rhs: 0 (unused)
1785    ExprStmt,
1786
1787    // ===== Expressions - Function Calls =====
1788    /// Function call: func(args...)
1789    /// - lhs: callee identifier node
1790    /// - rhs: index into extra (arg count + arg nodes)
1791    Call,
1792
1793    /// Method call: receiver.method(args...)
1794    /// - lhs: receiver expression node
1795    /// - rhs: index into extra (method name, arg count, arg nodes)
1796    MethodCall,
1797
1798    /// Intrinsic call: @intrinsic(args...)
1799    /// - lhs: intrinsic name identifier node
1800    /// - rhs: index into extra (arg count + arg nodes)
1801    IntrinsicCall,
1802
1803    /// Associated function call: Type::func(args...)
1804    /// - lhs: type name identifier node
1805    /// - rhs: index into extra (fn name, arg count, arg nodes)
1806    AssocFnCall,
1807
1808    // ===== Expressions - Struct Operations =====
1809    /// Struct literal: Point { x: 1, y: 2 }
1810    /// - lhs: struct name identifier node
1811    /// - rhs: index into extra (field init count + field init nodes)
1812    StructLit,
1813
1814    /// Field access: obj.field
1815    /// - lhs: base expression node
1816    /// - rhs: field name identifier node
1817    FieldExpr,
1818
1819    /// Field initializer in struct literal: field_name: value
1820    /// - lhs: field name identifier node
1821    /// - rhs: value expression node
1822    FieldInit,
1823
1824    // ===== Expressions - Array Operations =====
1825    /// Array literal: [1, 2, 3]
1826    /// - lhs: index into extra (element count + element nodes)
1827    /// - rhs: 0 (unused, count stored in extra)
1828    ArrayLit,
1829
1830    /// Array index: arr[index]
1831    /// - lhs: base expression node
1832    /// - rhs: index expression node
1833    IndexExpr,
1834
1835    // ===== Expressions - Special =====
1836    /// Self expression: self
1837    /// - lhs: 0 (unused)
1838    /// - rhs: 0 (unused)
1839    SelfExpr,
1840
1841    /// Comptime block: comptime { expr }
1842    /// - lhs: inner expression node
1843    /// - rhs: 0 (unused)
1844    ComptimeBlockExpr,
1845
1846    /// Checked block: checked { expr }
1847    /// - lhs: inner expression node
1848    /// - rhs: 0 (unused)
1849    CheckedBlockExpr,
1850
1851    /// Type literal: i32 (used as value)
1852    /// - lhs: type expression node
1853    /// - rhs: 0 (unused)
1854    TypeLit,
1855
1856    // ===== Type Expressions =====
1857    /// Named type: i32, MyStruct
1858    /// - lhs: name identifier node
1859    /// - rhs: 0 (unused)
1860    TypeNamed,
1861
1862    /// Unit type: ()
1863    /// - lhs: 0 (unused)
1864    /// - rhs: 0 (unused)
1865    TypeUnit,
1866
1867    /// Never type: !
1868    /// - lhs: 0 (unused)
1869    /// - rhs: 0 (unused)
1870    TypeNever,
1871
1872    /// Array type: [T; N]
1873    /// - lhs: element type expression node
1874    /// - rhs: length (u32, stored directly)
1875    TypeArray,
1876
1877    /// Anonymous struct type: struct { fields... methods... }
1878    /// - lhs: index into extra (field count + field nodes)
1879    /// - rhs: index into extra (method count + method nodes)
1880    TypeAnonStruct,
1881
1882    /// Const pointer type: ptr const T
1883    /// - lhs: pointee type expression node
1884    /// - rhs: 0 (unused)
1885    TypePointerConst,
1886
1887    /// Mutable pointer type: ptr mut T
1888    /// - lhs: pointee type expression node
1889    /// - rhs: 0 (unused)
1890    TypePointerMut,
1891
1892    // ===== Patterns =====
1893    /// Wildcard pattern: _
1894    /// - lhs: 0 (unused)
1895    /// - rhs: 0 (unused)
1896    PatternWildcard,
1897
1898    /// Integer literal pattern: 42, -1
1899    /// - lhs: low 32 bits of value
1900    /// - rhs: high 32 bits of value
1901    PatternInt,
1902
1903    /// Boolean literal pattern: true, false
1904    /// - lhs: 0 (false) or 1 (true)
1905    /// - rhs: 0 (unused)
1906    PatternBool,
1907
1908    /// Path pattern: Color::Red
1909    /// - lhs: type name identifier node
1910    /// - rhs: variant name identifier node
1911    PatternPath,
1912
1913    // ===== Other Nodes =====
1914    /// Function parameter: name: type
1915    /// - lhs: name identifier node
1916    /// - rhs: type expression node
1917    /// - extra: flags (is_comptime, mode)
1918    Param,
1919
1920    /// Method definition
1921    /// - lhs: index into extra (name, receiver?, param count, params)
1922    /// - rhs: index into extra (return_type or NULL_NODE, body_expr)
1923    Method,
1924
1925    /// Match arm: pattern => body
1926    /// - lhs: pattern node
1927    /// - rhs: body expression node
1928    MatchArm,
1929
1930    /// Call argument (wraps expression with mode flags)
1931    /// - lhs: expression node
1932    /// - rhs: flags (normal=0, inout=1, borrow=2)
1933    CallArg,
1934
1935    /// Field declaration in struct
1936    /// - lhs: name identifier node
1937    /// - rhs: type expression node
1938    FieldDecl,
1939
1940    /// Enum variant
1941    /// - lhs: name identifier node
1942    /// - rhs: 0 (unused, payload support future)
1943    EnumVariant,
1944
1945    /// Directive: @name(args...)
1946    /// - lhs: name identifier node
1947    /// - rhs: index into extra (arg count + arg nodes)
1948    Directive,
1949
1950    /// Directive argument (currently just identifiers)
1951    /// - lhs: identifier node
1952    /// - rhs: 0 (unused)
1953    DirectiveArg,
1954
1955    // ===== Error Recovery =====
1956    /// Error node (parse error recovery)
1957    /// - lhs: 0 (unused)
1958    /// - rhs: 0 (unused)
1959    ErrorNode,
1960}
1961
1962/// Fixed-size node data (12 bytes total).
1963///
1964/// Each node in the SOA AST has:
1965/// - A tag (stored in separate tags array)
1966/// - A main_token (for span information)
1967/// - Two u32 slots (lhs and rhs) whose meaning depends on the tag
1968///
1969/// This matches Zig's design: compact, cache-friendly, uniform size.
1970#[derive(Debug, Clone, Copy)]
1971pub struct NodeData {
1972    /// Primary token for this node.
1973    ///
1974    /// Used for:
1975    /// - Span information in error messages
1976    /// - Operator tokens (for BinaryExpr, UnaryExpr)
1977    /// - Keyword tokens (for if, while, etc.)
1978    pub main_token: u32,
1979
1980    /// Left child or first data slot.
1981    ///
1982    /// Interpretation depends on NodeTag - see NodeTag documentation.
1983    /// Common uses:
1984    /// - Left operand in binary expressions
1985    /// - Single child in unary expressions
1986    /// - Index into extra_data for multi-child nodes
1987    /// - Direct data storage (e.g., low 32 bits of u64)
1988    pub lhs: u32,
1989
1990    /// Right child or second data slot.
1991    ///
1992    /// Interpretation depends on NodeTag - see NodeTag documentation.
1993    /// Common uses:
1994    /// - Right operand in binary expressions
1995    /// - Index into extra_data for multi-child nodes
1996    /// - Direct data storage (e.g., high 32 bits of u64)
1997    /// - Flags and small enums
1998    pub rhs: u32,
1999}
2000
2001/// Struct-of-Arrays AST representation.
2002///
2003/// This is the new SOA-based AST that will replace the tree-based `Ast`.
2004/// During migration (Phases 2-3), both representations will coexist.
2005///
2006/// Design principles:
2007/// - All nodes stored in parallel arrays (tags, data, extra)
2008/// - Nodes reference children by index, not pointers
2009/// - Single allocation for entire AST (better cache locality)
2010/// - Cheap cloning (just clone Arc, not deep copy)
2011///
2012/// See docs/designs/soa-ast-layout.md for full design.
2013#[derive(Debug, Clone)]
2014pub struct SoaAst {
2015    /// Node tags (what kind of node is at each index).
2016    ///
2017    /// Index i contains the tag for node NodeIndex(i).
2018    /// Length of this vec == number of nodes in the AST.
2019    pub tags: Vec<NodeTag>,
2020
2021    /// Node data (main_token + lhs + rhs for each node).
2022    ///
2023    /// Parallel array to tags - tags[i] and data[i] together describe node i.
2024    pub data: Vec<NodeData>,
2025
2026    /// Extra data storage for nodes with >2 children.
2027    ///
2028    /// Nodes that can't fit their data in lhs+rhs store additional
2029    /// data here. The lhs or rhs field contains an index into this array.
2030    ///
2031    /// Layout is node-type specific - see NodeTag documentation.
2032    pub extra: Vec<u32>,
2033
2034    /// Root nodes (top-level items in the source file).
2035    ///
2036    /// These are the entry points for traversing the AST.
2037    /// Each element is a NodeIndex pointing to a Function, StructDecl, etc.
2038    pub items: Vec<NodeIndex>,
2039}
2040
2041impl SoaAst {
2042    /// Create a new empty SOA AST.
2043    pub fn new() -> Self {
2044        SoaAst {
2045            tags: Vec::new(),
2046            data: Vec::new(),
2047            extra: Vec::new(),
2048            items: Vec::new(),
2049        }
2050    }
2051
2052    /// Create a new SOA AST with pre-allocated capacity.
2053    pub fn with_capacity(nodes: usize, extra: usize) -> Self {
2054        SoaAst {
2055            tags: Vec::with_capacity(nodes),
2056            data: Vec::with_capacity(nodes),
2057            extra: Vec::with_capacity(extra),
2058            items: Vec::new(),
2059        }
2060    }
2061
2062    /// Get the tag for a node.
2063    pub fn node_tag(&self, idx: NodeIndex) -> NodeTag {
2064        self.tags[idx.as_usize()]
2065    }
2066
2067    /// Get the data for a node.
2068    pub fn node_data(&self, idx: NodeIndex) -> NodeData {
2069        self.data[idx.as_usize()]
2070    }
2071
2072    /// Get the main token for a node.
2073    pub fn main_token(&self, idx: NodeIndex) -> u32 {
2074        self.data[idx.as_usize()].main_token
2075    }
2076
2077    /// Get the number of nodes in the AST.
2078    pub fn node_count(&self) -> usize {
2079        self.tags.len()
2080    }
2081
2082    /// Get a slice of the extra data array.
2083    pub fn extra_slice(&self, start: usize, len: usize) -> &[u32] {
2084        &self.extra[start..start + len]
2085    }
2086
2087    // ===== Typed Accessors =====
2088    // These provide type-safe access to specific node types.
2089
2090    /// Get the value of an integer literal.
2091    pub fn int_value(&self, idx: NodeIndex) -> u64 {
2092        debug_assert_eq!(self.node_tag(idx), NodeTag::IntLit);
2093        let data = self.node_data(idx);
2094        (data.lhs as u64) | ((data.rhs as u64) << 32)
2095    }
2096
2097    /// Get the boolean value of a boolean literal.
2098    pub fn bool_value(&self, idx: NodeIndex) -> bool {
2099        debug_assert_eq!(self.node_tag(idx), NodeTag::BoolLit);
2100        let data = self.node_data(idx);
2101        data.lhs != 0
2102    }
2103
2104    /// Get the string spur of a string literal.
2105    pub fn string_spur(&self, idx: NodeIndex) -> Spur {
2106        debug_assert_eq!(self.node_tag(idx), NodeTag::StringLit);
2107        let data = self.node_data(idx);
2108        Spur::try_from_usize(data.lhs as usize).expect("invalid spur")
2109    }
2110
2111    /// Get the identifier spur.
2112    pub fn ident_spur(&self, idx: NodeIndex) -> Spur {
2113        debug_assert_eq!(self.node_tag(idx), NodeTag::Ident);
2114        let data = self.node_data(idx);
2115        Spur::try_from_usize(data.lhs as usize).expect("invalid spur")
2116    }
2117
2118    /// Get the operands of a binary expression.
2119    pub fn binary_operands(&self, idx: NodeIndex) -> (NodeIndex, NodeIndex) {
2120        debug_assert_eq!(self.node_tag(idx), NodeTag::BinaryExpr);
2121        let data = self.node_data(idx);
2122        (NodeIndex(data.lhs), NodeIndex(data.rhs))
2123    }
2124
2125    /// Get the operand of a unary expression.
2126    pub fn unary_operand(&self, idx: NodeIndex) -> NodeIndex {
2127        debug_assert_eq!(self.node_tag(idx), NodeTag::UnaryExpr);
2128        let data = self.node_data(idx);
2129        NodeIndex(data.lhs)
2130    }
2131
2132    /// Get the operator kind of a unary expression.
2133    pub fn unary_op(&self, idx: NodeIndex) -> UnaryOp {
2134        debug_assert_eq!(self.node_tag(idx), NodeTag::UnaryExpr);
2135        let data = self.node_data(idx);
2136        match data.rhs {
2137            0 => UnaryOp::Neg,
2138            1 => UnaryOp::Not,
2139            2 => UnaryOp::BitNot,
2140            _ => panic!("invalid UnaryOp encoding: {}", data.rhs),
2141        }
2142    }
2143}
2144
2145impl Default for SoaAst {
2146    fn default() -> Self {
2147        Self::new()
2148    }
2149}
2150
2151#[cfg(test)]
2152mod soa_tests {
2153    use super::*;
2154
2155    #[test]
2156    fn test_node_index() {
2157        let idx = NodeIndex::new(42);
2158        assert_eq!(idx.as_u32(), 42);
2159        assert_eq!(idx.as_usize(), 42);
2160    }
2161
2162    #[test]
2163    fn test_null_node() {
2164        assert_eq!(NULL_NODE.as_u32(), u32::MAX);
2165    }
2166
2167    #[test]
2168    fn test_soa_ast_creation() {
2169        let ast = SoaAst::new();
2170        assert_eq!(ast.node_count(), 0);
2171        assert_eq!(ast.tags.len(), 0);
2172        assert_eq!(ast.data.len(), 0);
2173        assert_eq!(ast.extra.len(), 0);
2174    }
2175
2176    #[test]
2177    fn test_soa_ast_with_capacity() {
2178        let ast = SoaAst::with_capacity(100, 50);
2179        assert!(ast.tags.capacity() >= 100);
2180        assert!(ast.data.capacity() >= 100);
2181        assert!(ast.extra.capacity() >= 50);
2182    }
2183
2184    #[test]
2185    fn test_int_literal_encoding() {
2186        let mut ast = SoaAst::new();
2187
2188        // Add an integer literal node
2189        let value = 0x123456789ABCDEF0u64;
2190        ast.tags.push(NodeTag::IntLit);
2191        ast.data.push(NodeData {
2192            main_token: 0,
2193            lhs: (value & 0xFFFFFFFF) as u32,         // low 32 bits
2194            rhs: ((value >> 32) & 0xFFFFFFFF) as u32, // high 32 bits
2195        });
2196
2197        let idx = NodeIndex(0);
2198        assert_eq!(ast.node_tag(idx), NodeTag::IntLit);
2199        assert_eq!(ast.int_value(idx), value);
2200    }
2201
2202    #[test]
2203    fn test_bool_literal_encoding() {
2204        let mut ast = SoaAst::new();
2205
2206        // Add true
2207        ast.tags.push(NodeTag::BoolLit);
2208        ast.data.push(NodeData {
2209            main_token: 0,
2210            lhs: 1,
2211            rhs: 0,
2212        });
2213
2214        // Add false
2215        ast.tags.push(NodeTag::BoolLit);
2216        ast.data.push(NodeData {
2217            main_token: 1,
2218            lhs: 0,
2219            rhs: 0,
2220        });
2221
2222        assert!(ast.bool_value(NodeIndex(0)));
2223        assert!(!ast.bool_value(NodeIndex(1)));
2224    }
2225
2226    #[test]
2227    fn test_binary_expr_encoding() {
2228        let mut ast = SoaAst::new();
2229
2230        // Create: 1 + 2
2231        // First add the literals
2232        ast.tags.push(NodeTag::IntLit);
2233        ast.data.push(NodeData {
2234            main_token: 0,
2235            lhs: 1,
2236            rhs: 0,
2237        });
2238
2239        ast.tags.push(NodeTag::IntLit);
2240        ast.data.push(NodeData {
2241            main_token: 1,
2242            lhs: 2,
2243            rhs: 0,
2244        });
2245
2246        // Then add the binary expression
2247        ast.tags.push(NodeTag::BinaryExpr);
2248        ast.data.push(NodeData {
2249            main_token: 2, // the '+' token
2250            lhs: 0,        // left operand (node 0)
2251            rhs: 1,        // right operand (node 1)
2252        });
2253
2254        let binop_idx = NodeIndex(2);
2255        assert_eq!(ast.node_tag(binop_idx), NodeTag::BinaryExpr);
2256
2257        let (left, right) = ast.binary_operands(binop_idx);
2258        assert_eq!(left, NodeIndex(0));
2259        assert_eq!(right, NodeIndex(1));
2260        assert_eq!(ast.int_value(left), 1);
2261        assert_eq!(ast.int_value(right), 2);
2262    }
2263
2264    #[test]
2265    fn test_unary_expr_encoding() {
2266        let mut ast = SoaAst::new();
2267
2268        // Create: -42
2269        // First add the literal
2270        ast.tags.push(NodeTag::IntLit);
2271        ast.data.push(NodeData {
2272            main_token: 0,
2273            lhs: 42,
2274            rhs: 0,
2275        });
2276
2277        // Then add the unary expression
2278        ast.tags.push(NodeTag::UnaryExpr);
2279        ast.data.push(NodeData {
2280            main_token: 1,                      // the '-' token
2281            lhs: 0,                             // operand (node 0)
2282            rhs: encode_unary_op(UnaryOp::Neg), // operator kind
2283        });
2284
2285        let unary_idx = NodeIndex(1);
2286        assert_eq!(ast.node_tag(unary_idx), NodeTag::UnaryExpr);
2287
2288        let operand = ast.unary_operand(unary_idx);
2289        assert_eq!(operand, NodeIndex(0));
2290        assert_eq!(ast.int_value(operand), 42);
2291        assert_eq!(ast.unary_op(unary_idx), UnaryOp::Neg);
2292    }
2293
2294    #[test]
2295    fn test_ident_encoding() {
2296        let mut ast = SoaAst::new();
2297
2298        // Mock identifier with spur index 123
2299        ast.tags.push(NodeTag::Ident);
2300        ast.data.push(NodeData {
2301            main_token: 0,
2302            lhs: 123, // spur index
2303            rhs: 0,
2304        });
2305
2306        let idx = NodeIndex(0);
2307        assert_eq!(ast.node_tag(idx), NodeTag::Ident);
2308        assert_eq!(ast.node_data(idx).lhs, 123);
2309    }
2310
2311    #[test]
2312    fn test_extra_data_slice() {
2313        let mut ast = SoaAst::new();
2314        ast.extra = vec![10, 20, 30, 40, 50];
2315
2316        let slice = ast.extra_slice(1, 3);
2317        assert_eq!(slice, &[20, 30, 40]);
2318    }
2319
2320    #[test]
2321    fn test_items() {
2322        let mut ast = SoaAst::new();
2323
2324        // Add two function nodes
2325        ast.tags.push(NodeTag::Function);
2326        ast.data.push(NodeData {
2327            main_token: 0,
2328            lhs: 0,
2329            rhs: 0,
2330        });
2331
2332        ast.tags.push(NodeTag::Function);
2333        ast.data.push(NodeData {
2334            main_token: 1,
2335            lhs: 0,
2336            rhs: 0,
2337        });
2338
2339        ast.items = vec![NodeIndex(0), NodeIndex(1)];
2340
2341        assert_eq!(ast.items.len(), 2);
2342        assert_eq!(ast.node_tag(ast.items[0]), NodeTag::Function);
2343        assert_eq!(ast.node_tag(ast.items[1]), NodeTag::Function);
2344    }
2345}