diff options
Diffstat (limited to 'tvix/eval/src')
20 files changed, 121 insertions, 7 deletions
diff --git a/tvix/eval/src/compiler.rs b/tvix/eval/src/compiler.rs index d48490dde7a6..6054bd3fdaff 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 fb9f3b6ec589..f7f64f4e687a 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 f682cfc8b83f..6800e8411f5c 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 000000000000..95a0e7378b5e --- /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 000000000000..2b511f56eecb --- /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 000000000000..9160829dde78 --- /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 000000000000..c505a85b1f3c --- /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 000000000000..95a0e7378b5e --- /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 000000000000..7d6b30419fac --- /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 000000000000..9160829dde78 --- /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 000000000000..0bf474e53fd0 --- /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 000000000000..95a0e7378b5e --- /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 000000000000..61b206c03376 --- /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 000000000000..9160829dde78 --- /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 000000000000..ad77074710ca --- /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 000000000000..95a0e7378b5e --- /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 000000000000..b5773a21d374 --- /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 000000000000..9160829dde78 --- /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 000000000000..172d2237e9e2 --- /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 db2f76d73145..30f2c5a4a414 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)), |