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