Skip to main content

proof_engine/scripting/
ast.rs

1//! Abstract Syntax Tree for the scripting language.
2
3use std::collections::HashMap;
4
5// ── Expressions ────────────────────────────────────────────────────────────
6
7#[derive(Debug, Clone, PartialEq)]
8pub enum Expr {
9    /// nil
10    Nil,
11    /// true / false
12    Bool(bool),
13    /// integer literal
14    Int(i64),
15    /// float literal
16    Float(f64),
17    /// string literal
18    Str(String),
19    /// ... (varargs)
20    Vararg,
21    /// identifier reference
22    Ident(String),
23    /// table access: expr[expr]
24    Index { table: Box<Expr>, key: Box<Expr> },
25    /// field access: expr.name
26    Field { table: Box<Expr>, name: String },
27    /// function call: expr(args)
28    Call { callee: Box<Expr>, args: Vec<Expr> },
29    /// method call: expr:name(args)
30    MethodCall { obj: Box<Expr>, method: String, args: Vec<Expr> },
31    /// unary op: op expr
32    Unary { op: UnOp, expr: Box<Expr> },
33    /// binary op: lhs op rhs
34    Binary { op: BinOp, lhs: Box<Expr>, rhs: Box<Expr> },
35    /// table constructor: { [k]=v, ... }
36    TableCtor(Vec<TableField>),
37    /// anonymous function: function(params) body end
38    FuncExpr { params: Vec<String>, vararg: bool, body: Vec<Stmt> },
39    /// ternary: cond ? then_val : else_val (sugar)
40    Ternary { cond: Box<Expr>, then_val: Box<Expr>, else_val: Box<Expr> },
41}
42
43#[derive(Debug, Clone, PartialEq)]
44pub enum TableField {
45    /// [expr] = expr
46    ExprKey(Expr, Expr),
47    /// name = expr
48    NameKey(String, Expr),
49    /// just expr (array position)
50    Value(Expr),
51}
52
53#[derive(Debug, Clone, Copy, PartialEq, Eq)]
54pub enum UnOp {
55    Neg,   // -
56    Not,   // not / !
57    Len,   // #
58    BitNot, // ~
59}
60
61#[derive(Debug, Clone, Copy, PartialEq, Eq)]
62pub enum BinOp {
63    Add, Sub, Mul, Div, IDiv, Mod, Pow,
64    Concat,         // ..
65    Eq, NotEq, Lt, LtEq, Gt, GtEq,
66    And, Or,
67    BitAnd, BitOr, BitXor, Shl, Shr,
68}
69
70impl BinOp {
71    pub fn precedence(self) -> u8 {
72        match self {
73            BinOp::Or                        => 1,
74            BinOp::And                       => 2,
75            BinOp::Eq | BinOp::NotEq
76              | BinOp::Lt | BinOp::LtEq
77              | BinOp::Gt | BinOp::GtEq      => 3,
78            BinOp::Concat                    => 4,
79            BinOp::BitOr                     => 5,
80            BinOp::BitXor                    => 6,
81            BinOp::BitAnd                    => 7,
82            BinOp::Shl | BinOp::Shr         => 8,
83            BinOp::Add | BinOp::Sub         => 9,
84            BinOp::Mul | BinOp::Div
85              | BinOp::IDiv | BinOp::Mod    => 10,
86            BinOp::Pow                       => 12,
87        }
88    }
89
90    pub fn is_right_assoc(self) -> bool {
91        matches!(self, BinOp::Pow | BinOp::Concat)
92    }
93}
94
95// ── Statements ─────────────────────────────────────────────────────────────
96
97#[derive(Debug, Clone, PartialEq)]
98pub enum Stmt {
99    /// local name [= expr]
100    LocalDecl { name: String, init: Option<Expr> },
101    /// local name1, name2 = expr1, expr2
102    LocalMulti { names: Vec<String>, inits: Vec<Expr> },
103    /// target = expr
104    Assign { target: Vec<Expr>, value: Vec<Expr> },
105    /// compound assign: target op= expr
106    CompoundAssign { target: Expr, op: BinOp, value: Expr },
107    /// function call statement
108    Call(Expr),
109    /// do ... end
110    Do(Vec<Stmt>),
111    /// while cond do ... end
112    While { cond: Expr, body: Vec<Stmt> },
113    /// repeat ... until cond
114    RepeatUntil { body: Vec<Stmt>, cond: Expr },
115    /// if cond then ... [elseif ...] [else ...] end
116    If { cond: Expr, then_body: Vec<Stmt>, elseif_branches: Vec<(Expr, Vec<Stmt>)>, else_body: Option<Vec<Stmt>> },
117    /// numeric for: for i = start, limit[, step] do ... end
118    NumericFor { var: String, start: Expr, limit: Expr, step: Option<Expr>, body: Vec<Stmt> },
119    /// generic for: for names in expr do ... end
120    GenericFor { vars: Vec<String>, iter: Vec<Expr>, body: Vec<Stmt> },
121    /// function name(params) body end
122    FuncDecl { name: Vec<String>, params: Vec<String>, vararg: bool, body: Vec<Stmt> },
123    /// local function name(params) body end
124    LocalFunc { name: String, params: Vec<String>, vararg: bool, body: Vec<Stmt> },
125    /// return [expr, ...]
126    Return(Vec<Expr>),
127    /// break
128    Break,
129    /// continue
130    Continue,
131    /// match expr { case pattern => body }
132    Match { expr: Expr, arms: Vec<MatchArm> },
133    /// import "module" [as name]
134    Import { path: String, alias: Option<String> },
135    /// export name
136    Export(String),
137    /// expr (standalone expression — error unless call)
138    Expr(Expr),
139}
140
141#[derive(Debug, Clone, PartialEq)]
142pub struct MatchArm {
143    pub pattern: MatchPattern,
144    pub body:    Vec<Stmt>,
145}
146
147#[derive(Debug, Clone, PartialEq)]
148pub enum MatchPattern {
149    Literal(Expr),
150    Ident(String),
151    Wildcard,
152    Table(Vec<(String, MatchPattern)>),
153}
154
155// ── Script ─────────────────────────────────────────────────────────────────
156
157/// The top-level AST for a compiled script.
158#[derive(Debug, Clone)]
159pub struct Script {
160    pub name:   String,
161    pub stmts:  Vec<Stmt>,
162}