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}