Skip to main content

intent_parser/
ast.rs

1//! Typed AST for the IntentLang specification language.
2//!
3//! Every node carries a `Span` with byte offsets into the source text,
4//! supporting Phase 3 (Audit Bridge) source-location tracing.
5
6use serde::{Deserialize, Serialize};
7
8/// Byte-offset span in the source text: `[start, end)`.
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
10pub struct Span {
11    pub start: usize,
12    pub end: usize,
13}
14
15// ── Top-level ────────────────────────────────────────────────
16
17/// A complete `.intent` file.
18#[derive(Debug, Clone, Serialize)]
19pub struct File {
20    pub module: ModuleDecl,
21    pub doc: Option<DocBlock>,
22    pub imports: Vec<UseDecl>,
23    pub items: Vec<TopLevelItem>,
24    pub span: Span,
25}
26
27/// `use ModuleName` or `use ModuleName.ItemName`
28#[derive(Debug, Clone, Serialize)]
29pub struct UseDecl {
30    pub module_name: String,
31    pub item: Option<String>,
32    pub span: Span,
33}
34
35/// `module ModuleName`
36#[derive(Debug, Clone, Serialize)]
37pub struct ModuleDecl {
38    pub name: String,
39    pub span: Span,
40}
41
42/// A sequence of `---` documentation lines.
43#[derive(Debug, Clone, Serialize)]
44pub struct DocBlock {
45    pub lines: Vec<String>,
46    pub span: Span,
47}
48
49/// Any top-level declaration.
50#[derive(Debug, Clone, Serialize)]
51pub enum TopLevelItem {
52    Entity(EntityDecl),
53    Action(ActionDecl),
54    Invariant(InvariantDecl),
55    EdgeCases(EdgeCasesDecl),
56    Test(TestDecl),
57}
58
59// ── Test ────────────────────────────────────────────────────
60
61/// `test "name" { given { ... } when Action { ... } then { ... } }`
62#[derive(Debug, Clone, Serialize)]
63pub struct TestDecl {
64    pub name: String,
65    pub given: Vec<GivenBinding>,
66    pub when_action: WhenAction,
67    pub then: ThenClause,
68    pub span: Span,
69}
70
71/// `name = TypeName { ... }` or `name = expr` — a concrete binding in a test.
72#[derive(Debug, Clone, Serialize)]
73pub struct GivenBinding {
74    pub name: String,
75    pub value: GivenValue,
76    pub span: Span,
77}
78
79/// The right-hand side of a given binding.
80#[derive(Debug, Clone, Serialize)]
81pub enum GivenValue {
82    /// `TypeName { field: value, ... }` — an entity instance.
83    EntityConstructor {
84        type_name: String,
85        fields: Vec<ConstructorField>,
86    },
87    /// A plain expression (number, string, identifier, etc.).
88    Expr(Expr),
89}
90
91/// `name: expr` inside an entity constructor or when block.
92#[derive(Debug, Clone, Serialize)]
93pub struct ConstructorField {
94    pub name: String,
95    pub value: Expr,
96    pub span: Span,
97}
98
99/// `when ActionName { param: value, ... }` — the action invocation in a test.
100#[derive(Debug, Clone, Serialize)]
101pub struct WhenAction {
102    pub action_name: String,
103    pub args: Vec<ConstructorField>,
104    pub span: Span,
105}
106
107/// The expected outcome of a test.
108#[derive(Debug, Clone, Serialize)]
109pub enum ThenClause {
110    /// `then { expr* }` — assertions to check against new state.
111    Asserts(Vec<Expr>, Span),
112    /// `then fails` with optional violation kind filter.
113    Fails(Option<String>, Span),
114}
115
116// ── Entity ───────────────────────────────────────────────────
117
118/// `entity EntityName { field* }`
119#[derive(Debug, Clone, Serialize)]
120pub struct EntityDecl {
121    pub doc: Option<DocBlock>,
122    pub name: String,
123    pub fields: Vec<FieldDecl>,
124    pub span: Span,
125}
126
127/// `name: Type` — a field or parameter declaration.
128#[derive(Debug, Clone, Serialize)]
129pub struct FieldDecl {
130    pub name: String,
131    pub ty: TypeExpr,
132    pub span: Span,
133}
134
135// ── Action ───────────────────────────────────────────────────
136
137/// `action ActionName { ... }`
138#[derive(Debug, Clone, Serialize)]
139pub struct ActionDecl {
140    pub doc: Option<DocBlock>,
141    pub name: String,
142    pub params: Vec<FieldDecl>,
143    pub requires: Option<RequiresBlock>,
144    pub ensures: Option<EnsuresBlock>,
145    pub properties: Option<PropertiesBlock>,
146    pub span: Span,
147}
148
149/// `requires { expr* }`
150#[derive(Debug, Clone, Serialize)]
151pub struct RequiresBlock {
152    pub conditions: Vec<Expr>,
153    pub span: Span,
154}
155
156/// `ensures { item* }`
157#[derive(Debug, Clone, Serialize)]
158pub struct EnsuresBlock {
159    pub items: Vec<EnsuresItem>,
160    pub span: Span,
161}
162
163/// A postcondition — either a bare expression or a `when` clause.
164#[derive(Debug, Clone, Serialize)]
165pub enum EnsuresItem {
166    Expr(Expr),
167    When(WhenClause),
168}
169
170/// `when condition => consequence`
171#[derive(Debug, Clone, Serialize)]
172pub struct WhenClause {
173    pub condition: Expr,
174    pub consequence: Expr,
175    pub span: Span,
176}
177
178/// `properties { entry* }`
179#[derive(Debug, Clone, Serialize)]
180pub struct PropertiesBlock {
181    pub entries: Vec<PropEntry>,
182    pub span: Span,
183}
184
185/// `key: value` inside a properties block.
186#[derive(Debug, Clone, Serialize)]
187pub struct PropEntry {
188    pub key: String,
189    pub value: PropValue,
190    pub span: Span,
191}
192
193/// The right-hand side of a property entry.
194#[derive(Debug, Clone, Serialize)]
195pub enum PropValue {
196    Literal(Literal),
197    Ident(String),
198    List(Vec<PropValue>),
199    Object(Vec<(String, PropValue)>),
200}
201
202// ── Invariant ────────────────────────────────────────────────
203
204/// `invariant InvariantName { doc? expr }`
205#[derive(Debug, Clone, Serialize)]
206pub struct InvariantDecl {
207    pub doc: Option<DocBlock>,
208    pub name: String,
209    pub body: Expr,
210    pub span: Span,
211}
212
213// ── Edge cases ───────────────────────────────────────────────
214
215/// `edge_cases { rule* }`
216#[derive(Debug, Clone, Serialize)]
217pub struct EdgeCasesDecl {
218    pub rules: Vec<EdgeRule>,
219    pub span: Span,
220}
221
222/// `when condition => action(args)`
223#[derive(Debug, Clone, Serialize)]
224pub struct EdgeRule {
225    pub condition: Expr,
226    pub action: ActionCall,
227    pub span: Span,
228}
229
230/// A function-call-style action on the RHS of an edge rule.
231#[derive(Debug, Clone, Serialize)]
232pub struct ActionCall {
233    pub name: String,
234    pub args: Vec<CallArg>,
235    pub span: Span,
236}
237
238/// A call argument — either named (`key: value`) or positional.
239#[derive(Debug, Clone, Serialize)]
240pub enum CallArg {
241    Named {
242        key: String,
243        value: Expr,
244        span: Span,
245    },
246    Positional(Expr),
247}
248
249// ── Types ────────────────────────────────────────────────────
250
251/// A full type expression: a union type optionally marked optional with `?`.
252#[derive(Debug, Clone, Serialize)]
253pub struct TypeExpr {
254    pub ty: TypeKind,
255    pub optional: bool,
256    pub span: Span,
257}
258
259/// The shape of a type.
260#[derive(Debug, Clone, Serialize)]
261pub enum TypeKind {
262    /// A single named type: `UUID`, `String`, `Active`.
263    Simple(String),
264    /// A union of two or more types: `Active | Frozen | Closed`.
265    Union(Vec<TypeKind>),
266    /// `List<T>`
267    List(Box<TypeExpr>),
268    /// `Set<T>`
269    Set(Box<TypeExpr>),
270    /// `Map<K, V>`
271    Map(Box<TypeExpr>, Box<TypeExpr>),
272    /// `Decimal(precision: 2)` — a type with parameters.
273    Parameterized {
274        name: String,
275        params: Vec<TypeParam>,
276    },
277}
278
279/// `name: value` inside a parameterized type like `Decimal(precision: 2)`.
280#[derive(Debug, Clone, Serialize)]
281pub struct TypeParam {
282    pub name: String,
283    pub value: Literal,
284    pub span: Span,
285}
286
287// ── Expressions ──────────────────────────────────────────────
288
289/// An expression node with its source span.
290#[derive(Debug, Clone, Serialize)]
291pub struct Expr {
292    pub kind: ExprKind,
293    pub span: Span,
294}
295
296/// Expression variants.
297#[derive(Debug, Clone, Serialize)]
298pub enum ExprKind {
299    /// `a => b` — logical implication.
300    Implies(Box<Expr>, Box<Expr>),
301    /// `a || b`
302    Or(Box<Expr>, Box<Expr>),
303    /// `a && b`
304    And(Box<Expr>, Box<Expr>),
305    /// `!a`
306    Not(Box<Expr>),
307    /// `a == b`, `a > b`, etc.
308    Compare {
309        left: Box<Expr>,
310        op: CmpOp,
311        right: Box<Expr>,
312    },
313    /// `a + b`, `a - b`
314    Arithmetic {
315        left: Box<Expr>,
316        op: ArithOp,
317        right: Box<Expr>,
318    },
319    /// `old(expr)` — pre-state reference.
320    Old(Box<Expr>),
321    /// `forall x: T => body` or `exists x: T => body`
322    Quantifier {
323        kind: QuantifierKind,
324        binding: String,
325        ty: String,
326        body: Box<Expr>,
327    },
328    /// `name(args)` — function call.
329    Call { name: String, args: Vec<CallArg> },
330    /// Field access chain: `a.b.c` or `f(x).y.z`.
331    /// `root` is the base expression, `fields` are the `.`-accessed names.
332    FieldAccess {
333        root: Box<Expr>,
334        fields: Vec<String>,
335    },
336    /// List literal: `[a, b, c]`.
337    List(Vec<Expr>),
338    /// A plain identifier: `amount`, `Active`, `email`.
339    Ident(String),
340    /// A literal value.
341    Literal(Literal),
342}
343
344impl Expr {
345    /// Call `f` for each immediate child expression.
346    ///
347    /// Handles all `ExprKind` variants so callers don't need to duplicate
348    /// the recursive descent boilerplate.
349    pub fn for_each_child(&self, mut f: impl FnMut(&Expr)) {
350        match &self.kind {
351            ExprKind::Implies(a, b)
352            | ExprKind::Or(a, b)
353            | ExprKind::And(a, b)
354            | ExprKind::Compare {
355                left: a, right: b, ..
356            }
357            | ExprKind::Arithmetic {
358                left: a, right: b, ..
359            } => {
360                f(a);
361                f(b);
362            }
363            ExprKind::Not(inner) | ExprKind::Old(inner) => f(inner),
364            ExprKind::Call { args, .. } => {
365                for arg in args {
366                    match arg {
367                        CallArg::Named { value, .. } => f(value),
368                        CallArg::Positional(e) => f(e),
369                    }
370                }
371            }
372            ExprKind::FieldAccess { root, .. } => f(root),
373            ExprKind::Quantifier { body, .. } => f(body),
374            ExprKind::List(items) => {
375                for item in items {
376                    f(item);
377                }
378            }
379            ExprKind::Ident(_) | ExprKind::Literal(_) => {}
380        }
381    }
382}
383
384/// Comparison operators.
385#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
386pub enum CmpOp {
387    Eq,
388    Ne,
389    Lt,
390    Gt,
391    Le,
392    Ge,
393}
394
395/// Arithmetic operators.
396#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
397pub enum ArithOp {
398    Add,
399    Sub,
400}
401
402/// Quantifier kind.
403#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
404pub enum QuantifierKind {
405    Forall,
406    Exists,
407}
408
409/// Literal values.
410#[derive(Debug, Clone, Serialize)]
411pub enum Literal {
412    Null,
413    Bool(bool),
414    Int(i64),
415    Decimal(String),
416    String(String),
417}