From 63433f1bc8d0d686a587ffdffcdca14aba5d21d1 Mon Sep 17 00:00:00 2001 From: JT <547158+jntrnr@users.noreply.github.com> Date: Thu, 10 Nov 2022 21:21:49 +1300 Subject: [PATCH] Split blocks and closures (#7075) * Split closures and blocks * Tests mostly working * finish last fixes, passes all tests * fmt --- crates/nu-cli/src/prompt_update.rs | 27 +- crates/nu-cli/src/reedline_config.rs | 8 +- crates/nu-cli/src/repl.rs | 36 +++ crates/nu-cli/src/syntax_highlight.rs | 1 + .../nu-command/src/charting/hashable_value.rs | 2 +- crates/nu-command/src/core_commands/def.rs | 6 +- .../nu-command/src/core_commands/def_env.rs | 6 +- crates/nu-command/src/core_commands/do_.rs | 6 +- .../src/core_commands/export_def.rs | 6 +- .../src/core_commands/export_def_env.rs | 6 +- crates/nu-command/src/core_commands/for_.rs | 10 +- crates/nu-command/src/core_commands/if_.rs | 17 +- crates/nu-command/src/core_commands/module.rs | 6 +- .../src/database/commands/into_sqlite.rs | 1 + crates/nu-command/src/env/export_env.rs | 6 +- crates/nu-command/src/env/with_env.rs | 6 +- crates/nu-command/src/example_test.rs | 41 ++- crates/nu-command/src/filesystem/watch.rs | 6 +- crates/nu-command/src/filters/all.rs | 4 +- crates/nu-command/src/filters/any.rs | 4 +- crates/nu-command/src/filters/collect.rs | 10 +- crates/nu-command/src/filters/each.rs | 12 +- crates/nu-command/src/filters/each_while.rs | 10 +- crates/nu-command/src/filters/find.rs | 2 + crates/nu-command/src/filters/group_by.rs | 6 +- crates/nu-command/src/filters/insert.rs | 4 +- crates/nu-command/src/filters/merge.rs | 4 +- crates/nu-command/src/filters/par_each.rs | 12 +- crates/nu-command/src/filters/reduce.rs | 8 +- .../nu-command/src/filters/skip/skip_until.rs | 4 +- .../nu-command/src/filters/skip/skip_while.rs | 4 +- .../nu-command/src/filters/take/take_until.rs | 4 +- .../nu-command/src/filters/take/take_while.rs | 4 +- crates/nu-command/src/filters/update.rs | 4 +- crates/nu-command/src/filters/update_cells.rs | 10 +- crates/nu-command/src/filters/upsert.rs | 4 +- crates/nu-command/src/filters/where_.rs | 13 +- crates/nu-command/src/formats/from/nuon.rs | 6 + crates/nu-command/src/formats/to/json.rs | 2 +- crates/nu-command/src/formats/to/nuon.rs | 4 + crates/nu-command/src/formats/to/text.rs | 1 + crates/nu-command/src/formats/to/toml.rs | 5 + crates/nu-command/src/formats/to/yaml.rs | 1 + crates/nu-command/src/system/benchmark.rs | 10 +- crates/nu-engine/src/eval.rs | 8 +- crates/nu-parser/src/flatten.rs | 5 +- crates/nu-parser/src/parser.rs | 260 ++++++++++++++---- crates/nu-parser/src/type_check.rs | 1 + crates/nu-parser/tests/test_parser.rs | 2 +- crates/nu-protocol/src/ast/expr.rs | 1 + crates/nu-protocol/src/ast/expression.rs | 59 ++++ .../nu-protocol/src/engine/capture_block.rs | 7 +- crates/nu-protocol/src/syntax_shape.rs | 11 +- crates/nu-protocol/src/ty.rs | 5 +- crates/nu-protocol/src/value/from_value.rs | 33 ++- crates/nu-protocol/src/value/mod.rs | 53 +++- src/tests/test_engine.rs | 2 +- 57 files changed, 576 insertions(+), 220 deletions(-) diff --git a/crates/nu-cli/src/prompt_update.rs b/crates/nu-cli/src/prompt_update.rs index c1ff23f1b..bead7e074 100644 --- a/crates/nu-cli/src/prompt_update.rs +++ b/crates/nu-cli/src/prompt_update.rs @@ -29,7 +29,7 @@ fn get_prompt_string( stack .get_env_var(engine_state, prompt) .and_then(|v| match v { - Value::Block { + Value::Closure { val: block_id, captures, .. @@ -59,6 +59,31 @@ fn get_prompt_string( } } } + Value::Block { val: block_id, .. } => { + let block = engine_state.get_block(block_id); + // Use eval_subexpression to force a redirection of output, so we can use everything in prompt + let ret_val = eval_subexpression( + engine_state, + stack, + block, + PipelineData::new(Span::new(0, 0)), // Don't try this at home, 0 span is ignored + ); + info!( + "get_prompt_string (block) {}:{}:{}", + file!(), + line!(), + column!() + ); + + match ret_val { + Ok(ret_val) => Some(ret_val), + Err(err) => { + let working_set = StateWorkingSet::new(engine_state); + report_error(&working_set, &err); + None + } + } + } Value::String { .. } => Some(PipelineData::Value(v.clone(), None)), _ => None, }) diff --git a/crates/nu-cli/src/reedline_config.rs b/crates/nu-cli/src/reedline_config.rs index 44d292770..61bd0acbb 100644 --- a/crates/nu-cli/src/reedline_config.rs +++ b/crates/nu-cli/src/reedline_config.rs @@ -251,7 +251,7 @@ pub(crate) fn add_columnar_menu( Value::Nothing { .. } => { Ok(line_editor.with_menu(ReedlineMenu::EngineCompleter(Box::new(columnar_menu)))) } - Value::Block { + Value::Closure { val, captures, span, @@ -337,7 +337,7 @@ pub(crate) fn add_list_menu( Value::Nothing { .. } => { Ok(line_editor.with_menu(ReedlineMenu::HistoryMenu(Box::new(list_menu)))) } - Value::Block { + Value::Closure { val, captures, span, @@ -459,7 +459,7 @@ pub(crate) fn add_description_menu( completer, })) } - Value::Block { + Value::Closure { val, captures, span, @@ -477,7 +477,7 @@ pub(crate) fn add_description_menu( })) } _ => Err(ShellError::UnsupportedConfigValue( - "block or omitted value".to_string(), + "closure or omitted value".to_string(), menu.source.into_abbreviated_string(config), menu.source.span()?, )), diff --git a/crates/nu-cli/src/repl.rs b/crates/nu-cli/src/repl.rs index 02f79343a..dbafd9eca 100644 --- a/crates/nu-cli/src/repl.rs +++ b/crates/nu-cli/src/repl.rs @@ -743,6 +743,11 @@ pub fn eval_hook( val: block_id, span: block_span, .. + } + | Value::Closure { + val: block_id, + span: block_span, + .. } => { match run_hook_block( engine_state, @@ -854,6 +859,20 @@ pub fn eval_hook( block_span, )?; } + Value::Closure { + val: block_id, + span: block_span, + .. + } => { + run_hook_block( + engine_state, + stack, + block_id, + input, + arguments, + block_span, + )?; + } other => { return Err(ShellError::UnsupportedConfigValue( "block or string".to_string(), @@ -881,6 +900,23 @@ pub fn eval_hook( None, ); } + Value::Closure { + val: block_id, + span: block_span, + .. + } => { + output = PipelineData::Value( + run_hook_block( + engine_state, + stack, + *block_id, + input, + arguments, + *block_span, + )?, + None, + ); + } other => { return Err(ShellError::UnsupportedConfigValue( "block, record, or list of records".into(), diff --git a/crates/nu-cli/src/syntax_highlight.rs b/crates/nu-cli/src/syntax_highlight.rs index e578b8c5b..fc5724797 100644 --- a/crates/nu-cli/src/syntax_highlight.rs +++ b/crates/nu-cli/src/syntax_highlight.rs @@ -372,6 +372,7 @@ fn find_matching_block_end_in_expr( } Expr::Block(block_id) + | Expr::Closure(block_id) | Expr::RowCondition(block_id) | Expr::Subexpression(block_id) => { if expr_last == global_cursor_offset { diff --git a/crates/nu-command/src/charting/hashable_value.rs b/crates/nu-command/src/charting/hashable_value.rs index 69e68ac35..5c81a84f4 100644 --- a/crates/nu-command/src/charting/hashable_value.rs +++ b/crates/nu-command/src/charting/hashable_value.rs @@ -228,7 +228,7 @@ mod test { vals: vec![Value::Bool { val: true, span }], span, }, - Value::Block { + Value::Closure { val: 0, captures: HashMap::new(), span, diff --git a/crates/nu-command/src/core_commands/def.rs b/crates/nu-command/src/core_commands/def.rs index f812efe0d..f60783f3a 100644 --- a/crates/nu-command/src/core_commands/def.rs +++ b/crates/nu-command/src/core_commands/def.rs @@ -19,11 +19,7 @@ impl Command for Def { .input_output_types(vec![(Type::Nothing, Type::Nothing)]) .required("def_name", SyntaxShape::String, "definition name") .required("params", SyntaxShape::Signature, "parameters") - .required( - "block", - SyntaxShape::Block(Some(vec![])), - "body of the definition", - ) + .required("body", SyntaxShape::Closure(None), "body of the definition") .category(Category::Core) } diff --git a/crates/nu-command/src/core_commands/def_env.rs b/crates/nu-command/src/core_commands/def_env.rs index 4cc0a668f..741dc08a8 100644 --- a/crates/nu-command/src/core_commands/def_env.rs +++ b/crates/nu-command/src/core_commands/def_env.rs @@ -19,11 +19,7 @@ impl Command for DefEnv { .input_output_types(vec![(Type::Nothing, Type::Nothing)]) .required("def_name", SyntaxShape::String, "definition name") .required("params", SyntaxShape::Signature, "parameters") - .required( - "block", - SyntaxShape::Block(Some(vec![])), - "body of the definition", - ) + .required("block", SyntaxShape::Block, "body of the definition") .category(Category::Core) } diff --git a/crates/nu-command/src/core_commands/do_.rs b/crates/nu-command/src/core_commands/do_.rs index 69efb4d6c..cefc84d94 100644 --- a/crates/nu-command/src/core_commands/do_.rs +++ b/crates/nu-command/src/core_commands/do_.rs @@ -1,6 +1,6 @@ use nu_engine::{eval_block, CallExt}; use nu_protocol::ast::Call; -use nu_protocol::engine::{CaptureBlock, Command, EngineState, Stack}; +use nu_protocol::engine::{Closure, Command, EngineState, Stack}; use nu_protocol::{ Category, Example, ListStream, PipelineData, RawStream, ShellError, Signature, SyntaxShape, Type, Value, @@ -20,8 +20,8 @@ impl Command for Do { fn signature(&self) -> nu_protocol::Signature { Signature::build("do") + .required("closure", SyntaxShape::Any, "the closure to run") .input_output_types(vec![(Type::Any, Type::Any)]) - .required("block", SyntaxShape::Any, "the block to run") .switch( "ignore-errors", "ignore shell errors as the block runs", @@ -43,7 +43,7 @@ impl Command for Do { call: &Call, input: PipelineData, ) -> Result { - let block: CaptureBlock = call.req(engine_state, stack, 0)?; + let block: Closure = call.req(engine_state, stack, 0)?; let rest: Vec = call.rest(engine_state, stack, 1)?; let ignore_errors = call.has_flag("ignore-errors"); let capture_errors = call.has_flag("capture-errors"); diff --git a/crates/nu-command/src/core_commands/export_def.rs b/crates/nu-command/src/core_commands/export_def.rs index dce5c81d7..64d4c13ad 100644 --- a/crates/nu-command/src/core_commands/export_def.rs +++ b/crates/nu-command/src/core_commands/export_def.rs @@ -19,11 +19,7 @@ impl Command for ExportDef { .input_output_types(vec![(Type::Nothing, Type::Nothing)]) .required("name", SyntaxShape::String, "definition name") .required("params", SyntaxShape::Signature, "parameters") - .required( - "block", - SyntaxShape::Block(Some(vec![])), - "body of the definition", - ) + .required("block", SyntaxShape::Block, "body of the definition") .category(Category::Core) } diff --git a/crates/nu-command/src/core_commands/export_def_env.rs b/crates/nu-command/src/core_commands/export_def_env.rs index 15ad86cdc..3d9519ebe 100644 --- a/crates/nu-command/src/core_commands/export_def_env.rs +++ b/crates/nu-command/src/core_commands/export_def_env.rs @@ -19,11 +19,7 @@ impl Command for ExportDefEnv { .input_output_types(vec![(Type::Nothing, Type::Nothing)]) .required("name", SyntaxShape::String, "definition name") .required("params", SyntaxShape::Signature, "parameters") - .required( - "block", - SyntaxShape::Block(Some(vec![])), - "body of the definition", - ) + .required("block", SyntaxShape::Block, "body of the definition") .category(Category::Core) } diff --git a/crates/nu-command/src/core_commands/for_.rs b/crates/nu-command/src/core_commands/for_.rs index b5c44ae75..158429c31 100644 --- a/crates/nu-command/src/core_commands/for_.rs +++ b/crates/nu-command/src/core_commands/for_.rs @@ -1,6 +1,6 @@ use nu_engine::{eval_block, eval_expression, CallExt}; use nu_protocol::ast::Call; -use nu_protocol::engine::{CaptureBlock, Command, EngineState, Stack}; +use nu_protocol::engine::{Closure, Command, EngineState, Stack}; use nu_protocol::{ Category, Example, IntoInterruptiblePipelineData, ListStream, PipelineData, Signature, Span, SyntaxShape, Type, Value, @@ -31,11 +31,7 @@ impl Command for For { SyntaxShape::Keyword(b"in".to_vec(), Box::new(SyntaxShape::Any)), "range of the loop", ) - .required( - "block", - SyntaxShape::Block(Some(vec![])), - "the block to run", - ) + .required("block", SyntaxShape::Block, "the block to run") .switch( "numbered", "returned a numbered item ($it.index and $it.item)", @@ -75,7 +71,7 @@ impl Command for For { .expect("internal error: missing keyword"); let values = eval_expression(engine_state, stack, keyword_expr)?; - let capture_block: CaptureBlock = call.req(engine_state, stack, 2)?; + let capture_block: Closure = call.req(engine_state, stack, 2)?; let numbered = call.has_flag("numbered"); diff --git a/crates/nu-command/src/core_commands/if_.rs b/crates/nu-command/src/core_commands/if_.rs index ebf975646..2e6580d73 100644 --- a/crates/nu-command/src/core_commands/if_.rs +++ b/crates/nu-command/src/core_commands/if_.rs @@ -1,8 +1,8 @@ use nu_engine::{eval_block, eval_expression, eval_expression_with_input, CallExt}; use nu_protocol::ast::Call; -use nu_protocol::engine::{CaptureBlock, Command, EngineState, Stack}; +use nu_protocol::engine::{Block, Command, EngineState, Stack}; use nu_protocol::{ - Category, Example, FromValue, PipelineData, ShellError, Signature, SyntaxShape, Type, Value, + Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type, Value, }; #[derive(Clone)] @@ -23,7 +23,7 @@ impl Command for If { .required("cond", SyntaxShape::Expression, "condition to check") .required( "then_block", - SyntaxShape::Block(Some(vec![])), + SyntaxShape::Block, "block to run if check succeeds", ) .optional( @@ -51,7 +51,7 @@ impl Command for If { input: PipelineData, ) -> Result { let cond = call.positional_nth(0).expect("checked through parser"); - let then_block: CaptureBlock = call.req(engine_state, stack, 1)?; + let then_block: Block = call.req(engine_state, stack, 1)?; let else_case = call.positional_nth(2); let result = eval_expression(engine_state, stack, cond)?; @@ -59,10 +59,9 @@ impl Command for If { Value::Bool { val, .. } => { if *val { let block = engine_state.get_block(then_block.block_id); - let mut stack = stack.captures_to_stack(&then_block.captures); eval_block( engine_state, - &mut stack, + stack, block, input, call.redirect_stdout, @@ -71,14 +70,10 @@ impl Command for If { } else if let Some(else_case) = else_case { if let Some(else_expr) = else_case.as_keyword() { if let Some(block_id) = else_expr.as_block() { - let result = eval_expression(engine_state, stack, else_expr)?; - let else_block: CaptureBlock = FromValue::from_value(&result)?; - - let mut stack = stack.captures_to_stack(&else_block.captures); let block = engine_state.get_block(block_id); eval_block( engine_state, - &mut stack, + stack, block, input, call.redirect_stdout, diff --git a/crates/nu-command/src/core_commands/module.rs b/crates/nu-command/src/core_commands/module.rs index c01dc30d1..de94f3518 100644 --- a/crates/nu-command/src/core_commands/module.rs +++ b/crates/nu-command/src/core_commands/module.rs @@ -18,11 +18,7 @@ impl Command for Module { Signature::build("module") .input_output_types(vec![(Type::Nothing, Type::Nothing)]) .required("module_name", SyntaxShape::String, "module name") - .required( - "block", - SyntaxShape::Block(Some(vec![])), - "body of the module", - ) + .required("block", SyntaxShape::Block, "body of the module") .category(Category::Core) } diff --git a/crates/nu-command/src/database/commands/into_sqlite.rs b/crates/nu-command/src/database/commands/into_sqlite.rs index 08b343831..59d63f7b3 100644 --- a/crates/nu-command/src/database/commands/into_sqlite.rs +++ b/crates/nu-command/src/database/commands/into_sqlite.rs @@ -263,6 +263,7 @@ fn nu_value_to_string(value: Value, separator: &str, config: &Config) -> String .collect::>() .join(separator), Value::Block { val, .. } => format!("", val), + Value::Closure { val, .. } => format!("", val), Value::Nothing { .. } => String::new(), Value::Error { error } => format!("{:?}", error), Value::Binary { val, .. } => format!("{:?}", val), diff --git a/crates/nu-command/src/env/export_env.rs b/crates/nu-command/src/env/export_env.rs index 88003495b..82260c656 100644 --- a/crates/nu-command/src/env/export_env.rs +++ b/crates/nu-command/src/env/export_env.rs @@ -1,7 +1,7 @@ use nu_engine::{eval_block, redirect_env, CallExt}; use nu_protocol::{ ast::Call, - engine::{CaptureBlock, Command, EngineState, Stack}, + engine::{Closure, Command, EngineState, Stack}, Category, Example, PipelineData, Signature, Span, SyntaxShape, Type, Value, }; @@ -18,7 +18,7 @@ impl Command for ExportEnv { .input_output_types(vec![(Type::Nothing, Type::Nothing)]) .required( "block", - SyntaxShape::Block(Some(vec![])), + SyntaxShape::Block, "the block to run to set the environment", ) .category(Category::Env) @@ -35,7 +35,7 @@ impl Command for ExportEnv { call: &Call, input: PipelineData, ) -> Result { - let capture_block: CaptureBlock = call.req(engine_state, caller_stack, 0)?; + let capture_block: Closure = call.req(engine_state, caller_stack, 0)?; let block = engine_state.get_block(capture_block.block_id); let mut callee_stack = caller_stack.captures_to_stack(&capture_block.captures); diff --git a/crates/nu-command/src/env/with_env.rs b/crates/nu-command/src/env/with_env.rs index a1f5b86e9..7c3b74e06 100644 --- a/crates/nu-command/src/env/with_env.rs +++ b/crates/nu-command/src/env/with_env.rs @@ -3,7 +3,7 @@ use std::collections::HashMap; use nu_engine::{eval_block, CallExt}; use nu_protocol::{ ast::Call, - engine::{CaptureBlock, Command, EngineState, Stack}, + engine::{Closure, Command, EngineState, Stack}, Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type, Value, }; @@ -25,7 +25,7 @@ impl Command for WithEnv { ) .required( "block", - SyntaxShape::Block(Some(vec![])), + SyntaxShape::Closure(None), "the block to run once the variable is set", ) .category(Category::Env) @@ -80,7 +80,7 @@ fn with_env( // let external_redirection = args.call_info.args.external_redirection; let variable: Value = call.req(engine_state, stack, 0)?; - let capture_block: CaptureBlock = call.req(engine_state, stack, 1)?; + let capture_block: Closure = call.req(engine_state, stack, 1)?; let block = engine_state.get_block(capture_block.block_id); let mut stack = stack.captures_to_stack(&capture_block.captures); diff --git a/crates/nu-command/src/example_test.rs b/crates/nu-command/src/example_test.rs index 549afd5a0..62aeb17a9 100644 --- a/crates/nu-command/src/example_test.rs +++ b/crates/nu-command/src/example_test.rs @@ -14,8 +14,6 @@ mod test_examples { }; use crate::To; use itertools::Itertools; - use nu_engine; - use nu_parser; use nu_protocol::{ ast::Block, engine::{Command, EngineState, Stack, StateDelta, StateWorkingSet}, @@ -95,7 +93,7 @@ mod test_examples { engine_state } - fn check_example_input_and_output_types_match_command_signature<'a>( + fn check_example_input_and_output_types_match_command_signature( example: &Example, cwd: &PathBuf, engine_state: &mut Box, @@ -108,7 +106,7 @@ mod test_examples { // Skip tests that don't have results to compare to if let Some(example_output) = example.result.as_ref() { if let Some(example_input_type) = - eval_pipeline_without_terminal_expression(example.example, &cwd, engine_state) + eval_pipeline_without_terminal_expression(example.example, cwd, engine_state) { let example_input_type = example_input_type.get_type(); let example_output_type = example_output.get_type(); @@ -177,19 +175,18 @@ mod test_examples { || example_matches_signature_via_vectorization_over_list || example_matches_signature_via_cell_path_operation) { - assert!( - false, - "The example `{}` demonstrates a transformation of type {:?} -> {:?}. \ - However, this does not match the declared signature: {:?}.{} \ - For this command, `vectorizes_over_list` is {} and `operates_on_cell_paths()` is {}.", - example.example, - example_input_type, - example_output_type, - signature_input_output_types, - if signature_input_output_types.is_empty() { " (Did you forget to declare the input and output types for the command?)" } else { "" }, - signature_vectorizes_over_list, - signature_operates_on_cell_paths - ); + panic!( + "The example `{}` demonstrates a transformation of type {:?} -> {:?}. \ + However, this does not match the declared signature: {:?}.{} \ + For this command, `vectorizes_over_list` is {} and `operates_on_cell_paths()` is {}.", + example.example, + example_input_type, + example_output_type, + signature_input_output_types, + if signature_input_output_types.is_empty() { " (Did you forget to declare the input and output types for the command?)" } else { "" }, + signature_vectorizes_over_list, + signature_operates_on_cell_paths + ); }; }; } @@ -217,7 +214,7 @@ mod test_examples { .expect("Error merging environment"); let empty_input = PipelineData::new(Span::test_data()); - let result = eval(example.example, empty_input, &cwd, engine_state); + let result = eval(example.example, empty_input, cwd, engine_state); // Note. Value implements PartialEq for Bool, Int, Float, String and Block // If the command you are testing requires to compare another case, then @@ -267,8 +264,8 @@ mod test_examples { eval_block(block, input, cwd, engine_state, delta) } - fn parse(contents: &str, engine_state: &Box) -> (Block, StateDelta) { - let mut working_set = StateWorkingSet::new(&*engine_state); + fn parse(contents: &str, engine_state: &EngineState) -> (Block, StateDelta) { + let mut working_set = StateWorkingSet::new(engine_state); let (output, err) = nu_parser::parse(&mut working_set, None, contents.as_bytes(), false, &[]); @@ -300,7 +297,7 @@ mod test_examples { }, ); - 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()), } @@ -319,7 +316,7 @@ mod test_examples { if !block.pipelines[0].expressions.is_empty() { let empty_input = PipelineData::new(Span::test_data()); - Some(eval_block(block, empty_input, &cwd, engine_state, delta)) + Some(eval_block(block, empty_input, cwd, engine_state, delta)) } else { Some(Value::nothing(Span::test_data())) } diff --git a/crates/nu-command/src/filesystem/watch.rs b/crates/nu-command/src/filesystem/watch.rs index e6162ee37..fb5c8bf2e 100644 --- a/crates/nu-command/src/filesystem/watch.rs +++ b/crates/nu-command/src/filesystem/watch.rs @@ -6,7 +6,7 @@ use std::time::Duration; use notify::{DebouncedEvent, RecommendedWatcher, RecursiveMode, Watcher}; use nu_engine::{current_dir, eval_block, CallExt}; use nu_protocol::ast::Call; -use nu_protocol::engine::{CaptureBlock, Command, EngineState, Stack, StateWorkingSet}; +use nu_protocol::engine::{Closure, Command, EngineState, Stack, StateWorkingSet}; use nu_protocol::{ format_error, Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Spanned, SyntaxShape, Value, @@ -35,7 +35,7 @@ impl Command for Watch { fn signature(&self) -> nu_protocol::Signature { Signature::build("watch") .required("path", SyntaxShape::Filepath, "the path to watch. Can be a file or directory") - .required("block", SyntaxShape::Block(None), "A Nu block of code to run whenever a file changes. The block will be passed `operation`, `path`, and `new_path` (for renames only) arguments in that order") + .required("block", SyntaxShape::Block, "A Nu block of code to run whenever a file changes. The block will be passed `operation`, `path`, and `new_path` (for renames only) arguments in that order") .named( "debounce-ms", SyntaxShape::Int, @@ -82,7 +82,7 @@ impl Command for Watch { } }; - let capture_block: CaptureBlock = call.req(engine_state, stack, 1)?; + let capture_block: Closure = call.req(engine_state, stack, 1)?; let block = engine_state .clone() .get_block(capture_block.block_id) diff --git a/crates/nu-command/src/filters/all.rs b/crates/nu-command/src/filters/all.rs index ce6ab9c83..f32f0f1f7 100644 --- a/crates/nu-command/src/filters/all.rs +++ b/crates/nu-command/src/filters/all.rs @@ -1,7 +1,7 @@ use nu_engine::{eval_block, CallExt}; use nu_protocol::{ ast::Call, - engine::{CaptureBlock, Command, EngineState, Stack}, + engine::{Closure, Command, EngineState, Stack}, Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, SyntaxShape, Type, Value, }; @@ -68,7 +68,7 @@ impl Command for All { ) -> Result { let span = call.head; - let capture_block: CaptureBlock = call.req(engine_state, stack, 0)?; + let capture_block: Closure = call.req(engine_state, stack, 0)?; let block_id = capture_block.block_id; let block = engine_state.get_block(block_id); diff --git a/crates/nu-command/src/filters/any.rs b/crates/nu-command/src/filters/any.rs index 62654f343..df83ed3b2 100644 --- a/crates/nu-command/src/filters/any.rs +++ b/crates/nu-command/src/filters/any.rs @@ -1,7 +1,7 @@ use nu_engine::{eval_block, CallExt}; use nu_protocol::{ ast::Call, - engine::{CaptureBlock, Command, EngineState, Stack}, + engine::{Closure, Command, EngineState, Stack}, Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, SyntaxShape, Type, Value, }; @@ -67,7 +67,7 @@ impl Command for Any { ) -> Result { let span = call.head; - let capture_block: CaptureBlock = call.req(engine_state, stack, 0)?; + let capture_block: Closure = call.req(engine_state, stack, 0)?; let block_id = capture_block.block_id; let block = engine_state.get_block(block_id); diff --git a/crates/nu-command/src/filters/collect.rs b/crates/nu-command/src/filters/collect.rs index f994fed71..33067529f 100644 --- a/crates/nu-command/src/filters/collect.rs +++ b/crates/nu-command/src/filters/collect.rs @@ -1,6 +1,6 @@ use nu_engine::{eval_block, redirect_env, CallExt}; use nu_protocol::ast::Call; -use nu_protocol::engine::{CaptureBlock, Command, EngineState, Stack}; +use nu_protocol::engine::{Closure, Command, EngineState, Stack}; use nu_protocol::{ Category, Example, IntoPipelineData, PipelineData, Signature, SyntaxShape, Type, Value, }; @@ -17,9 +17,9 @@ impl Command for Collect { Signature::build("collect") .input_output_types(vec![(Type::List(Box::new(Type::Any)), Type::Any)]) .required( - "block", - SyntaxShape::Block(Some(vec![SyntaxShape::Any])), - "the block to run once the stream is collected", + "closure", + SyntaxShape::Closure(Some(vec![SyntaxShape::Any])), + "the closure to run once the stream is collected", ) .switch( "keep-env", @@ -40,7 +40,7 @@ impl Command for Collect { call: &Call, input: PipelineData, ) -> Result { - let capture_block: CaptureBlock = call.req(engine_state, stack, 0)?; + let capture_block: Closure = call.req(engine_state, stack, 0)?; let block = engine_state.get_block(capture_block.block_id).clone(); let mut stack_captures = stack.captures_to_stack(&capture_block.captures); diff --git a/crates/nu-command/src/filters/each.rs b/crates/nu-command/src/filters/each.rs index 1fb9b1ea5..572e62053 100644 --- a/crates/nu-command/src/filters/each.rs +++ b/crates/nu-command/src/filters/each.rs @@ -1,7 +1,7 @@ use super::utils::chain_error_with_input; use nu_engine::{eval_block, CallExt}; use nu_protocol::ast::Call; -use nu_protocol::engine::{CaptureBlock, Command, EngineState, Stack}; +use nu_protocol::engine::{Closure, Command, EngineState, Stack}; use nu_protocol::{ Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, Signature, Span, SyntaxShape, Type, Value, @@ -16,7 +16,7 @@ impl Command for Each { } fn usage(&self) -> &str { - "Run a block on each row of input" + "Run a closure on each row of input" } fn extra_usage(&self) -> &str { @@ -40,9 +40,9 @@ with 'transpose' first."# Type::List(Box::new(Type::Any)), )]) .required( - "block", - SyntaxShape::Block(Some(vec![SyntaxShape::Any])), - "the block to run", + "closure", + SyntaxShape::Closure(Some(vec![SyntaxShape::Any])), + "the closure to run", ) .switch("keep-empty", "keep empty result cells", Some('k')) .switch("numbered", "iterate with an index", Some('n')) @@ -127,7 +127,7 @@ with 'transpose' first."# call: &Call, input: PipelineData, ) -> Result { - let capture_block: CaptureBlock = call.req(engine_state, stack, 0)?; + let capture_block: Closure = call.req(engine_state, stack, 0)?; let numbered = call.has_flag("numbered"); let keep_empty = call.has_flag("keep-empty"); diff --git a/crates/nu-command/src/filters/each_while.rs b/crates/nu-command/src/filters/each_while.rs index 42c4b8cda..dcf07aac5 100644 --- a/crates/nu-command/src/filters/each_while.rs +++ b/crates/nu-command/src/filters/each_while.rs @@ -1,6 +1,6 @@ use nu_engine::{eval_block, CallExt}; use nu_protocol::ast::Call; -use nu_protocol::engine::{CaptureBlock, Command, EngineState, Stack}; +use nu_protocol::engine::{Closure, Command, EngineState, Stack}; use nu_protocol::{ Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, Signature, Span, SyntaxShape, Type, Value, @@ -29,9 +29,9 @@ impl Command for EachWhile { Type::List(Box::new(Type::Any)), )]) .required( - "block", - SyntaxShape::Block(Some(vec![SyntaxShape::Any])), - "the block to run", + "closure", + SyntaxShape::Closure(Some(vec![SyntaxShape::Any])), + "the closure to run", ) .switch("numbered", "iterate with an index", Some('n')) .category(Category::Filters) @@ -97,7 +97,7 @@ impl Command for EachWhile { call: &Call, input: PipelineData, ) -> Result { - let capture_block: CaptureBlock = call.req(engine_state, stack, 0)?; + let capture_block: Closure = call.req(engine_state, stack, 0)?; let numbered = call.has_flag("numbered"); let metadata = input.metadata(); diff --git a/crates/nu-command/src/filters/find.rs b/crates/nu-command/src/filters/find.rs index a3cd41925..9a72d7b33 100644 --- a/crates/nu-command/src/filters/find.rs +++ b/crates/nu-command/src/filters/find.rs @@ -354,6 +354,7 @@ fn find_with_rest_and_highlight( | Value::Range { .. } | Value::Float { .. } | Value::Block { .. } + | Value::Closure { .. } | Value::Nothing { .. } | Value::Error { .. } => lower_value .eq(span, term, span) @@ -413,6 +414,7 @@ fn find_with_rest_and_highlight( | Value::Range { .. } | Value::Float { .. } | Value::Block { .. } + | Value::Closure { .. } | Value::Nothing { .. } | Value::Error { .. } => lower_value .eq(span, term, span) diff --git a/crates/nu-command/src/filters/group_by.rs b/crates/nu-command/src/filters/group_by.rs index 49d2364a0..477139e84 100644 --- a/crates/nu-command/src/filters/group_by.rs +++ b/crates/nu-command/src/filters/group_by.rs @@ -1,6 +1,6 @@ use nu_engine::{eval_block, CallExt}; use nu_protocol::ast::Call; -use nu_protocol::engine::{CaptureBlock, Command, EngineState, Stack}; +use nu_protocol::engine::{Closure, Command, EngineState, Stack}; use nu_protocol::{ Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value, @@ -117,8 +117,8 @@ pub fn group_by( }; match grouper { - Some(Value::Block { .. }) => { - let block: Option = call.opt(engine_state, stack, 0)?; + Some(Value::Block { .. }) | Some(Value::Closure { .. }) => { + let block: Option = call.opt(engine_state, stack, 0)?; let error_key = "error"; for value in values { diff --git a/crates/nu-command/src/filters/insert.rs b/crates/nu-command/src/filters/insert.rs index 6c07b15d8..f652a813c 100644 --- a/crates/nu-command/src/filters/insert.rs +++ b/crates/nu-command/src/filters/insert.rs @@ -1,6 +1,6 @@ use nu_engine::{eval_block, CallExt}; use nu_protocol::ast::{Call, CellPath, PathMember}; -use nu_protocol::engine::{CaptureBlock, Command, EngineState, Stack}; +use nu_protocol::engine::{Closure, Command, EngineState, Stack}; use nu_protocol::{ Category, Example, FromValue, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value, @@ -91,7 +91,7 @@ fn insert( // Replace is a block, so set it up and run it instead of using it as the replacement if replacement.as_block().is_ok() { - let capture_block: CaptureBlock = FromValue::from_value(&replacement)?; + let capture_block: Closure = FromValue::from_value(&replacement)?; let block = engine_state.get_block(capture_block.block_id).clone(); let mut stack = stack.captures_to_stack(&capture_block.captures); diff --git a/crates/nu-command/src/filters/merge.rs b/crates/nu-command/src/filters/merge.rs index b047d5e48..e1641eaa9 100644 --- a/crates/nu-command/src/filters/merge.rs +++ b/crates/nu-command/src/filters/merge.rs @@ -1,6 +1,6 @@ use nu_engine::{eval_block, CallExt}; use nu_protocol::ast::Call; -use nu_protocol::engine::{CaptureBlock, Command, EngineState, Stack}; +use nu_protocol::engine::{Closure, Command, EngineState, Stack}; use nu_protocol::{ Category, Example, FromValue, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value, @@ -113,7 +113,7 @@ repeating this process with row 1, and so on."# let merge_value: Value = if argument_was_block { // When given a block, run it to obtain the matching value. - let capture_block: CaptureBlock = FromValue::from_value(&replacement)?; + let capture_block: Closure = FromValue::from_value(&replacement)?; let mut stack = stack.captures_to_stack(&capture_block.captures); stack.with_env(&stack.env_vars.clone(), &stack.env_hidden.clone()); diff --git a/crates/nu-command/src/filters/par_each.rs b/crates/nu-command/src/filters/par_each.rs index fc1f4ea73..653ee03bb 100644 --- a/crates/nu-command/src/filters/par_each.rs +++ b/crates/nu-command/src/filters/par_each.rs @@ -1,6 +1,6 @@ use nu_engine::{eval_block, CallExt}; use nu_protocol::ast::Call; -use nu_protocol::engine::{CaptureBlock, Command, EngineState, Stack}; +use nu_protocol::engine::{Closure, Command, EngineState, Stack}; use nu_protocol::{ Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, Signature, Span, SyntaxShape, Type, Value, @@ -18,7 +18,7 @@ impl Command for ParEach { } fn usage(&self) -> &str { - "Run a block on each element of input in parallel" + "Run a closure on each element of input in parallel" } fn signature(&self) -> nu_protocol::Signature { @@ -28,9 +28,9 @@ impl Command for ParEach { Type::List(Box::new(Type::Any)), )]) .required( - "block", - SyntaxShape::Block(Some(vec![SyntaxShape::Any])), - "the block to run", + "closure", + SyntaxShape::Closure(Some(vec![SyntaxShape::Any])), + "the closure to run", ) .switch("numbered", "iterate with an index", Some('n')) .category(Category::Filters) @@ -64,7 +64,7 @@ impl Command for ParEach { call: &Call, input: PipelineData, ) -> Result { - let capture_block: CaptureBlock = call.req(engine_state, stack, 0)?; + let capture_block: Closure = call.req(engine_state, stack, 0)?; let numbered = call.has_flag("numbered"); let metadata = input.metadata(); diff --git a/crates/nu-command/src/filters/reduce.rs b/crates/nu-command/src/filters/reduce.rs index 3958bc112..b54937161 100644 --- a/crates/nu-command/src/filters/reduce.rs +++ b/crates/nu-command/src/filters/reduce.rs @@ -3,7 +3,7 @@ use std::sync::atomic::Ordering; use nu_engine::{eval_block, CallExt}; use nu_protocol::ast::Call; -use nu_protocol::engine::{CaptureBlock, Command, EngineState, Stack}; +use nu_protocol::engine::{Closure, Command, EngineState, Stack}; use nu_protocol::{ Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value, }; @@ -26,8 +26,8 @@ impl Command for Reduce { Some('f'), ) .required( - "block", - SyntaxShape::Block(Some(vec![SyntaxShape::Any, SyntaxShape::Any])), + "closure", + SyntaxShape::Closure(Some(vec![SyntaxShape::Any, SyntaxShape::Any])), "reducing function", ) .switch("numbered", "iterate with an index", Some('n')) @@ -103,7 +103,7 @@ impl Command for Reduce { let fold: Option = call.get_flag(engine_state, stack, "fold")?; let numbered = call.has_flag("numbered"); - let capture_block: CaptureBlock = call.req(engine_state, stack, 0)?; + let capture_block: Closure = call.req(engine_state, stack, 0)?; 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(); diff --git a/crates/nu-command/src/filters/skip/skip_until.rs b/crates/nu-command/src/filters/skip/skip_until.rs index 0d0dbfb49..5f9fbaae5 100644 --- a/crates/nu-command/src/filters/skip/skip_until.rs +++ b/crates/nu-command/src/filters/skip/skip_until.rs @@ -1,7 +1,7 @@ use nu_engine::{eval_block, CallExt}; use nu_protocol::{ ast::Call, - engine::{CaptureBlock, Command, EngineState, Stack}, + engine::{Closure, Command, EngineState, Stack}, Category, Example, IntoInterruptiblePipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value, }; @@ -73,7 +73,7 @@ impl Command for SkipUntil { let span = call.head; let metadata = input.metadata(); - let capture_block: CaptureBlock = call.req(engine_state, stack, 0)?; + let capture_block: Closure = call.req(engine_state, stack, 0)?; let block = engine_state.get_block(capture_block.block_id).clone(); let var_id = block.signature.get_positional(0).and_then(|arg| arg.var_id); diff --git a/crates/nu-command/src/filters/skip/skip_while.rs b/crates/nu-command/src/filters/skip/skip_while.rs index 2ce137d2d..fa27b6f2b 100644 --- a/crates/nu-command/src/filters/skip/skip_while.rs +++ b/crates/nu-command/src/filters/skip/skip_while.rs @@ -1,7 +1,7 @@ use nu_engine::{eval_block, CallExt}; use nu_protocol::{ ast::Call, - engine::{CaptureBlock, Command, EngineState, Stack}, + engine::{Closure, Command, EngineState, Stack}, Category, Example, IntoInterruptiblePipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value, }; @@ -74,7 +74,7 @@ impl Command for SkipWhile { let span = call.head; let metadata = input.metadata(); - let capture_block: CaptureBlock = call.req(engine_state, stack, 0)?; + let capture_block: Closure = call.req(engine_state, stack, 0)?; let block = engine_state.get_block(capture_block.block_id).clone(); let var_id = block.signature.get_positional(0).and_then(|arg| arg.var_id); diff --git a/crates/nu-command/src/filters/take/take_until.rs b/crates/nu-command/src/filters/take/take_until.rs index 9a1c4c018..e0018630b 100644 --- a/crates/nu-command/src/filters/take/take_until.rs +++ b/crates/nu-command/src/filters/take/take_until.rs @@ -1,7 +1,7 @@ use nu_engine::{eval_block, CallExt}; use nu_protocol::{ ast::Call, - engine::{CaptureBlock, Command, EngineState, Stack}, + engine::{Closure, Command, EngineState, Stack}, Category, Example, IntoInterruptiblePipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value, }; @@ -68,7 +68,7 @@ impl Command for TakeUntil { ) -> Result { let span = call.head; - let capture_block: CaptureBlock = call.req(engine_state, stack, 0)?; + let capture_block: Closure = call.req(engine_state, stack, 0)?; let block = engine_state.get_block(capture_block.block_id).clone(); let var_id = block.signature.get_positional(0).and_then(|arg| arg.var_id); diff --git a/crates/nu-command/src/filters/take/take_while.rs b/crates/nu-command/src/filters/take/take_while.rs index 6b4370902..5a19717d7 100644 --- a/crates/nu-command/src/filters/take/take_while.rs +++ b/crates/nu-command/src/filters/take/take_while.rs @@ -1,7 +1,7 @@ use nu_engine::{eval_block, CallExt}; use nu_protocol::{ ast::Call, - engine::{CaptureBlock, Command, EngineState, Stack}, + engine::{Closure, Command, EngineState, Stack}, Category, Example, IntoInterruptiblePipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value, }; @@ -68,7 +68,7 @@ impl Command for TakeWhile { ) -> Result { let span = call.head; - let capture_block: CaptureBlock = call.req(engine_state, stack, 0)?; + let capture_block: Closure = call.req(engine_state, stack, 0)?; let block = engine_state.get_block(capture_block.block_id).clone(); let var_id = block.signature.get_positional(0).and_then(|arg| arg.var_id); diff --git a/crates/nu-command/src/filters/update.rs b/crates/nu-command/src/filters/update.rs index be049ac09..07a028892 100644 --- a/crates/nu-command/src/filters/update.rs +++ b/crates/nu-command/src/filters/update.rs @@ -1,6 +1,6 @@ use nu_engine::{eval_block, CallExt}; use nu_protocol::ast::{Call, CellPath, PathMember}; -use nu_protocol::engine::{CaptureBlock, Command, EngineState, Stack}; +use nu_protocol::engine::{Closure, Command, EngineState, Stack}; use nu_protocol::{ Category, Example, FromValue, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value, @@ -90,7 +90,7 @@ fn update( // Replace is a block, so set it up and run it instead of using it as the replacement if replacement.as_block().is_ok() { - let capture_block: CaptureBlock = FromValue::from_value(&replacement)?; + let capture_block: Closure = FromValue::from_value(&replacement)?; let block = engine_state.get_block(capture_block.block_id).clone(); let mut stack = stack.captures_to_stack(&capture_block.captures); diff --git a/crates/nu-command/src/filters/update_cells.rs b/crates/nu-command/src/filters/update_cells.rs index 4fc8e8d6d..8bfb9b9d7 100644 --- a/crates/nu-command/src/filters/update_cells.rs +++ b/crates/nu-command/src/filters/update_cells.rs @@ -1,6 +1,6 @@ use nu_engine::{eval_block, CallExt}; use nu_protocol::ast::{Block, Call}; -use nu_protocol::engine::{CaptureBlock, Command, EngineState, Stack}; +use nu_protocol::engine::{Closure, Command, EngineState, Stack}; use nu_protocol::{ Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, PipelineIterator, ShellError, Signature, Span, SyntaxShape, Type, Value, @@ -20,9 +20,9 @@ impl Command for UpdateCells { Signature::build("update cells") .input_output_types(vec![(Type::Table(vec![]), Type::Table(vec![]))]) .required( - "block", - SyntaxShape::Block(Some(vec![SyntaxShape::Any])), - "the block to run an update for each cell", + "closure", + SyntaxShape::Closure(Some(vec![SyntaxShape::Any])), + "the closure to run an update for each cell", ) .named( "columns", @@ -125,7 +125,7 @@ impl Command for UpdateCells { ) -> Result { // the block to run on each cell let engine_state = engine_state.clone(); - let block: CaptureBlock = call.req(&engine_state, stack, 0)?; + let block: Closure = call.req(&engine_state, stack, 0)?; let mut stack = stack.captures_to_stack(&block.captures); let orig_env_vars = stack.env_vars.clone(); let orig_env_hidden = stack.env_hidden.clone(); diff --git a/crates/nu-command/src/filters/upsert.rs b/crates/nu-command/src/filters/upsert.rs index e565a1ab5..54d1306db 100644 --- a/crates/nu-command/src/filters/upsert.rs +++ b/crates/nu-command/src/filters/upsert.rs @@ -1,6 +1,6 @@ use nu_engine::{eval_block, CallExt}; use nu_protocol::ast::{Call, CellPath, PathMember}; -use nu_protocol::engine::{CaptureBlock, Command, EngineState, Stack}; +use nu_protocol::engine::{Closure, Command, EngineState, Stack}; use nu_protocol::{ Category, Example, FromValue, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value, @@ -110,7 +110,7 @@ fn upsert( // Replace is a block, so set it up and run it instead of using it as the replacement if replacement.as_block().is_ok() { - let capture_block: CaptureBlock = FromValue::from_value(&replacement)?; + let capture_block: Closure = FromValue::from_value(&replacement)?; let block = engine_state.get_block(capture_block.block_id).clone(); let mut stack = stack.captures_to_stack(&capture_block.captures); diff --git a/crates/nu-command/src/filters/where_.rs b/crates/nu-command/src/filters/where_.rs index bed3f723a..e0ac728fb 100644 --- a/crates/nu-command/src/filters/where_.rs +++ b/crates/nu-command/src/filters/where_.rs @@ -1,7 +1,7 @@ use super::utils::chain_error_with_input; use nu_engine::{eval_block, CallExt}; use nu_protocol::ast::Call; -use nu_protocol::engine::{CaptureBlock, Command, EngineState, Stack}; +use nu_protocol::engine::{Closure, Command, EngineState, Stack}; use nu_protocol::{ Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value, @@ -30,9 +30,9 @@ impl Command for Where { ]) .optional("cond", SyntaxShape::RowCondition, "condition") .named( - "block", - SyntaxShape::Block(Some(vec![SyntaxShape::Any])), - "use where with a block or variable instead", + "closure", + SyntaxShape::Closure(Some(vec![SyntaxShape::Any])), + "use where with a closure", Some('b'), ) .category(Category::Filters) @@ -49,8 +49,7 @@ impl Command for Where { call: &Call, input: PipelineData, ) -> Result { - if let Ok(Some(capture_block)) = call.get_flag::(engine_state, stack, "block") - { + if let Ok(Some(capture_block)) = call.get_flag::(engine_state, stack, "block") { let metadata = input.metadata(); let ctrlc = engine_state.ctrlc.clone(); let engine_state = engine_state.clone(); @@ -180,7 +179,7 @@ impl Command for Where { } .map(|x| x.set_metadata(metadata)) } else { - let capture_block: Option = call.opt(engine_state, stack, 0)?; + let capture_block: Option = call.opt(engine_state, stack, 0)?; if let Some(block) = capture_block { let span = call.head; diff --git a/crates/nu-command/src/formats/from/nuon.rs b/crates/nu-command/src/formats/from/nuon.rs index a11dfc010..6657480d4 100644 --- a/crates/nu-command/src/formats/from/nuon.rs +++ b/crates/nu-command/src/formats/from/nuon.rs @@ -217,6 +217,12 @@ fn convert_to_value( "blocks not supported in nuon".into(), expr.span, )), + Expr::Closure(..) => Err(ShellError::OutsideSpannedLabeledError( + original_text.to_string(), + "Error when loading".into(), + "closures not supported in nuon".into(), + expr.span, + )), Expr::Binary(val) => Ok(Value::Binary { val, span }), Expr::Bool(val) => Ok(Value::Bool { val, span }), Expr::Call(..) => Err(ShellError::OutsideSpannedLabeledError( diff --git a/crates/nu-command/src/formats/to/json.rs b/crates/nu-command/src/formats/to/json.rs index 0ab96681c..e594476a2 100644 --- a/crates/nu-command/src/formats/to/json.rs +++ b/crates/nu-command/src/formats/to/json.rs @@ -125,7 +125,7 @@ pub fn value_to_json_value(v: &Value) -> Result { Value::List { vals, .. } => nu_json::Value::Array(json_list(vals)?), Value::Error { error } => return Err(error.clone()), - Value::Block { .. } | Value::Range { .. } => nu_json::Value::Null, + Value::Closure { .. } | Value::Block { .. } | Value::Range { .. } => nu_json::Value::Null, Value::Binary { val, .. } => { nu_json::Value::Array(val.iter().map(|x| nu_json::Value::U64(*x as u64)).collect()) } diff --git a/crates/nu-command/src/formats/to/nuon.rs b/crates/nu-command/src/formats/to/nuon.rs index 99bcd3b71..a07ec3497 100644 --- a/crates/nu-command/src/formats/to/nuon.rs +++ b/crates/nu-command/src/formats/to/nuon.rs @@ -66,6 +66,10 @@ fn value_to_string(v: &Value, span: Span) -> Result { "block not supported".into(), span, )), + Value::Closure { .. } => Err(ShellError::UnsupportedInput( + "closure not supported".into(), + span, + )), Value::Bool { val, .. } => { if *val { Ok("true".to_string()) diff --git a/crates/nu-command/src/formats/to/text.rs b/crates/nu-command/src/formats/to/text.rs index cb7da20ce..8083c45db 100644 --- a/crates/nu-command/src/formats/to/text.rs +++ b/crates/nu-command/src/formats/to/text.rs @@ -100,6 +100,7 @@ fn local_into_string(value: Value, separator: &str, config: &Config) -> String { .collect::>() .join(separator), Value::Block { val, .. } => format!("", val), + Value::Closure { val, .. } => format!("", val), Value::Nothing { .. } => String::new(), Value::Error { error } => format!("{:?}", error), Value::Binary { val, .. } => format!("{:?}", val), diff --git a/crates/nu-command/src/formats/to/toml.rs b/crates/nu-command/src/formats/to/toml.rs index 1423d4817..db8cb227f 100644 --- a/crates/nu-command/src/formats/to/toml.rs +++ b/crates/nu-command/src/formats/to/toml.rs @@ -67,6 +67,11 @@ fn helper(engine_state: &EngineState, v: &Value) -> Result { + let code = engine_state.get_span_contents(span); + let code = String::from_utf8_lossy(code).to_string(); + toml::Value::String(code) + } Value::Nothing { .. } => toml::Value::String("".to_string()), Value::Error { error } => return Err(error.clone()), Value::Binary { val, .. } => toml::Value::Array( diff --git a/crates/nu-command/src/formats/to/yaml.rs b/crates/nu-command/src/formats/to/yaml.rs index 2009a1442..09c5a8b4f 100644 --- a/crates/nu-command/src/formats/to/yaml.rs +++ b/crates/nu-command/src/formats/to/yaml.rs @@ -72,6 +72,7 @@ pub fn value_to_yaml_value(v: &Value) -> Result { serde_yaml::Value::Sequence(out) } Value::Block { .. } => serde_yaml::Value::Null, + Value::Closure { .. } => serde_yaml::Value::Null, Value::Nothing { .. } => serde_yaml::Value::Null, Value::Error { error } => return Err(error.clone()), Value::Binary { val, .. } => serde_yaml::Value::Sequence( diff --git a/crates/nu-command/src/system/benchmark.rs b/crates/nu-command/src/system/benchmark.rs index ac1ea49e2..25ea831cb 100644 --- a/crates/nu-command/src/system/benchmark.rs +++ b/crates/nu-command/src/system/benchmark.rs @@ -2,7 +2,7 @@ use std::time::Instant; use nu_engine::{eval_block, CallExt}; use nu_protocol::ast::Call; -use nu_protocol::engine::{CaptureBlock, Command, EngineState, Stack}; +use nu_protocol::engine::{Closure, Command, EngineState, Stack}; use nu_protocol::{ Category, Example, IntoPipelineData, PipelineData, Signature, SyntaxShape, Value, }; @@ -21,11 +21,7 @@ impl Command for Benchmark { fn signature(&self) -> nu_protocol::Signature { Signature::build("benchmark") - .required( - "block", - SyntaxShape::Block(Some(vec![])), - "the block to run", - ) + .required("block", SyntaxShape::Block, "the block to run") .category(Category::System) } @@ -36,7 +32,7 @@ impl Command for Benchmark { call: &Call, _input: PipelineData, ) -> Result { - let capture_block: CaptureBlock = call.req(engine_state, stack, 0)?; + let capture_block: Closure = call.req(engine_state, stack, 0)?; let block = engine_state.get_block(capture_block.block_id); let redirect_stdout = call.redirect_stdout; diff --git a/crates/nu-engine/src/eval.rs b/crates/nu-engine/src/eval.rs index 953e6a9f0..b8e54ac15 100644 --- a/crates/nu-engine/src/eval.rs +++ b/crates/nu-engine/src/eval.rs @@ -492,19 +492,23 @@ pub fn eval_expression( .into_value(expr.span), ) } - Expr::RowCondition(block_id) | Expr::Block(block_id) => { + Expr::RowCondition(block_id) | Expr::Closure(block_id) => { let mut captures = HashMap::new(); let block = engine_state.get_block(*block_id); for var_id in &block.captures { captures.insert(*var_id, stack.get_var(*var_id, expr.span)?); } - Ok(Value::Block { + Ok(Value::Closure { val: *block_id, captures, span: expr.span, }) } + Expr::Block(block_id) => Ok(Value::Block { + val: *block_id, + span: expr.span, + }), Expr::List(x) => { let mut output = vec![]; for expr in x { diff --git a/crates/nu-parser/src/flatten.rs b/crates/nu-parser/src/flatten.rs index dddc78982..ba6b7e653 100644 --- a/crates/nu-parser/src/flatten.rs +++ b/crates/nu-parser/src/flatten.rs @@ -101,7 +101,10 @@ pub fn flatten_expression( output.extend(flatten_expression(working_set, inner_expr)); output } - Expr::Block(block_id) | Expr::RowCondition(block_id) | Expr::Subexpression(block_id) => { + Expr::Block(block_id) + | Expr::Closure(block_id) + | Expr::RowCondition(block_id) + | Expr::Subexpression(block_id) => { let outer_span = expr.span; let mut output = vec![]; diff --git a/crates/nu-parser/src/parser.rs b/crates/nu-parser/src/parser.rs index 9cd521cf4..6d946aa48 100644 --- a/crates/nu-parser/src/parser.rs +++ b/crates/nu-parser/src/parser.rs @@ -2708,7 +2708,8 @@ pub fn parse_shape_name( let result = match bytes { b"any" => SyntaxShape::Any, b"binary" => SyntaxShape::Binary, - b"block" => SyntaxShape::Block(None), //FIXME: Blocks should have known output types + b"block" => SyntaxShape::Block, //FIXME: Blocks should have known output types + b"closure" => SyntaxShape::Closure(None), //FIXME: Blocks should have known output types b"cell-path" => SyntaxShape::CellPath, b"duration" => SyntaxShape::Duration, b"path" => SyntaxShape::Filepath, @@ -3050,6 +3051,7 @@ pub fn parse_row_condition( let block_id = match expression.expr { Expr::Block(block_id) => block_id, + Expr::Closure(block_id) => block_id, _ => { // We have an expression, so let's convert this into a block. let mut block = Block::new(); @@ -3898,39 +3900,13 @@ pub fn parse_block_expression( contents: TokenContents::Pipe, span, }) => { - // We've found a parameter list - let start_point = span.start; - let mut token_iter = output.iter().enumerate().skip(1); - let mut end_span = None; - let mut amt_to_skip = 1; - - for token in &mut token_iter { - if let Token { - contents: TokenContents::Pipe, - span, - } = token.1 - { - end_span = Some(span); - amt_to_skip = token.0; - break; - } - } - - let end_point = if let Some(span) = end_span { - span.end - } else { - end - }; - - let signature_span = Span { - start: start_point, - end: end_point, - }; - let (signature, err) = - parse_signature_helper(working_set, signature_span, expand_aliases_denylist); - error = error.or(err); - - (Some((signature, signature_span)), amt_to_skip) + error = error.or_else(|| { + Some(ParseError::Expected( + "block but found closure".into(), + *span, + )) + }); + (None, 0) } _ => (None, 0), }; @@ -3939,7 +3915,7 @@ pub fn parse_block_expression( error = error.or(err); // TODO: Finish this - if let SyntaxShape::Block(Some(v)) = shape { + if let SyntaxShape::Closure(Some(v)) = shape { if let Some((sig, sig_span)) = &signature { if sig.num_positionals() > v.len() { error = error.or_else(|| { @@ -4010,6 +3986,175 @@ pub fn parse_block_expression( ) } +pub fn parse_closure_expression( + working_set: &mut StateWorkingSet, + shape: &SyntaxShape, + span: Span, + expand_aliases_denylist: &[usize], +) -> (Expression, Option) { + trace!("parsing: closure expression"); + + let bytes = working_set.get_span_contents(span); + let mut error = None; + + let mut start = span.start; + let mut end = span.end; + + if bytes.starts_with(b"{") { + start += 1; + } else { + return ( + garbage(span), + Some(ParseError::Expected("block".into(), span)), + ); + } + if bytes.ends_with(b"}") { + end -= 1; + } else { + error = error.or_else(|| Some(ParseError::Unclosed("}".into(), Span { start: end, end }))); + } + + let inner_span = Span { start, end }; + + let source = working_set.get_span_contents(inner_span); + + let (output, err) = lex(source, start, &[], &[], false); + error = error.or(err); + + working_set.enter_scope(); + + // Check to see if we have parameters + let (signature, amt_to_skip): (Option<(Box, Span)>, usize) = match output.first() { + Some(Token { + contents: TokenContents::Pipe, + span, + }) => { + // We've found a parameter list + let start_point = span.start; + let mut token_iter = output.iter().enumerate().skip(1); + let mut end_span = None; + let mut amt_to_skip = 1; + + for token in &mut token_iter { + if let Token { + contents: TokenContents::Pipe, + span, + } = token.1 + { + end_span = Some(span); + amt_to_skip = token.0; + break; + } + } + + let end_point = if let Some(span) = end_span { + span.end + } else { + end + }; + + let signature_span = Span { + start: start_point, + end: end_point, + }; + let (signature, err) = + parse_signature_helper(working_set, signature_span, expand_aliases_denylist); + error = error.or(err); + + (Some((signature, signature_span)), amt_to_skip) + } + Some(Token { + contents: TokenContents::Item, + span, + }) => { + let contents = working_set.get_span_contents(*span); + if contents == b"||" { + ( + Some((Box::new(Signature::new("closure".to_string())), *span)), + 1, + ) + } else { + (None, 0) + } + } + _ => (None, 0), + }; + + let (output, err) = lite_parse(&output[amt_to_skip..]); + error = error.or(err); + + // TODO: Finish this + if let SyntaxShape::Closure(Some(v)) = shape { + if let Some((sig, sig_span)) = &signature { + if sig.num_positionals() > v.len() { + error = error.or_else(|| { + Some(ParseError::Expected( + format!( + "{} block parameter{}", + v.len(), + if v.len() > 1 { "s" } else { "" } + ), + *sig_span, + )) + }); + } + + for (expected, PositionalArg { name, shape, .. }) in + v.iter().zip(sig.required_positional.iter()) + { + if expected != shape && *shape != SyntaxShape::Any { + error = error.or_else(|| { + Some(ParseError::ParameterMismatchType( + name.to_owned(), + expected.to_string(), + shape.to_string(), + *sig_span, + )) + }); + } + } + } + } + + let (mut output, err) = + parse_block(working_set, &output, false, expand_aliases_denylist, false); + error = error.or(err); + + if let Some(signature) = signature { + output.signature = signature.0; + } else if let Some(last) = working_set.delta.scope.last() { + // FIXME: this only supports the top $it. Is this sufficient? + + if let Some(var_id) = last.get_var(b"$it") { + let mut signature = Signature::new(""); + signature.required_positional.push(PositionalArg { + var_id: Some(*var_id), + name: "$it".into(), + desc: String::new(), + shape: SyntaxShape::Any, + default_value: None, + }); + output.signature = Box::new(signature); + } + } + + output.span = Some(span); + + working_set.exit_scope(); + + let block_id = working_set.add_block(output); + + ( + Expression { + expr: Expr::Closure(block_id), + span, + ty: Type::Closure, + custom_completion: None, + }, + error, + ) +} + pub fn parse_value( working_set: &mut StateWorkingSet, span: Span, @@ -4099,14 +4244,16 @@ pub fn parse_value( } } b'{' => { - if !matches!(shape, SyntaxShape::Block(..)) { + if !matches!(shape, SyntaxShape::Closure(..)) && !matches!(shape, SyntaxShape::Block) { if let (expr, None) = parse_full_cell_path(working_set, None, span, expand_aliases_denylist) { return (expr, None); } } - if matches!(shape, SyntaxShape::Block(_)) || matches!(shape, SyntaxShape::Any) { + if matches!(shape, SyntaxShape::Closure(_)) || matches!(shape, SyntaxShape::Any) { + return parse_closure_expression(working_set, shape, span, expand_aliases_denylist); + } else if matches!(shape, SyntaxShape::Block) { return parse_block_expression(working_set, shape, span, expand_aliases_denylist); } else if matches!(shape, SyntaxShape::Record) { return parse_record(working_set, span, expand_aliases_denylist); @@ -4236,7 +4383,8 @@ pub fn parse_value( SyntaxShape::Filesize, SyntaxShape::Duration, SyntaxShape::Record, - SyntaxShape::Block(None), + SyntaxShape::Closure(None), + SyntaxShape::Block, SyntaxShape::String, ]; for shape in shapes.iter() { @@ -4754,9 +4902,9 @@ pub fn parse_expression( custom_completion: None, }), Argument::Positional(Expression { - expr: Expr::Block(block_id), + expr: Expr::Closure(block_id), span: span(&spans[pos..]), - ty, + ty: Type::Closure, custom_completion: None, }), ]; @@ -4774,7 +4922,7 @@ pub fn parse_expression( expr, custom_completion: None, span: span(spans), - ty: Type::Any, + ty, }, err, ) @@ -5068,7 +5216,7 @@ pub fn parse_block( (block, error) } -pub fn discover_captures_in_block( +pub fn discover_captures_in_closure( working_set: &StateWorkingSet, block: &Block, seen: &mut Vec, @@ -5140,11 +5288,25 @@ pub fn discover_captures_in_expr( let result = discover_captures_in_expr(working_set, expr, seen, seen_blocks); output.extend(&result); } - Expr::Block(block_id) => { + Expr::Closure(block_id) => { let block = working_set.get_block(*block_id); let results = { let mut seen = vec![]; - discover_captures_in_block(working_set, block, &mut seen, seen_blocks) + discover_captures_in_closure(working_set, block, &mut seen, seen_blocks) + }; + seen_blocks.insert(*block_id, results.clone()); + for var_id in results.into_iter() { + if !seen.contains(&var_id) { + output.push(var_id) + } + } + } + Expr::Block(block_id) => { + let block = working_set.get_block(*block_id); + // FIXME: is this correct? + let results = { + let mut seen = vec![]; + discover_captures_in_closure(working_set, block, &mut seen, seen_blocks) }; seen_blocks.insert(*block_id, results.clone()); for var_id in results.into_iter() { @@ -5170,7 +5332,7 @@ pub fn discover_captures_in_expr( let mut seen = vec![]; seen_blocks.insert(block_id, output.clone()); - let result = discover_captures_in_block( + let result = discover_captures_in_closure( working_set, block, &mut seen, @@ -5294,7 +5456,7 @@ pub fn discover_captures_in_expr( let block = working_set.get_block(*block_id); let results = { let mut seen = vec![]; - discover_captures_in_block(working_set, block, &mut seen, seen_blocks) + discover_captures_in_closure(working_set, block, &mut seen, seen_blocks) }; seen_blocks.insert(*block_id, results.clone()); for var_id in results.into_iter() { @@ -5361,7 +5523,7 @@ fn wrap_expr_with_collect(working_set: &mut StateWorkingSet, expr: &Expression) let block_id = working_set.add_block(block); output.push(Argument::Positional(Expression { - expr: Expr::Block(block_id), + expr: Expr::Closure(block_id), span, ty: Type::Any, custom_completion: None, @@ -5432,7 +5594,7 @@ pub fn parse( let mut seen = vec![]; let mut seen_blocks = HashMap::new(); - let captures = discover_captures_in_block(working_set, &output, &mut seen, &mut seen_blocks); + let captures = discover_captures_in_closure(working_set, &output, &mut seen, &mut seen_blocks); output.captures = captures; // Also check other blocks that might have been imported @@ -5441,7 +5603,7 @@ pub fn parse( if !seen_blocks.contains_key(&block_id) { let captures = - discover_captures_in_block(working_set, block, &mut seen, &mut seen_blocks); + discover_captures_in_closure(working_set, block, &mut seen, &mut seen_blocks); seen_blocks.insert(block_id, captures); } } diff --git a/crates/nu-parser/src/type_check.rs b/crates/nu-parser/src/type_check.rs index a229ed231..c8cdd7e27 100644 --- a/crates/nu-parser/src/type_check.rs +++ b/crates/nu-parser/src/type_check.rs @@ -10,6 +10,7 @@ pub fn type_compatible(lhs: &Type, rhs: &Type) -> bool { (Type::List(c), Type::List(d)) => type_compatible(c, d), (Type::Number, Type::Int) => true, (Type::Number, Type::Float) => true, + (Type::Closure, Type::Block) => true, (Type::Any, _) => true, (_, Type::Any) => true, (lhs, rhs) => lhs == rhs, diff --git a/crates/nu-parser/tests/test_parser.rs b/crates/nu-parser/tests/test_parser.rs index c75a74c39..792b56315 100644 --- a/crates/nu-parser/tests/test_parser.rs +++ b/crates/nu-parser/tests/test_parser.rs @@ -937,7 +937,7 @@ mod input_types { .required("cond", SyntaxShape::Expression, "condition to check") .required( "then_block", - SyntaxShape::Block(Some(vec![])), + SyntaxShape::Block, "block to run if check succeeds", ) .optional( diff --git a/crates/nu-protocol/src/ast/expr.rs b/crates/nu-protocol/src/ast/expr.rs index 63e390765..e85cbe256 100644 --- a/crates/nu-protocol/src/ast/expr.rs +++ b/crates/nu-protocol/src/ast/expr.rs @@ -26,6 +26,7 @@ pub enum Expr { BinaryOp(Box, Box, Box), //lhs, op, rhs Subexpression(BlockId), Block(BlockId), + Closure(BlockId), List(Vec), Table(Vec, Vec>), Record(Vec<(Expression, Expression)>), diff --git a/crates/nu-protocol/src/ast/expression.rs b/crates/nu-protocol/src/ast/expression.rs index 7f414488a..f78397291 100644 --- a/crates/nu-protocol/src/ast/expression.rs +++ b/crates/nu-protocol/src/ast/expression.rs @@ -63,6 +63,7 @@ impl Expression { pub fn as_block(&self) -> Option { match self.expr { Expr::Block(block_id) => Some(block_id), + Expr::Closure(block_id) => Some(block_id), _ => None, } } @@ -139,6 +140,22 @@ impl Expression { false } } + Expr::Closure(block_id) => { + let block = working_set.get_block(*block_id); + + if block.captures.contains(&IN_VARIABLE_ID) { + return true; + } + + if let Some(pipeline) = block.pipelines.get(0) { + match pipeline.expressions.get(0) { + Some(expr) => expr.has_in_variable(working_set), + None => false, + } + } else { + false + } + } Expr::Binary(_) => false, Expr::Bool(_) => false, Expr::Call(call) => { @@ -310,6 +327,37 @@ impl Expression { .map(|x| if *x != IN_VARIABLE_ID { *x } else { new_var_id }) .collect(); } + Expr::Closure(block_id) => { + let block = working_set.get_block(*block_id); + + let new_expr = if let Some(pipeline) = block.pipelines.get(0) { + if let Some(expr) = pipeline.expressions.get(0) { + let mut new_expr = expr.clone(); + new_expr.replace_in_variable(working_set, new_var_id); + Some(new_expr) + } else { + None + } + } else { + None + }; + + let block = working_set.get_block_mut(*block_id); + + if let Some(new_expr) = new_expr { + if let Some(pipeline) = block.pipelines.get_mut(0) { + if let Some(expr) = pipeline.expressions.get_mut(0) { + *expr = new_expr + } + } + } + + block.captures = block + .captures + .iter() + .map(|x| if *x != IN_VARIABLE_ID { *x } else { new_var_id }) + .collect(); + } Expr::Binary(_) => {} Expr::Bool(_) => {} Expr::Call(call) => { @@ -456,6 +504,17 @@ impl Expression { *block_id = working_set.add_block(block); } + Expr::Closure(block_id) => { + let mut block = working_set.get_block(*block_id).clone(); + + for pipeline in block.pipelines.iter_mut() { + for expr in pipeline.expressions.iter_mut() { + expr.replace_span(working_set, replaced, new_span) + } + } + + *block_id = working_set.add_block(block); + } Expr::Binary(_) => {} Expr::Bool(_) => {} Expr::Call(call) => { diff --git a/crates/nu-protocol/src/engine/capture_block.rs b/crates/nu-protocol/src/engine/capture_block.rs index 447c33e5a..291a009e0 100644 --- a/crates/nu-protocol/src/engine/capture_block.rs +++ b/crates/nu-protocol/src/engine/capture_block.rs @@ -3,7 +3,12 @@ use std::collections::HashMap; use crate::{BlockId, Value, VarId}; #[derive(Clone, Debug)] -pub struct CaptureBlock { +pub struct Closure { pub block_id: BlockId, pub captures: HashMap, } + +#[derive(Clone, Debug)] +pub struct Block { + pub block_id: BlockId, +} diff --git a/crates/nu-protocol/src/syntax_shape.rs b/crates/nu-protocol/src/syntax_shape.rs index 4b9929e8b..d9d7244ac 100644 --- a/crates/nu-protocol/src/syntax_shape.rs +++ b/crates/nu-protocol/src/syntax_shape.rs @@ -46,8 +46,11 @@ pub enum SyntaxShape { /// A binary literal Binary, + /// A closure is allowed, eg `{|| start this thing}` + Closure(Option>), + /// A block is allowed, eg `{start this thing}` - Block(Option>), + Block, /// A table is allowed, eg `[[first, second]; [1, 2]]` Table, @@ -106,7 +109,8 @@ impl SyntaxShape { pub fn to_type(&self) -> Type { match self { SyntaxShape::Any => Type::Any, - SyntaxShape::Block(_) => Type::Block, + SyntaxShape::Block => Type::Block, + SyntaxShape::Closure(_) => Type::Closure, SyntaxShape::Binary => Type::Binary, SyntaxShape::CellPath => Type::Any, SyntaxShape::Custom(custom, _) => custom.to_type(), @@ -160,7 +164,8 @@ impl Display for SyntaxShape { SyntaxShape::Directory => write!(f, "directory"), SyntaxShape::GlobPattern => write!(f, "glob"), SyntaxShape::ImportPattern => write!(f, "import"), - SyntaxShape::Block(_) => write!(f, "block"), + SyntaxShape::Block => write!(f, "block"), + SyntaxShape::Closure(_) => write!(f, "closure"), SyntaxShape::Binary => write!(f, "binary"), SyntaxShape::Table => write!(f, "table"), SyntaxShape::List(x) => write!(f, "list<{}>", x), diff --git a/crates/nu-protocol/src/ty.rs b/crates/nu-protocol/src/ty.rs index bd7cbb6f7..f00675f97 100644 --- a/crates/nu-protocol/src/ty.rs +++ b/crates/nu-protocol/src/ty.rs @@ -13,6 +13,7 @@ pub enum Type { Bool, String, Block, + Closure, CellPath, Duration, Date, @@ -72,7 +73,8 @@ impl Type { Type::Range => SyntaxShape::Range, Type::Bool => SyntaxShape::Boolean, Type::String => SyntaxShape::String, - Type::Block => SyntaxShape::Block(None), // FIXME needs more accuracy + Type::Block => SyntaxShape::Block, // FIXME needs more accuracy + Type::Closure => SyntaxShape::Closure(None), // FIXME needs more accuracy Type::CellPath => SyntaxShape::CellPath, Type::Duration => SyntaxShape::Duration, Type::Date => SyntaxShape::DateTime, @@ -96,6 +98,7 @@ impl Display for Type { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Type::Block => write!(f, "block"), + Type::Closure => write!(f, "closure"), Type::Bool => write!(f, "bool"), Type::CellPath => write!(f, "cell path"), Type::Date => write!(f, "date"), diff --git a/crates/nu-protocol/src/value/from_value.rs b/crates/nu-protocol/src/value/from_value.rs index f5a792da9..ed437adaf 100644 --- a/crates/nu-protocol/src/value/from_value.rs +++ b/crates/nu-protocol/src/value/from_value.rs @@ -1,8 +1,9 @@ +use std::collections::HashMap; use std::path::PathBuf; use std::str::FromStr; use crate::ast::{CellPath, PathMember}; -use crate::engine::CaptureBlock; +use crate::engine::{Block, Closure}; use crate::ShellError; use crate::{Range, Spanned, Value}; use chrono::{DateTime, FixedOffset}; @@ -502,13 +503,31 @@ impl FromValue for (Vec, Vec) { } } -impl FromValue for CaptureBlock { +impl FromValue for Closure { fn from_value(v: &Value) -> Result { match v { - Value::Block { val, captures, .. } => Ok(CaptureBlock { + Value::Closure { val, captures, .. } => Ok(Closure { block_id: *val, captures: captures.clone(), }), + Value::Block { val, .. } => Ok(Closure { + block_id: *val, + captures: HashMap::new(), + }), + v => Err(ShellError::CantConvert( + "Closure".into(), + v.get_type().to_string(), + v.span()?, + None, + )), + } + } +} + +impl FromValue for Block { + fn from_value(v: &Value) -> Result { + match v { + Value::Block { val, .. } => Ok(Block { block_id: *val }), v => Err(ShellError::CantConvert( "Block".into(), v.get_type().to_string(), @@ -519,22 +538,22 @@ impl FromValue for CaptureBlock { } } -impl FromValue for Spanned { +impl FromValue for Spanned { fn from_value(v: &Value) -> Result { match v { - Value::Block { + Value::Closure { val, captures, span, } => Ok(Spanned { - item: CaptureBlock { + item: Closure { block_id: *val, captures: captures.clone(), }, span: *span, }), v => Err(ShellError::CantConvert( - "Block".into(), + "Closure".into(), v.get_type().to_string(), v.span()?, None, diff --git a/crates/nu-protocol/src/value/mod.rs b/crates/nu-protocol/src/value/mod.rs index c540f04a9..60ee3762d 100644 --- a/crates/nu-protocol/src/value/mod.rs +++ b/crates/nu-protocol/src/value/mod.rs @@ -78,6 +78,10 @@ pub enum Value { span: Span, }, Block { + val: BlockId, + span: Span, + }, + Closure { val: BlockId, captures: HashMap, span: Span, @@ -146,11 +150,15 @@ impl Clone for Value { vals: vals.clone(), span: *span, }, - Value::Block { + Value::Block { val, span } => Value::Block { + val: *val, + span: *span, + }, + Value::Closure { val, captures, span, - } => Value::Block { + } => Value::Closure { val: *val, captures: captures.clone(), span: *span, @@ -246,6 +254,7 @@ impl Value { pub fn as_block(&self) -> Result { match self { Value::Block { val, .. } => Ok(*val), + Value::Closure { val, .. } => Ok(*val), x => Err(ShellError::CantConvert( "block".into(), x.get_type().to_string(), @@ -344,6 +353,7 @@ impl Value { Value::Record { span, .. } => Ok(*span), Value::List { span, .. } => Ok(*span), Value::Block { span, .. } => Ok(*span), + Value::Closure { span, .. } => Ok(*span), Value::Nothing { span, .. } => Ok(*span), Value::Binary { span, .. } => Ok(*span), Value::CellPath { span, .. } => Ok(*span), @@ -364,6 +374,7 @@ impl Value { Value::String { span, .. } => *span = new_span, Value::Record { span, .. } => *span = new_span, Value::List { span, .. } => *span = new_span, + Value::Closure { span, .. } => *span = new_span, Value::Block { span, .. } => *span = new_span, Value::Nothing { span, .. } => *span = new_span, Value::Error { .. } => {} @@ -418,6 +429,7 @@ impl Value { } Value::Nothing { .. } => Type::Nothing, Value::Block { .. } => Type::Block, + Value::Closure { .. } => Type::Closure, Value::Error { .. } => Type::Error, Value::Binary { .. } => Type::Binary, Value::CellPath { .. } => Type::CellPath, @@ -490,6 +502,7 @@ impl Value { .join(separator) ), Value::Block { val, .. } => format!("", val), + Value::Closure { val, .. } => format!("", val), Value::Nothing { .. } => String::new(), Value::Error { error } => format!("{:?}", error), Value::Binary { val, .. } => format!("{:?}", val), @@ -533,6 +546,7 @@ impl Value { if cols.len() == 1 { "" } else { "s" } ), Value::Block { val, .. } => format!("", val), + Value::Closure { val, .. } => format!("", val), Value::Nothing { .. } => String::new(), Value::Error { error } => format!("{:?}", error), Value::Binary { val, .. } => format!("{:?}", val), @@ -579,6 +593,7 @@ impl Value { .join(separator) ), Value::Block { val, .. } => format!("", val), + Value::Closure { val, .. } => format!("", val), Value::Nothing { .. } => String::new(), Value::Error { error } => format!("{:?}", error), Value::Binary { val, .. } => format!("{:?}", val), @@ -1330,6 +1345,7 @@ impl PartialOrd for Value { Value::Record { .. } => Some(Ordering::Less), Value::List { .. } => Some(Ordering::Less), Value::Block { .. } => Some(Ordering::Less), + Value::Closure { .. } => Some(Ordering::Less), Value::Nothing { .. } => Some(Ordering::Less), Value::Error { .. } => Some(Ordering::Less), Value::Binary { .. } => Some(Ordering::Less), @@ -1348,6 +1364,7 @@ impl PartialOrd for Value { Value::Record { .. } => Some(Ordering::Less), Value::List { .. } => Some(Ordering::Less), Value::Block { .. } => Some(Ordering::Less), + Value::Closure { .. } => Some(Ordering::Less), Value::Nothing { .. } => Some(Ordering::Less), Value::Error { .. } => Some(Ordering::Less), Value::Binary { .. } => Some(Ordering::Less), @@ -1366,6 +1383,7 @@ impl PartialOrd for Value { Value::Record { .. } => Some(Ordering::Less), Value::List { .. } => Some(Ordering::Less), Value::Block { .. } => Some(Ordering::Less), + Value::Closure { .. } => Some(Ordering::Less), Value::Nothing { .. } => Some(Ordering::Less), Value::Error { .. } => Some(Ordering::Less), Value::Binary { .. } => Some(Ordering::Less), @@ -1384,6 +1402,7 @@ impl PartialOrd for Value { Value::Record { .. } => Some(Ordering::Less), Value::List { .. } => Some(Ordering::Less), Value::Block { .. } => Some(Ordering::Less), + Value::Closure { .. } => Some(Ordering::Less), Value::Nothing { .. } => Some(Ordering::Less), Value::Error { .. } => Some(Ordering::Less), Value::Binary { .. } => Some(Ordering::Less), @@ -1402,6 +1421,7 @@ impl PartialOrd for Value { Value::Record { .. } => Some(Ordering::Less), Value::List { .. } => Some(Ordering::Less), Value::Block { .. } => Some(Ordering::Less), + Value::Closure { .. } => Some(Ordering::Less), Value::Nothing { .. } => Some(Ordering::Less), Value::Error { .. } => Some(Ordering::Less), Value::Binary { .. } => Some(Ordering::Less), @@ -1420,6 +1440,7 @@ impl PartialOrd for Value { Value::Record { .. } => Some(Ordering::Less), Value::List { .. } => Some(Ordering::Less), Value::Block { .. } => Some(Ordering::Less), + Value::Closure { .. } => Some(Ordering::Less), Value::Nothing { .. } => Some(Ordering::Less), Value::Error { .. } => Some(Ordering::Less), Value::Binary { .. } => Some(Ordering::Less), @@ -1438,6 +1459,7 @@ impl PartialOrd for Value { Value::Record { .. } => Some(Ordering::Less), Value::List { .. } => Some(Ordering::Less), Value::Block { .. } => Some(Ordering::Less), + Value::Closure { .. } => Some(Ordering::Less), Value::Nothing { .. } => Some(Ordering::Less), Value::Error { .. } => Some(Ordering::Less), Value::Binary { .. } => Some(Ordering::Less), @@ -1456,6 +1478,7 @@ impl PartialOrd for Value { Value::Record { .. } => Some(Ordering::Less), Value::List { .. } => Some(Ordering::Less), Value::Block { .. } => Some(Ordering::Less), + Value::Closure { .. } => Some(Ordering::Less), Value::Nothing { .. } => Some(Ordering::Less), Value::Error { .. } => Some(Ordering::Less), Value::Binary { .. } => Some(Ordering::Less), @@ -1500,6 +1523,7 @@ impl PartialOrd for Value { } Value::List { .. } => Some(Ordering::Less), Value::Block { .. } => Some(Ordering::Less), + Value::Closure { .. } => Some(Ordering::Less), Value::Nothing { .. } => Some(Ordering::Less), Value::Error { .. } => Some(Ordering::Less), Value::Binary { .. } => Some(Ordering::Less), @@ -1518,6 +1542,7 @@ impl PartialOrd for Value { Value::Record { .. } => Some(Ordering::Greater), Value::List { vals: rhs, .. } => lhs.partial_cmp(rhs), Value::Block { .. } => Some(Ordering::Less), + Value::Closure { .. } => Some(Ordering::Less), Value::Nothing { .. } => Some(Ordering::Less), Value::Error { .. } => Some(Ordering::Less), Value::Binary { .. } => Some(Ordering::Less), @@ -1536,6 +1561,26 @@ impl PartialOrd for Value { Value::Record { .. } => Some(Ordering::Greater), Value::List { .. } => Some(Ordering::Greater), Value::Block { val: rhs, .. } => lhs.partial_cmp(rhs), + Value::Closure { .. } => Some(Ordering::Less), + Value::Nothing { .. } => Some(Ordering::Less), + Value::Error { .. } => Some(Ordering::Less), + Value::Binary { .. } => Some(Ordering::Less), + Value::CellPath { .. } => Some(Ordering::Less), + Value::CustomValue { .. } => Some(Ordering::Less), + }, + (Value::Closure { val: lhs, .. }, rhs) => match rhs { + Value::Bool { .. } => Some(Ordering::Greater), + Value::Int { .. } => Some(Ordering::Greater), + Value::Float { .. } => Some(Ordering::Greater), + Value::Filesize { .. } => Some(Ordering::Greater), + Value::Duration { .. } => Some(Ordering::Greater), + Value::Date { .. } => Some(Ordering::Greater), + Value::Range { .. } => Some(Ordering::Greater), + Value::String { .. } => Some(Ordering::Greater), + Value::Record { .. } => Some(Ordering::Greater), + Value::List { .. } => Some(Ordering::Greater), + Value::Block { .. } => Some(Ordering::Greater), + Value::Closure { val: rhs, .. } => lhs.partial_cmp(rhs), Value::Nothing { .. } => Some(Ordering::Less), Value::Error { .. } => Some(Ordering::Less), Value::Binary { .. } => Some(Ordering::Less), @@ -1554,6 +1599,7 @@ impl PartialOrd for Value { Value::Record { .. } => Some(Ordering::Greater), Value::List { .. } => Some(Ordering::Greater), Value::Block { .. } => Some(Ordering::Greater), + Value::Closure { .. } => Some(Ordering::Greater), Value::Nothing { .. } => Some(Ordering::Equal), Value::Error { .. } => Some(Ordering::Less), Value::Binary { .. } => Some(Ordering::Less), @@ -1572,6 +1618,7 @@ impl PartialOrd for Value { Value::Record { .. } => Some(Ordering::Greater), Value::List { .. } => Some(Ordering::Greater), Value::Block { .. } => Some(Ordering::Greater), + Value::Closure { .. } => Some(Ordering::Greater), Value::Nothing { .. } => Some(Ordering::Greater), Value::Error { .. } => Some(Ordering::Equal), Value::Binary { .. } => Some(Ordering::Less), @@ -1590,6 +1637,7 @@ impl PartialOrd for Value { Value::Record { .. } => Some(Ordering::Greater), Value::List { .. } => Some(Ordering::Greater), Value::Block { .. } => Some(Ordering::Greater), + Value::Closure { .. } => Some(Ordering::Greater), Value::Nothing { .. } => Some(Ordering::Greater), Value::Error { .. } => Some(Ordering::Greater), Value::Binary { val: rhs, .. } => lhs.partial_cmp(rhs), @@ -1608,6 +1656,7 @@ impl PartialOrd for Value { Value::Record { .. } => Some(Ordering::Greater), Value::List { .. } => Some(Ordering::Greater), Value::Block { .. } => Some(Ordering::Greater), + Value::Closure { .. } => Some(Ordering::Greater), Value::Nothing { .. } => Some(Ordering::Greater), Value::Error { .. } => Some(Ordering::Greater), Value::Binary { .. } => Some(Ordering::Greater), diff --git a/src/tests/test_engine.rs b/src/tests/test_engine.rs index df83bc0d6..9056ef456 100644 --- a/src/tests/test_engine.rs +++ b/src/tests/test_engine.rs @@ -107,7 +107,7 @@ fn missing_flags_are_nothing4() -> TestResult { #[test] fn proper_variable_captures() -> TestResult { run_test( - r#"def foo [x] { let y = 100; { $y + $x } }; do (foo 23)"#, + r#"def foo [x] { let y = 100; { || $y + $x } }; do (foo 23)"#, "123", ) }