diff options
-rw-r--r-- | tvix/eval/src/builtins/mod.rs | 7 | ||||
-rw-r--r-- | tvix/eval/src/tests/tvix_tests/eval-okay-builtins-lessThan.exp | 1 | ||||
-rw-r--r-- | tvix/eval/src/tests/tvix_tests/eval-okay-builtins-lessThan.nix | 15 | ||||
-rw-r--r-- | tvix/eval/src/vm.rs | 33 |
4 files changed, 42 insertions, 14 deletions
diff --git a/tvix/eval/src/builtins/mod.rs b/tvix/eval/src/builtins/mod.rs index f40c6f9574b1..650cfe674f52 100644 --- a/tvix/eval/src/builtins/mod.rs +++ b/tvix/eval/src/builtins/mod.rs @@ -16,7 +16,7 @@ use crate::{ vm::VM, }; -use crate::arithmetic_op; +use crate::{arithmetic_op, cmp_op}; use self::versions::{VersionPart, VersionPartsIter}; @@ -160,6 +160,11 @@ fn pure_builtins() -> Vec<Builtin> { .map(|list| Value::List(NixList::from(list))) .map_err(Into::into) }), + Builtin::new( + "lessThan", + &[false, false], + |args, vm| cmp_op!(&*args[0].force(vm)?, &*args[1].force(vm)?, <), + ), Builtin::new("hasAttr", &[true, true], |args, _| { let k = args[0].to_str()?; let xs = args[1].to_attrs()?; diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-lessThan.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-lessThan.exp new file mode 100644 index 000000000000..31f4598bb504 --- /dev/null +++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-lessThan.exp @@ -0,0 +1 @@ +[ true true true true false false false false true true true true false ] diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-lessThan.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-lessThan.nix new file mode 100644 index 000000000000..cd2d0c209c3a --- /dev/null +++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-lessThan.nix @@ -0,0 +1,15 @@ +[ + (builtins.lessThan 2 3) + (builtins.lessThan 2.0 3) + (builtins.lessThan 2 3.0) + (builtins.lessThan 2.0 3.0) + (builtins.lessThan 3 2) + (builtins.lessThan 3.0 2) + (builtins.lessThan 3 2.0) + (builtins.lessThan 3.0 2.0) + (builtins.lessThan 10 (builtins.add 9 2)) + (builtins.lessThan (builtins.add 9 1) 11) + (builtins.lessThan (builtins.add 9 1) (builtins.add 9 2)) + (builtins.lessThan "a" "b") + (builtins.lessThan "b" "a") +] diff --git a/tvix/eval/src/vm.rs b/tvix/eval/src/vm.rs index 87adfe5611a4..52627a1fea14 100644 --- a/tvix/eval/src/vm.rs +++ b/tvix/eval/src/vm.rs @@ -94,29 +94,36 @@ macro_rules! arithmetic_op { }}; } +#[macro_export] macro_rules! cmp_op { ( $self:ident, $op:tt ) => {{ let b = $self.pop(); let a = $self.pop(); + let result = fallible!($self, cmp_op!(&a, &b, $op)); + $self.push(result); + }}; + ( $a:expr, $b:expr, $op:tt ) => { // 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($self.error(ErrorKind::Incomparable { + match ($a, $b) { + // same types + (Value::Integer(i1), Value::Integer(i2)) => Ok(Value::Bool(i1 $op i2)), + (Value::Float(f1), Value::Float(f2)) => Ok(Value::Bool(f1 $op f2)), + (Value::String(s1), Value::String(s2)) => Ok(Value::Bool(s1 $op s2)), + + // different types + (Value::Integer(i1), Value::Float(f2)) => Ok(Value::Bool((*i1 as f64) $op *f2)), + (Value::Float(f1), Value::Integer(i2)) => Ok(Value::Bool(*f1 $op (*i2 as f64))), + + // unsupported types + (lhs, rhs) => Err(ErrorKind::Incomparable { lhs: lhs.type_of(), rhs: rhs.type_of(), - })), - }; - - $self.push(Value::Bool(result)); - }}; + }), + } + } } impl<'o> VM<'o> { |