Skip to main content

sage_parser/
ast.rs

1//! Abstract Syntax Tree definitions for the Sage language.
2//!
3//! This module defines all AST node types that the parser produces.
4//! Every node carries a `Span` for error reporting.
5
6use sage_types::{Ident, Span, TypeExpr};
7use std::fmt;
8
9// =============================================================================
10// Program (top-level)
11// =============================================================================
12
13/// A complete Sage program (or module).
14#[derive(Debug, Clone, PartialEq)]
15pub struct Program {
16    /// Module declarations (`mod foo`).
17    pub mod_decls: Vec<ModDecl>,
18    /// Use declarations (`use foo::Bar`).
19    pub use_decls: Vec<UseDecl>,
20    /// Record type declarations.
21    pub records: Vec<RecordDecl>,
22    /// Enum type declarations.
23    pub enums: Vec<EnumDecl>,
24    /// Constant declarations.
25    pub consts: Vec<ConstDecl>,
26    /// Tool declarations (RFC-0011).
27    pub tools: Vec<ToolDecl>,
28    /// Agent declarations.
29    pub agents: Vec<AgentDecl>,
30    /// Function declarations.
31    pub functions: Vec<FnDecl>,
32    /// The entry-point agent (from `run AgentName`).
33    /// None for library modules that don't have an entry point.
34    pub run_agent: Option<Ident>,
35    /// Span covering the entire program.
36    pub span: Span,
37}
38
39// =============================================================================
40// Module declarations
41// =============================================================================
42
43/// A module declaration: `mod name` or `pub mod name`
44#[derive(Debug, Clone, PartialEq)]
45pub struct ModDecl {
46    /// Whether this module is public.
47    pub is_pub: bool,
48    /// The module name.
49    pub name: Ident,
50    /// Span covering the declaration.
51    pub span: Span,
52}
53
54/// A use declaration: `use path::to::Item`
55#[derive(Debug, Clone, PartialEq)]
56pub struct UseDecl {
57    /// Whether this is a public re-export (`pub use`).
58    pub is_pub: bool,
59    /// The path segments (e.g., `["agents", "Researcher"]`).
60    pub path: Vec<Ident>,
61    /// The kind of import.
62    pub kind: UseKind,
63    /// Span covering the declaration.
64    pub span: Span,
65}
66
67/// The kind of use declaration.
68#[derive(Debug, Clone, PartialEq)]
69pub enum UseKind {
70    /// Simple import: `use a::B` or `use a::B as C`
71    /// The Option is the alias (e.g., `C` in `use a::B as C`).
72    Simple(Option<Ident>),
73    /// Glob import: `use a::*`
74    Glob,
75    /// Group import: `use a::{B, C as D}`
76    /// Each tuple is (name, optional alias).
77    Group(Vec<(Ident, Option<Ident>)>),
78}
79
80// =============================================================================
81// Type declarations (records, enums)
82// =============================================================================
83
84/// A record declaration: `record Point { x: Int, y: Int }`
85#[derive(Debug, Clone, PartialEq)]
86pub struct RecordDecl {
87    /// Whether this record is public.
88    pub is_pub: bool,
89    /// The record's name.
90    pub name: Ident,
91    /// The record's fields.
92    pub fields: Vec<RecordField>,
93    /// Span covering the declaration.
94    pub span: Span,
95}
96
97/// A field in a record declaration: `name: Type`
98#[derive(Debug, Clone, PartialEq)]
99pub struct RecordField {
100    /// The field's name.
101    pub name: Ident,
102    /// The field's type.
103    pub ty: TypeExpr,
104    /// Span covering the field.
105    pub span: Span,
106}
107
108/// An enum variant with optional payload: `Ok(T)` or `None`
109#[derive(Debug, Clone, PartialEq)]
110pub struct EnumVariant {
111    /// The variant's name.
112    pub name: Ident,
113    /// Optional payload type (e.g., `T` in `Ok(T)`).
114    pub payload: Option<TypeExpr>,
115    /// Span covering the variant.
116    pub span: Span,
117}
118
119/// An enum declaration: `enum Status { Active, Pending, Done }` or `enum Result { Ok(T), Err(E) }`
120#[derive(Debug, Clone, PartialEq)]
121pub struct EnumDecl {
122    /// Whether this enum is public.
123    pub is_pub: bool,
124    /// The enum's name.
125    pub name: Ident,
126    /// The enum's variants.
127    pub variants: Vec<EnumVariant>,
128    /// Span covering the declaration.
129    pub span: Span,
130}
131
132/// A const declaration: `const MAX_RETRIES: Int = 3`
133#[derive(Debug, Clone, PartialEq)]
134pub struct ConstDecl {
135    /// Whether this const is public.
136    pub is_pub: bool,
137    /// The constant's name.
138    pub name: Ident,
139    /// The constant's type.
140    pub ty: TypeExpr,
141    /// The constant's value.
142    pub value: Expr,
143    /// Span covering the declaration.
144    pub span: Span,
145}
146
147// =============================================================================
148// Tool declarations (RFC-0011)
149// =============================================================================
150
151/// A tool declaration: `tool Http { fn get(url: String) -> Result<String, String> }`
152#[derive(Debug, Clone, PartialEq)]
153pub struct ToolDecl {
154    /// Whether this tool is public.
155    pub is_pub: bool,
156    /// The tool's name.
157    pub name: Ident,
158    /// The tool's function signatures.
159    pub functions: Vec<ToolFnDecl>,
160    /// Span covering the declaration.
161    pub span: Span,
162}
163
164/// A function signature in a tool declaration (no body).
165#[derive(Debug, Clone, PartialEq)]
166pub struct ToolFnDecl {
167    /// The function's name.
168    pub name: Ident,
169    /// The function's parameters.
170    pub params: Vec<Param>,
171    /// The return type.
172    pub return_ty: TypeExpr,
173    /// Span covering the declaration.
174    pub span: Span,
175}
176
177// =============================================================================
178// Agent declarations
179// =============================================================================
180
181/// An agent declaration: `agent Name { ... }` or `pub agent Name receives MsgType { ... }`
182#[derive(Debug, Clone, PartialEq)]
183pub struct AgentDecl {
184    /// Whether this agent is public (can be imported by other modules).
185    pub is_pub: bool,
186    /// The agent's name.
187    pub name: Ident,
188    /// The message type this agent receives (for message passing).
189    pub receives: Option<TypeExpr>,
190    /// Tools this agent uses (RFC-0011): `use Http, Fs`
191    pub tool_uses: Vec<Ident>,
192    /// Belief declarations (agent state).
193    pub beliefs: Vec<BeliefDecl>,
194    /// Event handlers.
195    pub handlers: Vec<HandlerDecl>,
196    /// Span covering the entire declaration.
197    pub span: Span,
198}
199
200/// A belief declaration: `belief name: Type`
201#[derive(Debug, Clone, PartialEq)]
202pub struct BeliefDecl {
203    /// The belief's name.
204    pub name: Ident,
205    /// The belief's type.
206    pub ty: TypeExpr,
207    /// Span covering the declaration.
208    pub span: Span,
209}
210
211/// An event handler: `on start { ... }`, `on message(x: T) { ... }`, `on stop { ... }`
212#[derive(Debug, Clone, PartialEq)]
213pub struct HandlerDecl {
214    /// The event kind this handler responds to.
215    pub event: EventKind,
216    /// The handler body.
217    pub body: Block,
218    /// Span covering the entire handler.
219    pub span: Span,
220}
221
222/// The kind of event a handler responds to.
223#[derive(Debug, Clone, PartialEq)]
224pub enum EventKind {
225    /// `on start` — runs when the agent is spawned.
226    Start,
227    /// `on message(param: Type)` — runs when a message is received.
228    Message {
229        /// The parameter name for the incoming message.
230        param_name: Ident,
231        /// The type of the message.
232        param_ty: TypeExpr,
233    },
234    /// `on stop` — runs during graceful shutdown.
235    Stop,
236    /// `on error(e)` — runs when an unhandled error occurs in the agent.
237    Error {
238        /// The parameter name for the error.
239        param_name: Ident,
240    },
241}
242
243impl fmt::Display for EventKind {
244    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
245        match self {
246            EventKind::Start => write!(f, "start"),
247            EventKind::Message {
248                param_name,
249                param_ty,
250            } => {
251                write!(f, "message({param_name}: {param_ty})")
252            }
253            EventKind::Stop => write!(f, "stop"),
254            EventKind::Error { param_name } => {
255                write!(f, "error({param_name})")
256            }
257        }
258    }
259}
260
261// =============================================================================
262// Function declarations
263// =============================================================================
264
265/// A function declaration: `fn name(params) -> ReturnType { ... }` or `fn name(params) -> ReturnType fails { ... }`
266#[derive(Debug, Clone, PartialEq)]
267pub struct FnDecl {
268    /// Whether this function is public (can be imported by other modules).
269    pub is_pub: bool,
270    /// The function's name.
271    pub name: Ident,
272    /// The function's parameters.
273    pub params: Vec<Param>,
274    /// The return type.
275    pub return_ty: TypeExpr,
276    /// Whether this function can fail (marked with `fails`).
277    pub is_fallible: bool,
278    /// The function body.
279    pub body: Block,
280    /// Span covering the entire declaration.
281    pub span: Span,
282}
283
284/// A function parameter: `name: Type`
285#[derive(Debug, Clone, PartialEq)]
286pub struct Param {
287    /// The parameter name.
288    pub name: Ident,
289    /// The parameter type.
290    pub ty: TypeExpr,
291    /// Span covering the parameter.
292    pub span: Span,
293}
294
295/// A closure parameter: `name` or `name: Type`
296#[derive(Debug, Clone, PartialEq)]
297pub struct ClosureParam {
298    /// The parameter name.
299    pub name: Ident,
300    /// Optional type annotation (can be inferred).
301    pub ty: Option<TypeExpr>,
302    /// Span covering the parameter.
303    pub span: Span,
304}
305
306// =============================================================================
307// Blocks and statements
308// =============================================================================
309
310/// A block of statements: `{ stmt* }`
311#[derive(Debug, Clone, PartialEq)]
312pub struct Block {
313    /// The statements in this block.
314    pub stmts: Vec<Stmt>,
315    /// Span covering the entire block (including braces).
316    pub span: Span,
317}
318
319/// A statement.
320#[derive(Debug, Clone, PartialEq)]
321pub enum Stmt {
322    /// Variable binding: `let name: Type = expr` or `let name = expr`
323    Let {
324        /// The variable name.
325        name: Ident,
326        /// Optional type annotation.
327        ty: Option<TypeExpr>,
328        /// The initial value.
329        value: Expr,
330        /// Span covering the statement.
331        span: Span,
332    },
333
334    /// Assignment: `name = expr`
335    Assign {
336        /// The variable being assigned to.
337        name: Ident,
338        /// The new value.
339        value: Expr,
340        /// Span covering the statement.
341        span: Span,
342    },
343
344    /// Return statement: `return expr?`
345    Return {
346        /// The optional return value.
347        value: Option<Expr>,
348        /// Span covering the statement.
349        span: Span,
350    },
351
352    /// If statement: `if cond { ... } else { ... }`
353    If {
354        /// The condition (must be Bool).
355        condition: Expr,
356        /// The then branch.
357        then_block: Block,
358        /// The optional else branch (can be another If for else-if chains).
359        else_block: Option<ElseBranch>,
360        /// Span covering the statement.
361        span: Span,
362    },
363
364    /// For loop: `for x in iter { ... }` or `for (k, v) in map { ... }`
365    For {
366        /// The loop pattern (can be a simple binding or tuple destructuring).
367        pattern: Pattern,
368        /// The iterable expression (List<T> or Map<K, V>).
369        iter: Expr,
370        /// The loop body.
371        body: Block,
372        /// Span covering the statement.
373        span: Span,
374    },
375
376    /// While loop: `while cond { ... }`
377    While {
378        /// The condition (must be Bool).
379        condition: Expr,
380        /// The loop body.
381        body: Block,
382        /// Span covering the statement.
383        span: Span,
384    },
385
386    /// Infinite loop: `loop { ... }`
387    Loop {
388        /// The loop body.
389        body: Block,
390        /// Span covering the statement.
391        span: Span,
392    },
393
394    /// Break statement: `break`
395    Break {
396        /// Span covering the statement.
397        span: Span,
398    },
399
400    /// Expression statement: `expr`
401    Expr {
402        /// The expression.
403        expr: Expr,
404        /// Span covering the statement.
405        span: Span,
406    },
407
408    /// Tuple destructuring: `let (a, b) = expr;`
409    LetTuple {
410        /// The variable names.
411        names: Vec<Ident>,
412        /// Optional type annotation.
413        ty: Option<TypeExpr>,
414        /// The value expression.
415        value: Expr,
416        /// Span covering the statement.
417        span: Span,
418    },
419}
420
421impl Stmt {
422    /// Get the span of this statement.
423    #[must_use]
424    pub fn span(&self) -> &Span {
425        match self {
426            Stmt::Let { span, .. }
427            | Stmt::Assign { span, .. }
428            | Stmt::Return { span, .. }
429            | Stmt::If { span, .. }
430            | Stmt::For { span, .. }
431            | Stmt::While { span, .. }
432            | Stmt::Loop { span, .. }
433            | Stmt::Break { span, .. }
434            | Stmt::Expr { span, .. }
435            | Stmt::LetTuple { span, .. } => span,
436        }
437    }
438}
439
440/// The else branch of an if statement.
441#[derive(Debug, Clone, PartialEq)]
442pub enum ElseBranch {
443    /// `else { ... }`
444    Block(Block),
445    /// `else if ...` (chained if)
446    ElseIf(Box<Stmt>),
447}
448
449// =============================================================================
450// Expressions
451// =============================================================================
452
453/// An expression.
454#[derive(Debug, Clone, PartialEq)]
455pub enum Expr {
456    /// LLM inference: `infer("template")` or `infer("template" -> Type)`
457    Infer {
458        /// The prompt template (may contain `{ident}` interpolations).
459        template: StringTemplate,
460        /// Optional result type annotation.
461        result_ty: Option<TypeExpr>,
462        /// Span covering the expression.
463        span: Span,
464    },
465
466    /// Agent spawning: `spawn AgentName { field: value, ... }`
467    Spawn {
468        /// The agent type to spawn.
469        agent: Ident,
470        /// Initial belief values.
471        fields: Vec<FieldInit>,
472        /// Span covering the expression.
473        span: Span,
474    },
475
476    /// Await: `await expr`
477    Await {
478        /// The agent handle to await.
479        handle: Box<Expr>,
480        /// Span covering the expression.
481        span: Span,
482    },
483
484    /// Send message: `send(handle, message)`
485    Send {
486        /// The agent handle to send to.
487        handle: Box<Expr>,
488        /// The message to send.
489        message: Box<Expr>,
490        /// Span covering the expression.
491        span: Span,
492    },
493
494    /// Emit value: `emit(value)`
495    Emit {
496        /// The value to emit to the awaiter.
497        value: Box<Expr>,
498        /// Span covering the expression.
499        span: Span,
500    },
501
502    /// Function call: `name(args)`
503    Call {
504        /// The function name.
505        name: Ident,
506        /// The arguments.
507        args: Vec<Expr>,
508        /// Span covering the expression.
509        span: Span,
510    },
511
512    /// Method call on self: `self.method(args)`
513    SelfMethodCall {
514        /// The method name.
515        method: Ident,
516        /// The arguments.
517        args: Vec<Expr>,
518        /// Span covering the expression.
519        span: Span,
520    },
521
522    /// Self field access: `self.field`
523    SelfField {
524        /// The field (belief) name.
525        field: Ident,
526        /// Span covering the expression.
527        span: Span,
528    },
529
530    /// Binary operation: `left op right`
531    Binary {
532        /// The operator.
533        op: BinOp,
534        /// The left operand.
535        left: Box<Expr>,
536        /// The right operand.
537        right: Box<Expr>,
538        /// Span covering the expression.
539        span: Span,
540    },
541
542    /// Unary operation: `op operand`
543    Unary {
544        /// The operator.
545        op: UnaryOp,
546        /// The operand.
547        operand: Box<Expr>,
548        /// Span covering the expression.
549        span: Span,
550    },
551
552    /// List literal: `[a, b, c]`
553    List {
554        /// The list elements.
555        elements: Vec<Expr>,
556        /// Span covering the expression.
557        span: Span,
558    },
559
560    /// Literal value.
561    Literal {
562        /// The literal value.
563        value: Literal,
564        /// Span covering the expression.
565        span: Span,
566    },
567
568    /// Variable reference.
569    Var {
570        /// The variable name.
571        name: Ident,
572        /// Span covering the expression.
573        span: Span,
574    },
575
576    /// Parenthesized expression: `(expr)`
577    Paren {
578        /// The inner expression.
579        inner: Box<Expr>,
580        /// Span covering the expression (including parens).
581        span: Span,
582    },
583
584    /// Interpolated string: `"Hello, {name}!"`
585    StringInterp {
586        /// The string template with interpolations.
587        template: StringTemplate,
588        /// Span covering the expression.
589        span: Span,
590    },
591
592    /// Match expression: `match expr { Pattern => expr, ... }`
593    Match {
594        /// The scrutinee expression.
595        scrutinee: Box<Expr>,
596        /// The match arms.
597        arms: Vec<MatchArm>,
598        /// Span covering the expression.
599        span: Span,
600    },
601
602    /// Record construction: `Point { x: 1, y: 2 }`
603    RecordConstruct {
604        /// The record type name.
605        name: Ident,
606        /// Field initializations.
607        fields: Vec<FieldInit>,
608        /// Span covering the expression.
609        span: Span,
610    },
611
612    /// Field access: `record.field`
613    FieldAccess {
614        /// The record expression.
615        object: Box<Expr>,
616        /// The field name.
617        field: Ident,
618        /// Span covering the expression.
619        span: Span,
620    },
621
622    /// Receive message from mailbox: `receive()`
623    Receive {
624        /// Span covering the expression.
625        span: Span,
626    },
627
628    /// Try expression: `try expr` — propagates failure upward.
629    Try {
630        /// The expression that may fail.
631        expr: Box<Expr>,
632        /// Span covering the expression.
633        span: Span,
634    },
635
636    /// Catch expression: `expr catch { recovery }` or `expr catch(e) { recovery }`.
637    Catch {
638        /// The expression that may fail.
639        expr: Box<Expr>,
640        /// The optional error binding (e.g., `e` in `catch(e)`).
641        error_bind: Option<Ident>,
642        /// The recovery expression.
643        recovery: Box<Expr>,
644        /// Span covering the expression.
645        span: Span,
646    },
647
648    /// Closure expression: `|params| body`
649    Closure {
650        /// The closure parameters.
651        params: Vec<ClosureParam>,
652        /// The closure body (single expression).
653        body: Box<Expr>,
654        /// Span covering the expression.
655        span: Span,
656    },
657
658    /// Tuple literal: `(a, b, c)`
659    Tuple {
660        /// The tuple elements (at least 2).
661        elements: Vec<Expr>,
662        /// Span covering the expression.
663        span: Span,
664    },
665
666    /// Tuple index access: `tuple.0`
667    TupleIndex {
668        /// The tuple expression.
669        tuple: Box<Expr>,
670        /// The index (0-based).
671        index: usize,
672        /// Span covering the expression.
673        span: Span,
674    },
675
676    /// Map literal: `{ key: value, ... }` or `{}`
677    Map {
678        /// The map entries.
679        entries: Vec<MapEntry>,
680        /// Span covering the expression.
681        span: Span,
682    },
683
684    /// Enum variant construction: `MyEnum.Variant` or `MyEnum.Variant(payload)`
685    VariantConstruct {
686        /// The enum type name.
687        enum_name: Ident,
688        /// The variant name.
689        variant: Ident,
690        /// The optional payload expression.
691        payload: Option<Box<Expr>>,
692        /// Span covering the expression.
693        span: Span,
694    },
695
696    /// Tool function call (RFC-0011): `Http.get(url)`
697    ToolCall {
698        /// The tool name.
699        tool: Ident,
700        /// The function name.
701        function: Ident,
702        /// The arguments.
703        args: Vec<Expr>,
704        /// Span covering the expression.
705        span: Span,
706    },
707}
708
709/// A map entry: `key: value`
710#[derive(Debug, Clone, PartialEq)]
711pub struct MapEntry {
712    /// The key expression.
713    pub key: Expr,
714    /// The value expression.
715    pub value: Expr,
716    /// Span covering the entry.
717    pub span: Span,
718}
719
720impl Expr {
721    /// Get the span of this expression.
722    #[must_use]
723    pub fn span(&self) -> &Span {
724        match self {
725            Expr::Infer { span, .. }
726            | Expr::Spawn { span, .. }
727            | Expr::Await { span, .. }
728            | Expr::Send { span, .. }
729            | Expr::Emit { span, .. }
730            | Expr::Call { span, .. }
731            | Expr::SelfMethodCall { span, .. }
732            | Expr::SelfField { span, .. }
733            | Expr::Binary { span, .. }
734            | Expr::Unary { span, .. }
735            | Expr::List { span, .. }
736            | Expr::Literal { span, .. }
737            | Expr::Var { span, .. }
738            | Expr::Paren { span, .. }
739            | Expr::StringInterp { span, .. }
740            | Expr::Match { span, .. }
741            | Expr::RecordConstruct { span, .. }
742            | Expr::FieldAccess { span, .. }
743            | Expr::Receive { span, .. }
744            | Expr::Try { span, .. }
745            | Expr::Catch { span, .. }
746            | Expr::Closure { span, .. }
747            | Expr::Tuple { span, .. }
748            | Expr::TupleIndex { span, .. }
749            | Expr::Map { span, .. }
750            | Expr::VariantConstruct { span, .. }
751            | Expr::ToolCall { span, .. } => span,
752        }
753    }
754}
755
756/// A field initialization in a spawn or record construction expression: `field: value`
757#[derive(Debug, Clone, PartialEq)]
758pub struct FieldInit {
759    /// The field name.
760    pub name: Ident,
761    /// The initial value.
762    pub value: Expr,
763    /// Span covering the field initialization.
764    pub span: Span,
765}
766
767/// A match arm: `Pattern => expr`
768#[derive(Debug, Clone, PartialEq)]
769pub struct MatchArm {
770    /// The pattern to match.
771    pub pattern: Pattern,
772    /// The expression to evaluate if the pattern matches.
773    pub body: Expr,
774    /// Span covering the arm.
775    pub span: Span,
776}
777
778/// A pattern in a match expression.
779#[derive(Debug, Clone, PartialEq)]
780pub enum Pattern {
781    /// Wildcard pattern: `_`
782    Wildcard {
783        /// Span covering the pattern.
784        span: Span,
785    },
786    /// Enum variant pattern: `Status::Active`, `Ok(x)`, or just `Active`
787    Variant {
788        /// Optional enum type name (for qualified patterns).
789        enum_name: Option<Ident>,
790        /// The variant name.
791        variant: Ident,
792        /// Optional payload binding pattern (e.g., `x` in `Ok(x)`).
793        payload: Option<Box<Pattern>>,
794        /// Span covering the pattern.
795        span: Span,
796    },
797    /// Literal pattern: `42`, `"hello"`, `true`
798    Literal {
799        /// The literal value.
800        value: Literal,
801        /// Span covering the pattern.
802        span: Span,
803    },
804    /// Binding pattern: `x` (binds the matched value to a variable)
805    Binding {
806        /// The variable name.
807        name: Ident,
808        /// Span covering the pattern.
809        span: Span,
810    },
811    /// Tuple pattern: `(a, b, c)`
812    Tuple {
813        /// The element patterns.
814        elements: Vec<Pattern>,
815        /// Span covering the pattern.
816        span: Span,
817    },
818}
819
820impl Pattern {
821    /// Get the span of this pattern.
822    #[must_use]
823    pub fn span(&self) -> &Span {
824        match self {
825            Pattern::Wildcard { span }
826            | Pattern::Variant { span, .. }
827            | Pattern::Literal { span, .. }
828            | Pattern::Binding { span, .. }
829            | Pattern::Tuple { span, .. } => span,
830        }
831    }
832}
833
834// =============================================================================
835// Operators
836// =============================================================================
837
838/// Binary operators.
839#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
840pub enum BinOp {
841    // Arithmetic
842    /// `+`
843    Add,
844    /// `-`
845    Sub,
846    /// `*`
847    Mul,
848    /// `/`
849    Div,
850
851    // Comparison
852    /// `==`
853    Eq,
854    /// `!=`
855    Ne,
856    /// `<`
857    Lt,
858    /// `>`
859    Gt,
860    /// `<=`
861    Le,
862    /// `>=`
863    Ge,
864
865    // Logical
866    /// `&&`
867    And,
868    /// `||`
869    Or,
870
871    // String
872    /// `++` (string concatenation)
873    Concat,
874}
875
876impl BinOp {
877    /// Get the precedence of this operator (higher = binds tighter).
878    #[must_use]
879    pub fn precedence(self) -> u8 {
880        match self {
881            BinOp::Or => 1,
882            BinOp::And => 2,
883            BinOp::Eq | BinOp::Ne => 3,
884            BinOp::Lt | BinOp::Gt | BinOp::Le | BinOp::Ge => 4,
885            BinOp::Concat => 5,
886            BinOp::Add | BinOp::Sub => 6,
887            BinOp::Mul | BinOp::Div => 7,
888        }
889    }
890
891    /// Check if this operator is left-associative.
892    #[must_use]
893    pub fn is_left_assoc(self) -> bool {
894        // All our operators are left-associative
895        true
896    }
897}
898
899impl fmt::Display for BinOp {
900    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
901        match self {
902            BinOp::Add => write!(f, "+"),
903            BinOp::Sub => write!(f, "-"),
904            BinOp::Mul => write!(f, "*"),
905            BinOp::Div => write!(f, "/"),
906            BinOp::Eq => write!(f, "=="),
907            BinOp::Ne => write!(f, "!="),
908            BinOp::Lt => write!(f, "<"),
909            BinOp::Gt => write!(f, ">"),
910            BinOp::Le => write!(f, "<="),
911            BinOp::Ge => write!(f, ">="),
912            BinOp::And => write!(f, "&&"),
913            BinOp::Or => write!(f, "||"),
914            BinOp::Concat => write!(f, "++"),
915        }
916    }
917}
918
919/// Unary operators.
920#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
921pub enum UnaryOp {
922    /// `-` (negation)
923    Neg,
924    /// `!` (logical not)
925    Not,
926}
927
928impl fmt::Display for UnaryOp {
929    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
930        match self {
931            UnaryOp::Neg => write!(f, "-"),
932            UnaryOp::Not => write!(f, "!"),
933        }
934    }
935}
936
937// =============================================================================
938// Literals
939// =============================================================================
940
941/// A literal value.
942#[derive(Debug, Clone, PartialEq)]
943pub enum Literal {
944    /// Integer literal: `42`, `-7`
945    Int(i64),
946    /// Float literal: `3.14`, `-0.5`
947    Float(f64),
948    /// Boolean literal: `true`, `false`
949    Bool(bool),
950    /// String literal: `"hello"`
951    String(String),
952}
953
954impl fmt::Display for Literal {
955    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
956        match self {
957            Literal::Int(n) => write!(f, "{n}"),
958            Literal::Float(n) => write!(f, "{n}"),
959            Literal::Bool(b) => write!(f, "{b}"),
960            Literal::String(s) => write!(f, "\"{s}\""),
961        }
962    }
963}
964
965// =============================================================================
966// String templates (for interpolation)
967// =============================================================================
968
969/// A string template that may contain interpolations.
970///
971/// For example, `"Hello, {name}!"` becomes:
972/// ```text
973/// StringTemplate {
974///     parts: [
975///         StringPart::Literal("Hello, "),
976///         StringPart::Interpolation(Ident("name")),
977///         StringPart::Literal("!"),
978///     ]
979/// }
980/// ```
981#[derive(Debug, Clone, PartialEq)]
982pub struct StringTemplate {
983    /// The parts of the template.
984    pub parts: Vec<StringPart>,
985    /// Span covering the entire template string.
986    pub span: Span,
987}
988
989impl StringTemplate {
990    /// Create a simple template with no interpolations.
991    #[must_use]
992    pub fn literal(s: String, span: Span) -> Self {
993        Self {
994            parts: vec![StringPart::Literal(s)],
995            span,
996        }
997    }
998
999    /// Check if this template has any interpolations.
1000    #[must_use]
1001    pub fn has_interpolations(&self) -> bool {
1002        self.parts
1003            .iter()
1004            .any(|p| matches!(p, StringPart::Interpolation(_)))
1005    }
1006
1007    /// Get all interpolated identifiers.
1008    pub fn interpolations(&self) -> impl Iterator<Item = &Ident> {
1009        self.parts.iter().filter_map(|p| match p {
1010            StringPart::Interpolation(ident) => Some(ident),
1011            StringPart::Literal(_) => None,
1012        })
1013    }
1014}
1015
1016impl fmt::Display for StringTemplate {
1017    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1018        write!(f, "\"")?;
1019        for part in &self.parts {
1020            match part {
1021                StringPart::Literal(s) => write!(f, "{s}")?,
1022                StringPart::Interpolation(ident) => write!(f, "{{{ident}}}")?,
1023            }
1024        }
1025        write!(f, "\"")
1026    }
1027}
1028
1029/// A part of a string template.
1030#[derive(Debug, Clone, PartialEq)]
1031pub enum StringPart {
1032    /// A literal string segment.
1033    Literal(String),
1034    /// An interpolated identifier: `{ident}`
1035    Interpolation(Ident),
1036}
1037
1038// =============================================================================
1039// Tests
1040// =============================================================================
1041
1042#[cfg(test)]
1043mod tests {
1044    use super::*;
1045
1046    #[test]
1047    fn binop_precedence() {
1048        // Mul/Div > Add/Sub > Comparison > And > Or
1049        assert!(BinOp::Mul.precedence() > BinOp::Add.precedence());
1050        assert!(BinOp::Add.precedence() > BinOp::Lt.precedence());
1051        assert!(BinOp::Lt.precedence() > BinOp::And.precedence());
1052        assert!(BinOp::And.precedence() > BinOp::Or.precedence());
1053    }
1054
1055    #[test]
1056    fn binop_display() {
1057        assert_eq!(format!("{}", BinOp::Add), "+");
1058        assert_eq!(format!("{}", BinOp::Eq), "==");
1059        assert_eq!(format!("{}", BinOp::Concat), "++");
1060        assert_eq!(format!("{}", BinOp::And), "&&");
1061    }
1062
1063    #[test]
1064    fn unaryop_display() {
1065        assert_eq!(format!("{}", UnaryOp::Neg), "-");
1066        assert_eq!(format!("{}", UnaryOp::Not), "!");
1067    }
1068
1069    #[test]
1070    fn literal_display() {
1071        assert_eq!(format!("{}", Literal::Int(42)), "42");
1072        assert_eq!(format!("{}", Literal::Float(3.14)), "3.14");
1073        assert_eq!(format!("{}", Literal::Bool(true)), "true");
1074        assert_eq!(format!("{}", Literal::String("hello".into())), "\"hello\"");
1075    }
1076
1077    #[test]
1078    fn event_kind_display() {
1079        assert_eq!(format!("{}", EventKind::Start), "start");
1080        assert_eq!(format!("{}", EventKind::Stop), "stop");
1081
1082        let msg = EventKind::Message {
1083            param_name: Ident::dummy("msg"),
1084            param_ty: TypeExpr::String,
1085        };
1086        assert_eq!(format!("{msg}"), "message(msg: String)");
1087    }
1088
1089    #[test]
1090    fn string_template_literal() {
1091        let template = StringTemplate::literal("hello".into(), Span::dummy());
1092        assert!(!template.has_interpolations());
1093        assert_eq!(format!("{template}"), "\"hello\"");
1094    }
1095
1096    #[test]
1097    fn string_template_with_interpolation() {
1098        let template = StringTemplate {
1099            parts: vec![
1100                StringPart::Literal("Hello, ".into()),
1101                StringPart::Interpolation(Ident::dummy("name")),
1102                StringPart::Literal("!".into()),
1103            ],
1104            span: Span::dummy(),
1105        };
1106        assert!(template.has_interpolations());
1107        assert_eq!(format!("{template}"), "\"Hello, {name}!\"");
1108
1109        let interps: Vec<_> = template.interpolations().collect();
1110        assert_eq!(interps.len(), 1);
1111        assert_eq!(interps[0].name, "name");
1112    }
1113
1114    #[test]
1115    fn expr_span() {
1116        let span = Span::dummy();
1117        let expr = Expr::Literal {
1118            value: Literal::Int(42),
1119            span: span.clone(),
1120        };
1121        assert_eq!(expr.span(), &span);
1122    }
1123
1124    #[test]
1125    fn stmt_span() {
1126        let span = Span::dummy();
1127        let stmt = Stmt::Return {
1128            value: None,
1129            span: span.clone(),
1130        };
1131        assert_eq!(stmt.span(), &span);
1132    }
1133}