about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--tvix/eval/src/compiler.rs4
-rw-r--r--tvix/eval/src/errors.rs5
-rw-r--r--tvix/eval/src/opcode.rs4
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-cmp-float-false.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-cmp-float-false.nix8
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-cmp-float-true.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-cmp-float-true.nix8
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-cmp-int-false.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-cmp-int-false.nix8
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-cmp-int-true.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-cmp-int-true.nix8
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-cmp-num-false.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-cmp-num-false.nix8
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-cmp-num-true.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-cmp-num-true.nix8
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-cmp-str-false.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-cmp-str-false.nix8
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-cmp-str-true.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-cmp-str-true.nix8
-rw-r--r--tvix/eval/src/vm.rs43
20 files changed, 121 insertions, 7 deletions
diff --git a/tvix/eval/src/compiler.rs b/tvix/eval/src/compiler.rs
index d48490dde7..6054bd3fda 100644
--- a/tvix/eval/src/compiler.rs
+++ b/tvix/eval/src/compiler.rs
@@ -137,6 +137,10 @@ impl Compiler {
             BinOpKind::Div => self.chunk.add_op(OpCode::OpDiv),
             BinOpKind::Update => self.chunk.add_op(OpCode::OpAttrsUpdate),
             BinOpKind::Equal => self.chunk.add_op(OpCode::OpEqual),
+            BinOpKind::Less => self.chunk.add_op(OpCode::OpLess),
+            BinOpKind::LessOrEq => self.chunk.add_op(OpCode::OpLessOrEq),
+            BinOpKind::More => self.chunk.add_op(OpCode::OpMore),
+            BinOpKind::MoreOrEq => self.chunk.add_op(OpCode::OpMoreOrEq),
 
             BinOpKind::NotEqual => {
                 self.chunk.add_op(OpCode::OpEqual);
diff --git a/tvix/eval/src/errors.rs b/tvix/eval/src/errors.rs
index fb9f3b6ec5..f7f64f4e68 100644
--- a/tvix/eval/src/errors.rs
+++ b/tvix/eval/src/errors.rs
@@ -14,6 +14,11 @@ pub enum Error {
         expected: &'static str,
         actual: &'static str,
     },
+
+    Incomparable {
+        lhs: &'static str,
+        rhs: &'static str,
+    },
 }
 
 impl Display for Error {
diff --git a/tvix/eval/src/opcode.rs b/tvix/eval/src/opcode.rs
index f682cfc8b8..6800e8411f 100644
--- a/tvix/eval/src/opcode.rs
+++ b/tvix/eval/src/opcode.rs
@@ -29,6 +29,10 @@ pub enum OpCode {
 
     // Logical binary operators
     OpEqual,
+    OpLess,
+    OpLessOrEq,
+    OpMore,
+    OpMoreOrEq,
 
     // Attribute sets
     OpAttrs(usize),
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-float-false.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-float-false.exp
new file mode 100644
index 0000000000..95a0e7378b
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-float-false.exp
@@ -0,0 +1 @@
+{ eq = false; ge = false; gt = false; le = false; lt = false; ne = false; }
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-float-false.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-float-false.nix
new file mode 100644
index 0000000000..2b511f56ee
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-float-false.nix
@@ -0,0 +1,8 @@
+{
+  eq = 6.9 == 4.2;
+  ne = 4.2 != 4.2;
+  lt = 2.5 < 1.5;
+  le = 2.5 <= 1.5;
+  gt = 1.5 > 2.5;
+  ge = 1.5 >= 2.5;
+}
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-float-true.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-float-true.exp
new file mode 100644
index 0000000000..9160829dde
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-float-true.exp
@@ -0,0 +1 @@
+{ eq = true; ge = true; gt = true; le = true; lt = true; ne = true; }
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-float-true.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-float-true.nix
new file mode 100644
index 0000000000..c505a85b1f
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-float-true.nix
@@ -0,0 +1,8 @@
+{
+  eq = 4.2 == 4.2;
+  ne = 6.9 != 4.2;
+  lt = 1.5 < 2.5;
+  le = 2.5 <= 2.5;
+  gt = 2.3 > 1.2;
+  ge = 2.3 >= 2.3;
+}
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-int-false.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-int-false.exp
new file mode 100644
index 0000000000..95a0e7378b
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-int-false.exp
@@ -0,0 +1 @@
+{ eq = false; ge = false; gt = false; le = false; lt = false; ne = false; }
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-int-false.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-int-false.nix
new file mode 100644
index 0000000000..7d6b30419f
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-int-false.nix
@@ -0,0 +1,8 @@
+{
+  eq = 69 == 42;
+  ne = 42 != 42;
+  lt = 2 < 1;
+  le = 2 <= 1;
+  gt = 1 > 2;
+  ge = 1 >= 2;
+}
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-int-true.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-int-true.exp
new file mode 100644
index 0000000000..9160829dde
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-int-true.exp
@@ -0,0 +1 @@
+{ eq = true; ge = true; gt = true; le = true; lt = true; ne = true; }
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-int-true.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-int-true.nix
new file mode 100644
index 0000000000..0bf474e53f
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-int-true.nix
@@ -0,0 +1,8 @@
+{
+  eq = 42 == 42;
+  ne = 69 != 42;
+  lt = 1 < 2;
+  le = 2 <= 2;
+  gt = 2 > 1;
+  ge = 2 >= 2;
+}
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-num-false.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-num-false.exp
new file mode 100644
index 0000000000..95a0e7378b
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-num-false.exp
@@ -0,0 +1 @@
+{ eq = false; ge = false; gt = false; le = false; lt = false; ne = false; }
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-num-false.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-num-false.nix
new file mode 100644
index 0000000000..61b206c033
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-num-false.nix
@@ -0,0 +1,8 @@
+{
+  eq = 6.9 == 4;
+  ne = 4.0 != 4;
+  lt = 2.5 < 1;
+  le = 2 <= 1.5;
+  gt = 1 > 1.1;
+  ge = 1.5 >= 2;
+}
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-num-true.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-num-true.exp
new file mode 100644
index 0000000000..9160829dde
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-num-true.exp
@@ -0,0 +1 @@
+{ eq = true; ge = true; gt = true; le = true; lt = true; ne = true; }
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-num-true.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-num-true.nix
new file mode 100644
index 0000000000..ad77074710
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-num-true.nix
@@ -0,0 +1,8 @@
+{
+  eq = 42.0 == 42;
+  ne = 6.9 != 4;
+  lt = 1.5 < 2;
+  le = 2.0 <= 2.0;
+  gt = 1.1 > 1;
+  ge = 2.3 >= 2.3;
+}
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-str-false.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-str-false.exp
new file mode 100644
index 0000000000..95a0e7378b
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-str-false.exp
@@ -0,0 +1 @@
+{ eq = false; ge = false; gt = false; le = false; lt = false; ne = false; }
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-str-false.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-str-false.nix
new file mode 100644
index 0000000000..b5773a21d3
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-str-false.nix
@@ -0,0 +1,8 @@
+{
+  eq = "test" == "not test";
+  ne = "test" != "test";
+  lt = "bcd" < "abc";
+  le = "bcd" <= "abc";
+  gt = "abc" > "bcd";
+  ge = "abc" >= "bcd";
+}
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-str-true.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-str-true.exp
new file mode 100644
index 0000000000..9160829dde
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-str-true.exp
@@ -0,0 +1 @@
+{ eq = true; ge = true; gt = true; le = true; lt = true; ne = true; }
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-str-true.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-str-true.nix
new file mode 100644
index 0000000000..172d2237e9
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-str-true.nix
@@ -0,0 +1,8 @@
+{
+  eq = "test" == "test";
+  ne = "test" != "not test";
+  lt = "abc" < "bcd";
+  le = "bcd" <= "bcd";
+  gt = "bcd" > "abc";
+  ge = "bcd" >= "bcd";
+}
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)),