diff options
Diffstat (limited to 'tvix/cli')
-rw-r--r-- | tvix/cli/src/lib.rs | 67 | ||||
-rw-r--r-- | tvix/cli/src/main.rs | 4 | ||||
-rw-r--r-- | tvix/cli/src/repl.rs | 34 | ||||
-rw-r--r-- | tvix/cli/tests/.skip-tree | 0 | ||||
-rw-r--r-- | tvix/cli/tests/import.nix | 1 | ||||
-rw-r--r-- | tvix/cli/tests/repl.rs | 16 | ||||
-rw-r--r-- | tvix/cli/tests/six.nix | 1 |
7 files changed, 102 insertions, 21 deletions
diff --git a/tvix/cli/src/lib.rs b/tvix/cli/src/lib.rs index 008593c5d84f..800ffb4e0e75 100644 --- a/tvix/cli/src/lib.rs +++ b/tvix/cli/src/lib.rs @@ -8,7 +8,7 @@ use tvix_build::buildservice; use tvix_eval::{ builtins::impure_builtins, observer::{DisassemblingObserver, TracingObserver}, - ErrorKind, EvalIO, Value, + ErrorKind, EvalIO, GlobalsMap, SourceCode, Value, }; use tvix_glue::{ builtins::{add_derivation_builtins, add_fetcher_builtins, add_import_builtins}, @@ -83,7 +83,13 @@ impl AllowIncomplete { #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct IncompleteInput; +pub struct EvalResult { + value: Option<Value>, + globals: Rc<GlobalsMap>, +} + /// Interprets the given code snippet, printing out warnings and errors and returning the result +#[allow(clippy::too_many_arguments)] pub fn evaluate( tvix_store_io: Rc<TvixStoreIO>, code: &str, @@ -91,7 +97,9 @@ pub fn evaluate( args: &Args, allow_incomplete: AllowIncomplete, env: Option<&HashMap<SmolStr, Value>>, -) -> Result<Option<Value>, IncompleteInput> { + globals: Option<Rc<GlobalsMap>>, + source_map: Option<SourceCode>, +) -> Result<EvalResult, IncompleteInput> { let span = Span::current(); span.pb_start(); span.pb_set_style(&tvix_tracing::PB_SPINNER_STYLE); @@ -102,16 +110,27 @@ pub fn evaluate( )) as Box<dyn EvalIO>) .enable_import() .with_strict(args.strict) - .add_builtins(impure_builtins()) .env(env); - eval_builder = add_derivation_builtins(eval_builder, Rc::clone(&tvix_store_io)); - eval_builder = add_fetcher_builtins(eval_builder, Rc::clone(&tvix_store_io)); - eval_builder = add_import_builtins(eval_builder, tvix_store_io); - eval_builder = configure_nix_path(eval_builder, &args.nix_search_path); + match globals { + Some(globals) => { + eval_builder = eval_builder.with_globals(globals); + } + None => { + eval_builder = eval_builder.add_builtins(impure_builtins()); + eval_builder = add_derivation_builtins(eval_builder, Rc::clone(&tvix_store_io)); + eval_builder = add_fetcher_builtins(eval_builder, Rc::clone(&tvix_store_io)); + eval_builder = add_import_builtins(eval_builder, tvix_store_io); + eval_builder = configure_nix_path(eval_builder, &args.nix_search_path); + } + }; + + if let Some(source_map) = source_map { + eval_builder = eval_builder.with_source_map(source_map); + } let source_map = eval_builder.source_map().clone(); - let result = { + let (result, globals) = { let mut compiler_observer = DisassemblingObserver::new(source_map.clone(), std::io::stderr()); if args.dump_bytecode { @@ -129,7 +148,9 @@ pub fn evaluate( span.pb_set_message("Evaluating…"); let eval = eval_builder.build(); - eval.evaluate(code, path) + let globals = eval.globals(); + let result = eval.evaluate(code, path); + (result, globals) }; if allow_incomplete.allow() @@ -160,19 +181,24 @@ pub fn evaluate( } } - Ok(result.value) + Ok(EvalResult { + globals, + value: result.value, + }) } pub struct InterpretResult { output: String, success: bool, + pub(crate) globals: Option<Rc<GlobalsMap>>, } impl InterpretResult { - pub fn empty_success() -> Self { + pub fn empty_success(globals: Option<Rc<GlobalsMap>>) -> Self { Self { output: String::new(), success: true, + globals, } } @@ -194,6 +220,7 @@ impl InterpretResult { /// and the result itself. The return value indicates whether /// evaluation succeeded. #[instrument(skip_all, fields(indicatif.pb_show=1))] +#[allow(clippy::too_many_arguments)] pub fn interpret( tvix_store_io: Rc<TvixStoreIO>, code: &str, @@ -202,11 +229,22 @@ pub fn interpret( explain: bool, allow_incomplete: AllowIncomplete, env: Option<&HashMap<SmolStr, Value>>, + globals: Option<Rc<GlobalsMap>>, + source_map: Option<SourceCode>, ) -> Result<InterpretResult, IncompleteInput> { let mut output = String::new(); - let result = evaluate(tvix_store_io, code, path, args, allow_incomplete, env)?; + let result = evaluate( + tvix_store_io, + code, + path, + args, + allow_incomplete, + env, + globals, + source_map, + )?; - if let Some(value) = result.as_ref() { + if let Some(value) = result.value.as_ref() { if explain { writeln!(&mut output, "=> {}", value.explain()).unwrap(); } else if args.raw { @@ -219,6 +257,7 @@ pub fn interpret( // inform the caller about any errors Ok(InterpretResult { output, - success: result.is_some(), + success: result.value.is_some(), + globals: Some(result.globals), }) } diff --git a/tvix/cli/src/main.rs b/tvix/cli/src/main.rs index 0bd3be37eeb2..f927665aeb76 100644 --- a/tvix/cli/src/main.rs +++ b/tvix/cli/src/main.rs @@ -75,6 +75,8 @@ fn main() { false, AllowIncomplete::RequireComplete, None, // TODO(aspen): Pass in --arg/--argstr here + None, + None, ) .unwrap() .finalize() @@ -104,6 +106,8 @@ fn run_file(io_handle: Rc<TvixStoreIO>, mut path: PathBuf, args: &Args) { false, AllowIncomplete::RequireComplete, None, + None, + None, ) .unwrap() .finalize() diff --git a/tvix/cli/src/repl.rs b/tvix/cli/src/repl.rs index 5098fbaeedc3..6b34b6552da0 100644 --- a/tvix/cli/src/repl.rs +++ b/tvix/cli/src/repl.rs @@ -3,7 +3,7 @@ use std::{collections::HashMap, path::PathBuf}; use rustyline::{error::ReadlineError, Editor}; use smol_str::SmolStr; -use tvix_eval::Value; +use tvix_eval::{GlobalsMap, SourceCode, Value}; use tvix_glue::tvix_store_io::TvixStoreIO; use crate::{ @@ -92,6 +92,8 @@ pub struct Repl<'a> { io_handle: Rc<TvixStoreIO>, args: &'a Args, + source_map: SourceCode, + globals: Option<Rc<GlobalsMap>>, } impl<'a> Repl<'a> { @@ -103,6 +105,8 @@ impl<'a> Repl<'a> { env: HashMap::new(), io_handle, args, + source_map: Default::default(), + globals: None, } } @@ -179,7 +183,7 @@ impl<'a> Repl<'a> { } ReplCommand::Help => { println!("{}", ReplCommand::HELP); - Ok(InterpretResult::empty_success()) + Ok(InterpretResult::empty_success(None)) } ReplCommand::Expr(input) => interpret( Rc::clone(&self.io_handle), @@ -189,6 +193,8 @@ impl<'a> Repl<'a> { false, AllowIncomplete::Allow, Some(&self.env), + self.globals.clone(), + Some(self.source_map.clone()), ), ReplCommand::Assign(Assignment { ident, value }) => { match evaluate( @@ -198,12 +204,15 @@ impl<'a> Repl<'a> { self.args, AllowIncomplete::Allow, Some(&self.env), + self.globals.clone(), + Some(self.source_map.clone()), ) { - Ok(Some(value)) => { - self.env.insert(ident.into(), value); - Ok(InterpretResult::empty_success()) + Ok(result) => { + if let Some(value) = result.value { + self.env.insert(ident.into(), value); + } + Ok(InterpretResult::empty_success(Some(result.globals))) } - Ok(None) => Ok(InterpretResult::empty_success()), Err(incomplete) => Err(incomplete), } } @@ -215,6 +224,8 @@ impl<'a> Repl<'a> { true, AllowIncomplete::Allow, Some(&self.env), + self.globals.clone(), + Some(self.source_map.clone()), ), ReplCommand::Print(input) => interpret( Rc::clone(&self.io_handle), @@ -227,13 +238,22 @@ impl<'a> Repl<'a> { false, AllowIncomplete::Allow, Some(&self.env), + self.globals.clone(), + Some(self.source_map.clone()), ), }; match res { - Ok(InterpretResult { output, .. }) => { + Ok(InterpretResult { + output, + globals, + success: _, + }) => { self.rl.add_history_entry(input); self.multiline_input = None; + if globals.is_some() { + self.globals = globals; + } CommandResult { output, continue_: true, diff --git a/tvix/cli/tests/.skip-tree b/tvix/cli/tests/.skip-tree new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/tvix/cli/tests/.skip-tree diff --git a/tvix/cli/tests/import.nix b/tvix/cli/tests/import.nix new file mode 100644 index 000000000000..9ac2d0232ec8 --- /dev/null +++ b/tvix/cli/tests/import.nix @@ -0,0 +1 @@ +{ }: import ./six.nix { } diff --git a/tvix/cli/tests/repl.rs b/tvix/cli/tests/repl.rs index a14f4bff7e6a..c6644330976e 100644 --- a/tvix/cli/tests/repl.rs +++ b/tvix/cli/tests/repl.rs @@ -53,6 +53,22 @@ test_repl!(bind_lazy() { "#]]; }); +test_repl!(bind_lazy_errors() { + r#"x = (_: "x" + 1)"# => expect![[""]]; + "x null" => expect![[""]]; +}); + +test_repl!(bind_referencing_import() { + "six = import ./tests/six.nix {}" => expect![[""]]; + "six.six" => expect![[r#" + => 6 :: int + "#]]; + "imported = import ./tests/import.nix" => expect![[""]]; + "(imported {}).six" => expect![[r#" + => 6 :: int + "#]]; +}); + test_repl!(deep_print() { "builtins.map (x: x + 1) [ 1 2 3 ]" => expect![[r#" => [ <CODE> <CODE> <CODE> ] :: list diff --git a/tvix/cli/tests/six.nix b/tvix/cli/tests/six.nix new file mode 100644 index 000000000000..d466abe06a4c --- /dev/null +++ b/tvix/cli/tests/six.nix @@ -0,0 +1 @@ +{ }: { six = builtins.foldl' (x: y: x + y) 0 [ 1 2 3 ]; } |