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