about summary refs log tree commit diff
path: root/tvix/eval/src/vm.rs
diff options
context:
space:
mode:
Diffstat (limited to 'tvix/eval/src/vm.rs')
-rw-r--r--tvix/eval/src/vm.rs43
1 files changed, 36 insertions, 7 deletions
diff --git a/tvix/eval/src/vm.rs b/tvix/eval/src/vm.rs
index db2f76d731..30f2c5a4a4 100644
--- a/tvix/eval/src/vm.rs
+++ b/tvix/eval/src/vm.rs
@@ -18,15 +18,14 @@ pub struct VM {
 
 macro_rules! arithmetic_op {
     ( $self:ident, $op:tt ) => {{
-        let result = arithmetic_op!($self.pop(), $self.pop(), $op);
+        let b = $self.pop();
+        let a = $self.pop();
+        let result = arithmetic_op!(a, b, $op);
         $self.push(result);
     }};
 
-    ( $b:expr, $a:expr, $op:tt ) => {{
-        let b = $b;
-        let a = $a;
-
-        match (a, b) {
+    ( $a:ident, $b:ident, $op:tt ) => {{
+        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),
@@ -44,6 +43,31 @@ macro_rules! arithmetic_op {
     }};
 }
 
+macro_rules! cmp_op {
+    ( $self:ident, $op:tt ) => {{
+        let b = $self.pop();
+        let a = $self.pop();
+
+        // Comparable (in terms of ordering) values are numbers and
+        // strings. Numbers need to be coerced similarly to arithmetic
+        // ops if mixed types are encountered.
+        let result = match (a, b) {
+            (Value::Integer(i1), Value::Integer(i2)) => i1 $op i2,
+            (Value::Float(f1), Value::Float(f2)) => f1 $op f2,
+            (Value::Integer(i1), Value::Float(f2)) => (i1 as f64) $op f2,
+            (Value::Float(f1), Value::Integer(i2)) => f1 $op (i2 as f64),
+            (Value::String(s1), Value::String(s2)) => s1 $op s2,
+
+            (lhs, rhs) => return Err(Error::Incomparable {
+                lhs: lhs.type_of(),
+                rhs: rhs.type_of(),
+            }),
+        };
+
+        $self.push(Value::Bool(result));
+    }};
+}
+
 impl VM {
     fn inc_ip(&mut self) -> OpCode {
         let op = self.chunk.code[self.ip];
@@ -74,7 +98,7 @@ impl VM {
                     let result = if let (Value::String(s1), Value::String(s2)) = (&a, &b) {
                         Value::String(s1.concat(s2))
                     } else {
-                        arithmetic_op!(b, a, +)
+                        arithmetic_op!(a, b, +)
                     };
 
                     self.push(result)
@@ -107,6 +131,11 @@ impl VM {
                     self.push(Value::Bool(v1 == v2))
                 }
 
+                OpCode::OpLess => cmp_op!(self, <),
+                OpCode::OpLessOrEq => cmp_op!(self, <=),
+                OpCode::OpMore => cmp_op!(self, >),
+                OpCode::OpMoreOrEq => cmp_op!(self, >=),
+
                 OpCode::OpNull => self.push(Value::Null),
                 OpCode::OpTrue => self.push(Value::Bool(true)),
                 OpCode::OpFalse => self.push(Value::Bool(false)),