diff options
-rw-r--r-- | tvix/eval/src/builtins/mod.rs | 11 | ||||
-rw-r--r-- | tvix/eval/src/value/mod.rs | 21 | ||||
-rw-r--r-- | tvix/eval/src/vm.rs | 41 |
3 files changed, 47 insertions, 26 deletions
diff --git a/tvix/eval/src/builtins/mod.rs b/tvix/eval/src/builtins/mod.rs index e7eb4e8bf953..336c320e2f00 100644 --- a/tvix/eval/src/builtins/mod.rs +++ b/tvix/eval/src/builtins/mod.rs @@ -3,7 +3,7 @@ //! See //tvix/eval/docs/builtins.md for a some context on the //! available builtins in Nix. -use std::cmp; +use std::cmp::{self, Ordering}; use std::collections::{BTreeMap, HashMap, HashSet}; use std::path::PathBuf; @@ -16,7 +16,7 @@ use crate::{ vm::VM, }; -use crate::{arithmetic_op, cmp_op}; +use crate::arithmetic_op; use self::versions::{VersionPart, VersionPartsIter}; @@ -407,7 +407,12 @@ fn pure_builtins() -> Vec<Builtin> { Builtin::new( "lessThan", &[false, false], - |args: Vec<Value>, vm: &mut VM| cmp_op!(&*args[0].force(vm)?, &*args[1].force(vm)?, <), + |args: Vec<Value>, vm: &mut VM| { + Ok(Value::Bool(matches!( + args[0].force(vm)?.nix_cmp(&*args[1].force(vm)?)?, + Some(Ordering::Less) + ))) + }, ), Builtin::new("listToAttrs", &[true], |args: Vec<Value>, vm: &mut VM| { let list = args[0].to_list()?; diff --git a/tvix/eval/src/value/mod.rs b/tvix/eval/src/value/mod.rs index 14b1a5c6122e..82be292819ae 100644 --- a/tvix/eval/src/value/mod.rs +++ b/tvix/eval/src/value/mod.rs @@ -1,5 +1,6 @@ //! This module implements the backing representation of runtime //! values in the Nix language. +use std::cmp::Ordering; use std::ops::Deref; use std::path::PathBuf; use std::rc::Rc; @@ -348,6 +349,26 @@ impl Value { } } + /// Compare `self` against other using (fallible) Nix ordering semantics. + pub fn nix_cmp(&self, other: &Self) -> Result<Option<Ordering>, ErrorKind> { + match (self, other) { + // same types + (Value::Integer(i1), Value::Integer(i2)) => Ok(i1.partial_cmp(i2)), + (Value::Float(f1), Value::Float(f2)) => Ok(f1.partial_cmp(f2)), + (Value::String(s1), Value::String(s2)) => Ok(s1.partial_cmp(s2)), + + // different types + (Value::Integer(i1), Value::Float(f2)) => Ok((*i1 as f64).partial_cmp(f2)), + (Value::Float(f1), Value::Integer(i2)) => Ok(f1.partial_cmp(&(*i2 as f64))), + + // unsupported types + (lhs, rhs) => Err(ErrorKind::Incomparable { + lhs: lhs.type_of(), + rhs: rhs.type_of(), + }), + } + } + /// Ensure `self` is forced if it is a thunk, and return a reference to the resulting value. pub(crate) fn force(&self, vm: &mut VM) -> Result<ForceResult, ErrorKind> { match self { diff --git a/tvix/eval/src/vm.rs b/tvix/eval/src/vm.rs index 08c913ed2d6d..6a5c56dc03bf 100644 --- a/tvix/eval/src/vm.rs +++ b/tvix/eval/src/vm.rs @@ -2,7 +2,7 @@ //! Tvix bytecode. use serde_json::json; -use std::{ops::DerefMut, path::PathBuf, rc::Rc}; +use std::{cmp::Ordering, ops::DerefMut, path::PathBuf, rc::Rc}; use crate::{ chunk::Chunk, @@ -123,31 +123,26 @@ 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)); + let ordering = fallible!($self, a.nix_cmp(&b)); + let result = Value::Bool(cmp_op!(@order $op ordering)); $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. - 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(), - }), - } - } + (@order < $ordering:expr) => { + $ordering == Some(Ordering::Less) + }; + + (@order > $ordering:expr) => { + $ordering == Some(Ordering::Greater) + }; + + (@order <= $ordering:expr) => { + !matches!($ordering, None | Some(Ordering::Greater)) + }; + + (@order >= $ordering:expr) => { + !matches!($ordering, None | Some(Ordering::Less)) + }; } impl<'o> VM<'o> { |