joule_ast/
lib.rs

1//! Abstract Syntax Tree (AST) definitions for Joule
2//!
3//! This module defines the AST structures that represent parsed Joule code.
4//! The AST is an untyped, structural representation of the source code.
5
6use joule_common::{Span, Symbol};
7use std::fmt;
8
9pub mod generics;
10pub use generics::*;
11
12pub mod energy;
13pub use energy::*;
14
15/// Visibility of an item
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
17pub enum Visibility {
18    /// Not visible outside the module (default)
19    #[default]
20    Private,
21    /// Visible to all
22    Public,
23}
24
25/// A complete Joule source file
26#[derive(Debug, Clone, PartialEq)]
27pub struct File {
28    pub items: Vec<Item>,
29}
30
31/// Top-level items (functions, structs, etc.)
32#[derive(Debug, Clone, PartialEq)]
33pub enum Item {
34    Function(Function),
35    Struct(Struct),
36    Enum(Enum),
37    Trait(Trait),
38    Impl(Impl),
39    Use(Use),
40    Const(Const),
41    Static(Static),
42    TypeAlias(TypeAlias),
43    Module(ModuleDecl),
44    /// Opaque type: `opaque type UserId = i64;`
45    OpaqueType(OpaqueType),
46    /// Extension methods: `extend i64 { fn is_even(self) -> bool { ... } }`
47    Extend(Extend),
48    /// Algebraic effect declaration: `effect IO { fn read() -> String; }`
49    Effect(EffectDecl),
50    /// Supervisor tree: `supervisor MySup { ... }`
51    Supervisor(SupervisorDecl),
52}
53
54/// Module declaration: `mod name;` or `mod name { ... }`
55#[derive(Debug, Clone, PartialEq)]
56pub struct ModuleDecl {
57    pub visibility: Visibility,
58    pub name: Ident,
59    /// None = file-based `mod name;`, Some = inline `mod name { ... }`
60    pub contents: Option<File>,
61    pub span: Span,
62}
63
64/// Opaque type definition: `opaque type UserId = i64;`
65///
66/// Zero-cost type distinction — the underlying type is erased from the public API.
67/// Only the defining module can construct/deconstruct values of the opaque type.
68#[derive(Debug, Clone, PartialEq)]
69pub struct OpaqueType {
70    pub visibility: Visibility,
71    pub name: Ident,
72    pub generics: GenericParams,
73    pub underlying: Type,
74    pub span: Span,
75}
76
77/// Extension methods: `extend Type { fn method(&self) -> T { ... } }`
78///
79/// Adds methods to types you don't own — inspired by Kotlin/Swift/C#.
80#[derive(Debug, Clone, PartialEq)]
81pub struct Extend {
82    pub ty: Type,
83    pub methods: Vec<Function>,
84    pub span: Span,
85}
86
87/// Algebraic effect declaration: `effect IO { fn read() -> String; fn write(s: String); }`
88///
89/// Declares a set of operations that a function may perform.
90/// Inspired by Koka, OCaml 5, and Scala 3 capabilities.
91#[derive(Debug, Clone, PartialEq)]
92pub struct EffectDecl {
93    pub visibility: Visibility,
94    pub name: Ident,
95    pub operations: Vec<EffectOperation>,
96    pub span: Span,
97}
98
99/// A single operation within an effect declaration
100#[derive(Debug, Clone, PartialEq)]
101pub struct EffectOperation {
102    pub name: Ident,
103    pub params: Vec<Param>,
104    pub return_type: Option<Type>,
105    pub span: Span,
106}
107
108/// Supervisor tree declaration: `supervisor MySup { strategy: one_for_one, ... }`
109///
110/// Erlang/Elixir-inspired fault tolerance for concurrent programs.
111#[derive(Debug, Clone, PartialEq)]
112pub struct SupervisorDecl {
113    pub visibility: Visibility,
114    pub name: Ident,
115    pub strategy: SupervisorStrategy,
116    pub children: Vec<SupervisorChild>,
117    pub span: Span,
118}
119
120/// Restart strategy for supervisor trees
121#[derive(Debug, Clone, Copy, PartialEq, Eq)]
122pub enum SupervisorStrategy {
123    /// Restart only the failed child
124    OneForOne,
125    /// Restart all children if one fails
126    OneForAll,
127    /// Restart the failed child and all children started after it
128    RestForOne,
129}
130
131/// A child process specification in a supervisor tree
132#[derive(Debug, Clone, PartialEq)]
133pub struct SupervisorChild {
134    pub name: Ident,
135    pub func: Expr,
136    pub restart: ChildRestart,
137    pub span: Span,
138}
139
140/// Restart policy for a supervised child
141#[derive(Debug, Clone, Copy, PartialEq, Eq)]
142pub enum ChildRestart {
143    /// Always restart (default)
144    Permanent,
145    /// Never restart
146    Temporary,
147    /// Restart only on abnormal termination
148    Transient,
149}
150
151/// Type alias definition: `type Name = Type;`
152#[derive(Debug, Clone, PartialEq)]
153pub struct TypeAlias {
154    pub visibility: Visibility,
155    pub name: Ident,
156    pub generics: GenericParams,
157    pub ty: Type,
158    pub span: Span,
159}
160
161/// Procedural macro kind
162#[derive(Debug, Clone, Copy, PartialEq, Eq)]
163pub enum ProcMacroKind {
164    /// `#[proc_macro]` — function-like macro: `my_macro!(...)`
165    FunctionLike,
166    /// `#[proc_macro_derive(Name)]` — derive macro: `#[derive(Name)]`
167    Derive,
168    /// `#[proc_macro_attribute]` — attribute macro: `#[my_attr] fn ...`
169    Attribute,
170}
171
172/// Function definition
173#[derive(Debug, Clone, PartialEq)]
174pub struct Function {
175    pub visibility: Visibility,
176    pub name: Ident,
177    pub generics: GenericParams,
178    pub params: Vec<Param>,
179    pub return_type: Option<Type>,
180    pub where_clause: WhereClause,
181    /// Body is None for extern function declarations
182    pub body: Option<Block>,
183    /// Whether this is an extern function
184    pub is_extern: bool,
185    /// Whether this is an async function (`async fn`)
186    pub is_async: bool,
187    /// Whether this is a const function (`const fn`)
188    pub is_const: bool,
189    /// Attributes attached to this function (e.g., `#[energy_budget(...)]`)
190    pub attributes: Vec<Attribute>,
191    /// Parsed energy budget constraint from `#[energy_budget(...)]` attribute
192    pub energy_budget: Option<EnergyBudget>,
193    /// Procedural macro kind (if this function is a proc macro)
194    pub proc_macro_kind: Option<ProcMacroKind>,
195    pub span: Span,
196}
197
198/// Function parameter
199#[derive(Debug, Clone, PartialEq)]
200pub struct Param {
201    pub pattern: Pattern,
202    pub ty: Type,
203    pub span: Span,
204}
205
206/// Closure parameter (type is optional — can be inferred)
207#[derive(Debug, Clone, PartialEq)]
208pub struct ClosureParam {
209    pub pattern: Pattern,
210    pub ty: Option<Type>,
211    pub span: Span,
212}
213
214/// Struct definition
215#[derive(Debug, Clone, PartialEq)]
216pub struct Struct {
217    pub visibility: Visibility,
218    pub name: Ident,
219    pub generics: GenericParams,
220    pub fields: Vec<Field>,
221    pub where_clause: WhereClause,
222    pub attributes: Vec<Attribute>,
223    pub span: Span,
224}
225
226/// Struct field
227#[derive(Debug, Clone, PartialEq)]
228pub struct Field {
229    pub visibility: Visibility,
230    pub name: Ident,
231    pub ty: Type,
232    pub span: Span,
233}
234
235/// Enum definition
236#[derive(Debug, Clone, PartialEq)]
237pub struct Enum {
238    pub visibility: Visibility,
239    pub name: Ident,
240    pub generics: GenericParams,
241    pub variants: Vec<Variant>,
242    pub where_clause: WhereClause,
243    pub span: Span,
244}
245
246/// Enum variant
247#[derive(Debug, Clone, PartialEq)]
248pub struct Variant {
249    pub name: Ident,
250    pub fields: VariantFields,
251    pub span: Span,
252}
253
254#[derive(Debug, Clone, PartialEq)]
255pub enum VariantFields {
256    Unit,               // Variant
257    Tuple(Vec<Type>),   // Variant(T, U)
258    Struct(Vec<Field>), // Variant { x: T, y: U }
259}
260
261/// Trait definition
262#[derive(Debug, Clone, PartialEq)]
263pub struct Trait {
264    pub visibility: Visibility,
265    pub name: Ident,
266    pub generics: GenericParams,
267    pub items: Vec<TraitItem>,
268    pub where_clause: WhereClause,
269    pub span: Span,
270}
271
272#[derive(Debug, Clone, PartialEq)]
273pub enum TraitItem {
274    Function(TraitFunction),
275    TypeAlias {
276        name: Ident,
277        bounds: Vec<TypeBound>,
278        span: Span,
279    },
280}
281
282#[derive(Debug, Clone, PartialEq)]
283pub struct TraitFunction {
284    pub name: Ident,
285    pub generics: GenericParams,
286    pub params: Vec<Param>,
287    pub return_type: Option<Type>,
288    pub where_clause: WhereClause,
289    pub body: Option<Block>, // None for trait declarations
290    pub span: Span,
291}
292
293/// Impl block
294#[derive(Debug, Clone, PartialEq)]
295pub struct Impl {
296    pub generics: GenericParams,
297    pub ty: Type,
298    pub trait_: Option<Type>, // Some(Trait) for `impl Trait for Type`
299    pub items: Vec<ImplItem>,
300    pub where_clause: WhereClause,
301    pub span: Span,
302}
303
304#[derive(Debug, Clone, PartialEq)]
305pub enum ImplItem {
306    Function(Function),
307}
308
309/// Use statement
310#[derive(Debug, Clone, PartialEq)]
311pub struct Use {
312    pub tree: UseTree,
313    pub span: Span,
314}
315
316/// Use tree for imports
317#[derive(Debug, Clone, PartialEq)]
318pub enum UseTree {
319    /// Simple path: `use foo::bar;`
320    Path { path: Path, alias: Option<Ident> },
321    /// Glob import: `use foo::bar::*;`
322    Glob { path: Path },
323    /// Grouped imports: `use foo::{A, B};`
324    Group { path: Path, items: Vec<Self> },
325}
326
327/// Const declaration
328#[derive(Debug, Clone, PartialEq)]
329pub struct Const {
330    pub visibility: Visibility,
331    pub name: Ident,
332    pub ty: Type,
333    pub value: Expr,
334    pub span: Span,
335}
336
337/// Static declaration
338#[derive(Debug, Clone, PartialEq)]
339pub struct Static {
340    pub visibility: Visibility,
341    pub name: Ident,
342    pub ty: Type,
343    pub value: Expr,
344    pub mutable: bool,
345    pub span: Span,
346}
347
348/// Type annotations
349#[derive(Debug, Clone, PartialEq)]
350pub enum Type {
351    /// Named type with optional generic arguments (i32, Vec<T>, `HashMap`<K, V>)
352    Path {
353        path: Path,
354        generics: Option<GenericArgs>,
355    },
356    /// Reference type (&T or &mut T)
357    Reference { mutable: bool, inner: Box<Self> },
358    /// Array type ([T; N])
359    Array { element: Box<Self>, size: Box<Expr> },
360    /// Slice type ([T])
361    Slice { element: Box<Self> },
362    /// Tuple type ((T, U, V))
363    Tuple(Vec<Self>),
364    /// Function type (fn(T, U) -> V)
365    Function {
366        params: Vec<Self>,
367        return_type: Box<Self>,
368    },
369    /// Unit type ()
370    Unit,
371    /// Never type !
372    Never,
373    /// Self type (implicit type of self parameter in methods)
374    SelfType,
375    /// Trait object type (dyn Trait)
376    TraitObject { trait_name: Path, span: Span },
377    /// Union type: `i64 | f64 | String`
378    Union { types: Vec<Self>, span: Span },
379}
380
381/// Path (e.g., `std::io::File`)
382#[derive(Debug, Clone, PartialEq, Eq)]
383pub struct Path {
384    pub segments: Vec<Ident>,
385    pub span: Span,
386}
387
388/// Identifier
389#[derive(Debug, Clone, PartialEq, Eq, Hash)]
390pub struct Ident {
391    pub symbol: Symbol,
392    pub span: Span,
393}
394
395impl Ident {
396    pub const fn new(symbol: Symbol, span: Span) -> Self {
397        Self { symbol, span }
398    }
399
400    pub fn as_str(&self) -> String {
401        self.symbol.as_str()
402    }
403}
404
405impl fmt::Display for Ident {
406    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
407        write!(f, "{}", self.symbol)
408    }
409}
410
411/// Statement
412#[derive(Debug, Clone, PartialEq)]
413pub enum Statement {
414    /// Let binding: let x = 5;
415    Let {
416        pattern: Pattern,
417        ty: Option<Type>,
418        init: Option<Expr>,
419        span: Span,
420    },
421    /// Expression statement: `foo()`;
422    Expr(Expr),
423    /// Item statement (function, struct, etc.)
424    Item(Box<Item>),
425}
426
427/// Pattern (for destructuring)
428#[derive(Debug, Clone, PartialEq)]
429pub enum Pattern {
430    /// Identifier pattern: x
431    Ident { name: Ident, mutable: bool },
432    /// Wildcard pattern: _
433    Wildcard { span: Span },
434    /// Tuple pattern: (x, y, z)
435    Tuple { patterns: Vec<Self>, span: Span },
436    /// Struct pattern: Point { x, y }
437    Struct {
438        path: Path,
439        fields: Vec<FieldPattern>,
440        span: Span,
441    },
442    /// Path pattern (unit variant): None, `Option::None`
443    Path { path: Path },
444    /// Tuple variant pattern: Some(value), `Result::Ok(x)`
445    TupleVariant {
446        path: Path,
447        patterns: Vec<Self>,
448        span: Span,
449    },
450    /// Literal pattern: 'a', "hello", 42, true
451    Literal(Literal),
452    /// Or-pattern: `1 | 2 | 3`
453    Or {
454        patterns: Vec<Self>,
455        span: Span,
456    },
457    /// Range pattern: `1..=10`, `'a'..='z'`
458    Range {
459        lo: Literal,
460        hi: Literal,
461        inclusive: bool,
462        span: Span,
463    },
464}
465
466#[derive(Debug, Clone, PartialEq)]
467pub struct FieldPattern {
468    pub name: Ident,
469    pub pattern: Pattern,
470    pub span: Span,
471}
472
473/// Block expression
474#[derive(Debug, Clone, PartialEq)]
475pub struct Block {
476    pub statements: Vec<Statement>,
477    pub expr: Option<Box<Expr>>, // Trailing expression (implicit return)
478    pub span: Span,
479}
480
481/// Expression
482#[derive(Debug, Clone, PartialEq)]
483pub enum Expr {
484    /// Literal: 42, "hello", true
485    Literal(Literal),
486
487    /// Identifier: foo
488    Ident(Ident),
489
490    /// Path: `std::io::Error`, `Option::None`
491    Path(Path),
492
493    /// Macro invocation: vec![1, 2, 3], println!("hi")
494    Macro {
495        name: Path,
496        args: Vec<Self>,
497        span: Span,
498    },
499
500    /// Binary operation: x + y
501    Binary {
502        op: BinOp,
503        left: Box<Self>,
504        right: Box<Self>,
505        span: Span,
506    },
507
508    /// Unary operation: -x, !x
509    Unary {
510        op: UnOp,
511        expr: Box<Self>,
512        span: Span,
513    },
514
515    /// Function call: foo(x, y)
516    Call {
517        func: Box<Self>,
518        args: Vec<Self>,
519        span: Span,
520    },
521
522    /// Method call: x.foo(y)
523    MethodCall {
524        receiver: Box<Self>,
525        method: Ident,
526        args: Vec<Self>,
527        span: Span,
528    },
529
530    /// Field access: x.y
531    Field {
532        expr: Box<Self>,
533        field: Ident,
534        span: Span,
535    },
536
537    /// Index: x[i]
538    Index {
539        expr: Box<Self>,
540        index: Box<Self>,
541        span: Span,
542    },
543
544    /// Multi-dimensional index: arr[i, j], arr[1..5, ::2], arr[.., 3]
545    MultiDimIndex {
546        expr: Box<Self>,
547        indices: Vec<IndexComponent>,
548        span: Span,
549    },
550
551    /// Range: 0..10, 0..=10
552    Range {
553        start: Option<Box<Self>>,
554        end: Option<Box<Self>>,
555        inclusive: bool,
556        span: Span,
557    },
558
559    /// Tuple: (x, y, z)
560    Tuple { elements: Vec<Self>, span: Span },
561
562    /// Array: [1, 2, 3]
563    Array { elements: Vec<Self>, span: Span },
564
565    /// Array repeat: [0; 10]
566    ArrayRepeat {
567        element: Box<Self>,
568        count: Box<Self>,
569        span: Span,
570    },
571
572    /// Struct literal: Point { x: 1, y: 2 }
573    Struct {
574        path: Path,
575        fields: Vec<FieldInit>,
576        span: Span,
577    },
578
579    /// Block: { stmt1; stmt2; expr }
580    Block(Block),
581
582    /// If expression: if cond { a } else { b }
583    If {
584        condition: Box<Self>,
585        then_block: Block,
586        else_block: Option<Box<Self>>, // Can be another if or a block
587        span: Span,
588    },
589
590    /// Match expression: match x { ... }
591    Match {
592        expr: Box<Self>,
593        arms: Vec<MatchArm>,
594        span: Span,
595    },
596
597    /// Loop: loop { ... }
598    Loop { body: Block, span: Span },
599
600    /// While: while cond { ... }
601    While {
602        condition: Box<Self>,
603        body: Block,
604        span: Span,
605    },
606
607    /// For: for x in iter { ... }
608    For {
609        pattern: Pattern,
610        iter: Box<Self>,
611        body: Block,
612        span: Span,
613    },
614
615    /// Return: return x
616    Return {
617        value: Option<Box<Self>>,
618        span: Span,
619    },
620
621    /// Break: break
622    Break {
623        value: Option<Box<Self>>,
624        span: Span,
625    },
626
627    /// Continue: continue
628    Continue { span: Span },
629
630    /// Assignment: x = y
631    Assign {
632        target: Box<Self>,
633        value: Box<Self>,
634        span: Span,
635    },
636
637    /// Compound assignment: x += y
638    AssignOp {
639        op: BinOp,
640        target: Box<Self>,
641        value: Box<Self>,
642        span: Span,
643    },
644
645    /// Reference: &x, &mut x
646    Reference {
647        mutable: bool,
648        expr: Box<Self>,
649        span: Span,
650    },
651
652    /// Dereference: *x
653    Dereference { expr: Box<Self>, span: Span },
654
655    /// Type cast: x as T
656    Cast {
657        expr: Box<Self>,
658        ty: Type,
659        span: Span,
660    },
661
662    // ===== Concurrency Expressions =====
663    /// Spawn a task: spawn expr
664    Spawn { expr: Box<Self>, span: Span },
665
666    /// Spawn a task with deadline: `spawn_with_deadline(duration)` { ... }
667    SpawnWithDeadline {
668        deadline: Box<Self>,
669        body: Block,
670        span: Span,
671    },
672
673    /// Task group (structured concurrency): `task_group` { spawn...; spawn...; }
674    TaskGroup { body: Block, span: Span },
675
676    /// Task group collecting all results: `task_group_all` { spawn...; }
677    TaskGroupAll { body: Block, span: Span },
678
679    /// Channel creation: `channel::`<T>(capacity)
680    Channel {
681        element_type: Option<Type>,
682        capacity: Box<Self>,
683        span: Span,
684    },
685
686    /// Select expression: select { `rx.recv()` => |val| ..., timeout(dur) => ... }
687    Select { arms: Vec<SelectArm>, span: Span },
688
689    /// Biased select: `select_biased` { ... } (first-listed priority)
690    SelectBiased { arms: Vec<SelectArm>, span: Span },
691
692    /// GPU execution block: gpu { ... }
693    Gpu { body: Block, span: Span },
694
695    /// Distributed execution block: distributed { spawn@node ... }
696    Distributed { body: Block, span: Span },
697
698    /// Remote spawn: spawn@node expr
699    SpawnRemote {
700        node: Box<Self>,
701        expr: Box<Self>,
702        span: Span,
703    },
704
705    /// Check if current task is cancelled
706    Cancelled { span: Span },
707
708    /// Energy budget block: energy_budget(max_joules = 0.001) { ... }
709    EnergyBudgetBlock {
710        budget: EnergyBudget,
711        body: Block,
712        span: Span,
713    },
714
715    /// Thermal adaptation: thermal_adapt { Cool => { ... }, Hot => { ... } }
716    ThermalAdapt { arms: Vec<ThermalArm>, span: Span },
717
718    /// Await expression: expr.await
719    Await { expr: Box<Self>, span: Span },
720
721    /// Try operator: expr?
722    Try { expr: Box<Self>, span: Span },
723
724    /// Closure: |x, y| x + y, |x: i32| -> i32 { x * 2 }, move |x| x
725    Closure {
726        params: Vec<ClosureParam>,
727        return_type: Option<Type>,
728        body: Box<Self>,
729        is_move: bool,
730        span: Span,
731    },
732
733    /// Comptime block: `comptime { ... }` — evaluated at compile time
734    Comptime { body: Block, span: Span },
735
736    /// Perform an algebraic effect: `perform IO::read()`
737    Perform {
738        effect: Path,
739        args: Vec<Self>,
740        span: Span,
741    },
742
743    /// Handle algebraic effects: `handle { body } with { IO => |op| match op { ... } }`
744    Handle {
745        body: Block,
746        handlers: Vec<EffectHandler>,
747        span: Span,
748    },
749
750    /// Parallel for loop: `parallel for x in iter { ... }`
751    Parallel {
752        pattern: Pattern,
753        iter: Box<Self>,
754        body: Block,
755        span: Span,
756    },
757
758    /// Result builder expression: `build TypeName { stmt1; stmt2; ... }`
759    ///
760    /// Inspired by Swift's @resultBuilder. The builder type provides
761    /// `build_block`, `build_expression`, `build_optional` methods that
762    /// transform the block's statements into a single value.
763    Build {
764        builder_ty: Type,
765        body: Block,
766        span: Span,
767    },
768}
769
770/// Match arm
771#[derive(Debug, Clone, PartialEq)]
772pub struct MatchArm {
773    pub pattern: Pattern,
774    pub guard: Option<Expr>, // if condition
775    pub body: Expr,
776    pub span: Span,
777}
778
779/// Select arm for channel selection
780#[derive(Debug, Clone, PartialEq)]
781pub struct SelectArm {
782    /// The channel operation: `rx.recv()` or tx.send(val) or timeout(duration)
783    pub operation: SelectOperation,
784    /// Optional binding for received value: |val| or |_|
785    pub binding: Option<Pattern>,
786    /// Body to execute when this arm is selected
787    pub body: Expr,
788    pub span: Span,
789}
790
791/// Channel operation in a select arm
792#[derive(Debug, Clone, PartialEq)]
793pub enum SelectOperation {
794    /// Receive from channel: `rx.recv()`
795    Recv { channel: Expr, span: Span },
796    /// Send to channel: tx.send(value)
797    Send {
798        channel: Expr,
799        value: Expr,
800        span: Span,
801    },
802    /// Timeout: timeout(duration)
803    Timeout { duration: Expr, span: Span },
804}
805
806/// Thermal adaptation arm
807#[derive(Debug, Clone, PartialEq)]
808pub struct ThermalArm {
809    /// Thermal state: Cool, Nominal, Elevated, Hot, Critical
810    pub state: Ident,
811    /// Code to execute at this thermal level
812    pub body: Expr,
813    pub span: Span,
814}
815
816/// An effect handler clause in a `handle ... with { ... }` expression
817#[derive(Debug, Clone, PartialEq)]
818pub struct EffectHandler {
819    /// The effect being handled (e.g., `IO`)
820    pub effect: Path,
821    /// Handler function/closure for each operation
822    pub handler: Expr,
823    pub span: Span,
824}
825
826/// Struct field initialization
827#[derive(Debug, Clone, PartialEq)]
828pub struct FieldInit {
829    pub name: Ident,
830    pub value: Expr,
831    pub span: Span,
832}
833
834/// Literal values
835#[derive(Debug, Clone, PartialEq)]
836pub enum Literal {
837    Int(u64, Span),
838    Float(f64, Span),
839    String(String, Span),
840    Char(char, Span),
841    Bool(bool, Span),
842}
843
844impl Literal {
845    pub const fn span(&self) -> Span {
846        match self {
847            Self::Int(_, span) => *span,
848            Self::Float(_, span) => *span,
849            Self::String(_, span) => *span,
850            Self::Char(_, span) => *span,
851            Self::Bool(_, span) => *span,
852        }
853    }
854}
855
856/// Binary operators
857#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
858pub enum BinOp {
859    // Arithmetic
860    Add, // +
861    Sub, // -
862    Mul, // *
863    Div, // /
864    Rem, // %
865
866    // Bitwise
867    BitAnd, // &
868    BitOr,  // |
869    BitXor, // ^
870    Shl,    // <<
871    Shr,    // >>
872
873    // Logical
874    And, // &&
875    Or,  // ||
876
877    // Comparison
878    Eq, // ==
879    Ne, // !=
880    Lt, // <
881    Le, // <=
882    Gt, // >
883    Ge, // >=
884}
885
886/// Unary operators
887#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
888pub enum UnOp {
889    Neg, // -
890    Not, // !
891}
892
893/// A single component of a multi-dimensional index expression.
894///
895/// Supports single-element indexing, range slicing, and full-dimension selection:
896/// - `arr[i, j]`       → Single(i), Single(j)
897/// - `arr[1..5, ::2]`  → Range { start: 1, end: 5 }, Range { step: 2 }
898/// - `arr[.., 3]`      → Full, Single(3)
899#[derive(Debug, Clone, PartialEq)]
900pub enum IndexComponent {
901    /// Single index: `arr[i]`
902    Single(Expr),
903    /// Range with optional start, end, step: `arr[1..5]`, `arr[::2]`, `arr[1..5::2]`
904    Range {
905        start: Option<Box<Expr>>,
906        end: Option<Box<Expr>>,
907        step: Option<Box<Expr>>,
908        inclusive: bool,
909    },
910    /// Full dimension: `arr[..]`
911    Full,
912}
913
914impl fmt::Display for BinOp {
915    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
916        let s = match self {
917            Self::Add => "+",
918            Self::Sub => "-",
919            Self::Mul => "*",
920            Self::Div => "/",
921            Self::Rem => "%",
922            Self::BitAnd => "&",
923            Self::BitOr => "|",
924            Self::BitXor => "^",
925            Self::Shl => "<<",
926            Self::Shr => ">>",
927            Self::And => "&&",
928            Self::Or => "||",
929            Self::Eq => "==",
930            Self::Ne => "!=",
931            Self::Lt => "<",
932            Self::Le => "<=",
933            Self::Gt => ">",
934            Self::Ge => ">=",
935        };
936        write!(f, "{s}")
937    }
938}
939
940impl fmt::Display for UnOp {
941    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
942        let s = match self {
943            Self::Neg => "-",
944            Self::Not => "!",
945        };
946        write!(f, "{s}")
947    }
948}
949
950// Helper methods for getting spans
951impl Expr {
952    pub const fn span(&self) -> Span {
953        match self {
954            Self::Literal(lit) => lit.span(),
955            Self::Ident(ident) => ident.span,
956            Self::Path(path) => path.span,
957            Self::Macro { span, .. } => *span,
958            Self::Binary { span, .. } => *span,
959            Self::Unary { span, .. } => *span,
960            Self::Call { span, .. } => *span,
961            Self::MethodCall { span, .. } => *span,
962            Self::Field { span, .. } => *span,
963            Self::Index { span, .. } => *span,
964            Self::MultiDimIndex { span, .. } => *span,
965            Self::Range { span, .. } => *span,
966            Self::Tuple { span, .. } => *span,
967            Self::Array { span, .. } => *span,
968            Self::ArrayRepeat { span, .. } => *span,
969            Self::Struct { span, .. } => *span,
970            Self::Block(block) => block.span,
971            Self::If { span, .. } => *span,
972            Self::Match { span, .. } => *span,
973            Self::Loop { span, .. } => *span,
974            Self::While { span, .. } => *span,
975            Self::For { span, .. } => *span,
976            Self::Return { span, .. } => *span,
977            Self::Break { span, .. } => *span,
978            Self::Continue { span } => *span,
979            Self::Assign { span, .. } => *span,
980            Self::AssignOp { span, .. } => *span,
981            Self::Reference { span, .. } => *span,
982            Self::Dereference { span, .. } => *span,
983            Self::Cast { span, .. } => *span,
984            // Concurrency expressions
985            Self::Spawn { span, .. } => *span,
986            Self::SpawnWithDeadline { span, .. } => *span,
987            Self::TaskGroup { span, .. } => *span,
988            Self::TaskGroupAll { span, .. } => *span,
989            Self::Channel { span, .. } => *span,
990            Self::Select { span, .. } => *span,
991            Self::SelectBiased { span, .. } => *span,
992            Self::Gpu { span, .. } => *span,
993            Self::Distributed { span, .. } => *span,
994            Self::SpawnRemote { span, .. } => *span,
995            Self::Cancelled { span } => *span,
996            Self::EnergyBudgetBlock { span, .. } => *span,
997            Self::ThermalAdapt { span, .. } => *span,
998            Self::Await { span, .. } => *span,
999            Self::Try { span, .. } => *span,
1000            Self::Closure { span, .. } => *span,
1001            Self::Comptime { span, .. } => *span,
1002            Self::Perform { span, .. } => *span,
1003            Self::Handle { span, .. } => *span,
1004            Self::Parallel { span, .. } => *span,
1005            Self::Build { span, .. } => *span,
1006        }
1007    }
1008}
1009
1010impl Type {
1011    pub fn span(&self) -> Span {
1012        match self {
1013            Self::Path { path, generics } => {
1014                if let Some(args) = generics {
1015                    path.span.to(args.span)
1016                } else {
1017                    path.span
1018                }
1019            }
1020            Self::Reference { inner, .. } => inner.span(),
1021            Self::Array { element, size } => element.span().to(size.span()),
1022            Self::Slice { element } => element.span(),
1023            Self::Tuple(types) => {
1024                if let (Some(first), Some(last)) = (types.first(), types.last()) {
1025                    first.span().to(last.span())
1026                } else {
1027                    Span::new(0, 0, 0)
1028                }
1029            }
1030            Self::Function {
1031                params,
1032                return_type,
1033            } => {
1034                if let Some(first) = params.first() {
1035                    first.span().to(return_type.span())
1036                } else {
1037                    return_type.span()
1038                }
1039            }
1040            Self::Unit => Span::new(0, 0, 0),
1041            Self::Never => Span::new(0, 0, 0),
1042            Self::SelfType => Span::new(0, 0, 0),
1043            Self::TraitObject { span, .. } => *span,
1044            Self::Union { span, .. } => *span,
1045        }
1046    }
1047}
1048
1049impl Pattern {
1050    pub const fn span(&self) -> Span {
1051        match self {
1052            Self::Ident { name, .. } => name.span,
1053            Self::Wildcard { span } => *span,
1054            Self::Tuple { span, .. } => *span,
1055            Self::Struct { span, .. } => *span,
1056            Self::Path { path } => path.span,
1057            Self::TupleVariant { span, .. } => *span,
1058            Self::Literal(lit) => lit.span(),
1059            Self::Or { span, .. } => *span,
1060            Self::Range { span, .. } => *span,
1061        }
1062    }
1063}
1064
1065#[cfg(test)]
1066mod tests {
1067    use super::*;
1068    use joule_common::Symbol;
1069
1070    #[test]
1071    fn test_ident_creation() {
1072        let symbol = Symbol::from_u32(0);
1073        let span = Span::new(0, 3, 0);
1074        let ident = Ident::new(symbol, span);
1075
1076        assert_eq!(ident.span, span);
1077    }
1078
1079    #[test]
1080    fn test_literal_span() {
1081        let span = Span::new(0, 2, 0);
1082        let lit = Literal::Int(42, span);
1083        assert_eq!(lit.span(), span);
1084    }
1085
1086    #[test]
1087    fn test_binop_display() {
1088        assert_eq!(format!("{}", BinOp::Add), "+");
1089        assert_eq!(format!("{}", BinOp::Eq), "==");
1090        assert_eq!(format!("{}", BinOp::And), "&&");
1091    }
1092}