diff options
author | Griffin Smith <root@gws.fyi> | 2022-10-23T17·50-0400 |
---|---|---|
committer | tazjin <tazjin@tvl.su> | 2022-10-29T14·04+0000 |
commit | 3412ae495661e8073075bbb8c24ee0098ca2bede (patch) | |
tree | 9fa4775f7b059f855ae29e9aa895053bce8236a2 /tvix/eval/src/builtins | |
parent | d0a836b0e1ee1034144d1e5b71df6ab285c8450e (diff) |
feat(tvix/eval): Implement builtins.sort r/5222
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>
Diffstat (limited to 'tvix/eval/src/builtins')
-rw-r--r-- | tvix/eval/src/builtins/mod.rs | 41 |
1 files changed, 39 insertions, 2 deletions
diff --git a/tvix/eval/src/builtins/mod.rs b/tvix/eval/src/builtins/mod.rs index 5c029605f1ec..7f55c90c15ad 100644 --- a/tvix/eval/src/builtins/mod.rs +++ b/tvix/eval/src/builtins/mod.rs @@ -628,10 +628,47 @@ fn pure_builtins() -> Vec<Builtin> { }, ), 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 - // second and ignore the first + // The builtin calling infra has already forced both args for us, so + // we just return the second and ignore the first 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| { let s = args[0].to_str()?; let s = VersionPartsIter::new(s.as_str()); |