Skip to main content

luadec_rust/lua51/
ast.rs

1/// Lua AST node types for decompiled output.
2
3/// A complete Lua function body (chunk).
4#[derive(Debug, Clone, PartialEq)]
5pub struct Function {
6    pub params: Vec<String>,
7    pub is_vararg: bool,
8    pub body: Block,
9}
10
11/// A sequence of statements.
12pub type Block = Vec<Stat>;
13
14/// Lua statement.
15#[derive(Debug, Clone, PartialEq)]
16pub enum Stat {
17    /// `local var1, var2 = expr1, expr2`
18    LocalAssign {
19        names: Vec<String>,
20        exprs: Vec<Expr>,
21    },
22    /// `var1, var2 = expr1, expr2`
23    Assign {
24        targets: Vec<Expr>,
25        values: Vec<Expr>,
26    },
27    /// `expr(args)` (function call as statement)
28    Call(CallExpr),
29    /// `do ... end`
30    DoBlock(Block),
31    /// `while cond do ... end`
32    While {
33        cond: Expr,
34        body: Block,
35    },
36    /// `repeat ... until cond`
37    Repeat {
38        body: Block,
39        cond: Expr,
40    },
41    /// `if cond then ... elseif ... else ... end`
42    If {
43        cond: Expr,
44        then_block: Block,
45        elseif_clauses: Vec<(Expr, Block)>,
46        else_block: Option<Block>,
47    },
48    /// `for name = start, limit, step do ... end`
49    NumericFor {
50        name: String,
51        start: Expr,
52        limit: Expr,
53        step: Option<Expr>,
54        body: Block,
55    },
56    /// `for name1, name2 in iter do ... end`
57    GenericFor {
58        names: Vec<String>,
59        iterators: Vec<Expr>,
60        body: Block,
61    },
62    /// `return expr1, expr2, ...`
63    Return(Vec<Expr>),
64    /// `break`
65    Break,
66    /// Comment (for readability of decompiled output).
67    Comment(String),
68}
69
70/// Lua expression.
71#[derive(Debug, Clone, PartialEq)]
72pub enum Expr {
73    /// `nil`
74    Nil,
75    /// `true` / `false`
76    Bool(bool),
77    /// Numeric literal
78    Number(NumLit),
79    /// String literal (raw bytes; emitter handles encoding)
80    StringLit(Vec<u8>),
81    /// `...`
82    VarArg,
83    /// Variable reference by name
84    Name(String),
85    /// `table[key]` or `table.field`
86    Index(Box<Expr>, Box<Expr>),
87    /// `table.name` (syntactic sugar for Index with string key)
88    Field(Box<Expr>, String),
89    /// `prefix:method(args)` (syntactic sugar)
90    MethodCall(Box<CallExpr>),
91    /// Function call `f(args)`
92    FuncCall(Box<CallExpr>),
93    /// Binary operation
94    BinOp(BinOp, Box<Expr>, Box<Expr>),
95    /// Unary operation
96    UnOp(UnOp, Box<Expr>),
97    /// `function(...) ... end`
98    FunctionDef(Box<Function>),
99    /// Table constructor `{ ... }`
100    Table(Vec<TableField>),
101    /// Placeholder for registers not yet resolved.
102    Register(u32),
103    /// Upvalue reference by index.
104    Upvalue(u32),
105    /// Global variable reference.
106    Global(String),
107}
108
109/// Numeric literal (preserves integer vs float distinction).
110#[derive(Debug, Clone, PartialEq)]
111pub enum NumLit {
112    Int(i64),
113    Float(f64),
114}
115
116/// Function/method call.
117#[derive(Debug, Clone, PartialEq)]
118pub struct CallExpr {
119    pub func: Expr,
120    pub args: Vec<Expr>,
121}
122
123/// Table constructor field.
124#[derive(Debug, Clone, PartialEq)]
125pub enum TableField {
126    /// `[expr] = expr`
127    IndexField(Expr, Expr),
128    /// `name = expr`
129    NameField(String, Expr),
130    /// Positional value
131    Value(Expr),
132}
133
134/// Binary operators.
135#[derive(Debug, Clone, Copy, PartialEq, Eq)]
136pub enum BinOp {
137    Add,
138    Sub,
139    Mul,
140    Div,
141    Mod,
142    Pow,
143    Concat,
144    Eq,
145    Ne,
146    Lt,
147    Le,
148    Gt,
149    Ge,
150    And,
151    Or,
152}
153
154/// Unary operators.
155#[derive(Debug, Clone, Copy, PartialEq, Eq)]
156pub enum UnOp {
157    Neg,
158    Not,
159    Len,
160}
161
162/// Operator precedence (higher = binds tighter).
163impl BinOp {
164    pub fn precedence(self) -> u8 {
165        match self {
166            BinOp::Or => 1,
167            BinOp::And => 2,
168            BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge => 3,
169            BinOp::Concat => 4,
170            BinOp::Add | BinOp::Sub => 5,
171            BinOp::Mul | BinOp::Div | BinOp::Mod => 6,
172            BinOp::Pow => 7,
173        }
174    }
175
176    /// Whether the operator is right-associative.
177    pub fn is_right_assoc(self) -> bool {
178        matches!(self, BinOp::Pow | BinOp::Concat)
179    }
180
181    pub fn symbol(self) -> &'static str {
182        match self {
183            BinOp::Add => "+",
184            BinOp::Sub => "-",
185            BinOp::Mul => "*",
186            BinOp::Div => "/",
187            BinOp::Mod => "%",
188            BinOp::Pow => "^",
189            BinOp::Concat => "..",
190            BinOp::Eq => "==",
191            BinOp::Ne => "~=",
192            BinOp::Lt => "<",
193            BinOp::Le => "<=",
194            BinOp::Gt => ">",
195            BinOp::Ge => ">=",
196            BinOp::And => "and",
197            BinOp::Or => "or",
198        }
199    }
200}
201
202impl UnOp {
203    pub fn precedence(self) -> u8 {
204        8 // all unary ops have higher precedence than any binary op
205    }
206
207    pub fn symbol(self) -> &'static str {
208        match self {
209            UnOp::Neg => "-",
210            UnOp::Not => "not ",
211            UnOp::Len => "#",
212        }
213    }
214}