Skip to main content

factorio_ir/ast/
expression.rs

1use crate::ast::{block::Block, literal::Literal, operator::Operator};
2
3#[derive(Debug, PartialEq, Eq, Clone, Copy, Default)]
4pub enum MethodDispatch {
5    #[default]
6    Infer,
7    /// User / metatable / `data:extend`-style: `recv:method(args)`.
8    Colon,
9    /// Factorio `LuaObject`: attribute reads, setters, and `.method(args)`.
10    Factorio,
11    /// `storage[key]`
12    StorageGet,
13    /// `storage[key] = value`
14    StorageSet,
15    /// Mod-settings: `recv[key].value` (or `recv[key]` for `.setting`).
16    SettingsGet,
17}
18
19#[derive(Debug, PartialEq, Clone)]
20pub enum Expression {
21    Literal(Literal),
22    Identifier(String),
23    QualifiedPath {
24        segments: Vec<String>,
25    },
26    FieldAccess {
27        base: Box<Self>,
28        field: String,
29    },
30    Call {
31        func: Box<Self>,
32        args: Vec<Self>,
33    },
34    MethodCall {
35        receiver: Box<Self>,
36        method: String,
37        args: Vec<Self>,
38        dispatch: MethodDispatch,
39    },
40    StructLiteral {
41        /// The Rust struct name that produced this literal, used by codegen to inject
42        /// fixed Factorio prototype fields (e.g. `type = "bool-setting"`).
43        struct_name: Option<String>,
44        fields: Vec<(String, Self)>,
45    },
46    /// Tagged enum value `{ tag = "Variant", ...payload }`.
47    EnumLiteral {
48        enum_name: String,
49        variant: String,
50        /// Named fields, or `_1` / `_2` / ... for tuple variants.
51        fields: Vec<(String, Self)>,
52    },
53    /// An operation between a `lhs` and a `rhs` with an [`Operator`]
54    BinaryOp {
55        lhs: Box<Self>,
56        op: Operator,
57        rhs: Box<Self>,
58    },
59    /// String interpolation parts joined with `..` in Lua.
60    FormatConcat {
61        parts: Vec<Self>,
62    },
63    /// Lua array literal `{ a, b, c }`.
64    Array {
65        elements: Vec<Self>,
66    },
67    /// Lua table index expression `base[key]`.
68    Index {
69        base: Box<Self>,
70        key: Box<Self>,
71    },
72    /// Logical `not EXPR` in Lua.
73    Not(Box<Self>),
74    /// Length operator `#EXPR` in Lua.
75    Len(Box<Self>),
76    /// Safe if-expression (avoids falsey `and`/`or` pitfalls).
77    If {
78        condition: Box<Self>,
79        then_expr: Box<Self>,
80        else_expr: Box<Self>,
81    },
82    /// Anonymous Lua function value (`function(params) ... end`).
83    Closure {
84        params: Vec<String>,
85        body: Block,
86    },
87    /// Fat pointer for `dyn Trait`: `{ _data = ..., _vt = __vt_Trait_Concrete }`.
88    FatPointer {
89        data: Box<Self>,
90        /// Fully qualified vtable symbol name, e.g. `__vt_Display_Point`.
91        vtable: String,
92    },
93    /// Dynamic dispatch: `recv._vt.method(recv, args...)`.
94    DynMethodCall {
95        receiver: Box<Self>,
96        method: String,
97        args: Vec<Self>,
98    },
99}
100
101impl Expression {
102    /// Build a [`MethodCall`] with [`MethodDispatch::Infer`].
103    #[must_use]
104    pub fn method_call(receiver: Self, method: impl Into<String>, args: Vec<Self>) -> Self {
105        Self::MethodCall {
106            receiver: Box::new(receiver),
107            method: method.into(),
108            args,
109            dispatch: MethodDispatch::Infer,
110        }
111    }
112
113    /// Build a [`MethodCall`] with an explicit dispatch tag.
114    #[must_use]
115    pub fn method_call_with(
116        receiver: Self,
117        method: impl Into<String>,
118        args: Vec<Self>,
119        dispatch: MethodDispatch,
120    ) -> Self {
121        Self::MethodCall {
122            receiver: Box::new(receiver),
123            method: method.into(),
124            args,
125            dispatch,
126        }
127    }
128}