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}