feat(tvix/eval): Implement builtins.sort

This is a bit tricky because the comparator can throw errors, so we
need to propagate them out if they exist and try to avoid sorting
forever by returning a reasonable ordering in this case (as
short-circuiting is not available).

Co-Authored-By: Vincent Ambo <tazjin@tvl.su>
Change-Id: Icae1d30f43ec1ae64b2ba51e73ee467605686792
Reviewed-on: https://cl.tvl.fyi/c/depot/+/7072
Tested-by: BuildkiteCI
Reviewed-by: sterni <sternenseemann@systemli.org>
This commit is contained in:
Griffin Smith 2022-10-23 13:50:07 -04:00 committed by tazjin
parent d0a836b0e1
commit 3412ae4956
4 changed files with 46 additions and 3 deletions

View file

@ -628,10 +628,47 @@ fn pure_builtins() -> Vec<Builtin> {
}, },
), ),
Builtin::new("seq", &[true, true], |mut args: Vec<Value>, _: &mut VM| { Builtin::new("seq", &[true, true], |mut args: Vec<Value>, _: &mut VM| {
// The builtin calling infra has already forced both args for us, so we just return the // The builtin calling infra has already forced both args for us, so
// second and ignore the first // we just return the second and ignore the first
Ok(args.pop().unwrap()) Ok(args.pop().unwrap())
}), }),
Builtin::new("sort", &[true, true], |args: Vec<Value>, vm: &mut VM| {
let mut list = args[1].to_list()?;
let comparator = &args[0];
// Used to let errors "escape" from the sorting closure. If anything
// ends up setting an error, it is returned from this function.
let mut error: Option<ErrorKind> = None;
list.sort_by(|lhs, rhs| {
let result = vm
.call_with(comparator, [lhs.clone(), rhs.clone()])
.map_err(|err| ErrorKind::ThunkForce(Box::new(err)))
.and_then(|v| v.force(vm)?.as_bool());
match (&error, result) {
// The contained closure only returns a "less
// than?"-boolean, no way to yield "equal".
(None, Ok(true)) => Ordering::Less,
(None, Ok(false)) => Ordering::Greater,
// Closest thing to short-circuiting out if an error was
// thrown.
(Some(_), _) => Ordering::Equal,
// Propagate the error if one was encountered.
(_, Err(e)) => {
error = Some(e);
Ordering::Equal
}
}
});
match error {
None => Ok(Value::List(list)),
Some(e) => Err(e),
}
}),
Builtin::new("splitVersion", &[true], |args: Vec<Value>, _: &mut VM| { Builtin::new("splitVersion", &[true], |args: Vec<Value>, _: &mut VM| {
let s = args[0].to_str()?; let s = args[0].to_str()?;
let s = VersionPartsIter::new(s.as_str()); let s = VersionPartsIter::new(s.as_str());

View file

@ -1,5 +1,5 @@
//! This module implements Nix lists. //! This module implements Nix lists.
use std::ops::Deref; use std::ops::{Deref, DerefMut};
use crate::errors::ErrorKind; use crate::errors::ErrorKind;
use crate::vm::VM; use crate::vm::VM;
@ -135,3 +135,9 @@ impl Deref for NixList {
&self.0 &self.0
} }
} }
impl DerefMut for NixList {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}