diff options
author | Vincent Ambo <mail@tazj.in> | 2022-08-10T17·23+0300 |
---|---|---|
committer | tazjin <tazjin@tvl.su> | 2022-08-25T11·07+0000 |
commit | 322ce36cea732619c50220fc93f0eba51cf2eb8d (patch) | |
tree | d66080da9183cb0b375976f86dd8b269a884fb2d /tvix/eval | |
parent | 8150803e77fb749bb331cf4072ebc9e87d3d0e96 (diff) |
refactor(tvix/vm): use a macro to handle binary arithmetic operators r/4471
Instead of constructing another runtime value representing the pair on which to perform arithmetic, implement the same logic in the shape of a macro. This is designed to be compatible with operators like `+` that work both as an arithmetic operator AND as an operator on another pair of types. Change-Id: I1c83649ead6117f811f1fb45482d0cadf811125e Reviewed-on: https://cl.tvl.fyi/c/depot/+/6136 Tested-by: BuildkiteCI Reviewed-by: sterni <sternenseemann@systemli.org>
Diffstat (limited to 'tvix/eval')
-rw-r--r-- | tvix/eval/src/vm.rs | 81 |
1 files changed, 32 insertions, 49 deletions
diff --git a/tvix/eval/src/vm.rs b/tvix/eval/src/vm.rs index a529c5a799f7..3afda7a966eb 100644 --- a/tvix/eval/src/vm.rs +++ b/tvix/eval/src/vm.rs @@ -16,6 +16,34 @@ pub struct VM { stack: Vec<Value>, } +macro_rules! arithmetic_op { + ( $self:ident, $op:tt ) => {{ + let result = arithmetic_op!($self.pop(), $self.pop(), $op); + $self.push(result); + }}; + + ( $b:expr, $a:expr, $op:tt ) => {{ + let b = $b; + let a = $a; + + match (a, b) { + (Value::Integer(i1), Value::Integer(i2)) => Value::Integer(i1 $op i2), + (Value::Float(f1), Value::Float(f2)) => Value::Float(f1 $op f2), + (Value::Integer(i1), Value::Float(f2)) => Value::Float(i1 as f64 $op f2), + (Value::Float(f1), Value::Integer(i2)) => Value::Float(f1 $op i2 as f64), + + (v1, v2) => return Err(Error::TypeError { + expected: "number (either int or float)", + actual: if v1.is_number() { + v2.type_of() + } else { + v1.type_of() + }, + }), + } + }}; +} + impl VM { fn inc_ip(&mut self) -> OpCode { let op = self.chunk.code[self.ip]; @@ -27,30 +55,6 @@ impl VM { self.stack.pop().expect("TODO") } - fn pop_number_pair(&mut self) -> EvalResult<NumberPair> { - let v2 = self.pop(); - let v1 = self.pop(); - - match (v1, v2) { - (Value::Integer(i1), Value::Integer(i2)) => Ok(NumberPair::Integer(i1, i2)), - - (Value::Float(f1), Value::Float(f2)) => Ok(NumberPair::Floats(f1, f2)), - - (Value::Integer(i1), Value::Float(f2)) => Ok(NumberPair::Floats(i1 as f64, f2)), - - (Value::Float(f1), Value::Integer(i2)) => Ok(NumberPair::Floats(f1, i2 as f64)), - - (v1, v2) => Err(Error::TypeError { - expected: "number (either int or float)", - actual: if v1.is_number() { - v2.type_of() - } else { - v1.type_of() - }, - }), - } - } - fn push(&mut self, value: Value) { self.stack.push(value) } @@ -63,25 +67,10 @@ impl VM { self.push(c); } - OpCode::OpAdd => match self.pop_number_pair()? { - NumberPair::Floats(f1, f2) => self.push(Value::Float(f1 + f2)), - NumberPair::Integer(i1, i2) => self.push(Value::Integer(i1 + i2)), - }, - - OpCode::OpSub => match self.pop_number_pair()? { - NumberPair::Floats(f1, f2) => self.push(Value::Float(f1 - f2)), - NumberPair::Integer(i1, i2) => self.push(Value::Integer(i1 - i2)), - }, - - OpCode::OpMul => match self.pop_number_pair()? { - NumberPair::Floats(f1, f2) => self.push(Value::Float(f1 * f2)), - NumberPair::Integer(i1, i2) => self.push(Value::Integer(i1 * i2)), - }, - - OpCode::OpDiv => match self.pop_number_pair()? { - NumberPair::Floats(f1, f2) => self.push(Value::Float(f1 / f2)), - NumberPair::Integer(i1, i2) => self.push(Value::Integer(i1 / i2)), - }, + OpCode::OpAdd => arithmetic_op!(self, +), + OpCode::OpSub => arithmetic_op!(self, -), + OpCode::OpMul => arithmetic_op!(self, *), + OpCode::OpDiv => arithmetic_op!(self, /), OpCode::OpInvert => { let v = self.pop().as_bool()?; @@ -182,12 +171,6 @@ impl VM { } } -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum NumberPair { - Floats(f64, f64), - Integer(i64, i64), -} - pub fn run_chunk(chunk: Chunk) -> EvalResult<Value> { let mut vm = VM { chunk, |