diff options
Diffstat (limited to 'tvix/eval/src/builtins/impure.rs')
-rw-r--r-- | tvix/eval/src/builtins/impure.rs | 84 |
1 files changed, 79 insertions, 5 deletions
diff --git a/tvix/eval/src/builtins/impure.rs b/tvix/eval/src/builtins/impure.rs index 7073deaaa7ac..675bdd50950e 100644 --- a/tvix/eval/src/builtins/impure.rs +++ b/tvix/eval/src/builtins/impure.rs @@ -1,19 +1,23 @@ use std::{ - collections::BTreeMap, + collections::{BTreeMap, HashMap}, + rc::Rc, time::{SystemTime, UNIX_EPOCH}, }; use crate::{ - value::{Builtin, NixString}, - Value, + errors::ErrorKind, + observer::NoOpObserver, + value::{Builtin, NixString, Thunk}, + vm::VM, + SourceCode, Value, }; fn impure_builtins() -> Vec<Builtin> { vec![] } -/// Return all impure builtins, that is all builtins which may perform I/O outside of the VM and so -/// cannot be used in all contexts (e.g. WASM). +/// Return all impure builtins, that is all builtins which may perform I/O +/// outside of the VM and so cannot be used in all contexts (e.g. WASM). pub(super) fn builtins() -> BTreeMap<NixString, Value> { let mut map: BTreeMap<NixString, Value> = impure_builtins() .into_iter() @@ -34,3 +38,73 @@ pub(super) fn builtins() -> BTreeMap<NixString, Value> { map } + +/// Constructs and inserts the `import` builtin. This builtin is special in that +/// it needs to capture the [crate::SourceCode] structure to correctly track +/// source code locations while invoking a compiler. +// TODO: need to be able to pass through a CompilationObserver, too. +pub fn builtins_import(source: SourceCode) -> Builtin { + Builtin::new( + "import", + &[true], + move |mut args: Vec<Value>, _: &mut VM| { + let path = match args.pop().unwrap() { + Value::Path(path) => path, + Value::String(_) => { + return Err(ErrorKind::NotImplemented("importing from string-paths")) + } + other => { + return Err(ErrorKind::TypeError { + expected: "path or string", + actual: other.type_of(), + }) + } + }; + + let contents = + std::fs::read_to_string(&path).map_err(|err| ErrorKind::ReadFileError { + path: path.clone(), + error: Rc::new(err), + })?; + + let parsed = rnix::ast::Root::parse(&contents); + let errors = parsed.errors(); + + if !errors.is_empty() { + return Err(ErrorKind::ImportParseError { + path, + errors: errors.to_vec(), + }); + } + + let file = source.add_file(path.to_string_lossy().to_string(), contents); + + let result = crate::compile( + &parsed.tree().expr().unwrap(), + Some(path.clone()), + file, + HashMap::new(), // TODO: pass through globals + &mut NoOpObserver::default(), + ) + .map_err(|err| ErrorKind::ImportCompilerError { + path: path.clone(), + errors: vec![err], + })?; + + if !result.errors.is_empty() { + return Err(ErrorKind::ImportCompilerError { + path, + errors: result.errors, + }); + } + + // TODO: deal with runtime *warnings* (most likely through an + // emit_warning function on the VM that might return it together with + // the result) + + // Compilation succeeded, we can construct a thunk from whatever it spat + // out and return that. + Ok(Value::Thunk(Thunk::new(result.lambda))) + }, + ) +} |