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            (Value::Vector(a), Value::Vector(b)) => {
106                a.len() == b.len()
107                    && (0..a.len()).all(|i| match (a.get(i), b.get(i)) {
108                        (Some(x), Some(y)) => self.aver_eq(x, y),
109                        _ => false,
110                    })
111            }
112            _ => false,
113        }
114    }
115
116    pub(super) fn op_add(&self, a: Value, b: Value) -> Result<Value, RuntimeError> {
117        match (a, b) {
118            (Value::Int(x), Value::Int(y)) => Ok(Value::Int(x + y)),
119            (Value::Float(x), Value::Float(y)) => Ok(Value::Float(x + y)),
120            (Value::Str(mut x), Value::Str(y)) => {
121                x.push_str(&y);
122                Ok(Value::Str(x))
123            }
124            _ => Err(RuntimeError::Error(
125                "Operator '+' does not support these types".to_string(),
126            )),
127        }
128    }
129
130    pub(super) fn op_sub(&self, a: Value, b: Value) -> Result<Value, RuntimeError> {
131        match (&a, &b) {
132            (Value::Int(x), Value::Int(y)) => Ok(Value::Int(x - y)),
133            (Value::Float(x), Value::Float(y)) => Ok(Value::Float(x - y)),
134            // Unary minus: `- float_expr` is parsed as `0 - expr`
135            (Value::Int(0), Value::Float(y)) => Ok(Value::Float(-y)),
136            _ => Err(RuntimeError::Error(
137                "Operator '-' does not support these types".to_string(),
138            )),
139        }
140    }
141
142    pub(super) fn op_mul(&self, a: Value, b: Value) -> Result<Value, RuntimeError> {
143        match (&a, &b) {
144            (Value::Int(x), Value::Int(y)) => Ok(Value::Int(x * y)),
145            (Value::Float(x), Value::Float(y)) => Ok(Value::Float(x * y)),
146            _ => Err(RuntimeError::Error(
147                "Operator '*' does not support these types".to_string(),
148            )),
149        }
150    }
151
152    pub(super) fn op_div(&self, a: Value, b: Value) -> Result<Value, RuntimeError> {
153        match (&a, &b) {
154            (Value::Int(x), Value::Int(y)) => {
155                if *y == 0 {
156                    Err(RuntimeError::Error("division by zero".to_string()))
157                } else {
158                    Ok(Value::Int(x / y))
159                }
160            }
161            (Value::Float(x), Value::Float(y)) => {
162                if *y == 0.0 {
163                    Err(RuntimeError::Error("division by zero".to_string()))
164                } else {
165                    Ok(Value::Float(x / y))
166                }
167            }
168            _ => Err(RuntimeError::Error(
169                "Operator '/' does not support these types".to_string(),
170            )),
171        }
172    }
173
174    pub(super) fn op_compare(&self, a: &Value, b: &Value, op: &str) -> Result<Value, RuntimeError> {
175        let result = match (a, b) {
176            (Value::Int(x), Value::Int(y)) => match op {
177                "<" => x < y,
178                ">" => x > y,
179                "<=" => x <= y,
180                ">=" => x >= y,
181                _ => unreachable!(),
182            },
183            (Value::Float(x), Value::Float(y)) => match op {
184                "<" => x < y,
185                ">" => x > y,
186                "<=" => x <= y,
187                ">=" => x >= y,
188                _ => unreachable!(),
189            },
190            (Value::Str(x), Value::Str(y)) => match op {
191                "<" => x < y,
192                ">" => x > y,
193                "<=" => x <= y,
194                ">=" => x >= y,
195                _ => unreachable!(),
196            },
197            _ => {
198                return Err(RuntimeError::Error(format!(
199                    "Operator '{}' does not support these types",
200                    op
201                )));
202            }
203        };
204        Ok(Value::Bool(result))
205    }
206
207    // -- NanValue-native ops --------------------------------------------------
208
209    fn op_add_nv(&mut self, a: NanValue, b: NanValue) -> Result<NanValue, RuntimeError> {
210        if a.is_int() && b.is_int() {
211            let result = a.as_int(&self.arena) + b.as_int(&self.arena);
212            return Ok(NanValue::new_int(result, &mut self.arena));
213        }
214        if a.is_float() && b.is_float() {
215            return Ok(NanValue::new_float(a.as_float() + b.as_float()));
216        }
217        if a.is_string() && b.is_string() {
218            let sa = self.arena.get_string_value(a);
219            let sb = self.arena.get_string_value(b);
220            let mut buf = String::with_capacity(sa.len() + sb.len());
221            buf.push_str(sa.as_str());
222            buf.push_str(sb.as_str());
223            return Ok(NanValue::new_string_value(&buf, &mut self.arena));
224        }
225        Err(RuntimeError::Error(
226            "Operator '+' does not support these types".to_string(),
227        ))
228    }
229
230    fn op_sub_nv(&mut self, a: NanValue, b: NanValue) -> Result<NanValue, RuntimeError> {
231        if a.is_int() && b.is_int() {
232            let result = a.as_int(&self.arena) - b.as_int(&self.arena);
233            return Ok(NanValue::new_int(result, &mut self.arena));
234        }
235        if a.is_float() && b.is_float() {
236            return Ok(NanValue::new_float(a.as_float() - b.as_float()));
237        }
238        // Unary minus: 0 - float
239        if a.is_int() && a.as_int(&self.arena) == 0 && b.is_float() {
240            return Ok(NanValue::new_float(-b.as_float()));
241        }
242        Err(RuntimeError::Error(
243            "Operator '-' does not support these types".to_string(),
244        ))
245    }
246
247    fn op_mul_nv(&mut self, a: NanValue, b: NanValue) -> Result<NanValue, RuntimeError> {
248        if a.is_int() && b.is_int() {
249            let result = a.as_int(&self.arena) * b.as_int(&self.arena);
250            return Ok(NanValue::new_int(result, &mut self.arena));
251        }
252        if a.is_float() && b.is_float() {
253            return Ok(NanValue::new_float(a.as_float() * b.as_float()));
254        }
255        Err(RuntimeError::Error(
256            "Operator '*' does not support these types".to_string(),
257        ))
258    }
259
260    fn op_div_nv(&mut self, a: NanValue, b: NanValue) -> Result<NanValue, RuntimeError> {
261        if a.is_int() && b.is_int() {
262            let bv = b.as_int(&self.arena);
263            if bv == 0 {
264                return Err(RuntimeError::Error("division by zero".to_string()));
265            }
266            let result = a.as_int(&self.arena) / bv;
267            return Ok(NanValue::new_int(result, &mut self.arena));
268        }
269        if a.is_float() && b.is_float() {
270            let bv = b.as_float();
271            if bv == 0.0 {
272                return Err(RuntimeError::Error("division by zero".to_string()));
273            }
274            return Ok(NanValue::new_float(a.as_float() / bv));
275        }
276        Err(RuntimeError::Error(
277            "Operator '/' does not support these types".to_string(),
278        ))
279    }
280
281    fn op_compare_nv(&self, a: NanValue, b: NanValue, op: &str) -> Result<NanValue, RuntimeError> {
282        if a.is_int() && b.is_int() {
283            let x = a.as_int(&self.arena);
284            let y = b.as_int(&self.arena);
285            let result = match op {
286                "<" => x < y,
287                ">" => x > y,
288                "<=" => x <= y,
289                ">=" => x >= y,
290                _ => unreachable!(),
291            };
292            return Ok(NanValue::new_bool(result));
293        }
294        if a.is_float() && b.is_float() {
295            let x = a.as_float();
296            let y = b.as_float();
297            let result = match op {
298                "<" => x < y,
299                ">" => x > y,
300                "<=" => x <= y,
301                ">=" => x >= y,
302                _ => unreachable!(),
303            };
304            return Ok(NanValue::new_bool(result));
305        }
306        if a.is_string() && b.is_string() {
307            let x = self.arena.get_string_value(a);
308            let y = self.arena.get_string_value(b);
309            let result = match op {
310                "<" => x < y,
311                ">" => x > y,
312                "<=" => x <= y,
313                ">=" => x >= y,
314                _ => unreachable!(),
315            };
316            return Ok(NanValue::new_bool(result));
317        }
318        Err(RuntimeError::Error(format!(
319            "Operator '{}' does not support these types",
320            op
321        )))
322    }
323}