refactor(tvix/eval): pass a VM reference to builtins
This makes it possible for builtins to force values on their own, without the VM having to apply a strictness mask to the arguments first. Change-Id: Ib49a94e56ca2a8d515c39647381ab55a727766e3 Reviewed-on: https://cl.tvl.fyi/c/depot/+/6411 Reviewed-by: sterni <sternenseemann@systemli.org> Tested-by: BuildkiteCI
This commit is contained in:
		
							parent
							
								
									5ee89bcf5c
								
							
						
					
					
						commit
						0d7ad5e6d1
					
				
					 3 changed files with 34 additions and 24 deletions
				
			
		|  | @ -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())) | ||||
|         }), | ||||
|     ] | ||||
|  |  | |||
|  | @ -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.
 | ||||
|  |  | |||
|  | @ -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)), | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue