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