diff options
-rw-r--r-- | tvix/eval/src/value/thunk.rs | 46 |
1 files changed, 45 insertions, 1 deletions
diff --git a/tvix/eval/src/value/thunk.rs b/tvix/eval/src/value/thunk.rs index 9f1415989335..420c5fe039aa 100644 --- a/tvix/eval/src/value/thunk.rs +++ b/tvix/eval/src/value/thunk.rs @@ -27,9 +27,10 @@ use std::{ use codemap::Span; use crate::{ + chunk::Chunk, errors::{Error, ErrorKind}, upvalues::Upvalues, - value::Closure, + value::{Builtin, Closure}, vm::VM, Value, }; @@ -84,6 +85,49 @@ impl Thunk { }))) } + /// Create a new thunk from suspended Rust code. + /// + /// The suspended code will be executed and expected to return a + /// value whenever the thunk is forced like any other thunk. + pub fn new_suspended_native( + native: Rc<Box<dyn Fn(&mut VM) -> Result<Value, ErrorKind>>>, + ) -> Self { + let span = codemap::CodeMap::new() + .add_file("<internal>".to_owned(), "<internal>".to_owned()) + .span; + let builtin = Builtin::new( + "Thunk::new_suspended_native()", + &[crate::value::builtin::BuiltinArgument { + strict: true, + name: "fake", + }], + None, + move |v: Vec<Value>, vm: &mut VM| { + // sanity check that only the dummy argument was popped + assert_eq!(v.len(), 1); + assert!(matches!(v[0], Value::Null)); + native(vm) + }, + ); + let mut chunk = Chunk::default(); + let constant_idx = chunk.push_constant(Value::Builtin(builtin)); + // Tvix doesn't have "0-ary" builtins, so we have to push a fake argument + chunk.push_op(crate::opcode::OpCode::OpNull, span); + chunk.push_op(crate::opcode::OpCode::OpConstant(constant_idx), span); + chunk.push_op(crate::opcode::OpCode::OpCall, span); + let lambda = Lambda { + name: None, + formals: None, + upvalue_count: 0, + chunk, + }; + Thunk(Rc::new(RefCell::new(ThunkRepr::Suspended { + lambda: Rc::new(lambda), + upvalues: Rc::new(Upvalues::with_capacity(0)), + span: span, + }))) + } + /// Evaluate the content of a thunk, potentially repeatedly, until a /// non-thunk value is returned. /// |