Skip to main content

valua_ast/
lib.rs

1//! Typed AST for Lua 5.5 — shared by parser, transformer, and code generator.
2//!
3//! Also houses [`LuaTarget`], the cross-cutting compilation-target enum used by
4//! both `valua-lint` and `valua-codegen`. Placing it here avoids an illegal
5//! `valua-lint → valua-codegen` dependency (see architectural note in PRD §6.2).
6
7use valua_diagnostics::Span;
8
9#[cfg(feature = "serde")]
10use serde::Serialize;
11
12// ── Top-level ────────────────────────────────────────────────────────────────
13
14/// Root node: a sequence of statements followed by an optional return.
15#[cfg_attr(feature = "serde", derive(Serialize))]
16#[derive(Debug, Clone)]
17pub struct Block {
18    pub stmts: Vec<Statement>,
19    pub span: Span,
20}
21
22// ── Statements ───────────────────────────────────────────────────────────────
23
24/// Every kind of statement in Lua 5.5 (Lua 5.4 is a fully supported subset).
25#[cfg_attr(feature = "serde", derive(Serialize))]
26#[derive(Debug, Clone)]
27pub enum Statement {
28    /// `local x [<attr>] = expr`
29    LocalDecl(LocalDecl),
30    /// `var = expr` or multi-assign
31    Assign(Assign),
32    /// `do … end`
33    Do(Do),
34    /// `while expr do … end`
35    While(While),
36    /// `repeat … until expr`
37    Repeat(Repeat),
38    /// `if … then … [elseif …] [else …] end`
39    If(If),
40    /// `for i = start, limit [, step] do … end`
41    NumericFor(NumericFor),
42    /// `for k, v in iter do … end`
43    GenericFor(GenericFor),
44    /// `function name(…) … end`
45    FunctionDecl(FunctionDecl),
46    /// `local function name(…) … end`
47    LocalFunctionDecl(LocalFunctionDecl),
48    /// `return [exprlist]`
49    Return(Return),
50    /// `break`
51    Break(Span),
52    /// `goto label`
53    Goto(Goto),
54    /// `::label::`
55    Label(Label),
56    /// A function-call used as a statement.
57    ExprStmt(Expression),
58}
59
60/// `local varlist [<attr>] [= exprlist]`
61#[cfg_attr(feature = "serde", derive(Serialize))]
62#[derive(Debug, Clone)]
63pub struct LocalDecl {
64    pub names: Vec<LocalName>,
65    pub values: Vec<Expression>,
66    pub span: Span,
67}
68
69/// One name in a local declaration, with its optional attribute.
70#[cfg_attr(feature = "serde", derive(Serialize))]
71#[derive(Debug, Clone)]
72pub struct LocalName {
73    pub name: String,
74    /// Lua 5.4/5.5 `<const>` or `<close>` attribute.
75    pub attribute: Option<Attribute>,
76    pub span: Span,
77}
78
79/// Lua 5.4/5.5 variable attributes.
80#[cfg_attr(feature = "serde", derive(Serialize))]
81#[derive(Debug, Clone, Copy, PartialEq, Eq)]
82pub enum Attribute {
83    /// `<const>` — value must not change after initialisation.
84    Const,
85    /// `<close>` — to-be-closed variable; `__close` metamethod called on scope exit.
86    Close,
87}
88
89/// Simple or multi-target assignment: `targets = values`.
90#[cfg_attr(feature = "serde", derive(Serialize))]
91#[derive(Debug, Clone)]
92pub struct Assign {
93    pub targets: Vec<Expression>,
94    pub values: Vec<Expression>,
95    pub span: Span,
96}
97
98/// `do … end` block.
99#[cfg_attr(feature = "serde", derive(Serialize))]
100#[derive(Debug, Clone)]
101pub struct Do {
102    pub body: Block,
103    pub span: Span,
104}
105
106/// `while cond do body end`
107#[cfg_attr(feature = "serde", derive(Serialize))]
108#[derive(Debug, Clone)]
109pub struct While {
110    pub condition: Box<Expression>,
111    pub body: Block,
112    pub span: Span,
113}
114
115/// `repeat body until cond`
116#[cfg_attr(feature = "serde", derive(Serialize))]
117#[derive(Debug, Clone)]
118pub struct Repeat {
119    pub body: Block,
120    pub condition: Box<Expression>,
121    pub span: Span,
122}
123
124/// `if cond then body [elseif …]* [else …] end`
125#[cfg_attr(feature = "serde", derive(Serialize))]
126#[derive(Debug, Clone)]
127pub struct If {
128    pub condition: Box<Expression>,
129    pub then_block: Block,
130    pub elseif_clauses: Vec<ElseIf>,
131    pub else_block: Option<Block>,
132    pub span: Span,
133}
134
135/// One `elseif cond then body` branch.
136#[cfg_attr(feature = "serde", derive(Serialize))]
137#[derive(Debug, Clone)]
138pub struct ElseIf {
139    pub condition: Box<Expression>,
140    pub body: Block,
141    pub span: Span,
142}
143
144/// `for i = start, limit [, step] do body end`
145#[cfg_attr(feature = "serde", derive(Serialize))]
146#[derive(Debug, Clone)]
147pub struct NumericFor {
148    pub var: String,
149    pub start: Box<Expression>,
150    pub limit: Box<Expression>,
151    pub step: Option<Box<Expression>>,
152    pub body: Block,
153    pub span: Span,
154}
155
156/// `for namelist in exprlist do body end`
157#[cfg_attr(feature = "serde", derive(Serialize))]
158#[derive(Debug, Clone)]
159pub struct GenericFor {
160    pub vars: Vec<String>,
161    pub iterators: Vec<Expression>,
162    pub body: Block,
163    pub span: Span,
164}
165
166/// `function name.path:method (params) body end`
167#[cfg_attr(feature = "serde", derive(Serialize))]
168#[derive(Debug, Clone)]
169pub struct FunctionDecl {
170    pub name: FunctionName,
171    pub func: FunctionBody,
172    pub span: Span,
173}
174
175/// `local function name (params) body end`
176#[cfg_attr(feature = "serde", derive(Serialize))]
177#[derive(Debug, Clone)]
178pub struct LocalFunctionDecl {
179    pub name: String,
180    pub func: FunctionBody,
181    pub span: Span,
182}
183
184/// Dotted function name with optional method receiver: `a.b.c:method`.
185#[cfg_attr(feature = "serde", derive(Serialize))]
186#[derive(Debug, Clone)]
187pub struct FunctionName {
188    pub parts: Vec<String>,
189    /// Present when declared as a method (`:`).
190    pub method: Option<String>,
191    pub span: Span,
192}
193
194/// `return [exprlist]`
195#[cfg_attr(feature = "serde", derive(Serialize))]
196#[derive(Debug, Clone)]
197pub struct Return {
198    pub values: Vec<Expression>,
199    pub span: Span,
200}
201
202/// `goto label`
203#[cfg_attr(feature = "serde", derive(Serialize))]
204#[derive(Debug, Clone)]
205pub struct Goto {
206    pub label: String,
207    pub span: Span,
208}
209
210/// `::label::`
211#[cfg_attr(feature = "serde", derive(Serialize))]
212#[derive(Debug, Clone)]
213pub struct Label {
214    pub name: String,
215    pub span: Span,
216}
217
218// ── Function body ────────────────────────────────────────────────────────────
219
220/// Shared representation for anonymous and named function bodies.
221#[cfg_attr(feature = "serde", derive(Serialize))]
222#[derive(Debug, Clone)]
223pub struct FunctionBody {
224    pub params: Vec<Param>,
225    pub is_vararg: bool,
226    pub body: Block,
227    pub span: Span,
228}
229
230/// One parameter in a function signature.
231#[cfg_attr(feature = "serde", derive(Serialize))]
232#[derive(Debug, Clone)]
233pub struct Param {
234    pub name: String,
235    pub span: Span,
236}
237
238// ── Expressions ──────────────────────────────────────────────────────────────
239
240/// All expression forms in Lua 5.5.
241#[cfg_attr(feature = "serde", derive(Serialize))]
242#[derive(Debug, Clone)]
243pub enum Expression {
244    Nil(Span),
245    True(Span),
246    False(Span),
247    Integer(i64, Span),
248    Float(f64, Span),
249    String(String, Span),
250    Vararg(Span),
251    Name(String, Span),
252    /// `expr.field`
253    Index(Box<Expression>, String, Span),
254    /// `expr[key]`
255    IndexExpr(Box<Expression>, Box<Expression>, Span),
256    /// Binary operation: `lhs op rhs`.
257    BinOp(Box<Expression>, BinaryOp, Box<Expression>, Span),
258    /// Unary operation: `op expr`.
259    UnOp(UnaryOp, Box<Expression>, Span),
260    /// Function call: `func(args)` or `obj:method(args)`.
261    Call(Call),
262    /// `function (params) body end`
263    Function(FunctionBody),
264    /// Table constructor: `{ [field,]* }`.
265    Table(TableConstructor),
266}
267
268impl Expression {
269    /// Returns the span of this expression.
270    #[must_use]
271    #[allow(clippy::match_same_arms)]
272    pub fn span(&self) -> Span {
273        match self {
274            Expression::Nil(s)
275            | Expression::True(s)
276            | Expression::False(s)
277            | Expression::Vararg(s) => *s,
278            Expression::Integer(_, s) => *s,
279            Expression::Float(_, s) => *s,
280            Expression::String(_, s) => *s,
281            Expression::Name(_, s) => *s,
282            Expression::Index(_, _, s) => *s,
283            Expression::IndexExpr(_, _, s) => *s,
284            Expression::BinOp(_, _, _, s) => *s,
285            Expression::UnOp(_, _, s) => *s,
286            Expression::Call(c) => c.span(),
287            Expression::Function(f) => f.span,
288            Expression::Table(t) => t.span,
289        }
290    }
291}
292
293// ── Operators ────────────────────────────────────────────────────────────────
294
295/// Binary operators in Lua 5.5 (includes bitwise and integer division).
296#[cfg_attr(feature = "serde", derive(Serialize))]
297#[derive(Debug, Clone, Copy, PartialEq, Eq)]
298pub enum BinaryOp {
299    // Arithmetic
300    Add,
301    Sub,
302    Mul,
303    Div,
304    Mod,
305    Pow,
306    /// `//` integer division (5.3+).
307    IDiv,
308    // Comparison
309    Lt,
310    Le,
311    Gt,
312    Ge,
313    Eq,
314    Ne,
315    // Logical
316    And,
317    Or,
318    // Bitwise (5.3+)
319    /// `&`
320    BitwiseAnd,
321    /// `|`
322    BitwiseOr,
323    /// `~` (binary)
324    BitwiseXor,
325    /// `<<`
326    Shl,
327    /// `>>`
328    Shr,
329    // String
330    /// `..`
331    Concat,
332}
333
334/// Unary operators in Lua 5.5.
335#[cfg_attr(feature = "serde", derive(Serialize))]
336#[derive(Debug, Clone, Copy, PartialEq, Eq)]
337pub enum UnaryOp {
338    /// `-`
339    Neg,
340    /// `not`
341    Not,
342    /// `#`
343    Len,
344    /// `~` (unary bitwise NOT, 5.3+)
345    BitwiseNot,
346}
347
348// ── Function calls ───────────────────────────────────────────────────────────
349
350/// A function-call expression or method-call expression.
351#[cfg_attr(feature = "serde", derive(Serialize))]
352#[derive(Debug, Clone)]
353pub enum Call {
354    /// `func(args)`
355    Call {
356        func: Box<Expression>,
357        args: Vec<Expression>,
358        span: Span,
359    },
360    /// `obj:method(args)`
361    MethodCall {
362        obj: Box<Expression>,
363        method: String,
364        args: Vec<Expression>,
365        span: Span,
366    },
367}
368
369impl Call {
370    #[must_use]
371    pub fn span(&self) -> Span {
372        match self {
373            Call::Call { span, .. } | Call::MethodCall { span, .. } => *span,
374        }
375    }
376}
377
378// ── Table constructor ────────────────────────────────────────────────────────
379
380/// `{ field, field, … }`
381#[cfg_attr(feature = "serde", derive(Serialize))]
382#[derive(Debug, Clone)]
383pub struct TableConstructor {
384    pub fields: Vec<TableField>,
385    pub span: Span,
386}
387
388/// One field in a table constructor.
389#[cfg_attr(feature = "serde", derive(Serialize))]
390#[derive(Debug, Clone)]
391pub enum TableField {
392    /// `[expr] = expr`
393    ExprKey {
394        key: Box<Expression>,
395        value: Box<Expression>,
396        span: Span,
397    },
398    /// `name = expr`
399    NameKey {
400        key: String,
401        value: Box<Expression>,
402        span: Span,
403    },
404    /// `expr` (positional)
405    Positional(Expression),
406}
407
408#[cfg(test)]
409mod tests {
410    use super::*;
411
412    #[test]
413    #[ignore = "TODO: verify Block can be constructed and iterated"]
414    fn test_block_construction() {
415        todo!()
416    }
417
418    #[test]
419    #[ignore = "TODO: verify Attribute variants are distinct"]
420    fn test_attribute_variants() {
421        todo!()
422    }
423
424    #[test]
425    #[ignore = "TODO: Expression::span() returns the correct span for each variant"]
426    fn test_expression_span() {
427        todo!()
428    }
429
430    #[test]
431    #[ignore = "TODO: BinaryOp includes all bitwise variants"]
432    fn test_binary_op_bitwise_variants() {
433        todo!()
434    }
435}
436
437// ── Compilation target ────────────────────────────────────────────────────────
438
439/// Target Lua runtime — controls which built-ins and polyfills are assumed available.
440///
441/// Lives in `valua-ast` (not `valua-codegen`) so that both `valua-lint` and
442/// `valua-codegen` can depend on it without creating a cross-layer coupling.
443/// `valua-codegen` re-exports this type as `pub use valua_ast::LuaTarget`.
444#[cfg_attr(feature = "serde", derive(serde::Serialize))]
445#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
446pub enum LuaTarget {
447    /// Plain Lua 5.1 (PUC reference implementation).
448    #[default]
449    Lua51,
450    /// `LuaJIT` 2.x — has the `bit` library and some Lua 5.1 extensions.
451    LuaJIT,
452}