diff options
-rw-r--r-- | tvix/Cargo.lock | 12 | ||||
-rw-r--r-- | tvix/Cargo.nix | 60 | ||||
-rw-r--r-- | tvix/Cargo.toml | 1 | ||||
-rw-r--r-- | tvix/cli/Cargo.toml | 10 | ||||
-rw-r--r-- | tvix/cli/default.nix | 5 | ||||
-rw-r--r-- | tvix/cli/src/main.rs (renamed from tvix/eval/src/main.rs) | 57 | ||||
-rw-r--r-- | tvix/eval/Cargo.toml | 11 | ||||
-rw-r--r-- | tvix/eval/src/eval.rs | 145 | ||||
-rw-r--r-- | tvix/eval/src/lib.rs | 2 |
9 files changed, 105 insertions, 198 deletions
diff --git a/tvix/Cargo.lock b/tvix/Cargo.lock index 4e2197a90c3f..7dcf6c78f87e 100644 --- a/tvix/Cargo.lock +++ b/tvix/Cargo.lock @@ -1488,11 +1488,20 @@ dependencies = [ ] [[package]] +name = "tvix-cli" +version = "0.1.0" +dependencies = [ + "clap 3.2.23", + "dirs", + "rustyline", + "tvix-eval", +] + +[[package]] name = "tvix-eval" version = "0.1.0" dependencies = [ "backtrace-on-stack-overflow", - "clap 3.2.23", "codemap", "codemap-diagnostic", "criterion", @@ -1504,7 +1513,6 @@ dependencies = [ "regex", "rnix", "rowan", - "rustyline", "serde", "serde_json", "smol_str", diff --git a/tvix/Cargo.nix b/tvix/Cargo.nix index 075ca7219f4c..599da8108557 100644 --- a/tvix/Cargo.nix +++ b/tvix/Cargo.nix @@ -47,6 +47,16 @@ rec { # File a bug if you depend on any for non-debug work! debug = internal.debugCrate { inherit packageId; }; }; + "tvix-cli" = rec { + packageId = "tvix-cli"; + build = internal.buildRustCrateWithFeatures { + packageId = "tvix-cli"; + }; + + # Debug support which might change between releases. + # File a bug if you depend on any for non-debug work! + debug = internal.debugCrate { inherit packageId; }; + }; "tvix-eval" = rec { packageId = "tvix-eval"; build = internal.buildRustCrateWithFeatures { @@ -4176,13 +4186,39 @@ rec { ]; }; - "tvix-eval" = rec { - crateName = "tvix-eval"; + "tvix-cli" = rec { + crateName = "tvix-cli"; version = "0.1.0"; edition = "2021"; crateBin = [ - { name = "tvix-eval"; path = "src/main.rs"; } + { name = "tvix-cli"; path = "src/main.rs"; } ]; + src = lib.cleanSourceWith { filter = sourceFilter; src = ./cli; }; + dependencies = [ + { + name = "clap"; + packageId = "clap 3.2.23"; + features = [ "derive" "env" ]; + } + { + name = "dirs"; + packageId = "dirs"; + } + { + name = "rustyline"; + packageId = "rustyline"; + } + { + name = "tvix-eval"; + packageId = "tvix-eval"; + } + ]; + + }; + "tvix-eval" = rec { + crateName = "tvix-eval"; + version = "0.1.0"; + edition = "2021"; src = lib.cleanSourceWith { filter = sourceFilter; src = ./eval; }; libName = "tvix_eval"; dependencies = [ @@ -4192,12 +4228,6 @@ rec { optional = true; } { - name = "clap"; - packageId = "clap 3.2.23"; - optional = true; - features = [ "derive" "env" ]; - } - { name = "codemap"; packageId = "codemap"; } @@ -4233,11 +4263,6 @@ rec { packageId = "rowan"; } { - name = "rustyline"; - packageId = "rustyline"; - optional = true; - } - { name = "serde"; packageId = "serde"; } @@ -4290,14 +4315,11 @@ rec { "arbitrary" = [ "proptest" "test-strategy" ]; "backtrace-on-stack-overflow" = [ "dep:backtrace-on-stack-overflow" ]; "backtrace_overflow" = [ "backtrace-on-stack-overflow" ]; - "clap" = [ "dep:clap" ]; - "default" = [ "repl" "impure" "arbitrary" "nix_tests" "backtrace_overflow" ]; + "default" = [ "impure" "arbitrary" "nix_tests" "backtrace_overflow" ]; "proptest" = [ "dep:proptest" ]; - "repl" = [ "rustyline" "clap" ]; - "rustyline" = [ "dep:rustyline" ]; "test-strategy" = [ "dep:test-strategy" ]; }; - resolvedDefaultFeatures = [ "arbitrary" "backtrace-on-stack-overflow" "backtrace_overflow" "clap" "default" "impure" "nix_tests" "proptest" "repl" "rustyline" "test-strategy" ]; + resolvedDefaultFeatures = [ "arbitrary" "backtrace-on-stack-overflow" "backtrace_overflow" "default" "impure" "nix_tests" "proptest" "test-strategy" ]; }; "tvix-eval-builtin-macros" = rec { crateName = "tvix-eval-builtin-macros"; diff --git a/tvix/Cargo.toml b/tvix/Cargo.toml index 4cc70f9040a6..178802dd56ee 100644 --- a/tvix/Cargo.toml +++ b/tvix/Cargo.toml @@ -18,6 +18,7 @@ [workspace] members = [ + "cli", "eval", "eval/builtin-macros", "nix_cli", diff --git a/tvix/cli/Cargo.toml b/tvix/cli/Cargo.toml new file mode 100644 index 000000000000..605b2c80651d --- /dev/null +++ b/tvix/cli/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "tvix-cli" +version = "0.1.0" +edition = "2021" + +[dependencies] +tvix-eval = { path = "../eval" } +rustyline = "10.0.0" +clap = { version = "3.2.22", features = ["derive", "env"] } +dirs = "4.0.0" diff --git a/tvix/cli/default.nix b/tvix/cli/default.nix new file mode 100644 index 000000000000..7cb91979fd62 --- /dev/null +++ b/tvix/cli/default.nix @@ -0,0 +1,5 @@ +{ depot, pkgs, lib, ... }: + +depot.tvix.crates.workspaceMembers.tvix-cli.build.override { + runTests = true; +} diff --git a/tvix/eval/src/main.rs b/tvix/cli/src/main.rs index bef48d07a0c4..1c148cad3873 100644 --- a/tvix/eval/src/main.rs +++ b/tvix/cli/src/main.rs @@ -2,7 +2,7 @@ use std::{fs, path::PathBuf}; use clap::Parser; use rustyline::{error::ReadlineError, Editor}; -use tvix_eval::Value; +use tvix_eval::Value; //{Error, EvalWarning, Evaluation, Value}; #[derive(Parser)] struct Args { @@ -11,35 +11,57 @@ struct Args { #[clap(long, short = 'E')] expr: Option<String>, + // TODO: port these options here directly + // #[clap(flatten)] + // eval_options: tvix_eval::Options, +} + +/// Interprets the given code snippet, printing out warnings, errors +/// and the result itself. The return value indicates whether +/// evaluation succeeded. +fn interpret(code: &str, path: Option<PathBuf>) -> bool { + let mut eval = tvix_eval::Evaluation::new(code, path); + let result = eval.evaluate(); + + let source_map = eval.source_map(); + for error in &result.errors { + error.fancy_format_stderr(&source_map); + } + + for warning in &result.warnings { + warning.fancy_format_stderr(&source_map); + } + + if let Some(value) = result.value.as_ref() { + println_result(value, /* TODO raw = */ false); + } - #[clap(flatten)] - eval_options: tvix_eval::Options, + // inform the caller about any errors + result.errors.is_empty() } fn main() { let args = Args::parse(); if let Some(file) = args.script { - run_file(file, args.eval_options) + run_file(file /* TODO, args.eval_options*/) } else if let Some(expr) = args.expr { - let raw = args.eval_options.raw; - if let Ok(result) = tvix_eval::interpret(&expr, None, args.eval_options) { - println_result(&result, raw); + if !interpret(&expr, None) { + std::process::exit(1); } } else { - run_prompt(args.eval_options) + run_prompt(/* TODO args.eval_options */) } } -fn run_file(mut path: PathBuf, eval_options: tvix_eval::Options) { +fn run_file(mut path: PathBuf /* TODO: , eval_options: tvix_eval::Options */) { if path.is_dir() { path.push("default.nix"); } let contents = fs::read_to_string(&path).expect("failed to read the input file"); - let raw = eval_options.raw; - match tvix_eval::interpret(&contents, Some(path), eval_options) { - Ok(result) => println_result(&result, raw), - Err(err) => eprintln!("{}", err), + + if !interpret(&contents, Some(path)) { + std::process::exit(1); } } @@ -59,7 +81,7 @@ fn state_dir() -> Option<PathBuf> { path } -fn run_prompt(eval_options: tvix_eval::Options) { +fn run_prompt(/* TODO eval_options: tvix_eval::Options */) { let mut rl = Editor::<()>::new().expect("should be able to launch rustyline"); let history_path = match state_dir() { @@ -84,12 +106,7 @@ fn run_prompt(eval_options: tvix_eval::Options) { } rl.add_history_entry(&line); - match tvix_eval::interpret(&line, None, eval_options.clone()) { - Ok(result) => { - println!("=> {} :: {}", result, result.type_of()); - } - Err(_) => { /* interpret takes care of error formatting */ } - } + interpret(&line, None); } Err(ReadlineError::Interrupted) | Err(ReadlineError::Eof) => break, diff --git a/tvix/eval/Cargo.toml b/tvix/eval/Cargo.toml index 1b24f6401343..e4282507d7ec 100644 --- a/tvix/eval/Cargo.toml +++ b/tvix/eval/Cargo.toml @@ -8,13 +8,8 @@ edition = "2021" [lib] name = "tvix_eval" -[[bin]] -name = "tvix-eval" -required-features = [ "repl" ] - [dependencies] smol_str = "0.1" -rustyline = { version = "10.0.0", optional = true } dirs = "4.0.0" path-clean = "0.1" tabwriter = "1.2" @@ -23,7 +18,6 @@ codemap = "0.1.3" codemap-diagnostic = "0.1.1" proptest = { version = "1.0.0", default_features = false, features = ["std", "alloc", "break-dead-code", "tempfile"], optional = true } test-strategy = { version = "0.2.1", optional = true } -clap = { version = "3.2.22", optional = true, features = ["derive", "env"] } serde = "1.0" serde_json = "1.0" regex = "1.6.0" @@ -39,15 +33,12 @@ itertools = "0.10.3" tempdir = "0.3.7" [features] -default = [ "repl", "impure", "arbitrary", "nix_tests", "backtrace_overflow" ] +default = [ "impure", "arbitrary", "nix_tests", "backtrace_overflow" ] # Enables running the Nix language test suite from the original C++ # Nix implementation (at version 2.3) against Tvix. nix_tests = [] -# Enables building the binary (tvix-eval REPL) -repl = [ "rustyline", "clap" ] - # Enables operations in the VM which depend on the ability to perform I/O impure = [] diff --git a/tvix/eval/src/eval.rs b/tvix/eval/src/eval.rs deleted file mode 100644 index bfd827e2ca5b..000000000000 --- a/tvix/eval/src/eval.rs +++ /dev/null @@ -1,145 +0,0 @@ -use std::path::PathBuf; - -use crate::{ - builtins::global_builtins, - errors::{Error, ErrorKind, EvalResult}, - nix_search_path::NixSearchPath, - observer::{DisassemblingObserver, NoOpObserver, TracingObserver}, - pretty_ast::pretty_print_expr, - value::Value, - SourceCode, -}; - -/// Runtime options for the Tvix interpreter -#[derive(Debug, Clone, Default)] -#[cfg_attr(feature = "repl", derive(clap::Parser))] -pub struct Options { - /// Dump the raw AST to stdout before interpreting - #[cfg_attr(feature = "repl", clap(long, env = "TVIX_DISPLAY_AST"))] - display_ast: bool, - - /// Dump the bytecode to stdout before evaluating - #[cfg_attr(feature = "repl", clap(long, env = "TVIX_DUMP_BYTECODE"))] - dump_bytecode: bool, - - /// Trace the runtime of the VM - #[cfg_attr(feature = "repl", clap(long, env = "TVIX_TRACE_RUNTIME"))] - trace_runtime: bool, - - /// Print warnings - #[cfg_attr( - feature = "repl", - clap(long, env = "TVIX_WARNINGS", default_value = "true") - )] - warnings: bool, - - /// A colon-separated list of directories to use to resolve `<...>`-style paths - #[cfg_attr(feature = "repl", clap(long, short = 'I', env = "NIX_PATH"))] - nix_search_path: Option<NixSearchPath>, - - #[cfg_attr(feature = "repl", clap(long))] - pub raw: bool, -} - -impl Options { - #[cfg(test)] - pub(crate) fn test_options() -> Options { - Options { - warnings: false, - ..Options::default() - } - } -} - -pub fn interpret(code: &str, location: Option<PathBuf>, options: Options) -> EvalResult<Value> { - let source = SourceCode::new(); - let file = source.add_file( - location - .as_ref() - .map(|p| p.to_string_lossy().to_string()) - .unwrap_or_else(|| "[tvix-repl]".into()), - code.into(), - ); - - let parsed = rnix::ast::Root::parse(code); - let errors = parsed.errors(); - - if !errors.is_empty() { - let err = Error { - kind: ErrorKind::ParseErrors(errors.to_vec()), - span: file.span, - }; - err.fancy_format_stderr(&source); - return Err(err); - } - - // If we've reached this point, there are no errors. - let root_expr = parsed - .tree() - .expr() - .expect("expression should exist if no errors occured"); - - if options.display_ast { - println!("{}", pretty_print_expr(&root_expr)); - } - - let builtins = crate::compiler::prepare_globals(Box::new(global_builtins(source.clone()))); - let result = if options.dump_bytecode { - crate::compiler::compile( - &root_expr, - location, - file.clone(), - builtins, - &mut DisassemblingObserver::new(source.clone(), std::io::stderr()), - ) - } else { - crate::compiler::compile( - &root_expr, - location, - file.clone(), - builtins, - &mut NoOpObserver::default(), - ) - }?; - - if options.warnings { - for warning in result.warnings { - warning.fancy_format_stderr(&source); - } - } - - for error in &result.errors { - error.fancy_format_stderr(&source); - } - - if let Some(err) = result.errors.last() { - return Err(err.clone()); - } - - let result = if options.trace_runtime { - crate::vm::run_lambda( - options.nix_search_path.unwrap_or_default(), - &mut TracingObserver::new(std::io::stderr()), - result.lambda, - ) - } else { - crate::vm::run_lambda( - options.nix_search_path.unwrap_or_default(), - &mut NoOpObserver::default(), - result.lambda, - ) - }; - - if let Err(err) = &result { - err.fancy_format_stderr(&source); - } - - result.map(|r| { - if options.warnings { - for warning in r.warnings { - warning.fancy_format_stderr(&source); - } - } - r.value - }) -} diff --git a/tvix/eval/src/lib.rs b/tvix/eval/src/lib.rs index 43a085108258..8697c752f392 100644 --- a/tvix/eval/src/lib.rs +++ b/tvix/eval/src/lib.rs @@ -16,7 +16,6 @@ mod builtins; mod chunk; mod compiler; mod errors; -mod eval; pub mod observer; mod opcode; mod pretty_ast; @@ -44,7 +43,6 @@ use std::sync::Arc; pub use crate::builtins::global_builtins; pub use crate::compiler::{compile, prepare_globals}; pub use crate::errors::{Error, ErrorKind, EvalResult}; -pub use crate::eval::{interpret, Options}; pub use crate::pretty_ast::pretty_print_expr; pub use crate::source::SourceCode; pub use crate::value::Value; |