Skip to main content

lumen_compiler/compiler/
ast.rs

1use crate::compiler::tokens::Span;
2use serde::{Deserialize, Serialize};
3
4/// A complete Lumen program (one `.lm.md` file)
5#[derive(Debug, Clone, Serialize, Deserialize)]
6pub struct Program {
7    pub directives: Vec<Directive>,
8    pub items: Vec<Item>,
9    pub span: Span,
10}
11
12/// Top-level directive (@lumen, @package, etc.)
13#[derive(Debug, Clone, Serialize, Deserialize)]
14pub struct Directive {
15    pub name: String,
16    pub value: Option<String>,
17    pub span: Span,
18}
19
20/// Top-level items
21#[derive(Debug, Clone, Serialize, Deserialize)]
22pub enum Item {
23    Record(RecordDef),
24    Enum(EnumDef),
25    Cell(CellDef),
26    Agent(AgentDecl),
27    Process(ProcessDecl),
28    Effect(EffectDecl),
29    EffectBind(EffectBindDecl),
30    Handler(HandlerDecl),
31    Addon(AddonDecl),
32    UseTool(UseToolDecl),
33    Grant(GrantDecl),
34    TypeAlias(TypeAliasDef),
35    Trait(TraitDef),
36    Impl(ImplDef),
37    Import(ImportDecl),
38    ConstDecl(ConstDeclDef),
39    MacroDecl(MacroDeclDef),
40}
41
42impl Item {
43    pub fn span(&self) -> Span {
44        match self {
45            Item::Record(r) => r.span,
46            Item::Enum(e) => e.span,
47            Item::Cell(c) => c.span,
48            Item::Agent(a) => a.span,
49            Item::Process(p) => p.span,
50            Item::Effect(e) => e.span,
51            Item::EffectBind(b) => b.span,
52            Item::Handler(h) => h.span,
53            Item::Addon(a) => a.span,
54            Item::UseTool(u) => u.span,
55            Item::Grant(g) => g.span,
56            Item::TypeAlias(t) => t.span,
57            Item::Trait(t) => t.span,
58            Item::Impl(i) => i.span,
59            Item::Import(i) => i.span,
60            Item::ConstDecl(c) => c.span,
61            Item::MacroDecl(m) => m.span,
62        }
63    }
64}
65
66// ── Type System ──
67
68/// A type expression
69#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
70pub enum TypeExpr {
71    /// Named type: String, Int, Float, Bool, Bytes, Json, or user-defined
72    Named(String, Span),
73    /// list[T]
74    List(Box<TypeExpr>, Span),
75    /// map[String, T]
76    Map(Box<TypeExpr>, Box<TypeExpr>, Span),
77    /// result[Ok, Err]
78    Result(Box<TypeExpr>, Box<TypeExpr>, Span),
79    /// Union: A | B | C
80    Union(Vec<TypeExpr>, Span),
81    /// Null type
82    Null(Span),
83    /// Tuple type: (A, B, C)
84    Tuple(Vec<TypeExpr>, Span),
85    /// Set type: set[T]
86    Set(Box<TypeExpr>, Span),
87    /// Function type: fn(A, B) -> C / {effects}
88    Fn(Vec<TypeExpr>, Box<TypeExpr>, Vec<String>, Span),
89    /// Generic type: Name[T, U]
90    Generic(String, Vec<TypeExpr>, Span),
91}
92
93impl TypeExpr {
94    pub fn span(&self) -> Span {
95        match self {
96            TypeExpr::Named(_, s) => *s,
97            TypeExpr::List(_, s) => *s,
98            TypeExpr::Map(_, _, s) => *s,
99            TypeExpr::Result(_, _, s) => *s,
100            TypeExpr::Union(_, s) => *s,
101            TypeExpr::Null(s) => *s,
102            TypeExpr::Tuple(_, s) => *s,
103            TypeExpr::Set(_, s) => *s,
104            TypeExpr::Fn(_, _, _, s) => *s,
105            TypeExpr::Generic(_, _, s) => *s,
106        }
107    }
108}
109
110// ── Generic parameters ──
111
112#[derive(Debug, Clone, Serialize, Deserialize)]
113pub struct GenericParam {
114    pub name: String,
115    pub bounds: Vec<String>,
116    pub span: Span,
117}
118
119// ── Records ──
120
121#[derive(Debug, Clone, Serialize, Deserialize)]
122pub struct RecordDef {
123    pub name: String,
124    pub generic_params: Vec<GenericParam>,
125    pub fields: Vec<FieldDef>,
126    pub is_pub: bool,
127    pub span: Span,
128}
129
130#[derive(Debug, Clone, Serialize, Deserialize)]
131pub struct FieldDef {
132    pub name: String,
133    pub ty: TypeExpr,
134    pub default_value: Option<Expr>,
135    pub constraint: Option<Expr>,
136    pub span: Span,
137}
138
139// ── Enums ──
140
141#[derive(Debug, Clone, Serialize, Deserialize)]
142pub struct EnumDef {
143    pub name: String,
144    pub generic_params: Vec<GenericParam>,
145    pub variants: Vec<EnumVariant>,
146    pub methods: Vec<CellDef>,
147    pub is_pub: bool,
148    pub span: Span,
149}
150
151#[derive(Debug, Clone, Serialize, Deserialize)]
152pub struct EnumVariant {
153    pub name: String,
154    pub payload: Option<TypeExpr>,
155    pub span: Span,
156}
157
158// ── Cells (functions) ──
159
160#[derive(Debug, Clone, Serialize, Deserialize)]
161pub struct CellDef {
162    pub name: String,
163    pub generic_params: Vec<GenericParam>,
164    pub params: Vec<Param>,
165    pub return_type: Option<TypeExpr>,
166    pub effects: Vec<String>,
167    pub body: Vec<Stmt>,
168    pub is_pub: bool,
169    pub is_async: bool,
170    pub where_clauses: Vec<Expr>,
171    pub span: Span,
172}
173
174#[derive(Debug, Clone, Serialize, Deserialize)]
175pub struct AgentDecl {
176    pub name: String,
177    pub cells: Vec<CellDef>,
178    pub grants: Vec<GrantDecl>,
179    pub span: Span,
180}
181
182#[derive(Debug, Clone, Serialize, Deserialize)]
183pub struct ProcessDecl {
184    pub kind: String,
185    pub name: String,
186    pub cells: Vec<CellDef>,
187    pub grants: Vec<GrantDecl>,
188    pub pipeline_stages: Vec<String>,
189    pub machine_initial: Option<String>,
190    pub machine_states: Vec<MachineStateDecl>,
191    pub span: Span,
192}
193
194#[derive(Debug, Clone, Serialize, Deserialize)]
195pub struct MachineStateDecl {
196    pub name: String,
197    pub params: Vec<Param>,
198    pub terminal: bool,
199    pub guard: Option<Expr>,
200    pub transition_to: Option<String>,
201    pub transition_args: Vec<Expr>,
202    pub span: Span,
203}
204
205#[derive(Debug, Clone, Serialize, Deserialize)]
206pub struct EffectDecl {
207    pub name: String,
208    pub operations: Vec<CellDef>,
209    pub span: Span,
210}
211
212#[derive(Debug, Clone, Serialize, Deserialize)]
213pub struct EffectBindDecl {
214    pub effect_path: String,
215    pub tool_alias: String,
216    pub span: Span,
217}
218
219#[derive(Debug, Clone, Serialize, Deserialize)]
220pub struct HandlerDecl {
221    pub name: String,
222    pub handles: Vec<CellDef>,
223    pub span: Span,
224}
225
226#[derive(Debug, Clone, Serialize, Deserialize)]
227pub struct AddonDecl {
228    pub kind: String,
229    pub name: Option<String>,
230    pub span: Span,
231}
232
233#[derive(Debug, Clone, Serialize, Deserialize)]
234pub struct Param {
235    pub name: String,
236    pub ty: TypeExpr,
237    pub default_value: Option<Expr>,
238    pub variadic: bool,
239    pub span: Span,
240}
241
242// ── Type aliases, traits, impls, imports ──
243
244#[derive(Debug, Clone, Serialize, Deserialize)]
245pub struct TypeAliasDef {
246    pub name: String,
247    pub generic_params: Vec<GenericParam>,
248    pub type_expr: TypeExpr,
249    pub is_pub: bool,
250    pub span: Span,
251}
252
253#[derive(Debug, Clone, Serialize, Deserialize)]
254pub struct TraitDef {
255    pub name: String,
256    pub parent_traits: Vec<String>,
257    pub methods: Vec<CellDef>,
258    pub is_pub: bool,
259    pub span: Span,
260}
261
262#[derive(Debug, Clone, Serialize, Deserialize)]
263pub struct ImplDef {
264    pub trait_name: String,
265    pub generic_params: Vec<GenericParam>,
266    pub target_type: String,
267    pub cells: Vec<CellDef>,
268    pub span: Span,
269}
270
271#[derive(Debug, Clone, Serialize, Deserialize)]
272pub enum ImportList {
273    Names(Vec<ImportName>),
274    Wildcard,
275}
276
277#[derive(Debug, Clone, Serialize, Deserialize)]
278pub struct ImportName {
279    pub name: String,
280    pub alias: Option<String>,
281    pub span: Span,
282}
283
284#[derive(Debug, Clone, Serialize, Deserialize)]
285pub struct ImportDecl {
286    pub path: Vec<String>,
287    pub names: ImportList,
288    pub is_pub: bool,
289    pub span: Span,
290}
291
292#[derive(Debug, Clone, Serialize, Deserialize)]
293pub struct ConstDeclDef {
294    pub name: String,
295    pub type_ann: Option<TypeExpr>,
296    pub value: Expr,
297    pub span: Span,
298}
299
300#[derive(Debug, Clone, Serialize, Deserialize)]
301pub struct MacroDeclDef {
302    pub name: String,
303    pub params: Vec<String>,
304    pub body: Vec<Stmt>,
305    pub span: Span,
306}
307
308// ── Statements ──
309
310#[derive(Debug, Clone, Serialize, Deserialize)]
311pub enum CompoundOp {
312    AddAssign,
313    SubAssign,
314    MulAssign,
315    DivAssign,
316    FloorDivAssign,
317    ModAssign,
318    PowAssign,
319    BitAndAssign,
320    BitOrAssign,
321    BitXorAssign,
322}
323
324#[derive(Debug, Clone, Serialize, Deserialize)]
325pub enum Stmt {
326    Let(LetStmt),
327    If(IfStmt),
328    For(ForStmt),
329    Match(MatchStmt),
330    Return(ReturnStmt),
331    Halt(HaltStmt),
332    Assign(AssignStmt),
333    Expr(ExprStmt),
334    While(WhileStmt),
335    Loop(LoopStmt),
336    Break(BreakStmt),
337    Continue(ContinueStmt),
338    Emit(EmitStmt),
339    CompoundAssign(CompoundAssignStmt),
340    Defer(DeferStmt),
341}
342
343impl Stmt {
344    pub fn span(&self) -> Span {
345        match self {
346            Stmt::Let(s) => s.span,
347            Stmt::If(s) => s.span,
348            Stmt::For(s) => s.span,
349            Stmt::Match(s) => s.span,
350            Stmt::Return(s) => s.span,
351            Stmt::Halt(s) => s.span,
352            Stmt::Assign(s) => s.span,
353            Stmt::Expr(s) => s.span,
354            Stmt::While(s) => s.span,
355            Stmt::Loop(s) => s.span,
356            Stmt::Break(s) => s.span,
357            Stmt::Continue(s) => s.span,
358            Stmt::Emit(s) => s.span,
359            Stmt::CompoundAssign(s) => s.span,
360            Stmt::Defer(s) => s.span,
361        }
362    }
363}
364
365#[derive(Debug, Clone, Serialize, Deserialize)]
366pub struct LetStmt {
367    pub name: String,
368    pub mutable: bool,
369    pub pattern: Option<Pattern>,
370    pub ty: Option<TypeExpr>,
371    pub value: Expr,
372    pub span: Span,
373}
374
375#[derive(Debug, Clone, Serialize, Deserialize)]
376pub struct IfStmt {
377    pub condition: Expr,
378    pub then_body: Vec<Stmt>,
379    pub else_body: Option<Vec<Stmt>>,
380    pub span: Span,
381}
382
383#[derive(Debug, Clone, Serialize, Deserialize)]
384pub struct ForStmt {
385    pub label: Option<String>,
386    pub var: String,
387    pub pattern: Option<Pattern>,
388    pub iter: Expr,
389    pub filter: Option<Expr>,
390    pub body: Vec<Stmt>,
391    pub span: Span,
392}
393
394#[derive(Debug, Clone, Serialize, Deserialize)]
395pub struct MatchStmt {
396    pub subject: Expr,
397    pub arms: Vec<MatchArm>,
398    pub span: Span,
399}
400
401#[derive(Debug, Clone, Serialize, Deserialize)]
402pub struct MatchArm {
403    pub pattern: Pattern,
404    pub body: Vec<Stmt>,
405    pub span: Span,
406}
407
408#[derive(Debug, Clone, Serialize, Deserialize)]
409pub enum Pattern {
410    /// Literal pattern: 200, "hello", true
411    Literal(Expr),
412    /// Variant with optional sub-pattern: ok(value), err(e), Some(Value(n))
413    Variant(String, Option<Box<Pattern>>, Span),
414    /// Wildcard: _
415    Wildcard(Span),
416    /// Ident binding
417    Ident(String, Span),
418    /// Guard: pattern if condition
419    Guard {
420        inner: Box<Pattern>,
421        condition: Box<Expr>,
422        span: Span,
423    },
424    /// Or: pattern1 | pattern2
425    Or { patterns: Vec<Pattern>, span: Span },
426    /// List destructure: [a, b, ...rest]
427    ListDestructure {
428        elements: Vec<Pattern>,
429        rest: Option<String>,
430        span: Span,
431    },
432    /// Tuple destructure: (a, b, c)
433    TupleDestructure { elements: Vec<Pattern>, span: Span },
434    /// Record destructure: TypeName(field1:, field2: pat, ..)
435    RecordDestructure {
436        type_name: String,
437        fields: Vec<(String, Option<Pattern>)>,
438        open: bool,
439        span: Span,
440    },
441    /// Type check: name: Type
442    TypeCheck {
443        name: String,
444        type_expr: Box<TypeExpr>,
445        span: Span,
446    },
447}
448
449#[derive(Debug, Clone, Serialize, Deserialize)]
450pub struct ReturnStmt {
451    pub value: Expr,
452    pub span: Span,
453}
454
455#[derive(Debug, Clone, Serialize, Deserialize)]
456pub struct HaltStmt {
457    pub message: Expr,
458    pub span: Span,
459}
460
461#[derive(Debug, Clone, Serialize, Deserialize)]
462pub struct ExprStmt {
463    pub expr: Expr,
464    pub span: Span,
465}
466
467#[derive(Debug, Clone, Serialize, Deserialize)]
468pub struct AssignStmt {
469    pub target: String,
470    pub value: Expr,
471    pub span: Span,
472}
473
474#[derive(Debug, Clone, Serialize, Deserialize)]
475pub struct WhileStmt {
476    pub label: Option<String>,
477    pub condition: Expr,
478    pub body: Vec<Stmt>,
479    pub span: Span,
480}
481
482#[derive(Debug, Clone, Serialize, Deserialize)]
483pub struct LoopStmt {
484    pub label: Option<String>,
485    pub body: Vec<Stmt>,
486    pub span: Span,
487}
488
489#[derive(Debug, Clone, Serialize, Deserialize)]
490pub struct BreakStmt {
491    pub label: Option<String>,
492    pub value: Option<Expr>,
493    pub span: Span,
494}
495
496#[derive(Debug, Clone, Serialize, Deserialize)]
497pub struct ContinueStmt {
498    pub label: Option<String>,
499    pub span: Span,
500}
501
502#[derive(Debug, Clone, Serialize, Deserialize)]
503pub struct EmitStmt {
504    pub value: Expr,
505    pub span: Span,
506}
507
508#[derive(Debug, Clone, Serialize, Deserialize)]
509pub struct CompoundAssignStmt {
510    pub target: String,
511    pub op: CompoundOp,
512    pub value: Expr,
513    pub span: Span,
514}
515
516#[derive(Debug, Clone, Serialize, Deserialize)]
517pub struct DeferStmt {
518    pub body: Vec<Stmt>,
519    pub span: Span,
520}
521
522// ── Expressions ──
523
524/// Lambda body can be a single expression or a block
525#[derive(Debug, Clone, Serialize, Deserialize)]
526pub enum LambdaBody {
527    Expr(Box<Expr>),
528    Block(Vec<Stmt>),
529}
530
531/// Comprehension kinds
532#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
533pub enum ComprehensionKind {
534    List,
535    Map,
536    Set,
537}
538
539#[derive(Debug, Clone, Serialize, Deserialize)]
540pub enum Expr {
541    /// Integer literal
542    IntLit(i64, Span),
543    /// Float literal
544    FloatLit(f64, Span),
545    /// String literal (may contain interpolation)
546    StringLit(String, Span),
547    /// Interpolated string with segments
548    StringInterp(Vec<StringSegment>, Span),
549    /// Boolean literal
550    BoolLit(bool, Span),
551    /// Null literal
552    NullLit(Span),
553    /// Raw string literal
554    RawStringLit(String, Span),
555    /// Bytes literal
556    BytesLit(Vec<u8>, Span),
557    /// Variable reference
558    Ident(String, Span),
559    /// List literal: [a, b, c]
560    ListLit(Vec<Expr>, Span),
561    /// Map literal: {"key": value, ...}
562    MapLit(Vec<(Expr, Expr)>, Span),
563    /// Record literal: TypeName(field1: val1, field2: val2)
564    RecordLit(String, Vec<(String, Expr)>, Span),
565    /// Binary operation
566    BinOp(Box<Expr>, BinOp, Box<Expr>, Span),
567    /// Unary operation
568    UnaryOp(UnaryOp, Box<Expr>, Span),
569    /// Function/cell call: name(args)
570    Call(Box<Expr>, Vec<CallArg>, Span),
571    /// Tool call with role blocks
572    ToolCall(Box<Expr>, Vec<CallArg>, Span),
573    /// Dot access: expr.field
574    DotAccess(Box<Expr>, String, Span),
575    /// Index access: expr[index]
576    IndexAccess(Box<Expr>, Box<Expr>, Span),
577    /// Role block: role system: ... end
578    RoleBlock(String, Box<Expr>, Span),
579    /// expect schema Type
580    ExpectSchema(Box<Expr>, String, Span),
581    /// Lambda: fn(params) -> type => expr | fn(params) block end
582    Lambda {
583        params: Vec<Param>,
584        return_type: Option<Box<TypeExpr>>,
585        body: LambdaBody,
586        span: Span,
587    },
588    /// Tuple literal: (a, b, c)
589    TupleLit(Vec<Expr>, Span),
590    /// Set literal: set[a, b, c]
591    SetLit(Vec<Expr>, Span),
592    /// Range expression: start..end or start..=end
593    RangeExpr {
594        start: Option<Box<Expr>>,
595        end: Option<Box<Expr>>,
596        inclusive: bool,
597        step: Option<Box<Expr>>,
598        span: Span,
599    },
600    /// Postfix try: expr?
601    TryExpr(Box<Expr>, Span),
602    /// Null coalescing: lhs ?? rhs
603    NullCoalesce(Box<Expr>, Box<Expr>, Span),
604    /// Null-safe access: expr?.field
605    NullSafeAccess(Box<Expr>, String, Span),
606    /// Null-safe index: expr?[index]
607    NullSafeIndex(Box<Expr>, Box<Expr>, Span),
608    /// Null assert: expr!
609    NullAssert(Box<Expr>, Span),
610    /// Spread: ...expr
611    SpreadExpr(Box<Expr>, Span),
612    /// If expression: if cond then a else b
613    IfExpr {
614        cond: Box<Expr>,
615        then_val: Box<Expr>,
616        else_val: Box<Expr>,
617        span: Span,
618    },
619    /// Await expression: await expr
620    AwaitExpr(Box<Expr>, Span),
621    /// Comprehension: [expr for pat in iter if cond]
622    Comprehension {
623        body: Box<Expr>,
624        var: String,
625        iter: Box<Expr>,
626        condition: Option<Box<Expr>>,
627        kind: ComprehensionKind,
628        span: Span,
629    },
630    /// Match expression: match expr ... end (expression position)
631    MatchExpr {
632        subject: Box<Expr>,
633        arms: Vec<MatchArm>,
634        span: Span,
635    },
636    /// Block expression: evaluates a sequence of statements, value is last expression
637    BlockExpr(Vec<Stmt>, Span),
638    /// Pipe operator: x |> f desugars to f(x), x |> f(y) desugars to f(x, y)
639    Pipe {
640        left: Box<Expr>,
641        right: Box<Expr>,
642        span: Span,
643    },
644    /// Illuminate operator: data ~> transform calls an AI-capable cell with data as input
645    Illuminate {
646        input: Box<Expr>,
647        transform: Box<Expr>,
648        span: Span,
649    },
650    /// Type test: expr is TypeName -> Bool
651    IsType {
652        expr: Box<Expr>,
653        type_name: String,
654        span: Span,
655    },
656    /// Type cast: expr as Type -> converted value
657    TypeCast {
658        expr: Box<Expr>,
659        target_type: String,
660        span: Span,
661    },
662}
663
664impl Expr {
665    pub fn span(&self) -> Span {
666        match self {
667            Expr::IntLit(_, s)
668            | Expr::FloatLit(_, s)
669            | Expr::StringLit(_, s)
670            | Expr::StringInterp(_, s)
671            | Expr::BoolLit(_, s)
672            | Expr::NullLit(s)
673            | Expr::RawStringLit(_, s)
674            | Expr::BytesLit(_, s)
675            | Expr::Ident(_, s)
676            | Expr::ListLit(_, s)
677            | Expr::MapLit(_, s)
678            | Expr::RecordLit(_, _, s)
679            | Expr::BinOp(_, _, _, s)
680            | Expr::UnaryOp(_, _, s)
681            | Expr::Call(_, _, s)
682            | Expr::ToolCall(_, _, s)
683            | Expr::DotAccess(_, _, s)
684            | Expr::IndexAccess(_, _, s)
685            | Expr::RoleBlock(_, _, s)
686            | Expr::ExpectSchema(_, _, s)
687            | Expr::TupleLit(_, s)
688            | Expr::SetLit(_, s)
689            | Expr::TryExpr(_, s)
690            | Expr::NullCoalesce(_, _, s)
691            | Expr::NullSafeAccess(_, _, s)
692            | Expr::NullSafeIndex(_, _, s)
693            | Expr::NullAssert(_, s)
694            | Expr::SpreadExpr(_, s)
695            | Expr::AwaitExpr(_, s)
696            | Expr::BlockExpr(_, s) => *s,
697            Expr::Lambda { span, .. } => *span,
698            Expr::RangeExpr { span, .. } => *span,
699            Expr::IfExpr { span, .. } => *span,
700            Expr::Comprehension { span, .. } => *span,
701            Expr::MatchExpr { span, .. } => *span,
702            Expr::Pipe { span, .. } => *span,
703            Expr::Illuminate { span, .. } => *span,
704            Expr::IsType { span, .. } => *span,
705            Expr::TypeCast { span, .. } => *span,
706        }
707    }
708}
709
710#[derive(Debug, Clone, Serialize, Deserialize)]
711pub enum StringSegment {
712    Literal(String),
713    Interpolation(Box<Expr>),
714}
715
716#[derive(Debug, Clone, Serialize, Deserialize)]
717pub enum CallArg {
718    Positional(Expr),
719    Named(String, Expr, Span),
720    Role(String, Expr, Span),
721}
722
723#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
724pub enum BinOp {
725    Add,
726    Sub,
727    Mul,
728    Div,
729    FloorDiv,
730    Mod,
731    Eq,
732    NotEq,
733    Lt,
734    LtEq,
735    Gt,
736    GtEq,
737    And,
738    Or,
739    Pow,
740    PipeForward,
741    Concat,
742    In,
743    BitAnd,
744    BitOr,
745    BitXor,
746    Shl,
747    Shr,
748}
749
750impl fmt::Display for BinOp {
751    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
752        match self {
753            BinOp::Add => write!(f, "+"),
754            BinOp::Sub => write!(f, "-"),
755            BinOp::Mul => write!(f, "*"),
756            BinOp::Div => write!(f, "/"),
757            BinOp::FloorDiv => write!(f, "//"),
758            BinOp::Mod => write!(f, "%"),
759            BinOp::Eq => write!(f, "=="),
760            BinOp::NotEq => write!(f, "!="),
761            BinOp::Lt => write!(f, "<"),
762            BinOp::LtEq => write!(f, "<="),
763            BinOp::Gt => write!(f, ">"),
764            BinOp::GtEq => write!(f, ">="),
765            BinOp::And => write!(f, "and"),
766            BinOp::Or => write!(f, "or"),
767            BinOp::Pow => write!(f, "**"),
768            BinOp::PipeForward => write!(f, "|>"),
769            BinOp::Concat => write!(f, "++"),
770            BinOp::In => write!(f, "in"),
771            BinOp::BitAnd => write!(f, "&"),
772            BinOp::BitOr => write!(f, "|"),
773            BinOp::BitXor => write!(f, "^"),
774            BinOp::Shl => write!(f, "<<"),
775            BinOp::Shr => write!(f, ">>"),
776        }
777    }
778}
779
780use std::fmt;
781
782#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
783pub enum UnaryOp {
784    Neg,
785    Not,
786    BitNot,
787}
788
789// ── Tool Declarations ──
790
791#[derive(Debug, Clone, Serialize, Deserialize)]
792pub struct UseToolDecl {
793    pub tool_path: String,
794    pub alias: String,
795    pub mcp_url: Option<String>,
796    pub span: Span,
797}
798
799#[derive(Debug, Clone, Serialize, Deserialize)]
800pub struct GrantDecl {
801    pub tool_alias: String,
802    pub constraints: Vec<GrantConstraint>,
803    pub span: Span,
804}
805
806#[derive(Debug, Clone, Serialize, Deserialize)]
807pub struct GrantConstraint {
808    pub key: String,
809    pub value: Expr,
810    pub span: Span,
811}