diff options
-rw-r--r-- | tvix/eval/src/builtins/mod.rs | 38 | ||||
-rw-r--r-- | tvix/eval/src/value/builtin.rs | 18 | ||||
-rw-r--r-- | tvix/eval/src/vm.rs | 2 |
3 files changed, 34 insertions, 24 deletions
diff --git a/tvix/eval/src/builtins/mod.rs b/tvix/eval/src/builtins/mod.rs index 3e1f49d6995a..16ac418f6bd8 100644 --- a/tvix/eval/src/builtins/mod.rs +++ b/tvix/eval/src/builtins/mod.rs @@ -17,17 +17,17 @@ use crate::arithmetic_op; fn pure_builtins() -> Vec<Builtin> { vec![ - Builtin::new("add", 2, |mut args| { + Builtin::new("add", 2, |mut args, _| { let b = args.pop().unwrap(); let a = args.pop().unwrap(); arithmetic_op!(a, b, +) }), - Builtin::new("abort", 1, |mut args| { + Builtin::new("abort", 1, |mut args, _| { return Err(ErrorKind::Abort( args.pop().unwrap().to_string()?.as_str().to_owned(), )); }), - Builtin::new("catAttrs", 2, |mut args| { + Builtin::new("catAttrs", 2, |mut args, _| { let list = args.pop().unwrap().to_list()?; let key = args.pop().unwrap().to_string()?; let mut output = vec![]; @@ -40,64 +40,64 @@ fn pure_builtins() -> Vec<Builtin> { Ok(Value::List(NixList::construct(output.len(), output))) }), - Builtin::new("div", 2, |mut args| { + Builtin::new("div", 2, |mut args, _| { let b = args.pop().unwrap(); let a = args.pop().unwrap(); arithmetic_op!(a, b, /) }), - Builtin::new("length", 1, |args| { + Builtin::new("length", 1, |args, _| { Ok(Value::Integer(args[0].as_list()?.len() as i64)) }), - Builtin::new("isAttrs", 1, |args| { + Builtin::new("isAttrs", 1, |args, _| { Ok(Value::Bool(matches!(args[0], Value::Attrs(_)))) }), - Builtin::new("isBool", 1, |args| { + Builtin::new("isBool", 1, |args, _| { Ok(Value::Bool(matches!(args[0], Value::Bool(_)))) }), - Builtin::new("isFloat", 1, |args| { + Builtin::new("isFloat", 1, |args, _| { Ok(Value::Bool(matches!(args[0], Value::Float(_)))) }), - Builtin::new("isFunction", 1, |args| { + Builtin::new("isFunction", 1, |args, _| { Ok(Value::Bool(matches!( args[0], Value::Closure(_) | Value::Builtin(_) ))) }), - Builtin::new("isInt", 1, |args| { + Builtin::new("isInt", 1, |args, _| { Ok(Value::Bool(matches!(args[0], Value::Integer(_)))) }), - Builtin::new("isList", 1, |args| { + Builtin::new("isList", 1, |args, _| { Ok(Value::Bool(matches!(args[0], Value::List(_)))) }), - Builtin::new("isNull", 1, |args| { + Builtin::new("isNull", 1, |args, _| { Ok(Value::Bool(matches!(args[0], Value::Null))) }), - Builtin::new("isPath", 1, |args| { + Builtin::new("isPath", 1, |args, _| { Ok(Value::Bool(matches!(args[0], Value::Path(_)))) }), - Builtin::new("isString", 1, |args| { + Builtin::new("isString", 1, |args, _| { Ok(Value::Bool(matches!(args[0], Value::String(_)))) }), - Builtin::new("mul", 2, |mut args| { + Builtin::new("mul", 2, |mut args, _| { let b = args.pop().unwrap(); let a = args.pop().unwrap(); arithmetic_op!(a, b, *) }), - Builtin::new("sub", 2, |mut args| { + Builtin::new("sub", 2, |mut args, _| { let b = args.pop().unwrap(); let a = args.pop().unwrap(); arithmetic_op!(a, b, -) }), - Builtin::new("throw", 1, |mut args| { + Builtin::new("throw", 1, |mut args, _| { return Err(ErrorKind::Throw( args.pop().unwrap().to_string()?.as_str().to_owned(), )); }), - Builtin::new("toString", 1, |args| { + Builtin::new("toString", 1, |args, _| { // TODO: toString is actually not the same as Display Ok(Value::String(format!("{}", args[0]).into())) }), - Builtin::new("typeOf", 1, |args| { + Builtin::new("typeOf", 1, |args, _| { Ok(Value::String(args[0].type_of().into())) }), ] diff --git a/tvix/eval/src/value/builtin.rs b/tvix/eval/src/value/builtin.rs index d1248b1ec219..7572103ec96e 100644 --- a/tvix/eval/src/value/builtin.rs +++ b/tvix/eval/src/value/builtin.rs @@ -3,13 +3,23 @@ //! //! Builtins are directly backed by Rust code operating on Nix values. -use crate::errors::ErrorKind; +use crate::{errors::ErrorKind, vm::VM}; use super::Value; use std::fmt::{Debug, Display}; -pub type BuiltinFn = fn(arg: Vec<Value>) -> Result<Value, ErrorKind>; +/// Function pointer type for builtins implemented directly by backing +/// Rust code. +/// +/// Builtins declare their arity and are passed a vector with the +/// right number of arguments. Additionally, as they might have to +/// force the evaluation of thunks, they are passed a reference to the +/// current VM which they can use for forcing a value. +/// +/// Errors returned from a builtin will be annotated with the location +/// of the call to the builtin. +pub type BuiltinFn = fn(arg: Vec<Value>, vm: &mut VM) -> Result<Value, ErrorKind>; /// Represents a single built-in function which directly executes Rust /// code that operates on a Nix value. @@ -50,11 +60,11 @@ impl Builtin { /// Apply an additional argument to the builtin, which will either /// lead to execution of the function or to returning a partial /// builtin. - pub fn apply(mut self, arg: Value) -> Result<Value, ErrorKind> { + pub fn apply(mut self, vm: &mut VM, arg: Value) -> Result<Value, ErrorKind> { self.partials.push(arg); if self.partials.len() == self.arity { - return (self.func)(self.partials); + return (self.func)(self.partials, vm); } // Function is not yet ready to be called. diff --git a/tvix/eval/src/vm.rs b/tvix/eval/src/vm.rs index 5fabdb491e1f..318fc726abd7 100644 --- a/tvix/eval/src/vm.rs +++ b/tvix/eval/src/vm.rs @@ -418,7 +418,7 @@ impl VM { Value::Builtin(builtin) => { let arg = self.pop(); - let result = fallible!(self, builtin.apply(arg)); + let result = fallible!(self, builtin.apply(self, arg)); self.push(result); } _ => return Err(self.error(ErrorKind::NotCallable)), |