Skip to main content

luaparse_rs/ast/
stmt.rs

1//! Statement nodes in the syntax tree.
2
3use alloc::{boxed::Box, string::String, vec::Vec};
4
5use crate::Span;
6use super::common::{Identifier, Block, Parameter, TypeAnnotation};
7use super::expr::Expr;
8use crate::ast::VariableName;
9
10/// A single parsed statement.
11///
12/// Every statement has a [`kind`](Self::kind) that tells you what it is,
13/// and a [`span`](Self::span) pointing back into the source.
14#[derive(Debug, Clone, PartialEq)]
15pub struct Stmt {
16    /// What kind of statement this is.
17    pub kind: StmtKind,
18    /// Where it appears in the source.
19    pub span: Span,
20}
21
22impl Stmt {
23    pub fn new(kind: StmtKind, span: Span) -> Self {
24        Self { kind, span }
25    }
26    
27    pub fn synthetic(kind: StmtKind) -> Self {
28        Self { kind, span: 0..0 }
29    }
30}
31
32/// All the different kinds of statement the parser can produce.
33#[derive(Debug, Clone, PartialEq)]
34pub enum StmtKind {
35    /// `local x, y = 1, 2` or `local const z = 3` (Luau).
36    LocalDeclaration(LocalDeclaration),
37    /// `function foo.bar:baz() end`.
38    FunctionDeclaration(FunctionDeclaration),
39    /// `local function foo() end`.
40    LocalFunctionDeclaration(LocalFunctionDeclaration),
41
42    /// `x, y = 1, 2`.
43    Assignment(Assignment),
44    /// `x += 1` (Luau and some versions).
45    CompoundAssignment(CompoundAssignment),
46
47    /// `if cond then ... elseif ... else ... end`.
48    IfStatement(IfStatement),
49    /// `while cond do ... end`.
50    WhileLoop(WhileLoop),
51    /// `repeat ... until cond`.
52    RepeatLoop(RepeatLoop),
53    /// `for i = 1, 10, 2 do ... end`.
54    NumericForLoop(NumericForLoop),
55    /// `for k, v in pairs(t) do ... end`.
56    GenericForLoop(GenericForLoop),
57    /// `do ... end`.
58    DoBlock(Block),
59
60    /// `return expr, expr`.
61    ReturnStatement(ReturnStatement),
62    /// `break`.
63    BreakStatement,
64    /// `continue` (Luau only).
65    ContinueStatement,
66
67    /// A bare function or method call used as a statement (e.g. `print("hi")`).
68    CallStatement(Expr),
69
70    /// `type Foo = ...` (Luau).
71    TypeDeclaration(TypeDeclaration),
72    /// `export type Foo = ...` (Luau). Wraps the inner statement.
73    ExportStatement(Box<Stmt>),
74
75    /// `goto label` (Lua 5.2+).
76    GotoStatement(GotoStatement),
77    /// `::label::` (Lua 5.2+).
78    LabelStatement(LabelStatement),
79}
80
81/// A `local` variable declaration.
82///
83/// Covers `local x = 1`, `local x, y`, and Luau's `local const z = 3`.
84#[derive(Debug, Clone, PartialEq)]
85pub struct LocalDeclaration {
86    /// The names being declared, each possibly with an attribute.
87    pub names: Vec<VariableName>,
88    /// The values being assigned, if any.
89    pub values: Option<Vec<Expr>>,
90    /// `true` when declared with `const` (Luau immutable binding).
91    pub is_const: bool,
92    /// Where it appears in the source.
93    pub span: Span,
94}
95
96impl LocalDeclaration {
97    pub fn new(names: Vec<VariableName>, values: Option<Vec<Expr>>, span: Span) -> Self {
98        Self { names, values, is_const: false, span }
99    }
100    
101    pub fn new_const(names: Vec<VariableName>, values: Vec<Expr>, span: Span) -> Self {
102        Self { names, values: Some(values), is_const: true, span }
103    }
104}
105
106/// A named function declaration like `function foo() end`.
107///
108/// Includes dotted names (`function a.b.c() end`) and method syntax
109/// (`function a:b() end`).
110#[derive(Debug, Clone, PartialEq)]
111pub struct FunctionDeclaration {
112    /// Attributes like `@native` (Luau).
113    pub attributes: Vec<Attribute>,
114    /// The function's name, possibly dotted or with a method component.
115    pub name: FunctionName,
116    /// The parameter list.
117    pub parameters: Vec<Parameter>,
118    /// An optional return type annotation (Luau).
119    pub return_type: Option<TypeAnnotation>,
120    /// The function body.
121    pub body: Block,
122    /// Where it appears in the source.
123    pub span: Span,
124}
125
126impl FunctionDeclaration {
127    pub fn new(
128        attributes: Vec<Attribute>,
129        name: FunctionName,
130        parameters: Vec<Parameter>,
131        return_type: Option<TypeAnnotation>,
132        body: Block,
133        span: Span,
134    ) -> Self {
135        Self {
136            attributes,
137            name,
138            parameters,
139            return_type,
140            body,
141            span,
142        }
143    }
144}
145
146/// A `local function foo() end` declaration.
147#[derive(Debug, Clone, PartialEq)]
148pub struct LocalFunctionDeclaration {
149    /// Attributes like `@native` (Luau).
150    pub attributes: Vec<Attribute>,
151    /// The function name (always a single identifier for local functions).
152    pub name: Identifier,
153    /// The parameter list.
154    pub parameters: Vec<Parameter>,
155    /// An optional return type annotation (Luau).
156    pub return_type: Option<TypeAnnotation>,
157    /// The function body.
158    pub body: Block,
159    /// Where it appears in the source.
160    pub span: Span,
161}
162
163impl LocalFunctionDeclaration {
164    pub fn new(
165        attributes: Vec<Attribute>,
166        name: Identifier,
167        parameters: Vec<Parameter>,
168        return_type: Option<TypeAnnotation>,
169        body: Block,
170        span: Span,
171    ) -> Self {
172        Self {
173            attributes,
174            name,
175            parameters,
176            return_type,
177            body,
178            span,
179        }
180    }
181}
182
183/// The name part of a function declaration.
184///
185/// A name like `a.b.c:d` has segments `[a, b, c]` and method `d`.
186#[derive(Debug, Clone, PartialEq)]
187pub struct FunctionName {
188    /// The dotted path segments (at least one).
189    pub segments: Vec<Identifier>,
190    /// The method name after `:`, if any.
191    pub method: Option<Identifier>,
192}
193
194impl FunctionName {
195    pub fn new(segments: Vec<Identifier>, method: Option<Identifier>) -> Self {
196        Self { segments, method }
197    }
198    
199    pub fn simple(name: Identifier) -> Self {
200        Self {
201            segments: vec![name],
202            method: None,
203        }
204    }
205}
206
207/// An assignment like `x, y = 1, 2`.
208#[derive(Debug, Clone, PartialEq)]
209pub struct Assignment {
210    /// What's being assigned to.
211    pub targets: Vec<AssignmentTarget>,
212    /// The values on the right side.
213    pub values: Vec<Expr>,
214    /// Where it appears in the source.
215    pub span: Span,
216}
217
218impl Assignment {
219    pub fn new(targets: Vec<AssignmentTarget>, values: Vec<Expr>, span: Span) -> Self {
220        Self {
221            targets,
222            values,
223            span,
224        }
225    }
226}
227
228/// A compound assignment like `x += 1` or `s ..= "!"` (Luau).
229#[derive(Debug, Clone, PartialEq)]
230pub struct CompoundAssignment {
231    /// What's being assigned to.
232    pub target: AssignmentTarget,
233    /// Which compound operator (`+=`, `-=`, etc.).
234    pub operator: CompoundOperator,
235    /// The right side value.
236    pub value: Expr,
237    /// Where it appears in the source.
238    pub span: Span,
239}
240
241impl CompoundAssignment {
242    pub fn new(
243        target: AssignmentTarget,
244        operator: CompoundOperator,
245        value: Expr,
246        span: Span,
247    ) -> Self {
248        Self {
249            target,
250            operator,
251            value,
252            span,
253        }
254    }
255}
256
257/// Something that can appear on the left side of `=`.
258#[derive(Debug, Clone, PartialEq)]
259pub enum AssignmentTarget {
260    /// A plain variable name, like `x`.
261    Identifier(Identifier),
262    /// A dot field, like `obj.field`.
263    FieldAccess {
264        base: Box<Expr>,
265        field: Identifier,
266        span: Span,
267    },
268    /// A bracket index, like `tbl[key]`.
269    IndexAccess {
270        base: Box<Expr>,
271        index: Box<Expr>,
272        span: Span,
273    },
274}
275
276impl AssignmentTarget {
277    pub fn span(&self) -> &Span {
278        match self {
279            AssignmentTarget::Identifier(id) => &id.span,
280            AssignmentTarget::FieldAccess { span, .. } => span,
281            AssignmentTarget::IndexAccess { span, .. } => span,
282        }
283    }
284}
285
286/// The operator in a compound assignment (`+=`, `-=`, `..=`, etc.).
287#[derive(Debug, Clone, Copy, PartialEq, Eq)]
288pub enum CompoundOperator {
289    /// `+=`
290    Add,
291    /// `-=`
292    Subtract,
293    /// `*=`
294    Multiply,
295    /// `/=`
296    Divide,
297    /// `//=`
298    FloorDiv,
299    /// `%=`
300    Modulo,
301    /// `^=`
302    Power,
303    /// `..=`
304    Concat,
305}
306
307/// An `if/elseif/else/end` statement.
308#[derive(Debug, Clone, PartialEq)]
309pub struct IfStatement {
310    /// The condition after `if`.
311    pub condition: Expr,
312    /// The `then` block.
313    pub then_block: Block,
314    /// Zero or more `elseif` branches.
315    pub elseif_branches: Vec<ElseIfBranch>,
316    /// The `else` block, if present.
317    pub else_block: Option<Block>,
318    /// Where it appears in the source.
319    pub span: Span,
320}
321
322impl IfStatement {
323    pub fn new(
324        condition: Expr,
325        then_block: Block,
326        elseif_branches: Vec<ElseIfBranch>,
327        else_block: Option<Block>,
328        span: Span,
329    ) -> Self {
330        Self {
331            condition,
332            then_block,
333            elseif_branches,
334            else_block,
335            span,
336        }
337    }
338}
339
340/// One `elseif cond then ...` branch inside an [`IfStatement`].
341#[derive(Debug, Clone, PartialEq)]
342pub struct ElseIfBranch {
343    /// The condition after `elseif`.
344    pub condition: Expr,
345    /// The block to run if the condition is true.
346    pub then_block: Block,
347}
348
349impl ElseIfBranch {
350    pub fn new(condition: Expr, then_block: Block) -> Self {
351        Self {
352            condition,
353            then_block,
354        }
355    }
356}
357
358/// A `while cond do ... end` loop.
359#[derive(Debug, Clone, PartialEq)]
360pub struct WhileLoop {
361    /// The loop condition.
362    pub condition: Expr,
363    /// The loop body.
364    pub body: Block,
365    /// Where it appears in the source.
366    pub span: Span,
367}
368
369impl WhileLoop {
370    pub fn new(condition: Expr, body: Block, span: Span) -> Self {
371        Self {
372            condition,
373            body,
374            span,
375        }
376    }
377}
378
379/// A `repeat ... until cond` loop.
380#[derive(Debug, Clone, PartialEq)]
381pub struct RepeatLoop {
382    /// The loop body (runs at least once).
383    pub body: Block,
384    /// The condition checked after each iteration.
385    pub condition: Expr,
386    /// Where it appears in the source.
387    pub span: Span,
388}
389
390impl RepeatLoop {
391    pub fn new(body: Block, condition: Expr, span: Span) -> Self {
392        Self {
393            body,
394            condition,
395            span,
396        }
397    }
398}
399
400/// A numeric `for` loop: `for i = start, end, step do ... end`.
401#[derive(Debug, Clone, PartialEq)]
402pub struct NumericForLoop {
403    /// The loop variable.
404    pub variable: Identifier,
405    /// The starting value.
406    pub start: Expr,
407    /// The ending value.
408    pub end: Expr,
409    /// The step value (defaults to 1 if omitted).
410    pub step: Option<Expr>,
411    /// The loop body.
412    pub body: Block,
413    /// Where it appears in the source.
414    pub span: Span,
415}
416
417impl NumericForLoop {
418    pub fn new(
419        variable: Identifier,
420        start: Expr,
421        end: Expr,
422        step: Option<Expr>,
423        body: Block,
424        span: Span,
425    ) -> Self {
426        Self {
427            variable,
428            start,
429            end,
430            step,
431            body,
432            span,
433        }
434    }
435}
436
437/// A generic `for` loop: `for k, v in pairs(t) do ... end`.
438#[derive(Debug, Clone, PartialEq)]
439pub struct GenericForLoop {
440    /// The loop variables (e.g. `k, v`).
441    pub variables: Vec<Identifier>,
442    /// The iterator expressions (e.g. `pairs(t)`).
443    pub expressions: Vec<Expr>,
444    /// The loop body.
445    pub body: Block,
446    /// Where it appears in the source.
447    pub span: Span,
448}
449
450impl GenericForLoop {
451    pub fn new(
452        variables: Vec<Identifier>,
453        expressions: Vec<Expr>,
454        body: Block,
455        span: Span,
456    ) -> Self {
457        Self {
458            variables,
459            expressions,
460            body,
461            span,
462        }
463    }
464}
465
466/// A `return` statement with zero or more values.
467#[derive(Debug, Clone, PartialEq)]
468pub struct ReturnStatement {
469    /// The returned values (can be empty for a bare `return`).
470    pub values: Vec<Expr>,
471    /// Where it appears in the source.
472    pub span: Span,
473}
474
475impl ReturnStatement {
476    pub fn new(values: Vec<Expr>, span: Span) -> Self {
477        Self { values, span }
478    }
479}
480
481/// An attribute like `@native` or `@checked` (Luau).
482#[derive(Debug, Clone, PartialEq)]
483pub struct Attribute {
484    /// The attribute name (e.g. `native`).
485    pub name: Identifier,
486    /// Optional fields inside the attribute.
487    pub fields: Option<Vec<AttributeField>>,
488    /// Where it appears in the source.
489    pub span: Span,
490}
491
492impl Attribute {
493    pub fn new(name: Identifier, fields: Option<Vec<AttributeField>>, span: Span) -> Self {
494        Self { name, fields, span }
495    }
496}
497
498/// A single field inside an [`Attribute`].
499#[derive(Debug, Clone, PartialEq)]
500pub struct AttributeField {
501    /// The field name, if it's a key/value pair.
502    pub key: Option<Identifier>,
503    /// The field value.
504    pub value: AttributeValue,
505}
506
507impl AttributeField {
508    pub fn new(key: Option<Identifier>, value: AttributeValue) -> Self {
509        Self { key, value }
510    }
511}
512
513/// A value inside an [`AttributeField`].
514#[derive(Debug, Clone, PartialEq)]
515pub enum AttributeValue {
516    /// A string literal.
517    String(String),
518    /// A number literal (stored as the raw text).
519    Number(String),
520    /// `true` or `false`.
521    Boolean(bool),
522    /// A plain identifier.
523    Identifier(Identifier),
524}
525
526/// A `type Foo = ...` declaration (Luau).
527///
528/// This is the lightweight version stored in the statement list.
529/// For the fully resolved type expression, see [`TypeDeclarationFull`](super::types::TypeDeclarationFull).
530#[derive(Debug, Clone, PartialEq)]
531pub struct TypeDeclaration {
532    /// Whether this was declared with `export`.
533    pub exported: bool,
534    /// The name of the type alias.
535    pub name: Identifier,
536    /// The span of the generic parameters, if any.
537    pub generics_span: Option<Span>,
538    /// The span of the type expression on the right side.
539    pub type_span: Span,
540    /// Where the whole declaration appears in the source.
541    pub span: Span,
542}
543
544impl TypeDeclaration {
545    pub fn new(
546        exported: bool,
547        name: Identifier,
548        generics_span: Option<Span>,
549        type_span: Span,
550        span: Span,
551    ) -> Self {
552        Self {
553            exported,
554            name,
555            generics_span,
556            type_span,
557            span,
558        }
559    }
560}
561
562/// A `goto label` statement (Lua 5.2+).
563#[derive(Debug, Clone, PartialEq)]
564pub struct GotoStatement {
565    /// The label to jump to.
566    pub label: Identifier,
567    /// Where it appears in the source.
568    pub span: Span,
569}
570
571impl GotoStatement {
572    pub fn new(label: Identifier, span: Span) -> Self {
573        Self { label, span }
574    }
575}
576
577/// A `::label::` label definition (Lua 5.2+).
578#[derive(Debug, Clone, PartialEq)]
579pub struct LabelStatement {
580    /// The label name.
581    pub name: Identifier,
582    /// Where it appears in the source.
583    pub span: Span,
584}
585
586impl LabelStatement {
587    pub fn new(name: Identifier, span: Span) -> Self {
588        Self { name, span }
589    }
590}