diff --git a/crates/nu-cli/src/completions/completer.rs b/crates/nu-cli/src/completions/completer.rs index 5095f6bb4ff..9400568bc33 100644 --- a/crates/nu-cli/src/completions/completer.rs +++ b/crates/nu-cli/src/completions/completer.rs @@ -5,6 +5,7 @@ use crate::completions::{ use nu_color_config::{color_record_to_nustyle, lookup_ansi_color_style}; use nu_engine::eval_block; use nu_parser::{flatten_expression, parse, FlatShape}; +use nu_protocol::debugger::WithoutDebug; use nu_protocol::{ ast::PipelineElement, engine::{EngineState, Stack, StateWorkingSet}, @@ -83,7 +84,7 @@ impl NuCompleter { } } - let result = eval_block( + let result = eval_block::( &self.engine_state, &mut callee_stack, block, diff --git a/crates/nu-cli/src/completions/custom_completions.rs b/crates/nu-cli/src/completions/custom_completions.rs index a9c680075c3..741c6158afd 100644 --- a/crates/nu-cli/src/completions/custom_completions.rs +++ b/crates/nu-cli/src/completions/custom_completions.rs @@ -1,5 +1,6 @@ use crate::completions::{Completer, CompletionOptions, MatchAlgorithm, SortBy}; use nu_engine::eval_call; +use nu_protocol::debugger::WithoutDebug; use nu_protocol::{ ast::{Argument, Call, Expr, Expression}, engine::{EngineState, Stack, StateWorkingSet}, @@ -46,7 +47,7 @@ impl Completer for CustomCompletion { let line_pos = pos - offset; // Call custom declaration - let result = eval_call( + let result = eval_call::( &self.engine_state, &mut self.stack, &Call { diff --git a/crates/nu-cli/src/eval_cmds.rs b/crates/nu-cli/src/eval_cmds.rs index ea0742d4b33..daaa5c237f0 100644 --- a/crates/nu-cli/src/eval_cmds.rs +++ b/crates/nu-cli/src/eval_cmds.rs @@ -2,6 +2,7 @@ use log::info; use miette::Result; use nu_engine::{convert_env_values, eval_block}; use nu_parser::parse; +use nu_protocol::debugger::WithoutDebug; use nu_protocol::engine::Stack; use nu_protocol::report_error; use nu_protocol::{ @@ -55,21 +56,27 @@ pub fn evaluate_commands( } // Run the block - let exit_code = match eval_block(engine_state, stack, &block, input, false, false) { - Ok(pipeline_data) => { - let mut config = engine_state.get_config().clone(); - if let Some(t_mode) = table_mode { - config.table_mode = t_mode.coerce_str()?.parse().unwrap_or_default(); + let exit_code = + match eval_block::(engine_state, stack, &block, input, false, false) { + Ok(pipeline_data) => { + let mut config = engine_state.get_config().clone(); + if let Some(t_mode) = table_mode { + config.table_mode = t_mode.coerce_str()?.parse().unwrap_or_default(); + } + crate::eval_file::print_table_or_error( + engine_state, + stack, + pipeline_data, + &mut config, + ) } - crate::eval_file::print_table_or_error(engine_state, stack, pipeline_data, &mut config) - } - Err(err) => { - let working_set = StateWorkingSet::new(engine_state); + Err(err) => { + let working_set = StateWorkingSet::new(engine_state); - report_error(&working_set, &err); - std::process::exit(1); - } - }; + report_error(&working_set, &err); + std::process::exit(1); + } + }; info!("evaluate {}:{}:{}", file!(), line!(), column!()); diff --git a/crates/nu-cli/src/eval_file.rs b/crates/nu-cli/src/eval_file.rs index e94fc9859bd..8c0f4be2b27 100644 --- a/crates/nu-cli/src/eval_file.rs +++ b/crates/nu-cli/src/eval_file.rs @@ -6,6 +6,7 @@ use nu_engine::eval_block; use nu_engine::{convert_env_values, current_dir}; use nu_parser::parse; use nu_path::canonicalize_with; +use nu_protocol::debugger::WithoutDebug; use nu_protocol::report_error; use nu_protocol::{ ast::Call, @@ -130,7 +131,7 @@ pub fn evaluate_file( if engine_state.find_decl(b"main", &[]).is_some() { let args = format!("main {}", args.join(" ")); - let pipeline_data = eval_block( + let pipeline_data = eval_block::( engine_state, stack, &block, diff --git a/crates/nu-cli/src/menus/menu_completions.rs b/crates/nu-cli/src/menus/menu_completions.rs index 0f4814a5875..18e20222b47 100644 --- a/crates/nu-cli/src/menus/menu_completions.rs +++ b/crates/nu-cli/src/menus/menu_completions.rs @@ -1,4 +1,5 @@ use nu_engine::eval_block; +use nu_protocol::debugger::WithoutDebug; use nu_protocol::{ engine::{EngineState, Stack}, IntoPipelineData, Span, Value, @@ -55,7 +56,8 @@ impl Completer for NuMenuCompleter { } let input = Value::nothing(self.span).into_pipeline_data(); - let res = eval_block( + + let res = eval_block::( &self.engine_state, &mut self.stack, block, diff --git a/crates/nu-cli/src/prompt_update.rs b/crates/nu-cli/src/prompt_update.rs index 8b3f28b7178..4784c6693c1 100644 --- a/crates/nu-cli/src/prompt_update.rs +++ b/crates/nu-cli/src/prompt_update.rs @@ -1,6 +1,7 @@ use crate::NushellPrompt; use log::trace; -use nu_engine::eval_subexpression; +use nu_engine::get_eval_subexpression; + use nu_protocol::report_error; use nu_protocol::{ engine::{EngineState, Stack, StateWorkingSet}, @@ -35,6 +36,8 @@ fn get_prompt_string( engine_state: &EngineState, stack: &mut Stack, ) -> Option { + let eval_subexpression = get_eval_subexpression(engine_state); + stack .get_env_var(engine_state, prompt) .and_then(|v| match v { diff --git a/crates/nu-cli/src/reedline_config.rs b/crates/nu-cli/src/reedline_config.rs index ed0a4d4943c..24d770e1b71 100644 --- a/crates/nu-cli/src/reedline_config.rs +++ b/crates/nu-cli/src/reedline_config.rs @@ -3,6 +3,7 @@ use crossterm::event::{KeyCode, KeyModifiers}; use nu_color_config::{color_record_to_nustyle, lookup_ansi_color_style}; use nu_engine::eval_block; use nu_parser::parse; +use nu_protocol::debugger::WithoutDebug; use nu_protocol::{ create_menus, engine::{EngineState, Stack, StateWorkingSet}, @@ -110,7 +111,15 @@ pub(crate) fn add_menus( let mut temp_stack = Stack::new(); let input = PipelineData::Empty; - let res = eval_block(&engine_state, &mut temp_stack, &block, input, false, false)?; + + let res = eval_block::( + &engine_state, + &mut temp_stack, + &block, + input, + false, + false, + )?; if let PipelineData::Value(value, None) = res { for menu in create_menus(&value)? { diff --git a/crates/nu-cli/src/util.rs b/crates/nu-cli/src/util.rs index 7945bc23df5..369556326c5 100644 --- a/crates/nu-cli/src/util.rs +++ b/crates/nu-cli/src/util.rs @@ -1,6 +1,7 @@ use nu_cmd_base::hook::eval_hook; use nu_engine::{eval_block, eval_block_with_early_return}; use nu_parser::{escape_quote_string, lex, parse, unescape_unquote_string, Token, TokenContents}; +use nu_protocol::debugger::WithoutDebug; use nu_protocol::engine::StateWorkingSet; use nu_protocol::{ engine::{EngineState, Stack}, @@ -240,9 +241,16 @@ pub fn eval_source( } let b = if allow_return { - eval_block_with_early_return(engine_state, stack, &block, input, false, false) + eval_block_with_early_return::( + engine_state, + stack, + &block, + input, + false, + false, + ) } else { - eval_block(engine_state, stack, &block, input, false, false) + eval_block::(engine_state, stack, &block, input, false, false) }; match b { diff --git a/crates/nu-cli/tests/support/completions_helpers.rs b/crates/nu-cli/tests/support/completions_helpers.rs index 4ca974d5c80..880bf2fefe3 100644 --- a/crates/nu-cli/tests/support/completions_helpers.rs +++ b/crates/nu-cli/tests/support/completions_helpers.rs @@ -2,6 +2,7 @@ use std::path::PathBuf; use nu_engine::eval_block; use nu_parser::parse; +use nu_protocol::debugger::WithoutDebug; use nu_protocol::{ engine::{EngineState, Stack, StateWorkingSet}, eval_const::create_nu_constant, @@ -9,6 +10,7 @@ use nu_protocol::{ }; use nu_test_support::fs; use reedline::Suggestion; + const SEP: char = std::path::MAIN_SEPARATOR; fn create_default_context() -> EngineState { @@ -194,13 +196,13 @@ pub fn merge_input( engine_state.merge_delta(delta)?; - assert!(eval_block( + assert!(eval_block::( engine_state, stack, &block, PipelineData::Value(Value::nothing(Span::unknown(),), None), false, - false + false, ) .is_ok()); diff --git a/crates/nu-cmd-base/src/hook.rs b/crates/nu-cmd-base/src/hook.rs index 518f00b1736..8422233e71e 100644 --- a/crates/nu-cmd-base/src/hook.rs +++ b/crates/nu-cmd-base/src/hook.rs @@ -3,6 +3,7 @@ use miette::Result; use nu_engine::{eval_block, eval_block_with_early_return}; use nu_parser::parse; use nu_protocol::cli_error::{report_error, report_error_new}; +use nu_protocol::debugger::WithoutDebug; use nu_protocol::engine::{EngineState, Stack, StateWorkingSet}; use nu_protocol::{BlockId, PipelineData, PositionalArg, ShellError, Span, Type, Value, VarId}; @@ -115,7 +116,7 @@ pub fn eval_hook( }) .collect(); - match eval_block(engine_state, stack, &block, input, false, false) { + match eval_block::(engine_state, stack, &block, input, false, false) { Ok(pipeline_data) => { output = pipeline_data; } @@ -243,7 +244,14 @@ pub fn eval_hook( }) .collect(); - match eval_block(engine_state, stack, &block, input, false, false) { + match eval_block::( + engine_state, + stack, + &block, + input, + false, + false, + ) { Ok(pipeline_data) => { output = pipeline_data; } @@ -336,8 +344,14 @@ fn run_hook_block( } } - let pipeline_data = - eval_block_with_early_return(engine_state, &mut callee_stack, block, input, false, false)?; + let pipeline_data = eval_block_with_early_return::( + engine_state, + &mut callee_stack, + block, + input, + false, + false, + )?; if let PipelineData::Value(Value::Error { error, .. }, _) = pipeline_data { return Err(*error); diff --git a/crates/nu-cmd-dataframe/src/dataframe/test_dataframe.rs b/crates/nu-cmd-dataframe/src/dataframe/test_dataframe.rs index ce9f0497215..eef61207073 100644 --- a/crates/nu-cmd-dataframe/src/dataframe/test_dataframe.rs +++ b/crates/nu-cmd-dataframe/src/dataframe/test_dataframe.rs @@ -10,6 +10,7 @@ use super::expressions::ExprCol; use super::lazy::LazyFillNull; use super::lazy::{LazyCollect, ToLazyFrame}; use nu_cmd_lang::Let; +use nu_protocol::debugger::WithoutDebug; pub fn test_dataframe(cmds: Vec>) { if cmds.is_empty() { @@ -80,7 +81,7 @@ pub fn test_dataframe_example(engine_state: &mut Box, example: &Exa let mut stack = Stack::new(); - let result = eval_block( + let result = eval_block::( engine_state, &mut stack, &block, diff --git a/crates/nu-cmd-extra/src/extra/filters/each_while.rs b/crates/nu-cmd-extra/src/extra/filters/each_while.rs index 4b7151b05d7..03b16dc6d86 100644 --- a/crates/nu-cmd-extra/src/extra/filters/each_while.rs +++ b/crates/nu-cmd-extra/src/extra/filters/each_while.rs @@ -1,5 +1,6 @@ -use nu_engine::{eval_block_with_early_return, CallExt}; +use nu_engine::{get_eval_block_with_early_return, CallExt}; use nu_protocol::ast::Call; + use nu_protocol::engine::{Closure, Command, EngineState, Stack}; use nu_protocol::{ Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, ShellError, @@ -84,6 +85,7 @@ impl Command for EachWhile { let span = call.head; let redirect_stdout = call.redirect_stdout; let redirect_stderr = call.redirect_stderr; + let eval_block_with_early_return = get_eval_block_with_early_return(&engine_state); match input { PipelineData::Empty => Ok(PipelineData::Empty), diff --git a/crates/nu-cmd-extra/src/extra/filters/update_cells.rs b/crates/nu-cmd-extra/src/extra/filters/update_cells.rs index 0b058733209..85c6b6147b0 100644 --- a/crates/nu-cmd-extra/src/extra/filters/update_cells.rs +++ b/crates/nu-cmd-extra/src/extra/filters/update_cells.rs @@ -1,5 +1,6 @@ -use nu_engine::{eval_block, CallExt}; +use nu_engine::{get_eval_block, CallExt, EvalBlockFn}; use nu_protocol::ast::{Block, Call}; + use nu_protocol::engine::{Closure, Command, EngineState, Stack}; use nu_protocol::{ record, Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, @@ -102,6 +103,7 @@ impl Command for UpdateCells { let metadata = input.metadata(); let ctrlc = engine_state.ctrlc.clone(); let block: Block = engine_state.get_block(block.block_id).clone(); + let eval_block_fn = get_eval_block(&engine_state); let redirect_stdout = call.redirect_stdout; let redirect_stderr = call.redirect_stderr; @@ -131,6 +133,7 @@ impl Command for UpdateCells { redirect_stdout, redirect_stderr, span, + eval_block_fn, } .into_pipeline_data(ctrlc) .set_metadata(metadata)) @@ -145,6 +148,7 @@ struct UpdateCellIterator { block: Block, redirect_stdout: bool, redirect_stderr: bool, + eval_block_fn: EvalBlockFn, span: Span, } @@ -176,6 +180,7 @@ impl Iterator for UpdateCellIterator { self.redirect_stdout, self.redirect_stderr, span, + self.eval_block_fn, ), ), }) @@ -190,6 +195,7 @@ impl Iterator for UpdateCellIterator { self.redirect_stdout, self.redirect_stderr, self.span, + self.eval_block_fn, )), } } @@ -198,6 +204,7 @@ impl Iterator for UpdateCellIterator { } } +#[allow(clippy::too_many_arguments)] fn process_cell( val: Value, engine_state: &EngineState, @@ -206,13 +213,15 @@ fn process_cell( redirect_stdout: bool, redirect_stderr: bool, span: Span, + eval_block_fn: EvalBlockFn, ) -> Value { if let Some(var) = block.signature.get_positional(0) { if let Some(var_id) = &var.var_id { stack.add_var(*var_id, val.clone()); } } - match eval_block( + + match eval_block_fn( engine_state, stack, block, diff --git a/crates/nu-cmd-extra/src/extra/strings/format/command.rs b/crates/nu-cmd-extra/src/extra/strings/format/command.rs index 6bba76c776f..2a5a5a16115 100644 --- a/crates/nu-cmd-extra/src/extra/strings/format/command.rs +++ b/crates/nu-cmd-extra/src/extra/strings/format/command.rs @@ -1,8 +1,9 @@ use std::vec; -use nu_engine::{eval_expression, CallExt}; +use nu_engine::{get_eval_expression, CallExt}; use nu_parser::parse_expression; use nu_protocol::ast::{Call, PathMember}; + use nu_protocol::engine::{Command, EngineState, Stack, StateWorkingSet}; use nu_protocol::{ Category, Example, ListStream, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, @@ -271,6 +272,7 @@ fn format_record( ) -> Result { let config = engine_state.get_config(); let mut output = String::new(); + let eval_expression = get_eval_expression(engine_state); for op in format_operations { match op { diff --git a/crates/nu-cmd-lang/src/core_commands/collect.rs b/crates/nu-cmd-lang/src/core_commands/collect.rs index 3c1e71e3221..55fefec087b 100644 --- a/crates/nu-cmd-lang/src/core_commands/collect.rs +++ b/crates/nu-cmd-lang/src/core_commands/collect.rs @@ -1,5 +1,6 @@ -use nu_engine::{eval_block, redirect_env, CallExt}; +use nu_engine::{get_eval_block, redirect_env, CallExt}; use nu_protocol::ast::Call; + use nu_protocol::engine::{Closure, Command, EngineState, Stack}; use nu_protocol::{ Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, SyntaxShape, Type, @@ -57,6 +58,8 @@ impl Command for Collect { } } + let eval_block = get_eval_block(engine_state); + let result = eval_block( engine_state, &mut stack_captures, diff --git a/crates/nu-cmd-lang/src/core_commands/do_.rs b/crates/nu-cmd-lang/src/core_commands/do_.rs index 067120f6cb5..6406b336975 100644 --- a/crates/nu-cmd-lang/src/core_commands/do_.rs +++ b/crates/nu-cmd-lang/src/core_commands/do_.rs @@ -1,7 +1,8 @@ use std::thread; -use nu_engine::{eval_block_with_early_return, redirect_env, CallExt}; +use nu_engine::{get_eval_block_with_early_return, redirect_env, CallExt}; use nu_protocol::ast::Call; + use nu_protocol::engine::{Closure, Command, EngineState, Stack}; use nu_protocol::{ Category, Example, IntoSpanned, ListStream, PipelineData, RawStream, ShellError, Signature, @@ -116,6 +117,9 @@ impl Command for Do { ) } } + + let eval_block_with_early_return = get_eval_block_with_early_return(engine_state); + let result = eval_block_with_early_return( engine_state, &mut callee_stack, diff --git a/crates/nu-cmd-lang/src/core_commands/for_.rs b/crates/nu-cmd-lang/src/core_commands/for_.rs index 149309e94cc..b8c915f90cf 100644 --- a/crates/nu-cmd-lang/src/core_commands/for_.rs +++ b/crates/nu-cmd-lang/src/core_commands/for_.rs @@ -1,5 +1,6 @@ -use nu_engine::{eval_block, eval_expression, CallExt}; +use nu_engine::{get_eval_block, get_eval_expression, CallExt}; use nu_protocol::ast::Call; + use nu_protocol::engine::{Block, Command, EngineState, Stack}; use nu_protocol::{ record, Category, Example, ListStream, PipelineData, ShellError, Signature, SyntaxShape, Type, @@ -70,6 +71,10 @@ impl Command for For { .expect("checked through parser") .as_keyword() .expect("internal error: missing keyword"); + + let eval_expression = get_eval_expression(engine_state); + let eval_block = get_eval_block(engine_state); + let values = eval_expression(engine_state, stack, keyword_expr)?; let block: Block = call.req(engine_state, stack, 2)?; @@ -104,7 +109,6 @@ impl Command for For { }, ); - //let block = engine_state.get_block(block_id); match eval_block( &engine_state, stack, @@ -150,7 +154,6 @@ impl Command for For { }, ); - //let block = engine_state.get_block(block_id); match eval_block( &engine_state, stack, diff --git a/crates/nu-cmd-lang/src/core_commands/if_.rs b/crates/nu-cmd-lang/src/core_commands/if_.rs index 6450f0d742f..29a65843c98 100644 --- a/crates/nu-cmd-lang/src/core_commands/if_.rs +++ b/crates/nu-cmd-lang/src/core_commands/if_.rs @@ -1,5 +1,6 @@ -use nu_engine::{eval_block, eval_expression, eval_expression_with_input, CallExt}; +use nu_engine::{get_eval_block, get_eval_expression, get_eval_expression_with_input, CallExt}; use nu_protocol::ast::Call; + use nu_protocol::engine::{Block, Command, EngineState, Stack, StateWorkingSet}; use nu_protocol::eval_const::{eval_const_subexpression, eval_constant, eval_constant_with_input}; use nu_protocol::{ @@ -105,6 +106,9 @@ impl Command for If { let cond = call.positional_nth(0).expect("checked through parser"); let then_block: Block = call.req(engine_state, stack, 1)?; let else_case = call.positional_nth(2); + let eval_expression = get_eval_expression(engine_state); + let eval_expression_with_input = get_eval_expression_with_input(engine_state); + let eval_block = get_eval_block(engine_state); let result = eval_expression(engine_state, stack, cond)?; match &result { diff --git a/crates/nu-cmd-lang/src/core_commands/lazy_make.rs b/crates/nu-cmd-lang/src/core_commands/lazy_make.rs index 5d4f2408b13..e3a7414e82e 100644 --- a/crates/nu-cmd-lang/src/core_commands/lazy_make.rs +++ b/crates/nu-cmd-lang/src/core_commands/lazy_make.rs @@ -4,6 +4,7 @@ use std::sync::{Arc, Mutex}; use nu_engine::{eval_block, CallExt}; use nu_protocol::ast::Call; +use nu_protocol::debugger::WithoutDebug; use nu_protocol::engine::{Closure, Command, EngineState, Stack}; use nu_protocol::{ Category, Example, IntoPipelineData, LazyRecord, PipelineData, ShellError, Signature, Span, @@ -146,7 +147,7 @@ impl<'a> LazyRecord<'a> for NuLazyRecord { } } - let pipeline_result = eval_block( + let pipeline_result = eval_block::( &self.engine_state, &mut stack, block, diff --git a/crates/nu-cmd-lang/src/core_commands/let_.rs b/crates/nu-cmd-lang/src/core_commands/let_.rs index 0d9a489aff1..8aaa1788464 100644 --- a/crates/nu-cmd-lang/src/core_commands/let_.rs +++ b/crates/nu-cmd-lang/src/core_commands/let_.rs @@ -1,4 +1,4 @@ -use nu_engine::eval_block; +use nu_engine::get_eval_block; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ @@ -63,7 +63,7 @@ impl Command for Let { .expect("internal error: missing right hand side"); let block = engine_state.get_block(block_id); - + let eval_block = get_eval_block(engine_state); let pipeline_data = eval_block(engine_state, stack, block, input, true, false)?; let mut value = pipeline_data.into_value(call.head); diff --git a/crates/nu-cmd-lang/src/core_commands/loop_.rs b/crates/nu-cmd-lang/src/core_commands/loop_.rs index fa6dbe9fedc..63cabd6de04 100644 --- a/crates/nu-cmd-lang/src/core_commands/loop_.rs +++ b/crates/nu-cmd-lang/src/core_commands/loop_.rs @@ -1,5 +1,6 @@ -use nu_engine::{eval_block, CallExt}; +use nu_engine::{get_eval_block, CallExt}; use nu_protocol::ast::Call; + use nu_protocol::engine::{Block, Command, EngineState, Stack}; use nu_protocol::{ Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type, Value, @@ -33,6 +34,7 @@ impl Command for Loop { _input: PipelineData, ) -> Result { let block: Block = call.req(engine_state, stack, 0)?; + let eval_block = get_eval_block(engine_state); loop { if nu_utils::ctrl_c::was_pressed(&engine_state.ctrlc) { @@ -40,6 +42,7 @@ impl Command for Loop { } let block = engine_state.get_block(block.block_id); + match eval_block( engine_state, stack, diff --git a/crates/nu-cmd-lang/src/core_commands/match_.rs b/crates/nu-cmd-lang/src/core_commands/match_.rs index cf559701dae..aab9c7130ce 100644 --- a/crates/nu-cmd-lang/src/core_commands/match_.rs +++ b/crates/nu-cmd-lang/src/core_commands/match_.rs @@ -1,5 +1,6 @@ -use nu_engine::{eval_block, eval_expression, eval_expression_with_input, CallExt}; +use nu_engine::{get_eval_block, get_eval_expression, get_eval_expression_with_input, CallExt}; use nu_protocol::ast::{Call, Expr, Expression}; + use nu_protocol::engine::{Command, EngineState, Matcher, Stack}; use nu_protocol::{ Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type, Value, @@ -38,6 +39,9 @@ impl Command for Match { ) -> Result { let value: Value = call.req(engine_state, stack, 0)?; let block = call.positional_nth(1); + let eval_expression = get_eval_expression(engine_state); + let eval_expression_with_input = get_eval_expression_with_input(engine_state); + let eval_block = get_eval_block(engine_state); if let Some(Expression { expr: Expr::MatchBlock(matches), diff --git a/crates/nu-cmd-lang/src/core_commands/mut_.rs b/crates/nu-cmd-lang/src/core_commands/mut_.rs index 2788e3839f3..24f2a8119f2 100644 --- a/crates/nu-cmd-lang/src/core_commands/mut_.rs +++ b/crates/nu-cmd-lang/src/core_commands/mut_.rs @@ -1,5 +1,6 @@ -use nu_engine::eval_block; +use nu_engine::get_eval_block; use nu_protocol::ast::Call; + use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type, Value, @@ -63,6 +64,8 @@ impl Command for Mut { .expect("internal error: missing right hand side"); let block = engine_state.get_block(block_id); + let eval_block = get_eval_block(engine_state); + let pipeline_data = eval_block( engine_state, stack, diff --git a/crates/nu-cmd-lang/src/core_commands/overlay/use_.rs b/crates/nu-cmd-lang/src/core_commands/overlay/use_.rs index 4679829e424..865f8728d62 100644 --- a/crates/nu-cmd-lang/src/core_commands/overlay/use_.rs +++ b/crates/nu-cmd-lang/src/core_commands/overlay/use_.rs @@ -1,4 +1,4 @@ -use nu_engine::{eval_block, find_in_dirs_env, get_dirs_var_from_call, redirect_env, CallExt}; +use nu_engine::{find_in_dirs_env, get_dirs_var_from_call, get_eval_block, redirect_env, CallExt}; use nu_parser::trim_quotes_str; use nu_protocol::ast::{Call, Expr}; use nu_protocol::engine::{Command, EngineState, Stack}; @@ -141,6 +141,8 @@ impl Command for OverlayUse { callee_stack.add_env_var("CURRENT_FILE".to_string(), file_path); } + let eval_block = get_eval_block(engine_state); + let _ = eval_block( engine_state, &mut callee_stack, diff --git a/crates/nu-cmd-lang/src/core_commands/try_.rs b/crates/nu-cmd-lang/src/core_commands/try_.rs index 80543a05a13..a92ecadccb4 100644 --- a/crates/nu-cmd-lang/src/core_commands/try_.rs +++ b/crates/nu-cmd-lang/src/core_commands/try_.rs @@ -1,5 +1,6 @@ -use nu_engine::{eval_block, CallExt}; +use nu_engine::{get_eval_block, CallExt, EvalBlockFn}; use nu_protocol::ast::Call; + use nu_protocol::engine::{Block, Closure, Command, EngineState, Stack}; use nu_protocol::{ record, Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, @@ -47,6 +48,7 @@ impl Command for Try { let catch_block: Option = call.opt(engine_state, stack, 1)?; let try_block = engine_state.get_block(try_block.block_id); + let eval_block = get_eval_block(engine_state); let result = eval_block(engine_state, stack, try_block, input, false, false); @@ -54,12 +56,12 @@ impl Command for Try { Err(error) => { let error = intercept_block_control(error)?; let err_record = err_to_record(error, call.head); - handle_catch(err_record, catch_block, engine_state, stack) + handle_catch(err_record, catch_block, engine_state, stack, eval_block) } Ok(PipelineData::Value(Value::Error { error, .. }, ..)) => { let error = intercept_block_control(*error)?; let err_record = err_to_record(error, call.head); - handle_catch(err_record, catch_block, engine_state, stack) + handle_catch(err_record, catch_block, engine_state, stack, eval_block) } // external command may fail to run Ok(pipeline) => { @@ -69,7 +71,7 @@ impl Command for Try { // (unless do -c is in effect) // they can't be passed in as Nushell values. let err_value = Value::nothing(call.head); - handle_catch(err_value, catch_block, engine_state, stack) + handle_catch(err_value, catch_block, engine_state, stack, eval_block) } else { Ok(pipeline) } @@ -98,6 +100,7 @@ fn handle_catch( catch_block: Option, engine_state: &EngineState, stack: &mut Stack, + eval_block_fn: EvalBlockFn, ) -> Result { if let Some(catch_block) = catch_block { let catch_block = engine_state.get_block(catch_block.block_id); @@ -108,7 +111,7 @@ fn handle_catch( } } - eval_block( + eval_block_fn( engine_state, stack, catch_block, diff --git a/crates/nu-cmd-lang/src/core_commands/use_.rs b/crates/nu-cmd-lang/src/core_commands/use_.rs index c9be05e0a7b..29b7a905468 100644 --- a/crates/nu-cmd-lang/src/core_commands/use_.rs +++ b/crates/nu-cmd-lang/src/core_commands/use_.rs @@ -1,5 +1,6 @@ -use nu_engine::{eval_block, find_in_dirs_env, get_dirs_var_from_call, redirect_env}; +use nu_engine::{find_in_dirs_env, get_dirs_var_from_call, get_eval_block, redirect_env}; use nu_protocol::ast::{Call, Expr, Expression}; + use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type, Value, @@ -117,6 +118,8 @@ This command is a parser keyword. For details, check: callee_stack.add_env_var("CURRENT_FILE".to_string(), file_path); } + let eval_block = get_eval_block(engine_state); + // Run the block (discard the result) let _ = eval_block( engine_state, diff --git a/crates/nu-cmd-lang/src/core_commands/while_.rs b/crates/nu-cmd-lang/src/core_commands/while_.rs index 4e93eafda29..5795bf49230 100644 --- a/crates/nu-cmd-lang/src/core_commands/while_.rs +++ b/crates/nu-cmd-lang/src/core_commands/while_.rs @@ -1,5 +1,6 @@ -use nu_engine::{eval_block, eval_expression, CallExt}; +use nu_engine::{get_eval_block, get_eval_expression, CallExt}; use nu_protocol::ast::Call; + use nu_protocol::engine::{Block, Command, EngineState, Stack}; use nu_protocol::{ Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type, Value, @@ -44,16 +45,21 @@ impl Command for While { let cond = call.positional_nth(0).expect("checked through parser"); let block: Block = call.req(engine_state, stack, 1)?; + let eval_expression = get_eval_expression(engine_state); + let eval_block = get_eval_block(engine_state); + loop { if nu_utils::ctrl_c::was_pressed(&engine_state.ctrlc) { break; } let result = eval_expression(engine_state, stack, cond)?; + match &result { Value::Bool { val, .. } => { if *val { let block = engine_state.get_block(block.block_id); + match eval_block( engine_state, stack, diff --git a/crates/nu-cmd-lang/src/example_support.rs b/crates/nu-cmd-lang/src/example_support.rs index 9b86d75cf86..d4ff801ecac 100644 --- a/crates/nu-cmd-lang/src/example_support.rs +++ b/crates/nu-cmd-lang/src/example_support.rs @@ -1,4 +1,5 @@ use itertools::Itertools; +use nu_protocol::debugger::WithoutDebug; use nu_protocol::{ ast::{Block, RangeInclusion}, engine::{EngineState, Stack, StateDelta, StateWorkingSet}, @@ -115,7 +116,8 @@ pub fn eval_block( stack.add_env_var("PWD".to_string(), Value::test_string(cwd.to_string_lossy())); - match nu_engine::eval_block(engine_state, &mut stack, &block, input, true, true) { + match nu_engine::eval_block::(engine_state, &mut stack, &block, input, true, true) + { Err(err) => panic!("test eval error in `{}`: {:?}", "TODO", err), Ok(result) => result.into_value(Span::test_data()), } diff --git a/crates/nu-color-config/src/style_computer.rs b/crates/nu-color-config/src/style_computer.rs index 173c30c3bec..1c0851a1777 100644 --- a/crates/nu-color-config/src/style_computer.rs +++ b/crates/nu-color-config/src/style_computer.rs @@ -8,6 +8,7 @@ use nu_protocol::{ }; use std::collections::HashMap; +use nu_protocol::debugger::WithoutDebug; use std::fmt::{Debug, Formatter, Result}; // ComputableStyle represents the valid user style types: a single color value, or a closure which @@ -71,7 +72,7 @@ impl<'a> StyleComputer<'a> { } // Run the block. - match eval_block( + match eval_block::( self.engine_state, &mut stack, &block, diff --git a/crates/nu-command/src/bytes/build_.rs b/crates/nu-command/src/bytes/build_.rs index ef7c7624089..9fecdc45053 100644 --- a/crates/nu-command/src/bytes/build_.rs +++ b/crates/nu-command/src/bytes/build_.rs @@ -1,5 +1,6 @@ -use nu_engine::eval_expression; +use nu_engine::get_eval_expression; use nu_protocol::ast::Call; + use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape, @@ -48,7 +49,10 @@ impl Command for BytesBuild { _input: PipelineData, ) -> Result { let mut output = vec![]; - for val in call.rest_iter_flattened(0, |expr| eval_expression(engine_state, stack, expr))? { + for val in call.rest_iter_flattened(0, |expr| { + let eval_expression = get_eval_expression(engine_state); + eval_expression(engine_state, stack, expr) + })? { match val { Value::Binary { mut val, .. } => output.append(&mut val), // Explicitly propagate errors instead of dropping them. diff --git a/crates/nu-command/src/debug/explain.rs b/crates/nu-command/src/debug/explain.rs index da1088b7fce..b02bd968289 100644 --- a/crates/nu-command/src/debug/explain.rs +++ b/crates/nu-command/src/debug/explain.rs @@ -1,5 +1,6 @@ -use nu_engine::{eval_expression, CallExt}; +use nu_engine::{get_eval_expression, CallExt}; use nu_protocol::ast::{Argument, Block, Call, Expr, Expression}; + use nu_protocol::engine::{Closure, Command, EngineState, Stack}; use nu_protocol::{ record, Category, Example, IntoInterruptiblePipelineData, PipelineData, ShellError, Signature, @@ -43,7 +44,7 @@ impl Command for Explain { let ctrlc = engine_state.ctrlc.clone(); let mut stack = stack.captures_to_stack(capture_block.captures); - let elements = get_pipeline_elements(engine_state, &mut stack, block)?; + let elements = get_pipeline_elements(engine_state, &mut stack, block, call.head)?; Ok(elements.into_pipeline_data(ctrlc)) } @@ -62,9 +63,11 @@ pub fn get_pipeline_elements( engine_state: &EngineState, stack: &mut Stack, block: &Block, + span: Span, ) -> Result, ShellError> { let mut element_values = vec![]; - let span = Span::test_data(); + + let eval_expression = get_eval_expression(engine_state); for (pipeline_idx, pipeline) in block.pipelines.iter().enumerate() { let mut i = 0; @@ -80,7 +83,7 @@ pub fn get_pipeline_elements( let command = engine_state.get_decl(call.decl_id); ( command.name().to_string(), - get_arguments(engine_state, stack, *call), + get_arguments(engine_state, stack, *call, eval_expression), ) } else { ("no-op".to_string(), vec![]) @@ -106,7 +109,12 @@ pub fn get_pipeline_elements( Ok(element_values) } -fn get_arguments(engine_state: &EngineState, stack: &mut Stack, call: Call) -> Vec { +fn get_arguments( + engine_state: &EngineState, + stack: &mut Stack, + call: Call, + eval_expression_fn: fn(&EngineState, &mut Stack, &Expression) -> Result, +) -> Vec { let mut arg_value = vec![]; let span = Span::test_data(); for arg in &call.arguments { @@ -145,8 +153,12 @@ fn get_arguments(engine_state: &EngineState, stack: &mut Stack, call: Call) -> V }; if let Some(expression) = opt_expr { - let evaluated_expression = - get_expression_as_value(engine_state, stack, expression); + let evaluated_expression = get_expression_as_value( + engine_state, + stack, + expression, + eval_expression_fn, + ); let arg_type = "expr"; let arg_value_name = debug_string_without_formatting(&evaluated_expression); let arg_value_type = &evaluated_expression.get_type().to_string(); @@ -166,7 +178,8 @@ fn get_arguments(engine_state: &EngineState, stack: &mut Stack, call: Call) -> V } Argument::Positional(inner_expr) => { let arg_type = "positional"; - let evaluated_expression = get_expression_as_value(engine_state, stack, inner_expr); + let evaluated_expression = + get_expression_as_value(engine_state, stack, inner_expr, eval_expression_fn); let arg_value_name = debug_string_without_formatting(&evaluated_expression); let arg_value_type = &evaluated_expression.get_type().to_string(); let evaled_span = evaluated_expression.span(); @@ -184,7 +197,8 @@ fn get_arguments(engine_state: &EngineState, stack: &mut Stack, call: Call) -> V } Argument::Unknown(inner_expr) => { let arg_type = "unknown"; - let evaluated_expression = get_expression_as_value(engine_state, stack, inner_expr); + let evaluated_expression = + get_expression_as_value(engine_state, stack, inner_expr, eval_expression_fn); let arg_value_name = debug_string_without_formatting(&evaluated_expression); let arg_value_type = &evaluated_expression.get_type().to_string(); let evaled_span = evaluated_expression.span(); @@ -202,7 +216,8 @@ fn get_arguments(engine_state: &EngineState, stack: &mut Stack, call: Call) -> V } Argument::Spread(inner_expr) => { let arg_type = "spread"; - let evaluated_expression = get_expression_as_value(engine_state, stack, inner_expr); + let evaluated_expression = + get_expression_as_value(engine_state, stack, inner_expr, eval_expression_fn); let arg_value_name = debug_string_without_formatting(&evaluated_expression); let arg_value_type = &evaluated_expression.get_type().to_string(); let evaled_span = evaluated_expression.span(); @@ -228,8 +243,9 @@ fn get_expression_as_value( engine_state: &EngineState, stack: &mut Stack, inner_expr: &Expression, + eval_expression_fn: fn(&EngineState, &mut Stack, &Expression) -> Result, ) -> Value { - match eval_expression(engine_state, stack, inner_expr) { + match eval_expression_fn(engine_state, stack, inner_expr) { Ok(v) => v, Err(error) => Value::error(error, inner_expr.span), } diff --git a/crates/nu-command/src/debug/mod.rs b/crates/nu-command/src/debug/mod.rs index aa8e84e617b..0a324be10a4 100644 --- a/crates/nu-command/src/debug/mod.rs +++ b/crates/nu-command/src/debug/mod.rs @@ -5,6 +5,7 @@ mod info; mod inspect; mod inspect_table; mod metadata; +mod profile; mod timeit; mod view; mod view_files; @@ -18,6 +19,7 @@ pub use info::DebugInfo; pub use inspect::Inspect; pub use inspect_table::build_table; pub use metadata::Metadata; +pub use profile::DebugProfile; pub use timeit::TimeIt; pub use view::View; pub use view_files::ViewFiles; diff --git a/crates/nu-command/src/debug/profile.rs b/crates/nu-command/src/debug/profile.rs new file mode 100644 index 00000000000..1c0c712d0e9 --- /dev/null +++ b/crates/nu-command/src/debug/profile.rs @@ -0,0 +1,169 @@ +use nu_engine::{eval_block_with_early_return, CallExt}; +use nu_protocol::ast::Call; +use nu_protocol::debugger::{Profiler, WithDebug}; +use nu_protocol::engine::{Closure, Command, EngineState, Stack}; +use nu_protocol::{ + Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, SyntaxShape, Type, +}; + +#[derive(Clone)] +pub struct DebugProfile; + +impl Command for DebugProfile { + fn name(&self) -> &str { + "debug profile" + } + + fn signature(&self) -> nu_protocol::Signature { + Signature::build("debug profile") + .required( + "closure", + SyntaxShape::Closure(None), + "The closure to profile.", + ) + .switch("spans", "Collect spans of profiled elements", Some('s')) + .switch( + "expand-source", + "Collect full source fragments of profiled elements", + Some('e'), + ) + .switch( + "values", + "Collect pipeline element output values", + Some('v'), + ) + .switch("expr", "Collect expression types", Some('x')) + .named( + "max-depth", + SyntaxShape::Int, + "How many blocks/closures deep to step into (default 2)", + Some('m'), + ) + .input_output_types(vec![(Type::Any, Type::Table(vec![]))]) + .category(Category::Debug) + } + + fn usage(&self) -> &str { + "Profile pipeline elements in a closure." + } + + fn extra_usage(&self) -> &str { + r#"The profiler profiles every evaluated pipeline element inside a closure, stepping into all +commands calls and other blocks/closures. + +The output can be heavily customized. By default, the following columns are included: +- depth : Depth of the pipeline element. Each entered block adds one level of depth. How many + blocks deep to step into is controlled with the --max-depth option. +- id : ID of the pipeline element +- parent_id : ID of the parent element +- source : Source code of the pipeline element. If the element has multiple lines, only the + first line is used and `...` is appended to the end. Full source code can be shown + with the --expand-source flag. +- duration_ms : How long it took to run the pipeline element in milliseconds. +- (optional) span : Span of the element. Can be viewed via the `view span` command. Enabled with + the --spans flag. +- (optional) expr : The type of expression of the pipeline element. Enabled with the --expr flag. +- (optional) output : The output value of the pipeline element. Enabled with the --values flag. + +To illustrate the depth and IDs, consider `debug profile { if true { echo 'spam' } }`. There are +three pipeline elements: + +depth id parent_id + 0 0 0 debug profile { do { if true { 'spam' } } } + 1 1 0 if true { 'spam' } + 2 2 1 'spam' + +Each block entered increments depth by 1 and each block left decrements it by one. This way you can +control the profiling granularity. Passing --max-depth=1 to the above would stop at +`if true { 'spam' }`. The id is used to identify each element. The parent_id tells you that 'spam' +was spawned from `if true { 'spam' }` which was spawned from the root `debug profile { ... }`. + +Note: In some cases, the ordering of piepeline elements might not be intuitive. For example, +`[ a bb cc ] | each { $in | str length }` involves some implicit collects and lazy evaluation +confusing the id/parent_id hierarchy. The --expr flag is helpful for investigating these issues."# + } + + fn run( + &self, + engine_state: &EngineState, + caller_stack: &mut Stack, + call: &Call, + input: PipelineData, + ) -> Result { + let closure: Closure = call.req(engine_state, caller_stack, 0)?; + let mut callee_stack = caller_stack.captures_to_stack(closure.captures); + let block = engine_state.get_block(closure.block_id); + + let default_max_depth = 2; + let collect_spans = call.has_flag(engine_state, caller_stack, "spans")?; + let collect_expanded_source = + call.has_flag(engine_state, caller_stack, "expanded-source")?; + let collect_values = call.has_flag(engine_state, caller_stack, "values")?; + let collect_exprs = call.has_flag(engine_state, caller_stack, "expr")?; + let max_depth = call + .get_flag(engine_state, caller_stack, "max-depth")? + .unwrap_or(default_max_depth); + + let profiler = Profiler::new( + max_depth, + collect_spans, + true, + collect_expanded_source, + collect_values, + collect_exprs, + call.span(), + ); + + let lock_err = { + |_| ShellError::GenericError { + error: "Profiler Error".to_string(), + msg: "could not lock debugger, poisoned mutex".to_string(), + span: Some(call.head), + help: None, + inner: vec![], + } + }; + + engine_state + .activate_debugger(Box::new(profiler)) + .map_err(lock_err)?; + + let result = eval_block_with_early_return::( + engine_state, + &mut callee_stack, + block, + input, + call.redirect_stdout, + call.redirect_stdout, + ); + + // TODO: See eval_source() + match result { + Ok(pipeline_data) => { + let _ = pipeline_data.into_value(call.span()); + // pipeline_data.print(engine_state, caller_stack, true, false) + } + Err(_e) => (), // TODO: Report error + } + + let debugger = engine_state.deactivate_debugger().map_err(lock_err)?; + let res = debugger.report(engine_state, call.span()); + + res.map(|val| val.into_pipeline_data()) + } + + fn examples(&self) -> Vec { + vec![ + Example { + description: "Profile config evaluation", + example: "debug profile { source $nu.config-path }", + result: None, + }, + Example { + description: "Profile config evaluation with more granularity", + example: "debug profile { source $nu.config-path } --max-depth 4", + result: None, + }, + ] + } +} diff --git a/crates/nu-command/src/debug/timeit.rs b/crates/nu-command/src/debug/timeit.rs index c8da2daf374..c10b4a49987 100644 --- a/crates/nu-command/src/debug/timeit.rs +++ b/crates/nu-command/src/debug/timeit.rs @@ -1,4 +1,5 @@ -use nu_engine::{eval_block, eval_expression_with_input}; +use nu_engine::{get_eval_block, get_eval_expression_with_input}; + use nu_protocol::{ ast::Call, engine::{Command, EngineState, Stack}, @@ -52,6 +53,7 @@ impl Command for TimeIt { if let Some(command_to_run) = command_to_run { if let Some(block_id) = command_to_run.as_block() { + let eval_block = get_eval_block(engine_state); let block = engine_state.get_block(block_id); eval_block( engine_state, @@ -62,6 +64,7 @@ impl Command for TimeIt { call.redirect_stderr, )? } else { + let eval_expression_with_input = get_eval_expression_with_input(engine_state); eval_expression_with_input( engine_state, stack, diff --git a/crates/nu-command/src/default_context.rs b/crates/nu-command/src/default_context.rs index c16e14ea77f..9cda14d3a5b 100644 --- a/crates/nu-command/src/default_context.rs +++ b/crates/nu-command/src/default_context.rs @@ -140,6 +140,7 @@ pub fn add_shell_command_context(mut engine_state: EngineState) -> EngineState { Ast, Debug, DebugInfo, + DebugProfile, Explain, Inspect, Metadata, diff --git a/crates/nu-command/src/env/export_env.rs b/crates/nu-command/src/env/export_env.rs index 07057c82906..19d570b074a 100644 --- a/crates/nu-command/src/env/export_env.rs +++ b/crates/nu-command/src/env/export_env.rs @@ -1,4 +1,5 @@ -use nu_engine::{eval_block, redirect_env, CallExt}; +use nu_engine::{get_eval_block, redirect_env, CallExt}; + use nu_protocol::{ ast::Call, engine::{Closure, Command, EngineState, Stack}, @@ -39,6 +40,8 @@ impl Command for ExportEnv { let block = engine_state.get_block(capture_block.block_id); let mut callee_stack = caller_stack.captures_to_stack(capture_block.captures); + let eval_block = get_eval_block(engine_state); + let _ = eval_block( engine_state, &mut callee_stack, diff --git a/crates/nu-command/src/env/source_env.rs b/crates/nu-command/src/env/source_env.rs index 9ce69ca1784..e1bf4e12f97 100644 --- a/crates/nu-command/src/env/source_env.rs +++ b/crates/nu-command/src/env/source_env.rs @@ -1,9 +1,11 @@ use std::path::PathBuf; use nu_engine::{ - eval_block_with_early_return, find_in_dirs_env, get_dirs_var_from_call, redirect_env, CallExt, + find_in_dirs_env, get_dirs_var_from_call, get_eval_block_with_early_return, redirect_env, + CallExt, }; use nu_protocol::ast::Call; + use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ Category, Example, PipelineData, ShellError, Signature, Spanned, SyntaxShape, Type, Value, @@ -76,6 +78,8 @@ impl Command for SourceEnv { let block = engine_state.get_block(block_id as usize).clone(); let mut callee_stack = caller_stack.gather_captures(engine_state, &block.captures); + let eval_block_with_early_return = get_eval_block_with_early_return(engine_state); + let result = eval_block_with_early_return( engine_state, &mut callee_stack, diff --git a/crates/nu-command/src/env/with_env.rs b/crates/nu-command/src/env/with_env.rs index 1c7a621251a..b612b5af737 100644 --- a/crates/nu-command/src/env/with_env.rs +++ b/crates/nu-command/src/env/with_env.rs @@ -1,6 +1,7 @@ use std::collections::HashMap; use nu_engine::{eval_block, CallExt}; +use nu_protocol::debugger::WithoutDebug; use nu_protocol::{ ast::Call, engine::{Closure, Command, EngineState, Stack}, @@ -144,7 +145,7 @@ fn with_env( stack.add_env_var(k, v); } - eval_block( + eval_block::( engine_state, &mut stack, block, diff --git a/crates/nu-command/src/filesystem/open.rs b/crates/nu-command/src/filesystem/open.rs index 837ebd65d9e..3bbedcf7353 100644 --- a/crates/nu-command/src/filesystem/open.rs +++ b/crates/nu-command/src/filesystem/open.rs @@ -1,5 +1,5 @@ use super::util::get_rest_for_glob_pattern; -use nu_engine::{current_dir, eval_block, CallExt}; +use nu_engine::{current_dir, get_eval_block, CallExt}; use nu_path::expand_to_real_path; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; @@ -64,6 +64,7 @@ impl Command for Open { let ctrlc = engine_state.ctrlc.clone(); let cwd = current_dir(engine_state, stack)?; let mut paths = get_rest_for_glob_pattern(engine_state, stack, call, 0)?; + let eval_block = get_eval_block(engine_state); if paths.is_empty() && call.rest_iter(0).next().is_none() { // try to use path from pipeline input if there were no positional or spread args diff --git a/crates/nu-command/src/filesystem/util.rs b/crates/nu-command/src/filesystem/util.rs index 1341142dfc2..aee5bac687d 100644 --- a/crates/nu-command/src/filesystem/util.rs +++ b/crates/nu-command/src/filesystem/util.rs @@ -1,5 +1,5 @@ use dialoguer::Input; -use nu_engine::eval_expression; +use nu_engine::get_eval_expression; use nu_protocol::ast::Expr; use nu_protocol::{ ast::Call, @@ -221,6 +221,7 @@ pub fn get_rest_for_glob_pattern( starting_pos: usize, ) -> Result>, ShellError> { let mut output = vec![]; + let eval_expression = get_eval_expression(engine_state); for result in call.rest_iter_flattened(starting_pos, |expr| { let result = eval_expression(engine_state, stack, expr); @@ -261,6 +262,7 @@ pub fn opt_for_glob_pattern( pos: usize, ) -> Result>, ShellError> { if let Some(expr) = call.positional_nth(pos) { + let eval_expression = get_eval_expression(engine_state); let result = eval_expression(engine_state, stack, expr)?; let result_span = result.span(); let result = match result { diff --git a/crates/nu-command/src/filesystem/watch.rs b/crates/nu-command/src/filesystem/watch.rs index c5b36c72f25..06337a1d23a 100644 --- a/crates/nu-command/src/filesystem/watch.rs +++ b/crates/nu-command/src/filesystem/watch.rs @@ -9,8 +9,9 @@ use notify_debouncer_full::{ EventKind, RecursiveMode, Watcher, }, }; -use nu_engine::{current_dir, eval_block, CallExt}; +use nu_engine::{current_dir, get_eval_block, CallExt}; use nu_protocol::ast::Call; + use nu_protocol::engine::{Closure, Command, EngineState, Stack, StateWorkingSet}; use nu_protocol::{ format_error, Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, @@ -168,6 +169,8 @@ impl Command for Watch { eprintln!("Now watching files at {path:?}. Press ctrl+c to abort."); + let eval_block = get_eval_block(engine_state); + let event_handler = |operation: &str, path: PathBuf, new_path: Option| -> Result<(), ShellError> { let glob_pattern = glob_pattern.clone(); diff --git a/crates/nu-command/src/filters/each.rs b/crates/nu-command/src/filters/each.rs index eb0767a5a9d..029ddd6bac9 100644 --- a/crates/nu-command/src/filters/each.rs +++ b/crates/nu-command/src/filters/each.rs @@ -1,12 +1,14 @@ -use super::utils::chain_error_with_input; -use nu_engine::{eval_block_with_early_return, CallExt}; +use nu_engine::{get_eval_block_with_early_return, CallExt}; use nu_protocol::ast::Call; + use nu_protocol::engine::{Closure, Command, EngineState, Stack}; use nu_protocol::{ Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value, }; +use super::utils::chain_error_with_input; + #[derive(Clone)] pub struct Each; @@ -112,6 +114,8 @@ with 'transpose' first."# call: &Call, input: PipelineData, ) -> Result { + let eval_block_with_early_return = get_eval_block_with_early_return(engine_state); + let capture_block: Closure = call.req(engine_state, stack, 0)?; let keep_empty = call.has_flag(engine_state, stack, "keep-empty")?; @@ -155,6 +159,8 @@ with 'transpose' first."# x.into_pipeline_data(), redirect_stdout, redirect_stderr, + // WithoutDebug, + // &None, ) { Ok(v) => Some(v.into_value(span)), Err(ShellError::Continue { span }) => Some(Value::nothing(span)), diff --git a/crates/nu-command/src/filters/filter.rs b/crates/nu-command/src/filters/filter.rs index 1367201851e..0a81c6c83b7 100644 --- a/crates/nu-command/src/filters/filter.rs +++ b/crates/nu-command/src/filters/filter.rs @@ -1,6 +1,7 @@ use super::utils::chain_error_with_input; -use nu_engine::{eval_block, CallExt}; +use nu_engine::{get_eval_block, CallExt}; use nu_protocol::ast::Call; + use nu_protocol::engine::{Closure, Command, EngineState, Stack}; use nu_protocol::{ record, Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, @@ -63,6 +64,7 @@ a variable. On the other hand, the "row condition" syntax is not supported."# let span = call.head; let redirect_stdout = call.redirect_stdout; let redirect_stderr = call.redirect_stderr; + let eval_block = get_eval_block(&engine_state); match input { PipelineData::Empty => Ok(PipelineData::Empty), @@ -162,6 +164,7 @@ a variable. On the other hand, the "row condition" syntax is not supported."# stack.add_var(*var_id, x.clone()); } } + Ok(match eval_block( &engine_state, &mut stack, diff --git a/crates/nu-command/src/filters/group_by.rs b/crates/nu-command/src/filters/group_by.rs index bbf32f4c6f0..b80b330a242 100644 --- a/crates/nu-command/src/filters/group_by.rs +++ b/crates/nu-command/src/filters/group_by.rs @@ -1,4 +1,4 @@ -use nu_engine::{eval_block, CallExt}; +use nu_engine::{get_eval_block, CallExt}; use nu_protocol::ast::{Call, CellPath}; use nu_protocol::engine::{Closure, Command, EngineState, Stack}; use nu_protocol::{ @@ -238,12 +238,14 @@ fn group_closure( ) -> Result>, ShellError> { let error_key = "error"; let mut groups: IndexMap> = IndexMap::new(); + let eval_block = get_eval_block(engine_state); if let Some(capture_block) = &block { let block = engine_state.get_block(capture_block.block_id); for value in values { let mut stack = stack.captures_to_stack(capture_block.captures.clone()); + let pipeline = eval_block( engine_state, &mut stack, diff --git a/crates/nu-command/src/filters/insert.rs b/crates/nu-command/src/filters/insert.rs index 4a903f2faa5..3c9f3c27029 100644 --- a/crates/nu-command/src/filters/insert.rs +++ b/crates/nu-command/src/filters/insert.rs @@ -1,5 +1,6 @@ -use nu_engine::{eval_block, CallExt}; +use nu_engine::{get_eval_block, CallExt, EvalBlockFn}; use nu_protocol::ast::{Block, Call, CellPath, PathMember}; + use nu_protocol::engine::{Closure, Command, EngineState, Stack}; use nu_protocol::{ record, Category, Example, FromValue, IntoInterruptiblePipelineData, IntoPipelineData, @@ -134,6 +135,8 @@ fn insert( let ctrlc = engine_state.ctrlc.clone(); + let eval_block = get_eval_block(engine_state); + match input { PipelineData::Value(mut value, metadata) => { if replacement.coerce_block().is_ok() { @@ -155,6 +158,7 @@ fn insert( block, &cell_path.members, false, + eval_block, )?; } } @@ -168,6 +172,7 @@ fn insert( redirect_stderr, &cell_path.members, matches!(first, Some(PathMember::Int { .. })), + eval_block, )?; } } @@ -242,6 +247,7 @@ fn insert( redirect_stderr, path, true, + eval_block, )?; } else { value.insert_data_at_cell_path(path, replacement, span)?; @@ -281,6 +287,7 @@ fn insert( &block, &cell_path.members, false, + eval_block, ); if let Err(e) = err { @@ -328,6 +335,7 @@ fn insert_value_by_closure( block: &Block, cell_path: &[PathMember], first_path_member_int: bool, + eval_block_fn: EvalBlockFn, ) -> Result<(), ShellError> { let input_at_path = value.clone().follow_cell_path(cell_path, false); @@ -348,7 +356,7 @@ fn insert_value_by_closure( .map(IntoPipelineData::into_pipeline_data) .unwrap_or(PipelineData::Empty); - let output = eval_block( + let output = eval_block_fn( engine_state, stack, block, @@ -370,6 +378,7 @@ fn insert_single_value_by_closure( redirect_stderr: bool, cell_path: &[PathMember], first_path_member_int: bool, + eval_block_fn: EvalBlockFn, ) -> Result<(), ShellError> { let span = replacement.span(); let capture_block = Closure::from_value(replacement)?; @@ -386,6 +395,7 @@ fn insert_single_value_by_closure( block, cell_path, first_path_member_int, + eval_block_fn, ) } diff --git a/crates/nu-command/src/filters/interleave.rs b/crates/nu-command/src/filters/interleave.rs index 826811ee5a3..cc6d5c357a0 100644 --- a/crates/nu-command/src/filters/interleave.rs +++ b/crates/nu-command/src/filters/interleave.rs @@ -1,6 +1,6 @@ use std::{sync::mpsc, thread}; -use nu_engine::{eval_block_with_early_return, CallExt}; +use nu_engine::{get_eval_block_with_early_return, CallExt}; use nu_protocol::{ ast::Call, engine::{Closure, Command, EngineState, Stack}, @@ -118,6 +118,7 @@ interleave let (tx, rx) = mpsc::sync_channel(buffer_size); let closures: Vec = call.rest(engine_state, stack, 0)?; + let eval_block_with_early_return = get_eval_block_with_early_return(engine_state); // Spawn the threads for the input and closure outputs (!input.is_nothing()) diff --git a/crates/nu-command/src/filters/items.rs b/crates/nu-command/src/filters/items.rs index 0433fdee24c..693091a3ee6 100644 --- a/crates/nu-command/src/filters/items.rs +++ b/crates/nu-command/src/filters/items.rs @@ -1,5 +1,6 @@ -use nu_engine::{eval_block_with_early_return, CallExt}; +use nu_engine::{get_eval_block_with_early_return, CallExt}; use nu_protocol::ast::Call; + use nu_protocol::engine::{Closure, Command, EngineState, Stack}; use nu_protocol::{ Category, Example, IntoInterruptiblePipelineData, PipelineData, ShellError, Signature, Span, @@ -54,6 +55,7 @@ impl Command for Items { let orig_env_hidden = stack.env_hidden.clone(); let span = call.head; let redirect_stderr = call.redirect_stderr; + let eval_block_with_early_return = get_eval_block_with_early_return(&engine_state); let input_span = input.span().unwrap_or(call.head); let run_for_each_item = move |keyval: (String, Value)| -> Option { diff --git a/crates/nu-command/src/filters/par_each.rs b/crates/nu-command/src/filters/par_each.rs index 576a06555dc..a0de7918070 100644 --- a/crates/nu-command/src/filters/par_each.rs +++ b/crates/nu-command/src/filters/par_each.rs @@ -1,5 +1,6 @@ -use nu_engine::{eval_block_with_early_return, CallExt}; +use nu_engine::{get_eval_block_with_early_return, CallExt}; use nu_protocol::ast::Call; + use nu_protocol::engine::{Closure, Command, EngineState, Stack}; use nu_protocol::{ Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, ShellError, @@ -142,6 +143,8 @@ impl Command for ParEach { vec.into_iter().map(|(_, val)| val) }; + let eval_block_with_early_return = get_eval_block_with_early_return(engine_state); + match input { PipelineData::Empty => Ok(PipelineData::Empty), PipelineData::Value(Value::Range { val, .. }, ..) => Ok(create_pool(max_threads)? diff --git a/crates/nu-command/src/filters/reduce.rs b/crates/nu-command/src/filters/reduce.rs index 55f19e58867..8635fb81bfb 100644 --- a/crates/nu-command/src/filters/reduce.rs +++ b/crates/nu-command/src/filters/reduce.rs @@ -1,6 +1,7 @@ -use nu_engine::{eval_block_with_early_return, CallExt}; +use nu_engine::{get_eval_block_with_early_return, CallExt}; use nu_protocol::ast::Call; + use nu_protocol::engine::{Closure, Command, EngineState, Stack}; use nu_protocol::{ Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, SyntaxShape, Type, @@ -101,6 +102,7 @@ impl Command for Reduce { let mut stack = stack.captures_to_stack(capture_block.captures); let block = engine_state.get_block(capture_block.block_id); let ctrlc = engine_state.ctrlc.clone(); + let eval_block_with_early_return = get_eval_block_with_early_return(engine_state); let orig_env_vars = stack.env_vars.clone(); let orig_env_hidden = stack.env_hidden.clone(); diff --git a/crates/nu-command/src/filters/rename.rs b/crates/nu-command/src/filters/rename.rs index ac4dc9642c6..0998d99b5cf 100644 --- a/crates/nu-command/src/filters/rename.rs +++ b/crates/nu-command/src/filters/rename.rs @@ -1,6 +1,7 @@ use indexmap::IndexMap; -use nu_engine::{eval_block_with_early_return, CallExt}; +use nu_engine::{get_eval_block_with_early_return, CallExt}; use nu_protocol::ast::Call; + use nu_protocol::engine::{Closure, Command, EngineState, Stack}; use nu_protocol::{ record, Category, Example, IntoPipelineData, PipelineData, Record, ShellError, Signature, @@ -156,6 +157,8 @@ fn rename( let columns: Vec = call.rest(engine_state, stack, 0)?; let metadata = input.metadata(); + let eval_block_with_early_return = get_eval_block_with_early_return(engine_state); + let head_span = call.head; input .map( @@ -176,6 +179,7 @@ fn rename( stack.add_var(*var_id, Value::string(c.clone(), span)) } } + let eval_result = eval_block_with_early_return( &engine_state, &mut stack, @@ -184,6 +188,7 @@ fn rename( redirect_stdout, redirect_stderr, ); + match eval_result { Err(e) => return Value::error(e, span), Ok(res) => match res.collect_string_strict(span) { diff --git a/crates/nu-command/src/filters/skip/skip_until.rs b/crates/nu-command/src/filters/skip/skip_until.rs index e38beaa0d79..b26d134c59d 100644 --- a/crates/nu-command/src/filters/skip/skip_until.rs +++ b/crates/nu-command/src/filters/skip/skip_until.rs @@ -1,4 +1,5 @@ -use nu_engine::{eval_block, CallExt}; +use nu_engine::{get_eval_block, CallExt}; + use nu_protocol::{ ast::Call, engine::{Closure, Command, EngineState, Stack}, @@ -94,6 +95,8 @@ impl Command for SkipUntil { let redirect_stdout = call.redirect_stdout; let redirect_stderr = call.redirect_stderr; + let eval_block = get_eval_block(&engine_state); + Ok(input .into_iter_strict(span)? .skip_while(move |value| { diff --git a/crates/nu-command/src/filters/skip/skip_while.rs b/crates/nu-command/src/filters/skip/skip_while.rs index c2b275365e3..8dfbfe41624 100644 --- a/crates/nu-command/src/filters/skip/skip_while.rs +++ b/crates/nu-command/src/filters/skip/skip_while.rs @@ -1,4 +1,5 @@ -use nu_engine::{eval_block, CallExt}; +use nu_engine::{get_eval_block, CallExt}; + use nu_protocol::{ ast::Call, engine::{Closure, Command, EngineState, Stack}, @@ -99,6 +100,8 @@ impl Command for SkipWhile { let redirect_stdout = call.redirect_stdout; let redirect_stderr = call.redirect_stderr; + let eval_block = get_eval_block(&engine_state); + Ok(input .into_iter_strict(span)? .skip_while(move |value| { diff --git a/crates/nu-command/src/filters/take/take_until.rs b/crates/nu-command/src/filters/take/take_until.rs index a46de951d0a..88f64a3e614 100644 --- a/crates/nu-command/src/filters/take/take_until.rs +++ b/crates/nu-command/src/filters/take/take_until.rs @@ -1,4 +1,5 @@ -use nu_engine::{eval_block, CallExt}; +use nu_engine::{get_eval_block, CallExt}; + use nu_protocol::{ ast::Call, engine::{Closure, Command, EngineState, Stack}, @@ -91,6 +92,8 @@ impl Command for TakeUntil { let redirect_stdout = call.redirect_stdout; let redirect_stderr = call.redirect_stderr; + let eval_block = get_eval_block(&engine_state); + Ok(input .into_iter_strict(span)? .take_while(move |value| { diff --git a/crates/nu-command/src/filters/take/take_while.rs b/crates/nu-command/src/filters/take/take_while.rs index 3fd042e3e2a..3777af212e6 100644 --- a/crates/nu-command/src/filters/take/take_while.rs +++ b/crates/nu-command/src/filters/take/take_while.rs @@ -1,4 +1,4 @@ -use nu_engine::{eval_block, CallExt}; +use nu_engine::{get_eval_block, CallExt}; use nu_protocol::{ ast::Call, engine::{Closure, Command, EngineState, Stack}, @@ -91,6 +91,8 @@ impl Command for TakeWhile { let redirect_stdout = call.redirect_stdout; let redirect_stderr = call.redirect_stderr; + let eval_block = get_eval_block(&engine_state); + Ok(input .into_iter_strict(span)? .take_while(move |value| { diff --git a/crates/nu-command/src/filters/tee.rs b/crates/nu-command/src/filters/tee.rs index 95aaa19911d..19c04541973 100644 --- a/crates/nu-command/src/filters/tee.rs +++ b/crates/nu-command/src/filters/tee.rs @@ -1,6 +1,6 @@ use std::{sync::mpsc, thread}; -use nu_engine::{eval_block_with_early_return, CallExt}; +use nu_engine::{get_eval_block_with_early_return, CallExt}; use nu_protocol::{ ast::Call, engine::{Closure, Command, EngineState, Stack}, @@ -83,6 +83,8 @@ use it in your pipeline."# let metadata = input.metadata(); let metadata_clone = metadata.clone(); + let eval_block_with_early_return = get_eval_block_with_early_return(engine_state); + match input { // Handle external streams specially, to make sure they pass through PipelineData::ExternalStream { diff --git a/crates/nu-command/src/filters/update.rs b/crates/nu-command/src/filters/update.rs index 083cf11bf1a..02300bf5fea 100644 --- a/crates/nu-command/src/filters/update.rs +++ b/crates/nu-command/src/filters/update.rs @@ -1,5 +1,6 @@ -use nu_engine::{eval_block, CallExt}; +use nu_engine::{get_eval_block, CallExt, EvalBlockFn}; use nu_protocol::ast::{Block, Call, CellPath, PathMember}; + use nu_protocol::engine::{Closure, Command, EngineState, Stack}; use nu_protocol::{ record, Category, Example, FromValue, IntoInterruptiblePipelineData, IntoPipelineData, @@ -116,6 +117,8 @@ fn update( let ctrlc = engine_state.ctrlc.clone(); + let eval_block = get_eval_block(engine_state); + match input { PipelineData::Value(mut value, metadata) => { if replacement.coerce_block().is_ok() { @@ -137,6 +140,7 @@ fn update( block, &cell_path.members, false, + eval_block, )?; } } @@ -150,6 +154,7 @@ fn update( redirect_stderr, &cell_path.members, matches!(first, Some(PathMember::Int { .. })), + eval_block, )?; } } @@ -196,6 +201,7 @@ fn update( redirect_stderr, path, true, + eval_block, )?; } else { value.update_data_at_cell_path(path, replacement)?; @@ -228,6 +234,7 @@ fn update( &block, &cell_path.members, false, + eval_block, ); if let Err(e) = err { @@ -273,6 +280,7 @@ fn update_value_by_closure( block: &Block, cell_path: &[PathMember], first_path_member_int: bool, + eval_block_fn: EvalBlockFn, ) -> Result<(), ShellError> { let input_at_path = value.clone().follow_cell_path(cell_path, false)?; @@ -289,7 +297,7 @@ fn update_value_by_closure( } } - let output = eval_block( + let output = eval_block_fn( engine_state, stack, block, @@ -311,6 +319,7 @@ fn update_single_value_by_closure( redirect_stderr: bool, cell_path: &[PathMember], first_path_member_int: bool, + eval_block_fn: EvalBlockFn, ) -> Result<(), ShellError> { let span = replacement.span(); let capture_block = Closure::from_value(replacement)?; @@ -327,6 +336,7 @@ fn update_single_value_by_closure( block, cell_path, first_path_member_int, + eval_block_fn, ) } diff --git a/crates/nu-command/src/filters/upsert.rs b/crates/nu-command/src/filters/upsert.rs index fb2afd5dabe..4ca48f4fd65 100644 --- a/crates/nu-command/src/filters/upsert.rs +++ b/crates/nu-command/src/filters/upsert.rs @@ -1,5 +1,6 @@ -use nu_engine::{eval_block, CallExt}; +use nu_engine::{get_eval_block, CallExt, EvalBlockFn}; use nu_protocol::ast::{Block, Call, CellPath, PathMember}; + use nu_protocol::engine::{Closure, Command, EngineState, Stack}; use nu_protocol::{ record, Category, Example, FromValue, IntoInterruptiblePipelineData, IntoPipelineData, @@ -158,6 +159,7 @@ fn upsert( let redirect_stdout = call.redirect_stdout; let redirect_stderr = call.redirect_stderr; + let eval_block = get_eval_block(engine_state); let ctrlc = engine_state.ctrlc.clone(); @@ -182,6 +184,7 @@ fn upsert( block, &cell_path.members, false, + eval_block, )?; } } @@ -195,6 +198,7 @@ fn upsert( redirect_stderr, &cell_path.members, matches!(first, Some(PathMember::Int { .. })), + eval_block, )?; } } @@ -264,6 +268,7 @@ fn upsert( redirect_stderr, path, true, + eval_block, )?; } else { value.upsert_data_at_cell_path(path, replacement)?; @@ -303,6 +308,7 @@ fn upsert( &block, &cell_path.members, false, + eval_block, ); if let Err(e) = err { @@ -348,6 +354,7 @@ fn upsert_value_by_closure( block: &Block, cell_path: &[PathMember], first_path_member_int: bool, + eval_block_fn: EvalBlockFn, ) -> Result<(), ShellError> { let input_at_path = value.clone().follow_cell_path(cell_path, false); @@ -368,7 +375,7 @@ fn upsert_value_by_closure( .map(IntoPipelineData::into_pipeline_data) .unwrap_or(PipelineData::Empty); - let output = eval_block( + let output = eval_block_fn( engine_state, stack, block, @@ -390,6 +397,7 @@ fn upsert_single_value_by_closure( redirect_stderr: bool, cell_path: &[PathMember], first_path_member_int: bool, + eval_block_fn: EvalBlockFn, ) -> Result<(), ShellError> { let span = replacement.span(); let capture_block = Closure::from_value(replacement)?; @@ -406,6 +414,7 @@ fn upsert_single_value_by_closure( block, cell_path, first_path_member_int, + eval_block_fn, ) } diff --git a/crates/nu-command/src/filters/utils.rs b/crates/nu-command/src/filters/utils.rs index 719ed6e04cd..be58cff4b45 100644 --- a/crates/nu-command/src/filters/utils.rs +++ b/crates/nu-command/src/filters/utils.rs @@ -1,4 +1,5 @@ -use nu_engine::{eval_block, CallExt}; +use nu_engine::{get_eval_block, CallExt}; + use nu_protocol::{ ast::Call, engine::{Closure, EngineState, Stack}, @@ -40,6 +41,10 @@ pub fn boolean_fold( let ctrlc = engine_state.ctrlc.clone(); + // TODO: This Clippy lint is incorrectly triggered in our CI for come reason + #[allow(clippy::needless_borrow)] + let eval_block = get_eval_block(&engine_state); + for value in input.into_interruptible_iter(ctrlc) { // with_env() is used here to ensure that each iteration uses // a different set of environment variables. diff --git a/crates/nu-command/src/filters/where_.rs b/crates/nu-command/src/filters/where_.rs index 7d099d7e59c..d523518c9d5 100644 --- a/crates/nu-command/src/filters/where_.rs +++ b/crates/nu-command/src/filters/where_.rs @@ -1,5 +1,6 @@ -use nu_engine::{eval_block, CallExt}; +use nu_engine::{get_eval_block, CallExt}; use nu_protocol::ast::Call; + use nu_protocol::engine::{Closure, Command, EngineState, Stack}; use nu_protocol::{ record, Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, @@ -70,6 +71,9 @@ not supported."# let redirect_stdout = call.redirect_stdout; let redirect_stderr = call.redirect_stderr; + + let eval_block = get_eval_block(&engine_state); + Ok(input .into_iter_strict(span)? .filter_map(move |value| { @@ -80,6 +84,7 @@ not supported."# stack.add_var(*var_id, value.clone()); } } + let result = eval_block( &engine_state, &mut stack, diff --git a/crates/nu-command/src/filters/zip.rs b/crates/nu-command/src/filters/zip.rs index 6477f69bda0..033a1ca4bb0 100644 --- a/crates/nu-command/src/filters/zip.rs +++ b/crates/nu-command/src/filters/zip.rs @@ -1,4 +1,4 @@ -use nu_engine::{eval_block_with_early_return, CallExt}; +use nu_engine::{get_eval_block_with_early_return, CallExt}; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ @@ -102,6 +102,7 @@ impl Command for Zip { let head = call.head; let ctrlc = engine_state.ctrlc.clone(); let metadata = input.metadata(); + let eval_block_with_early_return = get_eval_block_with_early_return(engine_state); let other: PipelineData = match call.req(engine_state, stack, 0)? { // If a closure was provided, evaluate it and consume its stream output diff --git a/crates/nu-command/src/generators/generate.rs b/crates/nu-command/src/generators/generate.rs index 748f383d1a3..2cada09802c 100644 --- a/crates/nu-command/src/generators/generate.rs +++ b/crates/nu-command/src/generators/generate.rs @@ -1,5 +1,6 @@ use itertools::unfold; -use nu_engine::{eval_block_with_early_return, CallExt}; +use nu_engine::{get_eval_block_with_early_return, CallExt}; + use nu_protocol::{ ast::Call, engine::{Closure, Command, EngineState, Stack}, @@ -107,6 +108,7 @@ used as the next argument to the closure, otherwise generation stops. let orig_env_hidden = stack.env_hidden.clone(); let redirect_stdout = call.redirect_stdout; let redirect_stderr = call.redirect_stderr; + let eval_block_with_early_return = get_eval_block_with_early_return(&engine_state); // A type of Option is used to represent state. Invocation // will stop on None. Using Option allows functions to output diff --git a/crates/nu-command/src/misc/source.rs b/crates/nu-command/src/misc/source.rs index 87ce905a953..140dd51c39d 100644 --- a/crates/nu-command/src/misc/source.rs +++ b/crates/nu-command/src/misc/source.rs @@ -1,5 +1,6 @@ -use nu_engine::{eval_block_with_early_return, CallExt}; +use nu_engine::{get_eval_block_with_early_return, CallExt}; use nu_protocol::ast::Call; + use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type}; @@ -46,8 +47,10 @@ impl Command for Source { // Note: this hidden positional is the block_id that corresponded to the 0th position // it is put here by the parser let block_id: i64 = call.req_parser_info(engine_state, stack, "block_id")?; - let block = engine_state.get_block(block_id as usize).clone(); + + let eval_block_with_early_return = get_eval_block_with_early_return(engine_state); + eval_block_with_early_return( engine_state, stack, diff --git a/crates/nu-command/src/system/run_external.rs b/crates/nu-command/src/system/run_external.rs index fc2ef3c424f..90533f0964e 100644 --- a/crates/nu-command/src/system/run_external.rs +++ b/crates/nu-command/src/system/run_external.rs @@ -1,6 +1,6 @@ use nu_cmd_base::hook::eval_hook; use nu_engine::env_to_strings; -use nu_engine::eval_expression; +use nu_engine::get_eval_expression; use nu_engine::CallExt; use nu_protocol::IntoSpanned; use nu_protocol::NuGlob; @@ -133,6 +133,8 @@ pub fn create_external_command( }) } + let eval_expression = get_eval_expression(engine_state); + let mut spanned_args = vec![]; let mut arg_keep_raw = vec![]; for (arg, spread) in call.rest_iter(1) { diff --git a/crates/nu-engine/src/call_ext.rs b/crates/nu-engine/src/call_ext.rs index 54b5e7881d3..f705f19d2e6 100644 --- a/crates/nu-engine/src/call_ext.rs +++ b/crates/nu-engine/src/call_ext.rs @@ -1,3 +1,4 @@ +use nu_protocol::debugger::WithoutDebug; use nu_protocol::{ ast::Call, engine::{EngineState, Stack, StateWorkingSet}, @@ -69,7 +70,7 @@ impl CallExt for Call { if flag_name == name.0.item { return if let Some(expr) = &name.2 { // Check --flag=false - let result = eval_expression(engine_state, stack, expr)?; + let result = eval_expression::(engine_state, stack, expr)?; match result { Value::Bool { val, .. } => Ok(val), _ => Err(ShellError::CantConvert { @@ -95,7 +96,7 @@ impl CallExt for Call { name: &str, ) -> Result, ShellError> { if let Some(expr) = self.get_flag_expr(name) { - let result = eval_expression(engine_state, stack, expr)?; + let result = eval_expression::(engine_state, stack, expr)?; FromValue::from_value(result).map(Some) } else { Ok(None) @@ -111,7 +112,7 @@ impl CallExt for Call { let mut output = vec![]; for result in self.rest_iter_flattened(starting_pos, |expr| { - eval_expression(engine_state, stack, expr) + eval_expression::(engine_state, stack, expr) })? { output.push(FromValue::from_value(result)?); } @@ -126,7 +127,7 @@ impl CallExt for Call { pos: usize, ) -> Result, ShellError> { if let Some(expr) = self.positional_nth(pos) { - let result = eval_expression(engine_state, stack, expr)?; + let result = eval_expression::(engine_state, stack, expr)?; FromValue::from_value(result).map(Some) } else { Ok(None) @@ -153,7 +154,7 @@ impl CallExt for Call { pos: usize, ) -> Result { if let Some(expr) = self.positional_nth(pos) { - let result = eval_expression(engine_state, stack, expr)?; + let result = eval_expression::(engine_state, stack, expr)?; FromValue::from_value(result) } else if self.positional_len() == 0 { Err(ShellError::AccessEmptyContent { span: self.head }) @@ -172,7 +173,7 @@ impl CallExt for Call { name: &str, ) -> Result { if let Some(expr) = self.get_parser_info(name) { - let result = eval_expression(engine_state, stack, expr)?; + let result = eval_expression::(engine_state, stack, expr)?; FromValue::from_value(result) } else if self.parser_info.is_empty() { Err(ShellError::AccessEmptyContent { span: self.head }) diff --git a/crates/nu-engine/src/documentation.rs b/crates/nu-engine/src/documentation.rs index b626547d409..159810ec2b3 100644 --- a/crates/nu-engine/src/documentation.rs +++ b/crates/nu-engine/src/documentation.rs @@ -1,4 +1,5 @@ use nu_protocol::ast::{Argument, Expr, Expression, RecordItem}; +use nu_protocol::debugger::WithoutDebug; use nu_protocol::{ ast::Call, engine::{EngineState, Stack}, @@ -235,7 +236,7 @@ fn get_documentation( } let mut caller_stack = Stack::new(); - if let Ok(result) = eval_call( + if let Ok(result) = eval_call::( engine_state, &mut caller_stack, &Call { @@ -347,7 +348,7 @@ fn get_ansi_color_for_component_or_default( // Call ansi command using argument if let Some(argument) = argument_opt { if let Some(decl_id) = engine_state.find_decl(b"ansi", &[]) { - if let Ok(result) = eval_call( + if let Ok(result) = eval_call::( engine_state, &mut caller_stack, &Call { diff --git a/crates/nu-engine/src/env.rs b/crates/nu-engine/src/env.rs index 9f1906bd717..ea932ca90cb 100644 --- a/crates/nu-engine/src/env.rs +++ b/crates/nu-engine/src/env.rs @@ -6,6 +6,7 @@ use nu_protocol::engine::{EngineState, Stack, StateWorkingSet, PWD_ENV}; use nu_protocol::{Config, PipelineData, ShellError, Span, Value, VarId}; use nu_path::canonicalize_with; +use nu_protocol::debugger::WithoutDebug; use crate::eval_block; @@ -388,7 +389,8 @@ fn get_converted_value( } let val_span = orig_val.span(); - let result = eval_block( + // TODO DEBUG + let result = eval_block::( engine_state, &mut stack, block, diff --git a/crates/nu-engine/src/eval.rs b/crates/nu-engine/src/eval.rs index c4007042e4e..3fecf548842 100644 --- a/crates/nu-engine/src/eval.rs +++ b/crates/nu-engine/src/eval.rs @@ -1,5 +1,6 @@ use crate::{current_dir_str, get_config, get_full_help}; use nu_path::expand_path_with; +use nu_protocol::debugger::{DebugContext, WithoutDebug}; use nu_protocol::{ ast::{ Argument, Assignment, Block, Call, Expr, Expression, ExternalArgument, PathMember, @@ -10,10 +11,11 @@ use nu_protocol::{ Config, DeclId, IntoPipelineData, IntoSpanned, PipelineData, RawStream, ShellError, Span, Spanned, Type, Value, VarId, ENV_VARIABLE_ID, }; + use std::thread::{self, JoinHandle}; use std::{borrow::Cow, collections::HashMap}; -pub fn eval_call( +pub fn eval_call( engine_state: &EngineState, caller_stack: &mut Stack, call: &Call, @@ -75,7 +77,7 @@ pub fn eval_call( .expect("internal error: all custom parameters must have var_ids"); if let Some(arg) = call.positional_nth(param_idx) { - let result = eval_expression(engine_state, caller_stack, arg)?; + let result = eval_expression::(engine_state, caller_stack, arg)?; let param_type = param.shape.to_type(); if required && !result.get_type().is_subtype(¶m_type) { // need to check if result is an empty list, and param_type is table or list @@ -110,7 +112,7 @@ pub fn eval_call( for result in call.rest_iter_flattened( decl.signature().required_positional.len() + decl.signature().optional_positional.len(), - |expr| eval_expression(engine_state, caller_stack, expr), + |expr| eval_expression::(engine_state, caller_stack, expr), )? { rest_items.push(result); } @@ -136,7 +138,7 @@ pub fn eval_call( if let (Some(spanned), Some(short)) = (&call_named.1, named.short) { if spanned.item == short.to_string() { if let Some(arg) = &call_named.2 { - let result = eval_expression(engine_state, caller_stack, arg)?; + let result = eval_expression::(engine_state, caller_stack, arg)?; callee_stack.add_var(var_id, result); } else if let Some(value) = &named.default_value { @@ -148,7 +150,7 @@ pub fn eval_call( } } else if call_named.0.item == named.long { if let Some(arg) = &call_named.2 { - let result = eval_expression(engine_state, caller_stack, arg)?; + let result = eval_expression::(engine_state, caller_stack, arg)?; callee_stack.add_var(var_id, result); } else if let Some(value) = &named.default_value { @@ -172,7 +174,7 @@ pub fn eval_call( } } - let result = eval_block_with_early_return( + let result = eval_block_with_early_return::( engine_state, &mut callee_stack, block, @@ -293,12 +295,12 @@ fn eval_external( command.run(engine_state, stack, &call, input) } -pub fn eval_expression( +pub fn eval_expression( engine_state: &EngineState, stack: &mut Stack, expr: &Expression, ) -> Result { - ::eval(engine_state, stack, expr) + ::eval::(engine_state, stack, expr) } /// Checks the expression to see if it's a internal or external call. If so, passes the input @@ -307,7 +309,7 @@ pub fn eval_expression( /// /// It returns PipelineData with a boolean flag, indicating if the external failed to run. /// The boolean flag **may only be true** for external calls, for internal calls, it always to be false. -pub fn eval_expression_with_input( +pub fn eval_expression_with_input( engine_state: &EngineState, stack: &mut Stack, expr: &Expression, @@ -325,9 +327,9 @@ pub fn eval_expression_with_input( let mut call = call.clone(); call.redirect_stdout = redirect_stdout; call.redirect_stderr = redirect_stderr; - input = eval_call(engine_state, stack, &call, input)?; + input = eval_call::(engine_state, stack, &call, input)?; } else { - input = eval_call(engine_state, stack, call, input)?; + input = eval_call::(engine_state, stack, call, input)?; } } Expression { @@ -352,7 +354,7 @@ pub fn eval_expression_with_input( let block = engine_state.get_block(*block_id); // FIXME: protect this collect with ctrl-c - input = eval_subexpression(engine_state, stack, block, input)?; + input = eval_subexpression::(engine_state, stack, block, input)?; } elem @ Expression { @@ -367,19 +369,19 @@ pub fn eval_expression_with_input( let block = engine_state.get_block(*block_id); // FIXME: protect this collect with ctrl-c - input = eval_subexpression(engine_state, stack, block, input)?; + input = eval_subexpression::(engine_state, stack, block, input)?; let value = input.into_value(*span); input = value .follow_cell_path(&full_cell_path.tail, false)? .into_pipeline_data() } _ => { - input = eval_expression(engine_state, stack, elem)?.into_pipeline_data(); + input = eval_expression::(engine_state, stack, elem)?.into_pipeline_data(); } }, elem => { - input = eval_expression(engine_state, stack, elem)?.into_pipeline_data(); + input = eval_expression::(engine_state, stack, elem)?.into_pipeline_data(); } }; @@ -406,7 +408,7 @@ fn might_consume_external_result(input: PipelineData) -> (PipelineData, bool) { } #[allow(clippy::too_many_arguments)] -fn eval_element_with_input( +fn eval_element_with_input( engine_state: &EngineState, stack: &mut Stack, element: &PipelineElement, @@ -416,7 +418,9 @@ fn eval_element_with_input( redirect_combine: bool, stderr_writer_jobs: &mut Vec, ) -> Result<(PipelineData, bool), ShellError> { - match element { + D::enter_element(engine_state, element); + + let result = match element { PipelineElement::Expression(pipe_span, expr) | PipelineElement::OutErrPipedExpression(pipe_span, expr) => { if matches!(element, PipelineElement::OutErrPipedExpression(..)) @@ -446,7 +450,7 @@ fn eval_element_with_input( )?; Ok(might_consume_external_result(result)) } - _ => eval_expression_with_input( + _ => eval_expression_with_input::( engine_state, stack, expr, @@ -483,7 +487,7 @@ fn eval_element_with_input( }) } }; - eval_expression_with_input( + eval_expression_with_input::( engine_state, stack, expr, @@ -514,7 +518,7 @@ fn eval_element_with_input( ); match result_out_stream { None => { - eval_call(engine_state, stack, &save_call, input).map(|_| { + eval_call::(engine_state, stack, &save_call, input).map(|_| { // save is internal command, normally it exists with non-ExternalStream // but here in redirection context, we make it returns ExternalStream // So nu handles exit_code correctly @@ -602,7 +606,7 @@ fn eval_element_with_input( Some((*err_span, err_expr.clone(), *err_append_mode)), ); - eval_call(engine_state, stack, &save_call, input).map(|_| { + eval_call::(engine_state, stack, &save_call, input).map(|_| { // save is internal command, normally it exists with non-ExternalStream // but here in redirection context, we make it returns ExternalStream // So nu handles exit_code correctly @@ -648,7 +652,7 @@ fn eval_element_with_input( } _ => { // we need to redirect output, so the result can be saved and pass to `save` command. - eval_element_with_input( + eval_element_with_input::( engine_state, stack, &PipelineElement::Expression(*cmd_span, cmd_exp.clone()), @@ -661,7 +665,7 @@ fn eval_element_with_input( .map(|x| x.0)? } }; - eval_element_with_input( + eval_element_with_input::( engine_state, stack, &PipelineElement::Redirection( @@ -677,7 +681,7 @@ fn eval_element_with_input( stderr_writer_jobs, ) } - PipelineElement::And(_, expr) => eval_expression_with_input( + PipelineElement::And(_, expr) => eval_expression_with_input::( engine_state, stack, expr, @@ -685,7 +689,7 @@ fn eval_element_with_input( redirect_stdout, redirect_stderr, ), - PipelineElement::Or(_, expr) => eval_expression_with_input( + PipelineElement::Or(_, expr) => eval_expression_with_input::( engine_state, stack, expr, @@ -693,7 +697,11 @@ fn eval_element_with_input( redirect_stdout, redirect_stderr, ), - } + }; + + D::leave_element(engine_state, element, &result); + + result } // In redirection context, if nushell gets an ExternalStream @@ -836,7 +844,7 @@ fn is_redirect_combine_required(elements: &[PipelineElement], idx: usize) -> boo ) } -pub fn eval_block_with_early_return( +pub fn eval_block_with_early_return( engine_state: &EngineState, stack: &mut Stack, block: &Block, @@ -844,7 +852,7 @@ pub fn eval_block_with_early_return( redirect_stdout: bool, redirect_stderr: bool, ) -> Result { - match eval_block( + match eval_block::( engine_state, stack, block, @@ -857,7 +865,7 @@ pub fn eval_block_with_early_return( } } -pub fn eval_block( +pub fn eval_block( engine_state: &EngineState, stack: &mut Stack, block: &Block, @@ -865,6 +873,8 @@ pub fn eval_block( redirect_stdout: bool, redirect_stderr: bool, ) -> Result { + D::enter_block(engine_state, block); + let num_pipelines = block.len(); for (pipeline_idx, pipeline) in block.pipelines.iter().enumerate() { @@ -891,7 +901,7 @@ pub fn eval_block( let redirect_combine = is_redirect_combine_required(elements, idx); // if eval internal command failed, it can just make early return with `Err(ShellError)`. - let eval_result = eval_element_with_input( + let eval_result = eval_element_with_input::( engine_state, stack, element, @@ -962,16 +972,18 @@ pub fn eval_block( } } + D::leave_block(engine_state, block); + Ok(input) } -pub fn eval_subexpression( +pub fn eval_subexpression( engine_state: &EngineState, stack: &mut Stack, block: &Block, input: PipelineData, ) -> Result { - eval_block(engine_state, stack, block, input, true, false) + eval_block::(engine_state, stack, block, input, true, false) } pub fn eval_variable( @@ -1096,7 +1108,8 @@ impl DataSaveJob { inner: thread::Builder::new() .name("stderr saver".to_string()) .spawn(move || { - let result = eval_call(&engine_state, &mut stack, &save_call, input); + let result = + eval_call::(&engine_state, &mut stack, &save_call, input); if let Err(err) = result { eprintln!("WARNING: error occurred when redirect to stderr: {:?}", err); } @@ -1166,14 +1179,14 @@ impl Eval for EvalRuntime { eval_variable(engine_state, stack, var_id, span) } - fn eval_call( + fn eval_call( engine_state: &EngineState, stack: &mut Stack, call: &Call, _: Span, ) -> Result { // FIXME: protect this collect with ctrl-c - Ok(eval_call(engine_state, stack, call, PipelineData::empty())?.into_value(call.head)) + Ok(eval_call::(engine_state, stack, call, PipelineData::empty())?.into_value(call.head)) } fn eval_external_call( @@ -1198,7 +1211,7 @@ impl Eval for EvalRuntime { .into_value(span)) } - fn eval_subexpression( + fn eval_subexpression( engine_state: &EngineState, stack: &mut Stack, block_id: usize, @@ -1207,7 +1220,10 @@ impl Eval for EvalRuntime { let block = engine_state.get_block(block_id); // FIXME: protect this collect with ctrl-c - Ok(eval_subexpression(engine_state, stack, block, PipelineData::empty())?.into_value(span)) + Ok( + eval_subexpression::(engine_state, stack, block, PipelineData::empty())? + .into_value(span), + ) } fn regex_match( @@ -1221,7 +1237,7 @@ impl Eval for EvalRuntime { lhs.regex_match(engine_state, op_span, rhs, invert, expr_span) } - fn eval_assignment( + fn eval_assignment( engine_state: &EngineState, stack: &mut Stack, lhs: &Expression, @@ -1230,28 +1246,28 @@ impl Eval for EvalRuntime { op_span: Span, _expr_span: Span, ) -> Result { - let rhs = eval_expression(engine_state, stack, rhs)?; + let rhs = eval_expression::(engine_state, stack, rhs)?; let rhs = match assignment { Assignment::Assign => rhs, Assignment::PlusAssign => { - let lhs = eval_expression(engine_state, stack, lhs)?; + let lhs = eval_expression::(engine_state, stack, lhs)?; lhs.add(op_span, &rhs, op_span)? } Assignment::MinusAssign => { - let lhs = eval_expression(engine_state, stack, lhs)?; + let lhs = eval_expression::(engine_state, stack, lhs)?; lhs.sub(op_span, &rhs, op_span)? } Assignment::MultiplyAssign => { - let lhs = eval_expression(engine_state, stack, lhs)?; + let lhs = eval_expression::(engine_state, stack, lhs)?; lhs.mul(op_span, &rhs, op_span)? } Assignment::DivideAssign => { - let lhs = eval_expression(engine_state, stack, lhs)?; + let lhs = eval_expression::(engine_state, stack, lhs)?; lhs.div(op_span, &rhs, op_span)? } Assignment::AppendAssign => { - let lhs = eval_expression(engine_state, stack, lhs)?; + let lhs = eval_expression::(engine_state, stack, lhs)?; lhs.append(op_span, &rhs, op_span)? } }; @@ -1273,7 +1289,8 @@ impl Eval for EvalRuntime { // As such, give it special treatment here. let is_env = var_id == &ENV_VARIABLE_ID; if is_env || engine_state.get_var(*var_id).mutable { - let mut lhs = eval_expression(engine_state, stack, &cell_path.head)?; + let mut lhs = + eval_expression::(engine_state, stack, &cell_path.head)?; lhs.upsert_data_at_cell_path(&cell_path.tail, rhs)?; if is_env { diff --git a/crates/nu-engine/src/eval_helpers.rs b/crates/nu-engine/src/eval_helpers.rs new file mode 100644 index 00000000000..b8041420324 --- /dev/null +++ b/crates/nu-engine/src/eval_helpers.rs @@ -0,0 +1,95 @@ +use crate::{ + eval_block, eval_block_with_early_return, eval_expression, eval_expression_with_input, + eval_subexpression, +}; +use nu_protocol::ast::{Block, Expression}; +use nu_protocol::debugger::{WithDebug, WithoutDebug}; +use nu_protocol::engine::{EngineState, Stack}; +use nu_protocol::{PipelineData, ShellError, Value}; + +/// Type of eval_block() function +pub type EvalBlockFn = fn( + &EngineState, + &mut Stack, + &Block, + PipelineData, + bool, + bool, +) -> Result; + +/// Type of eval_block_with_early_return() function +pub type EvalBlockWithEarlyReturnFn = fn( + &EngineState, + &mut Stack, + &Block, + PipelineData, + bool, + bool, +) -> Result; + +/// Type of eval_expression() function +pub type EvalExpressionFn = fn(&EngineState, &mut Stack, &Expression) -> Result; + +/// Type of eval_expression_with_input() function +pub type EvalExpressionWithInputFn = fn( + &EngineState, + &mut Stack, + &Expression, + PipelineData, + bool, + bool, +) -> Result<(PipelineData, bool), ShellError>; + +/// Type of eval_subexpression() function +pub type EvalSubexpressionFn = + fn(&EngineState, &mut Stack, &Block, PipelineData) -> Result; + +/// Helper function to fetch `eval_block()` with the correct type parameter based on whether +/// engine_state is configured with or without a debugger. +pub fn get_eval_block(engine_state: &EngineState) -> EvalBlockFn { + if engine_state.is_debugging() { + eval_block:: + } else { + eval_block:: + } +} + +/// Helper function to fetch `eval_block_with_early_return()` with the correct type parameter based +/// on whether engine_state is configured with or without a debugger. +pub fn get_eval_block_with_early_return(engine_state: &EngineState) -> EvalBlockWithEarlyReturnFn { + if engine_state.is_debugging() { + eval_block_with_early_return:: + } else { + eval_block_with_early_return:: + } +} + +/// Helper function to fetch `eval_expression()` with the correct type parameter based on whether +/// engine_state is configured with or without a debugger. +pub fn get_eval_expression(engine_state: &EngineState) -> EvalExpressionFn { + if engine_state.is_debugging() { + eval_expression:: + } else { + eval_expression:: + } +} + +/// Helper function to fetch `eval_expression_with_input()` with the correct type parameter based +/// on whether engine_state is configured with or without a debugger. +pub fn get_eval_expression_with_input(engine_state: &EngineState) -> EvalExpressionWithInputFn { + if engine_state.is_debugging() { + eval_expression_with_input:: + } else { + eval_expression_with_input:: + } +} + +/// Helper function to fetch `eval_subexpression()` with the correct type parameter based on whether +/// engine_state is configured with or without a debugger. +pub fn get_eval_subexpression(engine_state: &EngineState) -> EvalSubexpressionFn { + if engine_state.is_debugging() { + eval_subexpression:: + } else { + eval_subexpression:: + } +} diff --git a/crates/nu-engine/src/lib.rs b/crates/nu-engine/src/lib.rs index c761c97696c..f013babefea 100644 --- a/crates/nu-engine/src/lib.rs +++ b/crates/nu-engine/src/lib.rs @@ -3,6 +3,7 @@ pub mod column; pub mod documentation; pub mod env; mod eval; +mod eval_helpers; mod glob_from; pub mod scope; @@ -14,4 +15,5 @@ pub use eval::{ eval_block, eval_block_with_early_return, eval_call, eval_expression, eval_expression_with_input, eval_subexpression, eval_variable, redirect_env, }; +pub use eval_helpers::*; pub use glob_from::glob_from; diff --git a/crates/nu-explore/src/nu_common/command.rs b/crates/nu-explore/src/nu_common/command.rs index 8367ea6a5a0..eb4c5a51913 100644 --- a/crates/nu-explore/src/nu_common/command.rs +++ b/crates/nu-explore/src/nu_common/command.rs @@ -1,5 +1,6 @@ use nu_engine::eval_block; use nu_parser::parse; +use nu_protocol::debugger::WithoutDebug; use nu_protocol::{ engine::{EngineState, Stack, StateWorkingSet}, PipelineData, ShellError, Value, @@ -90,5 +91,5 @@ fn eval_source2( block.pipelines.drain(..block.pipelines.len() - 1); } - eval_block(engine_state, stack, &block, input, true, true) + eval_block::(engine_state, stack, &block, input, true, true) } diff --git a/crates/nu-plugin/src/plugin/declaration.rs b/crates/nu-plugin/src/plugin/declaration.rs index 8890b68e219..0afadb82d25 100644 --- a/crates/nu-plugin/src/plugin/declaration.rs +++ b/crates/nu-plugin/src/plugin/declaration.rs @@ -3,7 +3,8 @@ use crate::protocol::{CallInfo, EvaluatedCall}; use std::path::{Path, PathBuf}; use std::sync::Arc; -use nu_engine::eval_block; +use nu_engine::{get_eval_block, get_eval_expression}; + use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ast::Call, PluginSignature, Signature}; use nu_protocol::{Example, PipelineData, ShellError, Value}; @@ -71,9 +72,13 @@ impl Command for PluginDeclaration { call: &Call, input: PipelineData, ) -> Result { + let eval_block = get_eval_block(engine_state); + let eval_expression = get_eval_expression(engine_state); + // Create the EvaluatedCall to send to the plugin first - it's best for this to fail early, // before we actually try to run the plugin command - let evaluated_call = EvaluatedCall::try_from_call(call, engine_state, stack)?; + let evaluated_call = + EvaluatedCall::try_from_call(call, engine_state, stack, eval_expression)?; // Fetch the configuration for a plugin // diff --git a/crates/nu-plugin/src/protocol/evaluated_call.rs b/crates/nu-plugin/src/protocol/evaluated_call.rs index be15d4e2240..f2f9df0cc9b 100644 --- a/crates/nu-plugin/src/protocol/evaluated_call.rs +++ b/crates/nu-plugin/src/protocol/evaluated_call.rs @@ -1,4 +1,5 @@ -use nu_engine::eval_expression; +use nu_protocol::ast::Expression; + use nu_protocol::{ ast::Call, engine::{EngineState, Stack}, @@ -32,15 +33,16 @@ impl EvaluatedCall { call: &Call, engine_state: &EngineState, stack: &mut Stack, + eval_expression_fn: fn(&EngineState, &mut Stack, &Expression) -> Result, ) -> Result { let positional = - call.rest_iter_flattened(0, |expr| eval_expression(engine_state, stack, expr))?; + call.rest_iter_flattened(0, |expr| eval_expression_fn(engine_state, stack, expr))?; let mut named = Vec::with_capacity(call.named_len()); for (string, _, expr) in call.named_iter() { let value = match expr { None => None, - Some(expr) => Some(eval_expression(engine_state, stack, expr)?), + Some(expr) => Some(eval_expression_fn(engine_state, stack, expr)?), }; named.push((string.clone(), value)) diff --git a/crates/nu-protocol/src/debugger/debugger_trait.rs b/crates/nu-protocol/src/debugger/debugger_trait.rs new file mode 100644 index 00000000000..9a003971483 --- /dev/null +++ b/crates/nu-protocol/src/debugger/debugger_trait.rs @@ -0,0 +1,149 @@ +//! Traits related to debugging +//! +//! The purpose of DebugContext is achieving static dispatch on `eval_xxx()` calls. +//! The main Debugger trait is intended to be used as a trait object. +//! +//! The debugging information is stored in `EngineState` as the `debugger` field storing a `Debugger` +//! trait object behind `Arc` and `Mutex`. To evaluate something (e.g., a block), first create a +//! `Debugger` trait object (such as the `Profiler`). Then, add it to engine state via +//! `engine_state.activate_debugger()`. This sets the internal state of EngineState to the debugging +//! mode and calls `Debugger::activate()`. Now, you can call `eval_xxx::()`. When you're +//! done, call `engine_state.deactivate_debugger()` which calls `Debugger::deactivate()`, sets the +//! EngineState into non-debugging mode, and returns the original mutated `Debugger` trait object. +//! (`NoopDebugger` is placed in its place inside `EngineState`.) After deactivating, you can call +//! `Debugger::report()` to get some output from the debugger, if necessary. + +use crate::ast::{Block, PipelineElement}; +use crate::engine::EngineState; +use crate::{PipelineData, ShellError, Span, Value}; +use std::fmt::Debug; +use std::ops::DerefMut; + +/// Trait used for static dispatch of `eval_xxx()` evaluator calls +/// +/// DebugContext implements the same interface as Debugger (except activate() and deactivate(). It +/// is intended to be implemented only by two structs +/// * WithDebug which calls down to the Debugger methods +/// * WithoutDebug with default implementation, i.e., empty calls to be optimized away +pub trait DebugContext: Clone + Copy + Debug { + /// Called when the evaluator enters a block + #[allow(unused_variables)] + fn enter_block(engine_state: &EngineState, block: &Block) {} + + /// Called when the evaluator leaves a block + #[allow(unused_variables)] + fn leave_block(engine_state: &EngineState, block: &Block) {} + + /// Called when the evaluator enters a pipeline element + #[allow(unused_variables)] + fn enter_element(engine_state: &EngineState, element: &PipelineElement) {} + + /// Called when the evaluator leaves a pipeline element + #[allow(unused_variables)] + fn leave_element( + engine_state: &EngineState, + element: &PipelineElement, + result: &Result<(PipelineData, bool), ShellError>, + ) { + } +} + +/// Marker struct signalizing that evaluation should use a Debugger +/// +/// Trait methods call to Debugger trait object inside the supplied EngineState. +#[derive(Clone, Copy, Debug)] +pub struct WithDebug; + +impl DebugContext for WithDebug { + fn enter_block(engine_state: &EngineState, block: &Block) { + if let Ok(mut debugger) = engine_state.debugger.lock() { + debugger.deref_mut().enter_block(engine_state, block); + } + } + + fn leave_block(engine_state: &EngineState, block: &Block) { + if let Ok(mut debugger) = engine_state.debugger.lock() { + debugger.deref_mut().leave_block(engine_state, block); + } + } + + fn enter_element(engine_state: &EngineState, element: &PipelineElement) { + if let Ok(mut debugger) = engine_state.debugger.lock() { + debugger.deref_mut().enter_element(engine_state, element); + } + } + + fn leave_element( + engine_state: &EngineState, + element: &PipelineElement, + result: &Result<(PipelineData, bool), ShellError>, + ) { + if let Ok(mut debugger) = engine_state.debugger.lock() { + debugger + .deref_mut() + .leave_element(engine_state, element, result); + } + } +} + +/// Marker struct signalizing that evaluation should NOT use a Debugger +/// +/// Trait methods are empty calls to be optimized away. +#[derive(Clone, Copy, Debug)] +pub struct WithoutDebug; + +impl DebugContext for WithoutDebug {} + +/// Debugger trait that every debugger needs to implement. +/// +/// By default, its methods are empty. Not every Debugger needs to implement all of them. +pub trait Debugger: Send + Debug { + /// Called by EngineState::activate_debugger(). + /// + /// Intended for initializing the debugger. + fn activate(&mut self) {} + + /// Called by EngineState::deactivate_debugger(). + /// + /// Intended for wrapping up the debugger after a debugging session before returning back to + /// normal evaluation without debugging. + fn deactivate(&mut self) {} + + /// Called when the evaluator enters a block + #[allow(unused_variables)] + fn enter_block(&mut self, engine_state: &EngineState, block: &Block) {} + + /// Called when the evaluator leaves a block + #[allow(unused_variables)] + fn leave_block(&mut self, engine_state: &EngineState, block: &Block) {} + + /// Called when the evaluator enters a pipeline element + #[allow(unused_variables)] + fn enter_element(&mut self, engine_state: &EngineState, pipeline_element: &PipelineElement) {} + + /// Called when the evaluator leaves a pipeline element + #[allow(unused_variables)] + fn leave_element( + &mut self, + engine_state: &EngineState, + element: &PipelineElement, + result: &Result<(PipelineData, bool), ShellError>, + ) { + } + + /// Create a final report as a Value + /// + /// Intended to be called after deactivate() + #[allow(unused_variables)] + fn report(&self, engine_state: &EngineState, debugger_span: Span) -> Result { + Ok(Value::nothing(debugger_span)) + } +} + +/// A debugger that does nothing +/// +/// Used as a placeholder debugger when not debugging. +#[derive(Debug)] +pub struct NoopDebugger; + +impl Debugger for NoopDebugger {} diff --git a/crates/nu-protocol/src/debugger/mod.rs b/crates/nu-protocol/src/debugger/mod.rs new file mode 100644 index 00000000000..03208fb3c77 --- /dev/null +++ b/crates/nu-protocol/src/debugger/mod.rs @@ -0,0 +1,5 @@ +pub mod debugger_trait; +pub mod profiler; + +pub use debugger_trait::*; +pub use profiler::*; diff --git a/crates/nu-protocol/src/debugger/profiler.rs b/crates/nu-protocol/src/debugger/profiler.rs new file mode 100644 index 00000000000..d52d8704e42 --- /dev/null +++ b/crates/nu-protocol/src/debugger/profiler.rs @@ -0,0 +1,360 @@ +//! Nushell Profiler +//! +//! Profiler implements the Debugger trait and can be used via the `debug profile` command for +//! profiling Nushell code. + +use crate::ast::{Block, Expr, PipelineElement}; +use crate::debugger::Debugger; +use crate::engine::EngineState; +use crate::record; +use crate::{PipelineData, ShellError, Span, Value}; +use std::time::Instant; + +#[derive(Debug, Clone, Copy)] +struct ElementId(usize); + +/// Stores profiling information about one pipeline element +#[derive(Debug, Clone)] +struct ElementInfo { + start: Instant, + duration_sec: f64, + depth: i64, + element_span: Span, + element_output: Option, + expr: Option, + children: Vec, +} + +impl ElementInfo { + pub fn new(depth: i64, element_span: Span) -> Self { + ElementInfo { + start: Instant::now(), + duration_sec: 0.0, + depth, + element_span, + element_output: None, + expr: None, + children: vec![], + } + } +} + +/// Basic profiler, used in `debug profile` +#[derive(Debug, Clone)] +pub struct Profiler { + depth: i64, + max_depth: i64, + collect_spans: bool, + collect_source: bool, + collect_expanded_source: bool, + collect_values: bool, + collect_exprs: bool, + elements: Vec, + element_stack: Vec, +} + +impl Profiler { + pub fn new( + max_depth: i64, + collect_spans: bool, + collect_source: bool, + collect_expanded_source: bool, + collect_values: bool, + collect_exprs: bool, + span: Span, + ) -> Self { + let first = ElementInfo { + start: Instant::now(), + duration_sec: 0.0, + depth: 0, + element_span: span, + element_output: if collect_values { + Some(Value::nothing(span)) + } else { + None + }, + expr: if collect_exprs { + Some("call".to_string()) + } else { + None + }, + children: vec![], + }; + + Profiler { + depth: 0, + max_depth, + collect_spans, + collect_source, + collect_expanded_source, + collect_values, + collect_exprs, + elements: vec![first], + element_stack: vec![ElementId(0)], + } + } + + fn last_element_id(&self) -> Option { + self.element_stack.last().copied() + } + + fn last_element_mut(&mut self) -> Option<&mut ElementInfo> { + self.last_element_id() + .and_then(|id| self.elements.get_mut(id.0)) + } +} + +impl Debugger for Profiler { + fn activate(&mut self) { + let Some(root_element) = self.last_element_mut() else { + eprintln!("Profiler Error: Missing root element."); + return; + }; + + root_element.start = Instant::now(); + } + + fn deactivate(&mut self) { + let Some(root_element) = self.last_element_mut() else { + eprintln!("Profiler Error: Missing root element."); + return; + }; + + root_element.duration_sec = root_element.start.elapsed().as_secs_f64(); + } + + fn enter_block(&mut self, _engine_state: &EngineState, _block: &Block) { + self.depth += 1; + } + + fn leave_block(&mut self, _engine_state: &EngineState, _block: &Block) { + self.depth -= 1; + } + + fn enter_element(&mut self, engine_state: &EngineState, element: &PipelineElement) { + if self.depth > self.max_depth { + return; + } + + let Some(parent_id) = self.last_element_id() else { + eprintln!("Profiler Error: Missing parent element ID."); + return; + }; + + let expr_opt = if self.collect_exprs { + Some(match element { + PipelineElement::Expression(_, expression) => { + expr_to_string(engine_state, &expression.expr) + } + _ => "other".to_string(), + }) + } else { + None + }; + + let new_id = ElementId(self.elements.len()); + + let mut new_element = ElementInfo::new(self.depth, element.span()); + new_element.expr = expr_opt; + + self.elements.push(new_element); + + let Some(parent) = self.elements.get_mut(parent_id.0) else { + eprintln!("Profiler Error: Missing parent element."); + return; + }; + + parent.children.push(new_id); + self.element_stack.push(new_id); + } + + fn leave_element( + &mut self, + _engine_state: &EngineState, + element: &PipelineElement, + result: &Result<(PipelineData, bool), ShellError>, + ) { + if self.depth > self.max_depth { + return; + } + + let element_span = element.span(); + + let out_opt = if self.collect_values { + Some(match result { + Ok((pipeline_data, _not_sure_what_this_is)) => match pipeline_data { + PipelineData::Value(val, ..) => val.clone(), + PipelineData::ListStream(..) => Value::string("list stream", element_span), + PipelineData::ExternalStream { .. } => { + Value::string("external stream", element_span) + } + _ => Value::nothing(element_span), + }, + Err(e) => Value::error(e.clone(), element_span), + }) + } else { + None + }; + + let Some(last_element) = self.last_element_mut() else { + eprintln!("Profiler Error: Missing last element."); + return; + }; + + last_element.duration_sec = last_element.start.elapsed().as_secs_f64(); + last_element.element_output = out_opt; + + self.element_stack.pop(); + } + + fn report(&self, engine_state: &EngineState, profiler_span: Span) -> Result { + Ok(Value::list( + collect_data( + engine_state, + self, + ElementId(0), + ElementId(0), + profiler_span, + )?, + profiler_span, + )) + } +} + +fn profiler_error(msg: impl Into, span: Span) -> ShellError { + ShellError::GenericError { + error: "Profiler Error".to_string(), + msg: msg.into(), + span: Some(span), + help: None, + inner: vec![], + } +} + +fn expr_to_string(engine_state: &EngineState, expr: &Expr) -> String { + match expr { + Expr::Binary(_) => "binary".to_string(), + Expr::BinaryOp(_, _, _) => "binary operation".to_string(), + Expr::Block(_) => "block".to_string(), + Expr::Bool(_) => "bool".to_string(), + Expr::Call(call) => { + let decl = engine_state.get_decl(call.decl_id); + if decl.name() == "collect" && call.head == Span::new(0, 0) { + "call (implicit collect)" + } else { + "call" + } + .to_string() + } + Expr::CellPath(_) => "cell path".to_string(), + Expr::Closure(_) => "closure".to_string(), + Expr::DateTime(_) => "datetime".to_string(), + Expr::Directory(_, _) => "directory".to_string(), + Expr::ExternalCall(_, _, _) => "external call".to_string(), + Expr::Filepath(_, _) => "filepath".to_string(), + Expr::Float(_) => "float".to_string(), + Expr::FullCellPath(full_cell_path) => { + let head = expr_to_string(engine_state, &full_cell_path.head.expr); + format!("full cell path ({head})") + } + Expr::Garbage => "garbage".to_string(), + Expr::GlobPattern(_, _) => "glob pattern".to_string(), + Expr::ImportPattern(_) => "import pattern".to_string(), + Expr::Int(_) => "int".to_string(), + Expr::Keyword(_, _, _) => "keyword".to_string(), + Expr::List(_) => "list".to_string(), + Expr::MatchBlock(_) => "match block".to_string(), + Expr::Nothing => "nothing".to_string(), + Expr::Operator(_) => "operator".to_string(), + Expr::Overlay(_) => "overlay".to_string(), + Expr::Range(_, _, _, _) => "range".to_string(), + Expr::Record(_) => "record".to_string(), + Expr::RowCondition(_) => "row condition".to_string(), + Expr::Signature(_) => "signature".to_string(), + Expr::Spread(_) => "spread".to_string(), + Expr::String(_) => "string".to_string(), + Expr::StringInterpolation(_) => "string interpolation".to_string(), + Expr::Subexpression(_) => "subexpression".to_string(), + Expr::Table(_, _) => "table".to_string(), + Expr::UnaryNot(_) => "unary not".to_string(), + Expr::ValueWithUnit(_, _) => "value with unit".to_string(), + Expr::Var(_) => "var".to_string(), + Expr::VarDecl(_) => "var decl".to_string(), + } +} + +fn collect_data( + engine_state: &EngineState, + profiler: &Profiler, + element_id: ElementId, + parent_id: ElementId, + profiler_span: Span, +) -> Result, ShellError> { + let element = &profiler.elements[element_id.0]; + + let mut row = record! { + "depth" => Value::int(element.depth, profiler_span), + "id" => Value::int(element_id.0 as i64, profiler_span), + "parent_id" => Value::int(parent_id.0 as i64, profiler_span), + }; + + if profiler.collect_spans { + let span_start = i64::try_from(element.element_span.start) + .map_err(|_| profiler_error("error converting span start to i64", profiler_span))?; + let span_end = i64::try_from(element.element_span.end) + .map_err(|_| profiler_error("error converting span end to i64", profiler_span))?; + + row.push( + "span", + Value::record( + record! { + "start" => Value::int(span_start, profiler_span), + "end" => Value::int(span_end, profiler_span), + }, + profiler_span, + ), + ); + } + + if profiler.collect_source { + let val = String::from_utf8_lossy(engine_state.get_span_contents(element.element_span)); + let val = val.trim(); + let nlines = val.lines().count(); + + let fragment = if profiler.collect_expanded_source { + val.to_string() + } else { + let mut first_line = val.lines().next().unwrap_or("").to_string(); + + if nlines > 1 { + first_line.push_str(" ..."); + } + + first_line + }; + + row.push("source", Value::string(fragment, profiler_span)); + } + + if let Some(expr_string) = &element.expr { + row.push("expr", Value::string(expr_string.clone(), profiler_span)); + } + + if let Some(val) = &element.element_output { + row.push("output", val.clone()); + } + + row.push( + "duration_ms", + Value::float(element.duration_sec * 1e3, profiler_span), + ); + + let mut rows = vec![Value::record(row, profiler_span)]; + + for child in &element.children { + let child_rows = collect_data(engine_state, profiler, *child, element_id, profiler_span)?; + rows.extend(child_rows); + } + + Ok(rows) +} diff --git a/crates/nu-protocol/src/engine/engine_state.rs b/crates/nu-protocol/src/engine/engine_state.rs index a904467c5da..90157caed01 100644 --- a/crates/nu-protocol/src/engine/engine_state.rs +++ b/crates/nu-protocol/src/engine/engine_state.rs @@ -4,6 +4,7 @@ use lru::LruCache; use super::{usage::build_usage, usage::Usage, StateDelta}; use super::{Command, EnvVars, OverlayFrame, ScopeFrame, Stack, Visibility, DEFAULT_OVERLAY_NAME}; use crate::ast::Block; +use crate::debugger::{Debugger, NoopDebugger}; use crate::{ BlockId, Config, DeclId, Example, FileId, HistoryConfig, Module, ModuleId, OverlayId, ShellError, Signature, Span, Type, VarId, Variable, VirtualPathId, @@ -14,11 +15,14 @@ use std::collections::HashMap; use std::num::NonZeroUsize; use std::path::Path; use std::path::PathBuf; +use std::sync::atomic::Ordering; use std::sync::{ atomic::{AtomicBool, AtomicU32}, - Arc, Mutex, + Arc, Mutex, MutexGuard, PoisonError, }; +type PoisonDebuggerError<'a> = PoisonError>>; + pub static PWD_ENV: &str = "PWD"; #[derive(Clone, Debug)] @@ -33,6 +37,20 @@ pub struct ReplState { pub cursor_pos: usize, } +pub struct IsDebugging(AtomicBool); + +impl IsDebugging { + pub fn new(val: bool) -> Self { + IsDebugging(AtomicBool::new(val)) + } +} + +impl Clone for IsDebugging { + fn clone(&self) -> Self { + IsDebugging(AtomicBool::new(self.0.load(Ordering::Relaxed))) + } +} + /// The core global engine state. This includes all global definitions as well as any global state that /// will persist for the whole session. /// @@ -104,6 +122,8 @@ pub struct EngineState { pub is_interactive: bool, pub is_login: bool, startup_time: i64, + is_debugging: IsDebugging, + pub debugger: Arc>>, } // The max number of compiled regexes to keep around in a LRU cache, arbitrarily chosen @@ -161,6 +181,8 @@ impl EngineState { is_interactive: false, is_login: false, startup_time: -1, + is_debugging: IsDebugging::new(false), + debugger: Arc::new(Mutex::new(Box::new(NoopDebugger))), } } @@ -916,6 +938,29 @@ impl EngineState { self.startup_time = startup_time; } + pub fn activate_debugger( + &self, + debugger: Box, + ) -> Result<(), PoisonDebuggerError> { + let mut locked_debugger = self.debugger.lock()?; + *locked_debugger = debugger; + locked_debugger.activate(); + self.is_debugging.0.store(true, Ordering::Relaxed); + Ok(()) + } + + pub fn deactivate_debugger(&self) -> Result, PoisonDebuggerError> { + let mut locked_debugger = self.debugger.lock()?; + locked_debugger.deactivate(); + let ret = std::mem::replace(&mut *locked_debugger, Box::new(NoopDebugger)); + self.is_debugging.0.store(false, Ordering::Relaxed); + Ok(ret) + } + + pub fn is_debugging(&self) -> bool { + self.is_debugging.0.load(Ordering::Relaxed) + } + pub fn recover_from_panic(&mut self) { if Mutex::is_poisoned(&self.repl_state) { self.repl_state = Arc::new(Mutex::new(ReplState { diff --git a/crates/nu-protocol/src/eval_base.rs b/crates/nu-protocol/src/eval_base.rs index 7b0492fb8b0..80230733a74 100644 --- a/crates/nu-protocol/src/eval_base.rs +++ b/crates/nu-protocol/src/eval_base.rs @@ -1,3 +1,4 @@ +use crate::debugger::DebugContext; use crate::{ ast::{ eval_operator, Assignment, Bits, Boolean, Call, Comparison, Expr, Expression, @@ -5,6 +6,7 @@ use crate::{ }, Config, IntoInterruptiblePipelineData, Range, Record, ShellError, Span, Value, VarId, }; + use std::{borrow::Cow, collections::HashMap}; /// To share implementations for regular eval and const eval @@ -17,7 +19,7 @@ pub trait Eval { /// This is the stack for regular eval, and unused by const eval type MutState; - fn eval( + fn eval( state: Self::State<'_>, mut_state: &mut Self::MutState, expr: &Expression, @@ -34,7 +36,7 @@ pub trait Eval { Expr::Var(var_id) => Self::eval_var(state, mut_state, *var_id, expr.span), Expr::CellPath(cell_path) => Ok(Value::cell_path(cell_path.clone(), expr.span)), Expr::FullCellPath(cell_path) => { - let value = Self::eval(state, mut_state, &cell_path.head)?; + let value = Self::eval::(state, mut_state, &cell_path.head)?; value.follow_cell_path(&cell_path.tail, false) } @@ -43,11 +45,11 @@ pub trait Eval { let mut output = vec![]; for expr in x { match &expr.expr { - Expr::Spread(expr) => match Self::eval(state, mut_state, expr)? { + Expr::Spread(expr) => match Self::eval::(state, mut_state, expr)? { Value::List { mut vals, .. } => output.append(&mut vals), _ => return Err(ShellError::CannotSpreadAsList { span: expr.span }), }, - _ => output.push(Self::eval(state, mut_state, expr)?), + _ => output.push(Self::eval::(state, mut_state, expr)?), } } Ok(Value::list(output, expr.span)) @@ -59,7 +61,7 @@ pub trait Eval { match item { RecordItem::Pair(col, val) => { // avoid duplicate cols - let col_name = Self::eval(state, mut_state, col)?.coerce_into_string()?; + let col_name = Self::eval::(state, mut_state, col)?.coerce_into_string()?; if let Some(orig_span) = col_names.get(&col_name) { return Err(ShellError::ColumnDefinedTwice { col_name, @@ -68,11 +70,11 @@ pub trait Eval { }); } else { col_names.insert(col_name.clone(), col.span); - record.push(col_name, Self::eval(state, mut_state, val)?); + record.push(col_name, Self::eval::(state, mut_state, val)?); } } RecordItem::Spread(_, inner) => { - match Self::eval(state, mut_state, inner)? { + match Self::eval::(state, mut_state, inner)? { Value::Record { val: inner_val, .. } => { for (col_name, val) in inner_val { if let Some(orig_span) = col_names.get(&col_name) { @@ -102,7 +104,7 @@ pub trait Eval { Expr::Table(headers, vals) => { let mut output_headers = vec![]; for expr in headers { - let header = Self::eval(state, mut_state, expr)?.coerce_into_string()?; + let header = Self::eval::(state, mut_state, expr)?.coerce_into_string()?; if let Some(idx) = output_headers .iter() .position(|existing| existing == &header) @@ -120,7 +122,7 @@ pub trait Eval { let mut output_rows = vec![]; for val in vals { let record = output_headers.iter().zip(val).map(|(col, expr)| { - Self::eval(state, mut_state, expr).map(|val| (col.clone(), val)) + Self::eval::(state, mut_state, expr).map(|val| (col.clone(), val)) }).collect::>()?; output_rows.push(Value::record( @@ -130,10 +132,10 @@ pub trait Eval { } Ok(Value::list(output_rows, expr.span)) } - Expr::Keyword(_, _, expr) => Self::eval(state, mut_state, expr), + Expr::Keyword(_, _, expr) => Self::eval::(state, mut_state, expr), Expr::String(s) => Ok(Value::string(s.clone(), expr.span)), Expr::Nothing => Ok(Value::nothing(expr.span)), - Expr::ValueWithUnit(e, unit) => match Self::eval(state, mut_state, e)? { + Expr::ValueWithUnit(e, unit) => match Self::eval::(state, mut_state, e)? { Value::Int { val, .. } => unit.item.to_value(val, unit.span), x => Err(ShellError::CantConvert { to_type: "unit value".into(), @@ -142,28 +144,28 @@ pub trait Eval { help: None, }), }, - Expr::Call(call) => Self::eval_call(state, mut_state, call, expr.span), + Expr::Call(call) => Self::eval_call::(state, mut_state, call, expr.span), Expr::ExternalCall(head, args, is_subexpression) => { Self::eval_external_call(state, mut_state, head, args, *is_subexpression, expr.span) } Expr::Subexpression(block_id) => { - Self::eval_subexpression(state, mut_state, *block_id, expr.span) + Self::eval_subexpression::(state, mut_state, *block_id, expr.span) } Expr::Range(from, next, to, operator) => { let from = if let Some(f) = from { - Self::eval(state, mut_state, f)? + Self::eval::(state, mut_state, f)? } else { Value::nothing(expr.span) }; let next = if let Some(s) = next { - Self::eval(state, mut_state, s)? + Self::eval::(state, mut_state, s)? } else { Value::nothing(expr.span) }; let to = if let Some(t) = to { - Self::eval(state, mut_state, t)? + Self::eval::(state, mut_state, t)? } else { Value::nothing(expr.span) }; @@ -173,7 +175,7 @@ pub trait Eval { )) } Expr::UnaryNot(expr) => { - let lhs = Self::eval(state, mut_state, expr)?; + let lhs = Self::eval::(state, mut_state, expr)?; match lhs { Value::Bool { val, .. } => Ok(Value::bool(!val, expr.span)), other => Err(ShellError::TypeMismatch { @@ -188,13 +190,13 @@ pub trait Eval { match op { Operator::Boolean(boolean) => { - let lhs = Self::eval(state, mut_state, lhs)?; + let lhs = Self::eval::(state, mut_state, lhs)?; match boolean { Boolean::And => { if lhs.is_false() { Ok(Value::bool(false, expr.span)) } else { - let rhs = Self::eval(state, mut_state, rhs)?; + let rhs = Self::eval::(state, mut_state, rhs)?; lhs.and(op_span, &rhs, expr.span) } } @@ -202,19 +204,19 @@ pub trait Eval { if lhs.is_true() { Ok(Value::bool(true, expr.span)) } else { - let rhs = Self::eval(state, mut_state, rhs)?; + let rhs = Self::eval::(state, mut_state, rhs)?; lhs.or(op_span, &rhs, expr.span) } } Boolean::Xor => { - let rhs = Self::eval(state, mut_state, rhs)?; + let rhs = Self::eval::(state, mut_state, rhs)?; lhs.xor(op_span, &rhs, expr.span) } } } Operator::Math(math) => { - let lhs = Self::eval(state, mut_state, lhs)?; - let rhs = Self::eval(state, mut_state, rhs)?; + let lhs = Self::eval::(state, mut_state, lhs)?; + let rhs = Self::eval::(state, mut_state, rhs)?; match math { Math::Plus => lhs.add(op_span, &rhs, expr.span), @@ -228,8 +230,8 @@ pub trait Eval { } } Operator::Comparison(comparison) => { - let lhs = Self::eval(state, mut_state, lhs)?; - let rhs = Self::eval(state, mut_state, rhs)?; + let lhs = Self::eval::(state, mut_state, lhs)?; + let rhs = Self::eval::(state, mut_state, rhs)?; match comparison { Comparison::LessThan => lhs.lt(op_span, &rhs, expr.span), Comparison::LessThanOrEqual => lhs.lte(op_span, &rhs, expr.span), @@ -250,8 +252,8 @@ pub trait Eval { } } Operator::Bits(bits) => { - let lhs = Self::eval(state, mut_state, lhs)?; - let rhs = Self::eval(state, mut_state, rhs)?; + let lhs = Self::eval::(state, mut_state, lhs)?; + let rhs = Self::eval::(state, mut_state, rhs)?; match bits { Bits::BitAnd => lhs.bit_and(op_span, &rhs, expr.span), Bits::BitOr => lhs.bit_or(op_span, &rhs, expr.span), @@ -260,8 +262,8 @@ pub trait Eval { Bits::ShiftRight => lhs.bit_shr(op_span, &rhs, expr.span), } } - Operator::Assignment(assignment) => Self::eval_assignment( - state, mut_state, lhs, rhs, assignment, op_span, expr.span, + Operator::Assignment(assignment) => Self::eval_assignment::( + state, mut_state, lhs, rhs, assignment, op_span, expr.span ), } } @@ -272,7 +274,7 @@ pub trait Eval { Expr::StringInterpolation(exprs) => { let mut parts = vec![]; for expr in exprs { - parts.push(Self::eval(state, mut_state, expr)?); + parts.push(Self::eval::(state, mut_state, expr)?); } let config = Self::get_config(state, mut_state); @@ -324,7 +326,7 @@ pub trait Eval { span: Span, ) -> Result; - fn eval_call( + fn eval_call( state: Self::State<'_>, mut_state: &mut Self::MutState, call: &Call, @@ -340,7 +342,7 @@ pub trait Eval { span: Span, ) -> Result; - fn eval_subexpression( + fn eval_subexpression( state: Self::State<'_>, mut_state: &mut Self::MutState, block_id: usize, @@ -356,7 +358,8 @@ pub trait Eval { expr_span: Span, ) -> Result; - fn eval_assignment( + #[allow(clippy::too_many_arguments)] + fn eval_assignment( state: Self::State<'_>, mut_state: &mut Self::MutState, lhs: &Expression, diff --git a/crates/nu-protocol/src/eval_const.rs b/crates/nu-protocol/src/eval_const.rs index 0f1de11a5c1..bb5652cc6a2 100644 --- a/crates/nu-protocol/src/eval_const.rs +++ b/crates/nu-protocol/src/eval_const.rs @@ -1,3 +1,4 @@ +use crate::debugger::{DebugContext, WithoutDebug}; use crate::{ ast::{Assignment, Block, Call, Expr, Expression, ExternalArgument, PipelineElement}, engine::{EngineState, StateWorkingSet}, @@ -5,10 +6,8 @@ use crate::{ record, Config, HistoryFileFormat, PipelineData, Record, ShellError, Span, Value, VarId, }; use nu_system::os_info::{get_kernel_version, get_os_arch, get_os_family, get_os_name}; -use std::{ - borrow::Cow, - path::{Path, PathBuf}, -}; +use std::borrow::Cow; +use std::path::{Path, PathBuf}; pub fn create_nu_constant(engine_state: &EngineState, span: Span) -> Result { fn canonicalize_path(engine_state: &EngineState, path: &Path) -> PathBuf { @@ -257,7 +256,8 @@ pub fn eval_constant( working_set: &StateWorkingSet, expr: &Expression, ) -> Result { - ::eval(working_set, &mut (), expr) + // TODO: Allow debugging const eval + ::eval::(working_set, &mut (), expr) } struct EvalConst; @@ -303,12 +303,13 @@ impl Eval for EvalConst { } } - fn eval_call( + fn eval_call( working_set: &StateWorkingSet, _: &mut (), call: &Call, span: Span, ) -> Result { + // TODO: Allow debugging const eval // TODO: eval.rs uses call.head for the span rather than expr.span Ok(eval_const_call(working_set, call, PipelineData::empty())?.into_value(span)) } @@ -325,12 +326,13 @@ impl Eval for EvalConst { Err(ShellError::NotAConstant { span }) } - fn eval_subexpression( + fn eval_subexpression( working_set: &StateWorkingSet, _: &mut (), block_id: usize, span: Span, ) -> Result { + // TODO: Allow debugging const eval let block = working_set.get_block(block_id); Ok( eval_const_subexpression(working_set, block, PipelineData::empty(), span)? @@ -349,7 +351,7 @@ impl Eval for EvalConst { Err(ShellError::NotAConstant { span: expr_span }) } - fn eval_assignment( + fn eval_assignment( _: &StateWorkingSet, _: &mut (), _: &Expression, @@ -358,6 +360,7 @@ impl Eval for EvalConst { _op_span: Span, expr_span: Span, ) -> Result { + // TODO: Allow debugging const eval Err(ShellError::NotAConstant { span: expr_span }) } diff --git a/crates/nu-protocol/src/lib.rs b/crates/nu-protocol/src/lib.rs index 9b8083a2219..b95daaf5172 100644 --- a/crates/nu-protocol/src/lib.rs +++ b/crates/nu-protocol/src/lib.rs @@ -2,6 +2,7 @@ mod alias; pub mod ast; pub mod cli_error; pub mod config; +pub mod debugger; mod did_you_mean; pub mod engine; pub mod eval_base; diff --git a/crates/nu-std/src/lib.rs b/crates/nu-std/src/lib.rs index ea47f8ef042..0ed6876e3a0 100644 --- a/crates/nu-std/src/lib.rs +++ b/crates/nu-std/src/lib.rs @@ -2,6 +2,7 @@ use std::path::PathBuf; use nu_engine::{env::current_dir, eval_block}; use nu_parser::parse; +use nu_protocol::debugger::WithoutDebug; use nu_protocol::engine::{Stack, StateWorkingSet, VirtualPath}; use nu_protocol::{report_error, PipelineData}; @@ -92,7 +93,8 @@ use std pwd // We need to evaluate the module in order to run the `export-env` blocks. let mut stack = Stack::new(); let pipeline_data = PipelineData::Empty; - eval_block( + + eval_block::( engine_state, &mut stack, &block, diff --git a/src/test_bins.rs b/src/test_bins.rs index 23d5fd8fa91..c66340fc24c 100644 --- a/src/test_bins.rs +++ b/src/test_bins.rs @@ -1,6 +1,7 @@ use nu_cmd_base::hook::{eval_env_change_hook, eval_hook}; use nu_engine::eval_block; use nu_parser::parse; +use nu_protocol::debugger::WithoutDebug; use nu_protocol::engine::{EngineState, Stack, StateWorkingSet}; use nu_protocol::{CliError, PipelineData, Value}; use nu_std::load_standard_library; @@ -320,7 +321,7 @@ pub fn nu_repl() { let input = PipelineData::empty(); let config = engine_state.get_config(); - match eval_block(&engine_state, &mut stack, &block, input, false, false) { + match eval_block::(&engine_state, &mut stack, &block, input, false, false) { Ok(pipeline_data) => match pipeline_data.collect_string("", config) { Ok(s) => last_output = s, Err(err) => outcome_err(&engine_state, &err),