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}
57
58// ── Entity ───────────────────────────────────────────────────
59
60/// `entity EntityName { field* }`
61#[derive(Debug, Clone, Serialize)]
62pub struct EntityDecl {
63    pub doc: Option<DocBlock>,
64    pub name: String,
65    pub fields: Vec<FieldDecl>,
66    pub span: Span,
67}
68
69/// `name: Type` — a field or parameter declaration.
70#[derive(Debug, Clone, Serialize)]
71pub struct FieldDecl {
72    pub name: String,
73    pub ty: TypeExpr,
74    pub span: Span,
75}
76
77// ── Action ───────────────────────────────────────────────────
78
79/// `action ActionName { ... }`
80#[derive(Debug, Clone, Serialize)]
81pub struct ActionDecl {
82    pub doc: Option<DocBlock>,
83    pub name: String,
84    pub params: Vec<FieldDecl>,
85    pub requires: Option<RequiresBlock>,
86    pub ensures: Option<EnsuresBlock>,
87    pub properties: Option<PropertiesBlock>,
88    pub span: Span,
89}
90
91/// `requires { expr* }`
92#[derive(Debug, Clone, Serialize)]
93pub struct RequiresBlock {
94    pub conditions: Vec<Expr>,
95    pub span: Span,
96}
97
98/// `ensures { item* }`
99#[derive(Debug, Clone, Serialize)]
100pub struct EnsuresBlock {
101    pub items: Vec<EnsuresItem>,
102    pub span: Span,
103}
104
105/// A postcondition — either a bare expression or a `when` clause.
106#[derive(Debug, Clone, Serialize)]
107pub enum EnsuresItem {
108    Expr(Expr),
109    When(WhenClause),
110}
111
112/// `when condition => consequence`
113#[derive(Debug, Clone, Serialize)]
114pub struct WhenClause {
115    pub condition: Expr,
116    pub consequence: Expr,
117    pub span: Span,
118}
119
120/// `properties { entry* }`
121#[derive(Debug, Clone, Serialize)]
122pub struct PropertiesBlock {
123    pub entries: Vec<PropEntry>,
124    pub span: Span,
125}
126
127/// `key: value` inside a properties block.
128#[derive(Debug, Clone, Serialize)]
129pub struct PropEntry {
130    pub key: String,
131    pub value: PropValue,
132    pub span: Span,
133}
134
135/// The right-hand side of a property entry.
136#[derive(Debug, Clone, Serialize)]
137pub enum PropValue {
138    Literal(Literal),
139    Ident(String),
140    List(Vec<PropValue>),
141    Object(Vec<(String, PropValue)>),
142}
143
144// ── Invariant ────────────────────────────────────────────────
145
146/// `invariant InvariantName { doc? expr }`
147#[derive(Debug, Clone, Serialize)]
148pub struct InvariantDecl {
149    pub doc: Option<DocBlock>,
150    pub name: String,
151    pub body: Expr,
152    pub span: Span,
153}
154
155// ── Edge cases ───────────────────────────────────────────────
156
157/// `edge_cases { rule* }`
158#[derive(Debug, Clone, Serialize)]
159pub struct EdgeCasesDecl {
160    pub rules: Vec<EdgeRule>,
161    pub span: Span,
162}
163
164/// `when condition => action(args)`
165#[derive(Debug, Clone, Serialize)]
166pub struct EdgeRule {
167    pub condition: Expr,
168    pub action: ActionCall,
169    pub span: Span,
170}
171
172/// A function-call-style action on the RHS of an edge rule.
173#[derive(Debug, Clone, Serialize)]
174pub struct ActionCall {
175    pub name: String,
176    pub args: Vec<CallArg>,
177    pub span: Span,
178}
179
180/// A call argument — either named (`key: value`) or positional.
181#[derive(Debug, Clone, Serialize)]
182pub enum CallArg {
183    Named {
184        key: String,
185        value: Expr,
186        span: Span,
187    },
188    Positional(Expr),
189}
190
191// ── Types ────────────────────────────────────────────────────
192
193/// A full type expression: a union type optionally marked optional with `?`.
194#[derive(Debug, Clone, Serialize)]
195pub struct TypeExpr {
196    pub ty: TypeKind,
197    pub optional: bool,
198    pub span: Span,
199}
200
201/// The shape of a type.
202#[derive(Debug, Clone, Serialize)]
203pub enum TypeKind {
204    /// A single named type: `UUID`, `String`, `Active`.
205    Simple(String),
206    /// A union of two or more types: `Active | Frozen | Closed`.
207    Union(Vec<TypeKind>),
208    /// `List<T>`
209    List(Box<TypeExpr>),
210    /// `Set<T>`
211    Set(Box<TypeExpr>),
212    /// `Map<K, V>`
213    Map(Box<TypeExpr>, Box<TypeExpr>),
214    /// `Decimal(precision: 2)` — a type with parameters.
215    Parameterized {
216        name: String,
217        params: Vec<TypeParam>,
218    },
219}
220
221/// `name: value` inside a parameterized type like `Decimal(precision: 2)`.
222#[derive(Debug, Clone, Serialize)]
223pub struct TypeParam {
224    pub name: String,
225    pub value: Literal,
226    pub span: Span,
227}
228
229// ── Expressions ──────────────────────────────────────────────
230
231/// An expression node with its source span.
232#[derive(Debug, Clone, Serialize)]
233pub struct Expr {
234    pub kind: ExprKind,
235    pub span: Span,
236}
237
238/// Expression variants.
239#[derive(Debug, Clone, Serialize)]
240pub enum ExprKind {
241    /// `a => b` — logical implication.
242    Implies(Box<Expr>, Box<Expr>),
243    /// `a || b`
244    Or(Box<Expr>, Box<Expr>),
245    /// `a && b`
246    And(Box<Expr>, Box<Expr>),
247    /// `!a`
248    Not(Box<Expr>),
249    /// `a == b`, `a > b`, etc.
250    Compare {
251        left: Box<Expr>,
252        op: CmpOp,
253        right: Box<Expr>,
254    },
255    /// `a + b`, `a - b`
256    Arithmetic {
257        left: Box<Expr>,
258        op: ArithOp,
259        right: Box<Expr>,
260    },
261    /// `old(expr)` — pre-state reference.
262    Old(Box<Expr>),
263    /// `forall x: T => body` or `exists x: T => body`
264    Quantifier {
265        kind: QuantifierKind,
266        binding: String,
267        ty: String,
268        body: Box<Expr>,
269    },
270    /// `name(args)` — function call.
271    Call { name: String, args: Vec<CallArg> },
272    /// Field access chain: `a.b.c` or `f(x).y.z`.
273    /// `root` is the base expression, `fields` are the `.`-accessed names.
274    FieldAccess {
275        root: Box<Expr>,
276        fields: Vec<String>,
277    },
278    /// List literal: `[a, b, c]`.
279    List(Vec<Expr>),
280    /// A plain identifier: `amount`, `Active`, `email`.
281    Ident(String),
282    /// A literal value.
283    Literal(Literal),
284}
285
286impl Expr {
287    /// Call `f` for each immediate child expression.
288    ///
289    /// Handles all `ExprKind` variants so callers don't need to duplicate
290    /// the recursive descent boilerplate.
291    pub fn for_each_child(&self, mut f: impl FnMut(&Expr)) {
292        match &self.kind {
293            ExprKind::Implies(a, b)
294            | ExprKind::Or(a, b)
295            | ExprKind::And(a, b)
296            | ExprKind::Compare {
297                left: a, right: b, ..
298            }
299            | ExprKind::Arithmetic {
300                left: a, right: b, ..
301            } => {
302                f(a);
303                f(b);
304            }
305            ExprKind::Not(inner) | ExprKind::Old(inner) => f(inner),
306            ExprKind::Call { args, .. } => {
307                for arg in args {
308                    match arg {
309                        CallArg::Named { value, .. } => f(value),
310                        CallArg::Positional(e) => f(e),
311                    }
312                }
313            }
314            ExprKind::FieldAccess { root, .. } => f(root),
315            ExprKind::Quantifier { body, .. } => f(body),
316            ExprKind::List(items) => {
317                for item in items {
318                    f(item);
319                }
320            }
321            ExprKind::Ident(_) | ExprKind::Literal(_) => {}
322        }
323    }
324}
325
326/// Comparison operators.
327#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
328pub enum CmpOp {
329    Eq,
330    Ne,
331    Lt,
332    Gt,
333    Le,
334    Ge,
335}
336
337/// Arithmetic operators.
338#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
339pub enum ArithOp {
340    Add,
341    Sub,
342}
343
344/// Quantifier kind.
345#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
346pub enum QuantifierKind {
347    Forall,
348    Exists,
349}
350
351/// Literal values.
352#[derive(Debug, Clone, Serialize)]
353pub enum Literal {
354    Null,
355    Bool(bool),
356    Int(i64),
357    Decimal(String),
358    String(String),
359}