Skip to main content

aver/
ast.rs

1#[derive(Debug, Clone, PartialEq)]
2pub enum Literal {
3    Int(i64),
4    Float(f64),
5    Str(String),
6    Bool(bool),
7    Unit,
8}
9
10#[derive(Debug, Clone, Copy, PartialEq)]
11pub enum BinOp {
12    Add,
13    Sub,
14    Mul,
15    Div,
16    Eq,
17    Neq,
18    Lt,
19    Gt,
20    Lte,
21    Gte,
22}
23
24#[derive(Debug, Clone, PartialEq)]
25pub struct MatchArm {
26    pub pattern: Pattern,
27    pub body: Box<Expr>,
28}
29
30#[derive(Debug, Clone, PartialEq)]
31pub enum Pattern {
32    Wildcard,
33    Literal(Literal),
34    Ident(String),
35    /// Empty list pattern: `[]`
36    EmptyList,
37    /// Cons-like list pattern: `[head, ..tail]`
38    Cons(String, String),
39    /// Tuple pattern: `(a, b)` / `(_, x)` / nested tuples.
40    Tuple(Vec<Pattern>),
41    /// Constructor pattern: fully-qualified name + list of binding names.
42    /// Built-ins: Result.Ok(x), Result.Err(x), Option.Some(x), Option.None.
43    /// User-defined: Shape.Circle(r), Shape.Rect(w, h), Shape.Point.
44    Constructor(String, Vec<String>),
45}
46
47#[derive(Debug, Clone, PartialEq)]
48pub enum StrPart {
49    Literal(String),
50    Parsed(Box<Expr>),
51}
52
53#[derive(Debug, Clone, PartialEq)]
54pub enum Expr {
55    Literal(Literal),
56    Ident(String),
57    Attr(Box<Expr>, String),
58    FnCall(Box<Expr>, Vec<Expr>),
59    BinOp(BinOp, Box<Expr>, Box<Expr>),
60    Match {
61        subject: Box<Expr>,
62        arms: Vec<MatchArm>,
63        line: usize,
64    },
65    Constructor(String, Option<Box<Expr>>),
66    ErrorProp(Box<Expr>),
67    InterpolatedStr(Vec<StrPart>),
68    List(Vec<Expr>),
69    Tuple(Vec<Expr>),
70    /// Map literal: `{"a" => 1, "b" => 2}`
71    MapLiteral(Vec<(Expr, Expr)>),
72    /// Record creation: `User(name = "Alice", age = 30)`
73    RecordCreate {
74        type_name: String,
75        fields: Vec<(String, Expr)>,
76    },
77    /// Record update: `User.update(base, field = newVal, ...)`
78    RecordUpdate {
79        type_name: String,
80        base: Box<Expr>,
81        updates: Vec<(String, Expr)>,
82    },
83    /// Tail-position call to a function in the same SCC (self or mutual recursion).
84    /// Produced by the TCO transform pass before type-checking.
85    /// Boxed to keep Expr enum at its original size (48 bytes).
86    TailCall(Box<(String, Vec<Expr>)>),
87    /// Compiled variable lookup: `env[last][slot]` — O(1) instead of HashMap scan.
88    /// Produced by the resolver pass for locals inside function bodies.
89    Resolved(u16),
90}
91
92#[derive(Debug, Clone, PartialEq)]
93pub enum Stmt {
94    Binding(String, Option<String>, Expr),
95    Expr(Expr),
96}
97
98#[derive(Debug, Clone, PartialEq)]
99pub enum FnBody {
100    Block(Vec<Stmt>),
101}
102
103impl FnBody {
104    pub fn from_expr(expr: Expr) -> Self {
105        Self::Block(vec![Stmt::Expr(expr)])
106    }
107
108    pub fn stmts(&self) -> &[Stmt] {
109        match self {
110            Self::Block(stmts) => stmts,
111        }
112    }
113
114    pub fn stmts_mut(&mut self) -> &mut Vec<Stmt> {
115        match self {
116            Self::Block(stmts) => stmts,
117        }
118    }
119
120    pub fn tail_expr(&self) -> Option<&Expr> {
121        match self.stmts().last() {
122            Some(Stmt::Expr(expr)) => Some(expr),
123            _ => None,
124        }
125    }
126
127    pub fn tail_expr_mut(&mut self) -> Option<&mut Expr> {
128        match self.stmts_mut().last_mut() {
129            Some(Stmt::Expr(expr)) => Some(expr),
130            _ => None,
131        }
132    }
133}
134
135/// Compile-time resolution metadata for a function body.
136/// Produced by `resolver::resolve_fn` — maps local variable names to slot indices
137/// so the interpreter can use `Vec<Value>` instead of `HashMap` lookups.
138#[derive(Debug, Clone, PartialEq)]
139pub struct FnResolution {
140    /// Total number of local slots needed (params + bindings in body).
141    pub local_count: u16,
142    /// Map from local variable name → slot index in the local `Slots` frame.
143    pub local_slots: std::rc::Rc<std::collections::HashMap<String, u16>>,
144}
145
146#[derive(Debug, Clone, PartialEq)]
147pub struct FnDef {
148    pub name: String,
149    pub line: usize,
150    pub params: Vec<(String, String)>,
151    pub return_type: String,
152    pub effects: Vec<String>,
153    pub desc: Option<String>,
154    pub body: std::rc::Rc<FnBody>,
155    /// `None` for unresolved (REPL, module sub-interpreters).
156    pub resolution: Option<FnResolution>,
157}
158
159#[derive(Debug, Clone, PartialEq)]
160pub struct Module {
161    pub name: String,
162    pub line: usize,
163    pub depends: Vec<String>,
164    pub exposes: Vec<String>,
165    pub exposes_opaque: Vec<String>,
166    pub exposes_line: Option<usize>,
167    pub intent: String,
168}
169
170#[derive(Debug, Clone, PartialEq)]
171pub enum VerifyGivenDomain {
172    /// Integer range domain in verify law: `1..50` (inclusive).
173    IntRange { start: i64, end: i64 },
174    /// Explicit domain values in verify law: `[v1, v2, ...]`.
175    Explicit(Vec<Expr>),
176}
177
178#[derive(Debug, Clone, PartialEq)]
179pub struct VerifyGiven {
180    pub name: String,
181    pub type_name: String,
182    pub domain: VerifyGivenDomain,
183}
184
185#[derive(Debug, Clone, PartialEq)]
186pub struct VerifyLaw {
187    pub name: String,
188    pub givens: Vec<VerifyGiven>,
189    /// Optional precondition for the law template, written as `when <bool-expr>`.
190    pub when: Option<Expr>,
191    /// Template assertion from source before given-domain expansion.
192    pub lhs: Expr,
193    pub rhs: Expr,
194    /// Per-sample substituted guards for `when`, aligned with `VerifyBlock.cases`.
195    pub sample_guards: Vec<Expr>,
196}
197
198/// Source range for AST nodes that need location tracking.
199/// Used by verify case spans: `cases[i] <-> case_spans[i]`.
200#[derive(Debug, Clone, PartialEq, Default)]
201pub struct SourceSpan {
202    pub line: usize,
203    pub col: usize,
204    pub end_line: usize,
205    pub end_col: usize,
206}
207
208#[derive(Debug, Clone, PartialEq)]
209pub enum VerifyKind {
210    Cases,
211    Law(Box<VerifyLaw>),
212}
213
214#[derive(Debug, Clone, PartialEq)]
215pub struct VerifyBlock {
216    pub fn_name: String,
217    pub line: usize,
218    pub cases: Vec<(Expr, Expr)>,
219    pub case_spans: Vec<SourceSpan>,
220    /// Per-case given bindings for law verify (empty for Cases kind).
221    pub case_givens: Vec<Vec<(String, Expr)>>,
222    pub kind: VerifyKind,
223}
224
225impl VerifyBlock {
226    /// Construct a VerifyBlock with default (zero) spans for each case.
227    /// Use when source location tracking is not needed (codegen, tests).
228    pub fn new_unspanned(
229        fn_name: String,
230        line: usize,
231        cases: Vec<(Expr, Expr)>,
232        kind: VerifyKind,
233    ) -> Self {
234        let case_spans = vec![SourceSpan::default(); cases.len()];
235        Self {
236            fn_name,
237            line,
238            cases,
239            case_spans,
240            case_givens: vec![],
241            kind,
242        }
243    }
244
245    pub fn iter_cases_with_spans(&self) -> impl Iterator<Item = (&(Expr, Expr), &SourceSpan)> {
246        debug_assert_eq!(self.cases.len(), self.case_spans.len());
247        self.cases.iter().zip(&self.case_spans)
248    }
249}
250
251#[derive(Debug, Clone, PartialEq)]
252pub struct DecisionBlock {
253    pub name: String,
254    pub line: usize,
255    pub date: String,
256    pub reason: String,
257    pub chosen: DecisionImpact,
258    pub rejected: Vec<DecisionImpact>,
259    pub impacts: Vec<DecisionImpact>,
260    pub author: Option<String>,
261}
262
263#[derive(Debug, Clone, PartialEq, Eq, Hash)]
264pub enum DecisionImpact {
265    Symbol(String),
266    Semantic(String),
267}
268
269impl DecisionImpact {
270    pub fn text(&self) -> &str {
271        match self {
272            DecisionImpact::Symbol(s) | DecisionImpact::Semantic(s) => s,
273        }
274    }
275
276    pub fn as_context_string(&self) -> String {
277        match self {
278            DecisionImpact::Symbol(s) => s.clone(),
279            DecisionImpact::Semantic(s) => format!("\"{}\"", s),
280        }
281    }
282}
283
284/// A variant in a sum type definition.
285/// e.g. `Circle(Float)` → `TypeVariant { name: "Circle", fields: ["Float"] }`
286#[derive(Debug, Clone, PartialEq)]
287pub struct TypeVariant {
288    pub name: String,
289    pub fields: Vec<String>, // type annotations (e.g. "Float", "String")
290}
291
292/// A user-defined type definition.
293#[derive(Debug, Clone, PartialEq)]
294pub enum TypeDef {
295    /// `type Shape` with variants Circle(Float), Rect(Float, Float), Point
296    Sum {
297        name: String,
298        variants: Vec<TypeVariant>,
299        line: usize,
300    },
301    /// `record User` with fields name: String, age: Int
302    Product {
303        name: String,
304        fields: Vec<(String, String)>,
305        line: usize,
306    },
307}
308
309#[derive(Debug, Clone, PartialEq)]
310pub enum TopLevel {
311    Module(Module),
312    FnDef(FnDef),
313    Verify(VerifyBlock),
314    Decision(DecisionBlock),
315    Stmt(Stmt),
316    TypeDef(TypeDef),
317}