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 sa = self.arena.get_string(a.arena_index()).to_string();
209            let sb = self.arena.get_string(b.arena_index());
210            let combined = format!("{}{}", sa, sb);
211            let idx = self.arena.push_string(&combined);
212            return Ok(NanValue::new_string(idx));
213        }
214        Err(RuntimeError::Error(
215            "Operator '+' does not support these types".to_string(),
216        ))
217    }
218
219    fn op_sub_nv(&mut self, a: NanValue, b: NanValue) -> Result<NanValue, RuntimeError> {
220        if a.is_int() && b.is_int() {
221            let result = a.as_int(&self.arena) - b.as_int(&self.arena);
222            return Ok(NanValue::new_int(result, &mut self.arena));
223        }
224        if a.is_float() && b.is_float() {
225            return Ok(NanValue::new_float(a.as_float() - b.as_float()));
226        }
227        // Unary minus: 0 - float
228        if a.is_int() && a.as_int(&self.arena) == 0 && b.is_float() {
229            return Ok(NanValue::new_float(-b.as_float()));
230        }
231        Err(RuntimeError::Error(
232            "Operator '-' does not support these types".to_string(),
233        ))
234    }
235
236    fn op_mul_nv(&mut self, a: NanValue, b: NanValue) -> Result<NanValue, RuntimeError> {
237        if a.is_int() && b.is_int() {
238            let result = a.as_int(&self.arena) * b.as_int(&self.arena);
239            return Ok(NanValue::new_int(result, &mut self.arena));
240        }
241        if a.is_float() && b.is_float() {
242            return Ok(NanValue::new_float(a.as_float() * b.as_float()));
243        }
244        Err(RuntimeError::Error(
245            "Operator '*' does not support these types".to_string(),
246        ))
247    }
248
249    fn op_div_nv(&mut self, a: NanValue, b: NanValue) -> Result<NanValue, RuntimeError> {
250        if a.is_int() && b.is_int() {
251            let bv = b.as_int(&self.arena);
252            if bv == 0 {
253                return Err(RuntimeError::Error("Division by zero".to_string()));
254            }
255            let result = a.as_int(&self.arena) / bv;
256            return Ok(NanValue::new_int(result, &mut self.arena));
257        }
258        if a.is_float() && b.is_float() {
259            let bv = b.as_float();
260            if bv == 0.0 {
261                return Err(RuntimeError::Error("Division by zero".to_string()));
262            }
263            return Ok(NanValue::new_float(a.as_float() / bv));
264        }
265        Err(RuntimeError::Error(
266            "Operator '/' does not support these types".to_string(),
267        ))
268    }
269
270    fn op_compare_nv(&self, a: NanValue, b: NanValue, op: &str) -> Result<NanValue, RuntimeError> {
271        if a.is_int() && b.is_int() {
272            let x = a.as_int(&self.arena);
273            let y = b.as_int(&self.arena);
274            let result = match op {
275                "<" => x < y,
276                ">" => x > y,
277                "<=" => x <= y,
278                ">=" => x >= y,
279                _ => unreachable!(),
280            };
281            return Ok(NanValue::new_bool(result));
282        }
283        if a.is_float() && b.is_float() {
284            let x = a.as_float();
285            let y = b.as_float();
286            let result = match op {
287                "<" => x < y,
288                ">" => x > y,
289                "<=" => x <= y,
290                ">=" => x >= y,
291                _ => unreachable!(),
292            };
293            return Ok(NanValue::new_bool(result));
294        }
295        if a.is_string() && b.is_string() {
296            let x = self.arena.get_string(a.arena_index());
297            let y = self.arena.get_string(b.arena_index());
298            let result = match op {
299                "<" => x < y,
300                ">" => x > y,
301                "<=" => x <= y,
302                ">=" => x >= y,
303                _ => unreachable!(),
304            };
305            return Ok(NanValue::new_bool(result));
306        }
307        Err(RuntimeError::Error(format!(
308            "Operator '{}' does not support these types",
309            op
310        )))
311    }
312}