Skip to main content

luaparse_rs/ast/
expr.rs

1//! Expression 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};
7
8/// A single parsed expression.
9///
10/// Every expression has a [`kind`](Self::kind) that tells you what it is,
11/// and a [`span`](Self::span) pointing back into the source.
12#[derive(Debug, Clone, PartialEq)]
13pub struct Expr {
14    /// What kind of expression this is.
15    pub kind: ExprKind,
16    /// Where it appears in the source.
17    pub span: Span,
18}
19
20impl Expr {
21    pub fn new(kind: ExprKind, span: Span) -> Self {
22        Self { kind, span }
23    }
24    
25    pub fn synthetic(kind: ExprKind) -> Self {
26        Self { kind, span: 0..0 }
27    }
28}
29
30/// All the different kinds of expression the parser can produce.
31#[derive(Debug, Clone, PartialEq)]
32pub enum ExprKind {
33    /// The `nil` literal.
34    Nil,
35    /// `true` or `false`.
36    Boolean(bool),
37    /// A number like `42`, `3.14`, or `0xFF`.
38    Number(NumberLiteral),
39    /// A string like `"hello"` or `'world'`.
40    String(StringLiteral),
41    /// The `...` vararg expression.
42    Vararg,
43
44    /// A table constructor: `{1, 2, key = "val"}`.
45    Table(TableConstructor),
46    /// An anonymous function: `function(x) return x end`.
47    Function(FunctionExpr),
48
49    /// A variable reference: `foo`.
50    Identifier(Identifier),
51    /// A dot field access: `obj.field`.
52    FieldAccess(FieldAccess),
53    /// A bracket index: `tbl[key]`.
54    IndexAccess(IndexAccess),
55
56    /// A unary operation: `-x`, `not x`, `#t`, `~x`.
57    Unary(UnaryExpr),
58    /// A binary operation: `a + b`, `x and y`, `s .. t`.
59    Binary(BinaryExpr),
60
61    /// A function call: `foo(1, 2)`.
62    Call(CallExpr),
63    /// A method call: `obj:method(1, 2)`.
64    MethodCall(MethodCallExpr),
65
66    /// A Luau if expression: `if cond then a else b`.
67    IfExpression(IfExpression),
68    /// A Luau interpolated string: `` `hello {name}` ``.
69    InterpolatedString(InterpolatedString),
70    /// A Luau type assertion: `expr :: Type`.
71    TypeAssertion(TypeAssertion),
72
73    /// A parenthesized expression: `(expr)`.
74    Parenthesized(Box<Expr>),
75}
76
77/// A number literal, stored as the raw source text.
78///
79/// Use [`parse_f64`](Self::parse_f64) to get the numeric value.
80#[derive(Debug, Clone, PartialEq)]
81pub struct NumberLiteral {
82    /// The raw text from the source (e.g. `"0xFF"`, `"3.14e2"`).
83    pub raw: String,
84    /// Where it appears in the source.
85    pub span: Span,
86}
87
88impl NumberLiteral {
89    pub fn new(raw: String, span: Span) -> Self {
90        Self { raw, span }
91    }
92    
93    pub fn parse_f64(&self) -> Option<f64> {
94        let cleaned = self.raw.replace('_', "");
95        
96        if cleaned.starts_with("0x") || cleaned.starts_with("0X") {
97            // TODO
98            u64::from_str_radix(&cleaned[2..].split('.').next()?, 16)
99                .ok()
100                .map(|v| v as f64)
101        } else if cleaned.starts_with("0b") || cleaned.starts_with("0B") {
102            u64::from_str_radix(&cleaned[2..], 2)
103                .ok()
104                .map(|v| v as f64)
105        } else {
106            cleaned.parse().ok()
107        }
108    }
109}
110
111/// A string literal with escape sequences already resolved.
112#[derive(Debug, Clone, PartialEq)]
113pub struct StringLiteral {
114    /// The string content after processing escapes.
115    pub value: String,
116    /// Where it appears in the source.
117    pub span: Span,
118}
119
120impl StringLiteral {
121    pub fn new(value: String, span: Span) -> Self {
122        Self { value, span }
123    }
124}
125
126/// A table constructor: `{1, 2, key = "val", [expr] = val}`.
127#[derive(Debug, Clone, PartialEq)]
128pub struct TableConstructor {
129    /// The fields in the table, in order.
130    pub fields: Vec<TableField>,
131    /// Where it appears in the source.
132    pub span: Span,
133}
134
135impl TableConstructor {
136    pub fn new(fields: Vec<TableField>, span: Span) -> Self {
137        Self { fields, span }
138    }
139}
140
141/// A single entry in a table constructor.
142#[derive(Debug, Clone, PartialEq)]
143pub struct TableField {
144    /// What kind of table entry this is.
145    pub kind: TableFieldKind,
146    /// Where it appears in the source.
147    pub span: Span,
148}
149
150impl TableField {
151    pub fn new(kind: TableFieldKind, span: Span) -> Self {
152        Self { kind, span }
153    }
154}
155
156/// The different ways to define a table entry.
157#[derive(Debug, Clone, PartialEq)]
158pub enum TableFieldKind {
159    /// `[expr] = value`.
160    Bracketed { key: Expr, value: Expr },
161    /// `name = value`.
162    Named { name: Identifier, value: Expr },
163    /// A positional value (no key), like `{1, 2, 3}`.
164    Positional(Expr),
165}
166
167/// An anonymous function expression: `function(x, y) return x + y end`.
168#[derive(Debug, Clone, PartialEq)]
169pub struct FunctionExpr {
170    /// The parameter list.
171    pub parameters: Vec<Parameter>,
172    /// An optional return type annotation (Luau).
173    pub return_type: Option<TypeAnnotation>,
174    /// The function body.
175    pub body: Block,
176    /// Where it appears in the source.
177    pub span: Span,
178}
179
180impl FunctionExpr {
181    pub fn new(
182        parameters: Vec<Parameter>,
183        return_type: Option<TypeAnnotation>,
184        body: Block,
185        span: Span,
186    ) -> Self {
187        Self {
188            parameters,
189            return_type,
190            body,
191            span,
192        }
193    }
194}
195
196/// A dot field access: `obj.field`.
197#[derive(Debug, Clone, PartialEq)]
198pub struct FieldAccess {
199    /// The expression being accessed.
200    pub base: Box<Expr>,
201    /// The field name after the dot.
202    pub field: Identifier,
203    /// Where it appears in the source.
204    pub span: Span,
205}
206
207impl FieldAccess {
208    pub fn new(base: Expr, field: Identifier, span: Span) -> Self {
209        Self {
210            base: Box::new(base),
211            field,
212            span,
213        }
214    }
215}
216
217/// A bracket index: `tbl[key]`.
218#[derive(Debug, Clone, PartialEq)]
219pub struct IndexAccess {
220    /// The expression being indexed.
221    pub base: Box<Expr>,
222    /// The index expression inside the brackets.
223    pub index: Box<Expr>,
224    /// Where it appears in the source.
225    pub span: Span,
226}
227
228impl IndexAccess {
229    pub fn new(base: Expr, index: Expr, span: Span) -> Self {
230        Self {
231            base: Box::new(base),
232            index: Box::new(index),
233            span,
234        }
235    }
236}
237
238/// A unary operation like `-x` or `not cond`.
239#[derive(Debug, Clone, PartialEq)]
240pub struct UnaryExpr {
241    /// Which operator.
242    pub operator: UnaryOperator,
243    /// The expression it applies to.
244    pub operand: Box<Expr>,
245    /// Where it appears in the source.
246    pub span: Span,
247}
248
249impl UnaryExpr {
250    pub fn new(operator: UnaryOperator, operand: Expr, span: Span) -> Self {
251        Self {
252            operator,
253            operand: Box::new(operand),
254            span,
255        }
256    }
257}
258
259/// The unary operators.
260#[derive(Debug, Clone, Copy, PartialEq, Eq)]
261pub enum UnaryOperator {
262    /// `-` (negation).
263    Minus,
264    /// `not` (logical negation).
265    Not,
266    /// `#` (length).
267    Length,
268    /// `~` (bitwise not, Lua 5.3+).
269    BitwiseNot,
270}
271
272impl UnaryOperator {
273    pub const fn binding_power(self) -> u8 {
274        14
275    }
276}
277
278/// A binary operation like `a + b` or `x and y`.
279#[derive(Debug, Clone, PartialEq)]
280pub struct BinaryExpr {
281    /// Which operator.
282    pub operator: BinaryOperator,
283    /// The left operand.
284    pub left: Box<Expr>,
285    /// The right operand.
286    pub right: Box<Expr>,
287    /// Where it appears in the source.
288    pub span: Span,
289}
290
291impl BinaryExpr {
292    pub fn new(operator: BinaryOperator, left: Expr, right: Expr, span: Span) -> Self {
293        Self {
294            operator,
295            left: Box::new(left),
296            right: Box::new(right),
297            span,
298        }
299    }
300}
301
302/// All the binary operators.
303#[derive(Debug, Clone, Copy, PartialEq, Eq)]
304pub enum BinaryOperator {
305    /// `+`
306    Add,
307    /// Subtraction.
308    Subtract,
309    /// `*`
310    Multiply,
311    /// `/`
312    Divide,
313    /// `//` (Lua 5.3+, Luau).
314    FloorDiv,
315    /// `%`
316    Modulo,
317    /// `^`
318    Power,
319
320    /// `..` (string concatenation).
321    Concat,
322
323    /// `==`
324    Equal,
325    /// `~=`
326    NotEqual,
327    /// `<`
328    Less,
329    /// `<=`
330    LessEqual,
331    /// `>`
332    Greater,
333    /// `>=`
334    GreaterEqual,
335
336    /// `and`
337    And,
338    /// `or`
339    Or,
340
341    /// `&` (Lua 5.3+).
342    BitwiseAnd,
343    /// `|` (Lua 5.3+).
344    BitwiseOr,
345    /// `~` (bitwise xor, Lua 5.3+).
346    BitwiseXor,
347    /// `<<` (Lua 5.3+).
348    LeftShift,
349    /// `>>` (Lua 5.3+).
350    RightShift,
351}
352
353impl BinaryOperator {
354    pub const fn binding_power(self) -> (u8, u8) {
355        match self {
356            Self::Or => (1, 2),
357            Self::And => (3, 4),
358            Self::Equal | Self::NotEqual | Self::Less | Self::LessEqual
359            | Self::Greater | Self::GreaterEqual => (5, 6),
360            Self::BitwiseOr => (6, 7),
361            Self::BitwiseXor => (7, 8),
362            Self::BitwiseAnd => (8, 9),
363            Self::LeftShift | Self::RightShift => (9, 10),
364            Self::Concat => (10, 9),
365            Self::Add | Self::Subtract => (11, 12),
366            Self::Multiply | Self::Divide | Self::FloorDiv | Self::Modulo => (13, 14),
367            Self::Power => (16, 15),
368        }
369    }
370    
371    pub const fn is_right_associative(self) -> bool {
372        matches!(self, Self::Concat | Self::Power)
373    }
374}
375
376/// A function call: `foo(1, 2)` or `foo "hello"` or `foo {1,2}`.
377#[derive(Debug, Clone, PartialEq)]
378pub struct CallExpr {
379    /// The expression being called.
380    pub function: Box<Expr>,
381    /// The arguments passed.
382    pub arguments: Vec<Expr>,
383    /// Where it appears in the source.
384    pub span: Span,
385}
386
387impl CallExpr {
388    pub fn new(function: Expr, arguments: Vec<Expr>, span: Span) -> Self {
389        Self {
390            function: Box::new(function),
391            arguments,
392            span,
393        }
394    }
395}
396
397/// A method call: `obj:method(1, 2)`.
398#[derive(Debug, Clone, PartialEq)]
399pub struct MethodCallExpr {
400    /// The object being called on.
401    pub base: Box<Expr>,
402    /// The method name.
403    pub method: Identifier,
404    /// The arguments passed.
405    pub arguments: Vec<Expr>,
406    /// Where it appears in the source.
407    pub span: Span,
408}
409
410impl MethodCallExpr {
411    pub fn new(base: Expr, method: Identifier, arguments: Vec<Expr>, span: Span) -> Self {
412        Self {
413            base: Box::new(base),
414            method,
415            arguments,
416            span,
417        }
418    }
419}
420
421/// A Luau if/then/else expression: `if cond then a elseif cond2 then b else c`.
422#[derive(Debug, Clone, PartialEq)]
423pub struct IfExpression {
424    /// The condition after `if`.
425    pub condition: Box<Expr>,
426    /// The value when the condition is true.
427    pub then_branch: Box<Expr>,
428    /// Zero or more `elseif` branches.
429    pub elseif_branches: Vec<ElseIfExprBranch>,
430    /// The `else` value (always present in if expressions).
431    pub else_branch: Box<Expr>,
432    /// Where it appears in the source.
433    pub span: Span,
434}
435
436impl IfExpression {
437    pub fn new(
438        condition: Expr,
439        then_branch: Expr,
440        elseif_branches: Vec<ElseIfExprBranch>,
441        else_branch: Expr,
442        span: Span,
443    ) -> Self {
444        Self {
445            condition: Box::new(condition),
446            then_branch: Box::new(then_branch),
447            elseif_branches,
448            else_branch: Box::new(else_branch),
449            span,
450        }
451    }
452}
453
454/// One `elseif cond then value` branch inside an [`IfExpression`].
455#[derive(Debug, Clone, PartialEq)]
456pub struct ElseIfExprBranch {
457    /// The condition after `elseif`.
458    pub condition: Expr,
459    /// The value when this condition is true.
460    pub then_branch: Expr,
461}
462
463impl ElseIfExprBranch {
464    pub fn new(condition: Expr, then_branch: Expr) -> Self {
465        Self {
466            condition,
467            then_branch,
468        }
469    }
470}
471
472/// A Luau interpolated string: `` `hello {name}, you are {age}` ``.
473#[derive(Debug, Clone, PartialEq)]
474pub struct InterpolatedString {
475    /// The alternating text and expression segments.
476    pub segments: Vec<InterpolationSegment>,
477    /// Where it appears in the source.
478    pub span: Span,
479}
480
481impl InterpolatedString {
482    pub fn new(segments: Vec<InterpolationSegment>, span: Span) -> Self {
483        Self { segments, span }
484    }
485}
486
487/// A piece of an interpolated string.
488#[derive(Debug, Clone, PartialEq)]
489pub enum InterpolationSegment {
490    /// A literal text segment between expressions.
491    Text(String),
492    /// An embedded `{expression}`.
493    Expression(Expr),
494}
495
496/// A Luau type assertion: `expr :: Type`.
497#[derive(Debug, Clone, PartialEq)]
498pub struct TypeAssertion {
499    /// The expression being asserted.
500    pub expression: Box<Expr>,
501    /// The type being asserted.
502    pub type_annotation: TypeAnnotation,
503    /// Where it appears in the source.
504    pub span: Span,
505}
506
507impl TypeAssertion {
508    pub fn new(expression: Expr, type_annotation: TypeAnnotation, span: Span) -> Self {
509        Self {
510            expression: Box::new(expression),
511            type_annotation,
512            span,
513        }
514    }
515}