From b530e496a5962a3998773343d7ed6a9dd84b7753 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Tue, 4 Oct 2022 18:27:49 +0300 Subject: feat(tvix/eval): initial implementation of `builtins.import` This adds an initial working version of builtins.import which encapsulates the entire functionality of `import` within the builtin itself, without requiring any changes in the compiler or VM. The key insight that enables this is that we can simply return a Thunk from `import` that is constructed from the output of running the compiler and - ta-da! - no other component needs to know about it. A couple of notes: * builtins.import needs to capture variables like the SourceCode structure. This means it can not currently be constructed the same way as other builtins and has special handling, which leaks out to `eval.rs`. I have postponed dealing with that until we have this working a bit more. * the `globals` are not yet passed through * the error representation for the new variants is absolutely not done yet, we probably want to switch to something that supports cause-chaining now (like miette) * there is no mechanism for emitting warnings at runtime; we need to add that Change-Id: I3117a7ae3ff2432bf44f5ff05ad35f47faca31d5 Reviewed-on: https://cl.tvl.fyi/c/depot/+/6857 Reviewed-by: sterni Reviewed-by: wpcarro Tested-by: BuildkiteCI Reviewed-by: grfn --- tvix/eval/src/builtins/impure.rs | 84 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 79 insertions(+), 5 deletions(-) (limited to 'tvix/eval/src/builtins/impure.rs') 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 { 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 { let mut map: BTreeMap = impure_builtins() .into_iter() @@ -34,3 +38,73 @@ pub(super) fn builtins() -> BTreeMap { 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, _: &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))) + }, + ) +} -- cgit 1.4.1