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