solverforge_core/wasm/expression/
substitution.rs

1use super::Expression;
2
3impl Expression {
4    /// Substitute all occurrences of a parameter with a replacement expression.
5    ///
6    /// This is used for method inlining: when inlining `obj.method()`, we replace
7    /// `Param(0)` (self) with `obj`, and other parameters with their call arguments.
8    pub fn substitute_param(self, from_index: u32, substitute: &Expression) -> Expression {
9        macro_rules! sub {
10            ($e:expr) => {
11                Box::new((*$e).substitute_param(from_index, substitute))
12            };
13        }
14
15        macro_rules! binary {
16            ($variant:ident, $left:expr, $right:expr) => {
17                Expression::$variant {
18                    left: sub!($left),
19                    right: sub!($right),
20                }
21            };
22        }
23
24        macro_rules! unary {
25            ($variant:ident, $operand:expr) => {
26                Expression::$variant {
27                    operand: sub!($operand),
28                }
29            };
30        }
31
32        match self {
33            // Parameter substitution - the core operation
34            Expression::Param { index } if index == from_index => substitute.clone(),
35            Expression::Param { index } => Expression::Param { index },
36
37            // Field access
38            Expression::FieldAccess {
39                object,
40                class_name,
41                field_name,
42                field_type,
43            } => Expression::FieldAccess {
44                object: sub!(object),
45                class_name,
46                field_name,
47                field_type,
48            },
49
50            // Comparisons (i32)
51            Expression::Eq { left, right } => binary!(Eq, left, right),
52            Expression::Ne { left, right } => binary!(Ne, left, right),
53            Expression::Lt { left, right } => binary!(Lt, left, right),
54            Expression::Le { left, right } => binary!(Le, left, right),
55            Expression::Gt { left, right } => binary!(Gt, left, right),
56            Expression::Ge { left, right } => binary!(Ge, left, right),
57
58            // Comparisons (i64)
59            Expression::Eq64 { left, right } => binary!(Eq64, left, right),
60            Expression::Ne64 { left, right } => binary!(Ne64, left, right),
61            Expression::Lt64 { left, right } => binary!(Lt64, left, right),
62            Expression::Le64 { left, right } => binary!(Le64, left, right),
63            Expression::Gt64 { left, right } => binary!(Gt64, left, right),
64            Expression::Ge64 { left, right } => binary!(Ge64, left, right),
65
66            // Arithmetic (i32)
67            Expression::Add { left, right } => binary!(Add, left, right),
68            Expression::Sub { left, right } => binary!(Sub, left, right),
69            Expression::Mul { left, right } => binary!(Mul, left, right),
70            Expression::Div { left, right } => binary!(Div, left, right),
71
72            // Arithmetic (i64)
73            Expression::Add64 { left, right } => binary!(Add64, left, right),
74            Expression::Sub64 { left, right } => binary!(Sub64, left, right),
75            Expression::Mul64 { left, right } => binary!(Mul64, left, right),
76            Expression::Div64 { left, right } => binary!(Div64, left, right),
77
78            // Arithmetic (f64)
79            Expression::FloatAdd { left, right } => binary!(FloatAdd, left, right),
80            Expression::FloatSub { left, right } => binary!(FloatSub, left, right),
81            Expression::FloatMul { left, right } => binary!(FloatMul, left, right),
82            Expression::FloatDiv { left, right } => binary!(FloatDiv, left, right),
83
84            // Math functions - unary
85            Expression::Sqrt { operand } => unary!(Sqrt, operand),
86            Expression::FloatAbs { operand } => unary!(FloatAbs, operand),
87            Expression::Round { operand } => unary!(Round, operand),
88            Expression::Floor { operand } => unary!(Floor, operand),
89            Expression::Ceil { operand } => unary!(Ceil, operand),
90            Expression::Sin { operand } => unary!(Sin, operand),
91            Expression::Cos { operand } => unary!(Cos, operand),
92            Expression::Asin { operand } => unary!(Asin, operand),
93            Expression::Acos { operand } => unary!(Acos, operand),
94            Expression::Atan { operand } => unary!(Atan, operand),
95            Expression::Radians { operand } => unary!(Radians, operand),
96            Expression::IntToFloat { operand } => unary!(IntToFloat, operand),
97            Expression::FloatToInt { operand } => unary!(FloatToInt, operand),
98
99            // Math functions - binary
100            Expression::Atan2 { y, x } => Expression::Atan2 {
101                y: sub!(y),
102                x: sub!(x),
103            },
104
105            // Logical operations
106            Expression::And { left, right } => binary!(And, left, right),
107            Expression::Or { left, right } => binary!(Or, left, right),
108            Expression::Not { operand } => unary!(Not, operand),
109            Expression::IsNull { operand } => unary!(IsNull, operand),
110            Expression::IsNotNull { operand } => unary!(IsNotNull, operand),
111            Expression::IsNull64 { operand } => unary!(IsNull64, operand),
112            Expression::IsNotNull64 { operand } => unary!(IsNotNull64, operand),
113
114            // Host calls
115            Expression::HostCall {
116                function_name,
117                args,
118            } => Expression::HostCall {
119                function_name,
120                args: args
121                    .into_iter()
122                    .map(|arg| arg.substitute_param(from_index, substitute))
123                    .collect(),
124            },
125
126            // List operations
127            Expression::ListContains { list, element } => Expression::ListContains {
128                list: sub!(list),
129                element: sub!(element),
130            },
131            Expression::Length { collection } => Expression::Length {
132                collection: sub!(collection),
133            },
134
135            // Sum with index adjustment
136            Expression::Sum {
137                collection,
138                item_var_name,
139                item_param_index,
140                item_class_name,
141                accumulator_expr,
142            } => {
143                let new_index = if from_index < item_param_index {
144                    item_param_index - 1
145                } else {
146                    item_param_index
147                };
148
149                Expression::Sum {
150                    collection: sub!(collection),
151                    item_var_name,
152                    item_param_index: new_index,
153                    item_class_name,
154                    accumulator_expr: sub!(accumulator_expr),
155                }
156            }
157
158            Expression::LastElement {
159                collection,
160                item_class_name,
161            } => Expression::LastElement {
162                collection: sub!(collection),
163                item_class_name,
164            },
165
166            // Conditional
167            Expression::IfThenElse {
168                condition,
169                then_branch,
170                else_branch,
171            } => Expression::IfThenElse {
172                condition: sub!(condition),
173                then_branch: sub!(then_branch),
174                else_branch: sub!(else_branch),
175            },
176            Expression::IfThenElse64 {
177                condition,
178                then_branch,
179                else_branch,
180            } => Expression::IfThenElse64 {
181                condition: sub!(condition),
182                then_branch: sub!(then_branch),
183                else_branch: sub!(else_branch),
184            },
185
186            // Type conversions
187            Expression::I64ToI32 { operand } => unary!(I64ToI32, operand),
188            Expression::I32ToI64 { operand } => unary!(I32ToI64, operand),
189
190            // Literals - no params, return as-is
191            Expression::Null
192            | Expression::BoolLiteral { .. }
193            | Expression::IntLiteral { .. }
194            | Expression::Int64Literal { .. }
195            | Expression::FloatLiteral { .. }
196            | Expression::StringLiteral { .. } => self,
197        }
198    }
199}