From 876c4772563e8129e069f1d107b2a21378609ace Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Tue, 20 Sep 2022 23:47:30 +0300 Subject: feat(tvix/eval): implement builtins.map As we already have a VM passed to the builtins, we can simply execute the provided closure/lambda in it for each value. The primary annoyance with this is that we have to clone the upvalues for each element, but we can try making this cheaper in the future (it's also a general problem in the VM itself). Change-Id: I5bcf56d58c509c0eb081e7cf52f6093216451ce4 Reviewed-on: https://cl.tvl.fyi/c/depot/+/6714 Tested-by: BuildkiteCI Reviewed-by: sterni --- tvix/eval/src/builtins/mod.rs | 18 +++++++++++++++++- tvix/eval/src/errors.rs | 9 +++++++++ .../src/tests/tvix_tests/eval-okay-builtins-map.exp | 1 + .../src/tests/tvix_tests/eval-okay-builtins-map.nix | 16 ++++++++++++++++ 4 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 tvix/eval/src/tests/tvix_tests/eval-okay-builtins-map.exp create mode 100644 tvix/eval/src/tests/tvix_tests/eval-okay-builtins-map.nix (limited to 'tvix/eval/src') diff --git a/tvix/eval/src/builtins/mod.rs b/tvix/eval/src/builtins/mod.rs index dfc1986141a0..598c8aa08ece 100644 --- a/tvix/eval/src/builtins/mod.rs +++ b/tvix/eval/src/builtins/mod.rs @@ -12,7 +12,8 @@ use std::{ use crate::{ errors::ErrorKind, - value::{Builtin, CoercionKind, NixAttrs, NixList, NixString, Value}, + upvalues::UpvalueCarrier, + value::{Builtin, Closure, CoercionKind, NixAttrs, NixList, NixString, Value}, vm::VM, }; @@ -144,6 +145,21 @@ fn pure_builtins() -> Vec { Builtin::new("length", &[true], |args, _| { Ok(Value::Integer(args[0].to_list()?.len() as i64)) }), + Builtin::new("map", &[true, true], |args, vm| { + let list: NixList = args[1].to_list()?; + let func: Closure = args[0].to_closure()?; + + list.into_iter() + .map(|val| { + // Leave the argument on the stack before calling the + // function. + vm.push(val); + vm.call(func.lambda(), func.upvalues().clone(), 1) + }) + .collect::, _>>() + .map(|list| Value::List(NixList::from(list))) + .map_err(Into::into) + }), Builtin::new("hasAttr", &[true, true], |args, _| { let k = args[0].to_str()?; let xs = args[1].to_attrs()?; diff --git a/tvix/eval/src/errors.rs b/tvix/eval/src/errors.rs index 10bc9276adde..17b236d0385f 100644 --- a/tvix/eval/src/errors.rs +++ b/tvix/eval/src/errors.rs @@ -100,6 +100,15 @@ impl From for ErrorKind { } } +/// Implementation used if errors occur while forcing thunks (which +/// can potentially be threaded through a few contexts, i.e. nested +/// thunks). +impl From for ErrorKind { + fn from(e: Error) -> Self { + Self::ThunkForce(Box::new(e)) + } +} + #[derive(Clone, Debug)] pub struct Error { pub kind: ErrorKind, diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-map.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-map.exp new file mode 100644 index 000000000000..e1ff70800245 --- /dev/null +++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-map.exp @@ -0,0 +1 @@ +[ [ 1 2 3 4 5 ] [ 2 4 6 8 10 ] [ 2 4 6 8 10 ] [ 1 2 3 4 5 ] ] diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-map.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-map.nix new file mode 100644 index 000000000000..6ff42d0891dc --- /dev/null +++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-map.nix @@ -0,0 +1,16 @@ +[ + # identity function + (builtins.map (x: x) [ 1 2 3 4 5 ]) + + # double stuff + (builtins.map (x: x * 2) [ 1 2 3 4 5 ]) + + # same but with a closure this time + ( + let n = 2; + in builtins.map (x: x * n) [ 1 2 3 4 5 ] + ) + + # from global scope + (map (x: x) [ 1 2 3 4 5 ]) +] -- cgit 1.4.1