Skip to main content

aver/interpreter/
ops.rs

1use super::*;
2
3impl Interpreter {
4    #[allow(dead_code)]
5    pub(super) fn eval_binop(
6        &self,
7        op: &BinOp,
8        left: Value,
9        right: Value,
10    ) -> Result<Value, RuntimeError> {
11        match op {
12            BinOp::Add => self.op_add(left, right),
13            BinOp::Sub => self.op_sub(left, right),
14            BinOp::Mul => self.op_mul(left, right),
15            BinOp::Div => self.op_div(left, right),
16            BinOp::Eq => Ok(Value::Bool(self.aver_eq(&left, &right))),
17            BinOp::Neq => Ok(Value::Bool(!self.aver_eq(&left, &right))),
18            BinOp::Lt => self.op_compare(&left, &right, "<"),
19            BinOp::Gt => self.op_compare(&left, &right, ">"),
20            BinOp::Lte => self.op_compare(&left, &right, "<="),
21            BinOp::Gte => self.op_compare(&left, &right, ">="),
22        }
23    }
24
25    /// NanValue-native binop — avoids Value conversion.
26    pub(super) fn eval_binop_nv(
27        &mut self,
28        op: &BinOp,
29        left: NanValue,
30        right: NanValue,
31    ) -> Result<NanValue, RuntimeError> {
32        match op {
33            BinOp::Add => self.op_add_nv(left, right),
34            BinOp::Sub => self.op_sub_nv(left, right),
35            BinOp::Mul => self.op_mul_nv(left, right),
36            BinOp::Div => self.op_div_nv(left, right),
37            BinOp::Eq => Ok(NanValue::new_bool(left.eq_in(right, &self.arena))),
38            BinOp::Neq => Ok(NanValue::new_bool(!left.eq_in(right, &self.arena))),
39            BinOp::Lt => self.op_compare_nv(left, right, "<"),
40            BinOp::Gt => self.op_compare_nv(left, right, ">"),
41            BinOp::Lte => self.op_compare_nv(left, right, "<="),
42            BinOp::Gte => self.op_compare_nv(left, right, ">="),
43        }
44    }
45
46    pub fn aver_eq(&self, a: &Value, b: &Value) -> bool {
47        if let (Some(xs), Some(ys)) = (list_view(a), list_view(b)) {
48            return xs.len() == ys.len()
49                && xs.iter().zip(ys.iter()).all(|(x, y)| self.aver_eq(x, y));
50        }
51
52        match (a, b) {
53            (Value::Int(x), Value::Int(y)) => x == y,
54            (Value::Float(x), Value::Float(y)) => x == y,
55            (Value::Str(x), Value::Str(y)) => x == y,
56            (Value::Bool(x), Value::Bool(y)) => x == y,
57            (Value::Unit, Value::Unit) => true,
58            (Value::None, Value::None) => true,
59            (Value::Ok(x), Value::Ok(y)) => self.aver_eq(x, y),
60            (Value::Err(x), Value::Err(y)) => self.aver_eq(x, y),
61            (Value::Some(x), Value::Some(y)) => self.aver_eq(x, y),
62            (Value::Tuple(xs), Value::Tuple(ys)) => {
63                xs.len() == ys.len() && xs.iter().zip(ys.iter()).all(|(x, y)| self.aver_eq(x, y))
64            }
65            (Value::Map(m1), Value::Map(m2)) => {
66                m1.len() == m2.len()
67                    && m1
68                        .iter()
69                        .all(|(k, v1)| m2.get(k).map(|v2| self.aver_eq(v1, v2)).unwrap_or(false))
70            }
71            (
72                Value::Variant {
73                    type_name: t1,
74                    variant: v1,
75                    fields: f1,
76                },
77                Value::Variant {
78                    type_name: t2,
79                    variant: v2,
80                    fields: f2,
81                },
82            ) => {
83                t1 == t2
84                    && v1 == v2
85                    && f1.len() == f2.len()
86                    && f1.iter().zip(f2.iter()).all(|(x, y)| self.aver_eq(x, y))
87            }
88            (
89                Value::Record {
90                    type_name: t1,
91                    fields: f1,
92                },
93                Value::Record {
94                    type_name: t2,
95                    fields: f2,
96                },
97            ) => {
98                t1 == t2
99                    && f1.len() == f2.len()
100                    && f1
101                        .iter()
102                        .zip(f2.iter())
103                        .all(|((k1, v1), (k2, v2))| k1 == k2 && self.aver_eq(v1, v2))
104            }
105            _ => false,
106        }
107    }
108
109    pub(super) fn op_add(&self, a: Value, b: Value) -> Result<Value, RuntimeError> {
110        match (&a, &b) {
111            (Value::Int(x), Value::Int(y)) => Ok(Value::Int(x + y)),
112            (Value::Float(x), Value::Float(y)) => Ok(Value::Float(x + y)),
113            (Value::Str(x), Value::Str(y)) => Ok(Value::Str(format!("{}{}", x, y))),
114            _ => Err(RuntimeError::Error(
115                "Operator '+' does not support these types".to_string(),
116            )),
117        }
118    }
119
120    pub(super) fn op_sub(&self, a: Value, b: Value) -> Result<Value, RuntimeError> {
121        match (&a, &b) {
122            (Value::Int(x), Value::Int(y)) => Ok(Value::Int(x - y)),
123            (Value::Float(x), Value::Float(y)) => Ok(Value::Float(x - y)),
124            // Unary minus: `- float_expr` is parsed as `0 - expr`
125            (Value::Int(0), Value::Float(y)) => Ok(Value::Float(-y)),
126            _ => Err(RuntimeError::Error(
127                "Operator '-' does not support these types".to_string(),
128            )),
129        }
130    }
131
132    pub(super) fn op_mul(&self, a: Value, b: Value) -> Result<Value, RuntimeError> {
133        match (&a, &b) {
134            (Value::Int(x), Value::Int(y)) => Ok(Value::Int(x * y)),
135            (Value::Float(x), Value::Float(y)) => Ok(Value::Float(x * y)),
136            _ => Err(RuntimeError::Error(
137                "Operator '*' does not support these types".to_string(),
138            )),
139        }
140    }
141
142    pub(super) fn op_div(&self, a: Value, b: Value) -> Result<Value, RuntimeError> {
143        match (&a, &b) {
144            (Value::Int(x), Value::Int(y)) => {
145                if *y == 0 {
146                    Err(RuntimeError::Error("Division by zero".to_string()))
147                } else {
148                    Ok(Value::Int(x / y))
149                }
150            }
151            (Value::Float(x), Value::Float(y)) => {
152                if *y == 0.0 {
153                    Err(RuntimeError::Error("Division by zero".to_string()))
154                } else {
155                    Ok(Value::Float(x / y))
156                }
157            }
158            _ => Err(RuntimeError::Error(
159                "Operator '/' does not support these types".to_string(),
160            )),
161        }
162    }
163
164    pub(super) fn op_compare(&self, a: &Value, b: &Value, op: &str) -> Result<Value, RuntimeError> {
165        let result = match (a, b) {
166            (Value::Int(x), Value::Int(y)) => match op {
167                "<" => x < y,
168                ">" => x > y,
169                "<=" => x <= y,
170                ">=" => x >= y,
171                _ => unreachable!(),
172            },
173            (Value::Float(x), Value::Float(y)) => match op {
174                "<" => x < y,
175                ">" => x > y,
176                "<=" => x <= y,
177                ">=" => x >= y,
178                _ => unreachable!(),
179            },
180            (Value::Str(x), Value::Str(y)) => match op {
181                "<" => x < y,
182                ">" => x > y,
183                "<=" => x <= y,
184                ">=" => x >= y,
185                _ => unreachable!(),
186            },
187            _ => {
188                return Err(RuntimeError::Error(format!(
189                    "Operator '{}' does not support these types",
190                    op
191                )));
192            }
193        };
194        Ok(Value::Bool(result))
195    }
196
197    // -- NanValue-native ops --------------------------------------------------
198
199    fn op_add_nv(&mut self, a: NanValue, b: NanValue) -> Result<NanValue, RuntimeError> {
200        if a.is_int() && b.is_int() {
201            let result = a.as_int(&self.arena) + b.as_int(&self.arena);
202            return Ok(NanValue::new_int(result, &mut self.arena));
203        }
204        if a.is_float() && b.is_float() {
205            return Ok(NanValue::new_float(a.as_float() + b.as_float()));
206        }
207        if a.is_string() && b.is_string() {
208            let combined = format!(
209                "{}{}",
210                self.arena.get_string_value(a),
211                self.arena.get_string_value(b)
212            );
213            return Ok(NanValue::new_string_value(&combined, &mut self.arena));
214        }
215        Err(RuntimeError::Error(
216            "Operator '+' does not support these types".to_string(),
217        ))
218    }
219
220    fn op_sub_nv(&mut self, a: NanValue, b: NanValue) -> Result<NanValue, RuntimeError> {
221        if a.is_int() && b.is_int() {
222            let result = a.as_int(&self.arena) - b.as_int(&self.arena);
223            return Ok(NanValue::new_int(result, &mut self.arena));
224        }
225        if a.is_float() && b.is_float() {
226            return Ok(NanValue::new_float(a.as_float() - b.as_float()));
227        }
228        // Unary minus: 0 - float
229        if a.is_int() && a.as_int(&self.arena) == 0 && b.is_float() {
230            return Ok(NanValue::new_float(-b.as_float()));
231        }
232        Err(RuntimeError::Error(
233            "Operator '-' does not support these types".to_string(),
234        ))
235    }
236
237    fn op_mul_nv(&mut self, a: NanValue, b: NanValue) -> Result<NanValue, RuntimeError> {
238        if a.is_int() && b.is_int() {
239            let result = a.as_int(&self.arena) * b.as_int(&self.arena);
240            return Ok(NanValue::new_int(result, &mut self.arena));
241        }
242        if a.is_float() && b.is_float() {
243            return Ok(NanValue::new_float(a.as_float() * b.as_float()));
244        }
245        Err(RuntimeError::Error(
246            "Operator '*' does not support these types".to_string(),
247        ))
248    }
249
250    fn op_div_nv(&mut self, a: NanValue, b: NanValue) -> Result<NanValue, RuntimeError> {
251        if a.is_int() && b.is_int() {
252            let bv = b.as_int(&self.arena);
253            if bv == 0 {
254                return Err(RuntimeError::Error("Division by zero".to_string()));
255            }
256            let result = a.as_int(&self.arena) / bv;
257            return Ok(NanValue::new_int(result, &mut self.arena));
258        }
259        if a.is_float() && b.is_float() {
260            let bv = b.as_float();
261            if bv == 0.0 {
262                return Err(RuntimeError::Error("Division by zero".to_string()));
263            }
264            return Ok(NanValue::new_float(a.as_float() / bv));
265        }
266        Err(RuntimeError::Error(
267            "Operator '/' does not support these types".to_string(),
268        ))
269    }
270
271    fn op_compare_nv(&self, a: NanValue, b: NanValue, op: &str) -> Result<NanValue, RuntimeError> {
272        if a.is_int() && b.is_int() {
273            let x = a.as_int(&self.arena);
274            let y = b.as_int(&self.arena);
275            let result = match op {
276                "<" => x < y,
277                ">" => x > y,
278                "<=" => x <= y,
279                ">=" => x >= y,
280                _ => unreachable!(),
281            };
282            return Ok(NanValue::new_bool(result));
283        }
284        if a.is_float() && b.is_float() {
285            let x = a.as_float();
286            let y = b.as_float();
287            let result = match op {
288                "<" => x < y,
289                ">" => x > y,
290                "<=" => x <= y,
291                ">=" => x >= y,
292                _ => unreachable!(),
293            };
294            return Ok(NanValue::new_bool(result));
295        }
296        if a.is_string() && b.is_string() {
297            let x = self.arena.get_string_value(a);
298            let y = self.arena.get_string_value(b);
299            let result = match op {
300                "<" => x < y,
301                ">" => x > y,
302                "<=" => x <= y,
303                ">=" => x >= y,
304                _ => unreachable!(),
305            };
306            return Ok(NanValue::new_bool(result));
307        }
308        Err(RuntimeError::Error(format!(
309            "Operator '{}' does not support these types",
310            op
311        )))
312    }
313}