diff --git a/crates/nu-cli/src/cli.rs b/crates/nu-cli/src/cli.rs index 99caba38bd..a248fc0487 100644 --- a/crates/nu-cli/src/cli.rs +++ b/crates/nu-cli/src/cli.rs @@ -9,7 +9,7 @@ use crate::EnvironmentSyncer; use futures_codec::FramedRead; use nu_errors::ShellError; use nu_protocol::hir::{ClassifiedCommand, Expression, InternalCommand, Literal, NamedArguments}; -use nu_protocol::{Primitive, ReturnSuccess, UntaggedValue, Value}; +use nu_protocol::{Primitive, ReturnSuccess, Scope, UntaggedValue, Value}; use log::{debug, trace}; #[cfg(feature = "rustyline-support")] @@ -395,9 +395,7 @@ pub async fn cli(mut context: EvaluationContext) -> Result<(), Box> { &prompt_block.block, &mut context, InputStream::empty(), - &Value::nothing(), - &IndexMap::new(), - &env, + Scope::from_env(env), ) .await { @@ -862,9 +860,7 @@ pub async fn parse_and_eval(line: &str, ctx: &mut EvaluationContext) -> Result RawComm external_redirection: ExternalRedirection::Stdout, }, name_tag: context.name.clone(), - scope: Scope::new(), + scope: Scope::create(), }, } } diff --git a/crates/nu-cli/src/commands/benchmark.rs b/crates/nu-cli/src/commands/benchmark.rs index ee82966dc0..5991977fb9 100644 --- a/crates/nu-cli/src/commands/benchmark.rs +++ b/crates/nu-cli/src/commands/benchmark.rs @@ -83,16 +83,18 @@ async fn benchmark( let scope = raw_args.call_info.scope.clone(); let (BenchmarkArgs { block, passthrough }, input) = raw_args.process(®istry).await?; - let mut env = scope.env.clone(); + let env = scope.env(); let name = generate_free_name(&env); + let mut env = IndexMap::new(); env.insert(name, generate_random_env_value()); + let scope = Scope::append_env(scope, env); let start_time = Instant::now(); #[cfg(feature = "rich-benchmark")] let start = time().await; - let result = run_block(&block, &mut context, input, &scope.it, &scope.vars, &env).await; + let result = run_block(&block, &mut context, input, scope.clone()).await; let output = result?.into_vec().await; #[cfg(feature = "rich-benchmark")] @@ -108,7 +110,7 @@ async fn benchmark( let real_time = into_big_int(end_time - start_time); indexmap.insert("real time".to_string(), real_time); - benchmark_output(indexmap, output, passthrough, &tag, &mut context, &scope).await + benchmark_output(indexmap, output, passthrough, &tag, &mut context, scope).await } // return advanced stats #[cfg(feature = "rich-benchmark")] @@ -127,7 +129,7 @@ async fn benchmark( let idle_time = into_big_int(end.idle() - start.idle()); indexmap.insert("idle time".to_string(), idle_time); - benchmark_output(indexmap, output, passthrough, &tag, &mut context, &scope).await + benchmark_output(indexmap, output, passthrough, &tag, &mut context, scope).await } else { Err(ShellError::untagged_runtime_error( "Could not retreive CPU time", @@ -141,7 +143,7 @@ async fn benchmark_output( passthrough: Option, tag: T, context: &mut EvaluationContext, - scope: &Scope, + scope: Arc, ) -> Result where T: Into + Copy, @@ -161,15 +163,7 @@ where // add autoview for an empty block let time_block = add_implicit_autoview(time_block); - let _ = run_block( - &time_block, - context, - benchmark_output, - &scope.it, - &scope.vars, - &scope.env, - ) - .await?; + let _ = run_block(&time_block, context, benchmark_output, scope).await?; context.clear_errors(); Ok(block_output.into()) diff --git a/crates/nu-cli/src/commands/classified/block.rs b/crates/nu-cli/src/commands/classified/block.rs index be0722a67b..ee2aa282d1 100644 --- a/crates/nu-cli/src/commands/classified/block.rs +++ b/crates/nu-cli/src/commands/classified/block.rs @@ -6,16 +6,14 @@ use crate::stream::InputStream; use futures::stream::TryStreamExt; use nu_errors::ShellError; use nu_protocol::hir::{Block, ClassifiedCommand, Commands}; -use nu_protocol::{ReturnSuccess, UntaggedValue, Value}; +use nu_protocol::{ReturnSuccess, Scope, UntaggedValue, Value}; use std::sync::atomic::Ordering; pub(crate) async fn run_block( block: &Block, ctx: &mut EvaluationContext, mut input: InputStream, - it: &Value, - vars: &IndexMap, - env: &IndexMap, + scope: Arc, ) -> Result { let mut output: Result = Ok(InputStream::empty()); for pipeline in &block.block { @@ -54,7 +52,7 @@ pub(crate) async fn run_block( return Err(e); } } - output = run_pipeline(pipeline, ctx, input, it, vars, env).await; + output = run_pipeline(pipeline, ctx, input, scope.clone()).await; input = InputStream::empty(); } @@ -66,9 +64,7 @@ async fn run_pipeline( commands: &Commands, ctx: &mut EvaluationContext, mut input: InputStream, - it: &Value, - vars: &IndexMap, - env: &IndexMap, + scope: Arc, ) -> Result { for item in commands.list.clone() { input = match item { @@ -77,13 +73,13 @@ async fn run_pipeline( } ClassifiedCommand::Expr(expr) => { - run_expression_block(*expr, ctx, it, vars, env).await? + run_expression_block(*expr, ctx, scope.clone()).await? } ClassifiedCommand::Error(err) => return Err(err.into()), ClassifiedCommand::Internal(left) => { - run_internal_command(left, ctx, input, it, vars, env).await? + run_internal_command(left, ctx, input, scope.clone()).await? } }; } diff --git a/crates/nu-cli/src/commands/classified/expr.rs b/crates/nu-cli/src/commands/classified/expr.rs index bdeaa211d8..d9e3e3e382 100644 --- a/crates/nu-cli/src/commands/classified/expr.rs +++ b/crates/nu-cli/src/commands/classified/expr.rs @@ -6,14 +6,12 @@ use log::{log_enabled, trace}; use futures::stream::once; use nu_errors::ShellError; use nu_protocol::hir::SpannedExpression; -use nu_protocol::Value; +use nu_protocol::Scope; pub(crate) async fn run_expression_block( expr: SpannedExpression, context: &mut EvaluationContext, - it: &Value, - vars: &IndexMap, - env: &IndexMap, + scope: Arc, ) -> Result { if log_enabled!(log::Level::Trace) { trace!(target: "nu::run::expr", "->"); @@ -21,7 +19,7 @@ pub(crate) async fn run_expression_block( } let registry = context.registry().clone(); - let output = evaluate_baseline_expr(&expr, ®istry, it, vars, env).await?; + let output = evaluate_baseline_expr(&expr, ®istry, scope).await?; Ok(once(async { Ok(output) }).to_input_stream()) } diff --git a/crates/nu-cli/src/commands/classified/external.rs b/crates/nu-cli/src/commands/classified/external.rs index 6d7e2c8769..c5d5d1b302 100644 --- a/crates/nu-cli/src/commands/classified/external.rs +++ b/crates/nu-cli/src/commands/classified/external.rs @@ -21,7 +21,7 @@ pub(crate) async fn run_external_command( command: ExternalCommand, context: &mut EvaluationContext, input: InputStream, - scope: &Scope, + scope: Arc, external_redirection: ExternalRedirection, ) -> Result { trace!(target: "nu::run::external", "-> {}", command.name); @@ -41,7 +41,7 @@ async fn run_with_stdin( command: ExternalCommand, context: &mut EvaluationContext, input: InputStream, - scope: &Scope, + scope: Arc, external_redirection: ExternalRedirection, ) -> Result { let path = context.shell_manager.path(); @@ -50,9 +50,7 @@ async fn run_with_stdin( let mut command_args = vec![]; for arg in command.args.iter() { - let value = - evaluate_baseline_expr(arg, &context.registry, &scope.it, &scope.vars, &scope.env) - .await?; + let value = evaluate_baseline_expr(arg, &context.registry, scope.clone()).await?; // Skip any arguments that don't really exist, treating them as optional // FIXME: we may want to preserve the gap in the future, though it's hard to say @@ -138,7 +136,7 @@ fn spawn( args: &[String], input: InputStream, external_redirection: ExternalRedirection, - scope: &Scope, + scope: Arc, ) -> Result { let command = command.clone(); @@ -169,7 +167,7 @@ fn spawn( trace!(target: "nu::run::external", "cwd = {:?}", &path); process.env_clear(); - process.envs(scope.env.iter()); + process.envs(scope.env()); // We want stdout regardless of what // we are doing ($it case or pipe stdin) @@ -580,7 +578,7 @@ mod tests { cmd, &mut ctx, input, - &Scope::new(), + Scope::create(), ExternalRedirection::Stdout ) .await diff --git a/crates/nu-cli/src/commands/classified/internal.rs b/crates/nu-cli/src/commands/classified/internal.rs index 0145256367..bfb2bb13da 100644 --- a/crates/nu-cli/src/commands/classified/internal.rs +++ b/crates/nu-cli/src/commands/classified/internal.rs @@ -11,20 +11,13 @@ pub(crate) async fn run_internal_command( command: InternalCommand, context: &mut EvaluationContext, input: InputStream, - it: &Value, - vars: &IndexMap, - env: &IndexMap, + scope: Arc, ) -> Result { if log_enabled!(log::Level::Trace) { trace!(target: "nu::run::internal", "->"); trace!(target: "nu::run::internal", "{}", command.name); } - let scope = Scope { - it: it.clone(), - vars: vars.clone(), - env: env.clone(), - }; let objects: InputStream = trace_stream!(target: "nu::trace_stream::internal", "input" = input); let internal_command = context.expect_command(&command.name); @@ -38,7 +31,7 @@ pub(crate) async fn run_internal_command( internal_command?, Tag::unknown_anchor(command.name_span), command.args.clone(), - &scope, + scope.clone(), objects, ) .await? @@ -48,8 +41,6 @@ pub(crate) async fn run_internal_command( //let context = Arc::new(context.clone()); let context = context.clone(); let command = Arc::new(command); - let scope = Arc::new(scope); - // let scope = scope.clone(); Ok(InputStream::from_stream( result @@ -90,7 +81,7 @@ pub(crate) async fn run_internal_command( external_redirection: ExternalRedirection::Stdout, }, name_tag: Tag::unknown_anchor(command.name_span), - scope: (&*scope).clone(), + scope, }, }; let result = converter diff --git a/crates/nu-cli/src/commands/classified/plugin.rs b/crates/nu-cli/src/commands/classified/plugin.rs index 593b4804aa..9b063c23ff 100644 --- a/crates/nu-cli/src/commands/classified/plugin.rs +++ b/crates/nu-cli/src/commands/classified/plugin.rs @@ -130,7 +130,7 @@ async fn run_filter( UntaggedValue::Primitive(Primitive::EndOfStream).into_untagged_value() ]); - let args = args.evaluate_once_with_scope(®istry, &scope).await?; + let args = args.evaluate_once_with_scope(®istry, scope).await?; let real_path = Path::new(&path); let ext = real_path.extension(); diff --git a/crates/nu-cli/src/commands/command.rs b/crates/nu-cli/src/commands/command.rs index 06a0d15d65..3a8dec95dc 100644 --- a/crates/nu-cli/src/commands/command.rs +++ b/crates/nu-cli/src/commands/command.rs @@ -17,27 +17,12 @@ use std::sync::atomic::AtomicBool; pub struct UnevaluatedCallInfo { pub args: hir::Call, pub name_tag: Tag, - pub scope: Scope, + pub scope: Arc, } impl UnevaluatedCallInfo { pub async fn evaluate(self, registry: &CommandRegistry) -> Result { - let args = evaluate_args(&self.args, registry, &self.scope).await?; - - Ok(CallInfo { - args, - name_tag: self.name_tag, - }) - } - - pub async fn evaluate_with_new_it( - self, - registry: &CommandRegistry, - it: &Value, - ) -> Result { - let mut scope = self.scope.clone(); - scope.it = it.clone(); - let args = evaluate_args(&self.args, registry, &scope).await?; + let args = evaluate_args(&self.args, registry, self.scope.clone()).await?; Ok(CallInfo { args, @@ -115,7 +100,7 @@ impl CommandArgs { pub async fn evaluate_once_with_scope( self, registry: &CommandRegistry, - scope: &Scope, + scope: Arc, ) -> Result { let host = self.host.clone(); let ctrl_c = self.ctrl_c.clone(); @@ -215,37 +200,6 @@ impl EvaluatedWholeStreamCommandArgs { } } -#[derive(Getters)] -#[get = "pub"] -pub struct EvaluatedFilterCommandArgs { - args: EvaluatedCommandArgs, -} - -impl Deref for EvaluatedFilterCommandArgs { - type Target = EvaluatedCommandArgs; - fn deref(&self) -> &Self::Target { - &self.args - } -} - -impl EvaluatedFilterCommandArgs { - pub fn new( - host: Arc>, - ctrl_c: Arc, - shell_manager: ShellManager, - call_info: CallInfo, - ) -> EvaluatedFilterCommandArgs { - EvaluatedFilterCommandArgs { - args: EvaluatedCommandArgs { - host, - ctrl_c, - shell_manager, - call_info, - }, - } - } -} - #[derive(Getters, new)] #[get = "pub(crate)"] pub struct EvaluatedCommandArgs { @@ -381,69 +335,6 @@ impl Command { } } -pub struct FnFilterCommand { - name: String, - func: fn(EvaluatedFilterCommandArgs) -> Result, -} - -#[async_trait] -impl WholeStreamCommand for FnFilterCommand { - fn name(&self) -> &str { - &self.name - } - - fn usage(&self) -> &str { - "usage" - } - - async fn run( - &self, - CommandArgs { - host, - ctrl_c, - shell_manager, - call_info, - input, - .. - }: CommandArgs, - registry: &CommandRegistry, - ) -> Result { - let registry = Arc::new(registry.clone()); - let func = self.func; - - Ok(input - .then(move |it| { - let host = host.clone(); - let registry = registry.clone(); - let ctrl_c = ctrl_c.clone(); - let shell_manager = shell_manager.clone(); - let call_info = call_info.clone(); - async move { - let call_info = match call_info.evaluate_with_new_it(&*registry, &it).await { - Err(err) => { - return OutputStream::one(Err(err)); - } - Ok(args) => args, - }; - - let args = EvaluatedFilterCommandArgs::new( - host.clone(), - ctrl_c.clone(), - shell_manager.clone(), - call_info, - ); - - match func(args) { - Err(err) => return OutputStream::one(Err(err)), - Ok(stream) => stream, - } - } - }) - .flatten() - .to_output_stream()) - } -} - pub fn whole_stream_command(command: impl WholeStreamCommand + 'static) -> Command { Command(Arc::new(command)) } diff --git a/crates/nu-cli/src/commands/compact.rs b/crates/nu-cli/src/commands/compact.rs index fd8f5d10c5..d869dc28d9 100644 --- a/crates/nu-cli/src/commands/compact.rs +++ b/crates/nu-cli/src/commands/compact.rs @@ -37,22 +37,11 @@ impl WholeStreamCommand for Compact { } fn examples(&self) -> Vec { - vec![ - Example { - description: "Filter out all null entries in a list", - example: "echo [1 2 $null 3 $null $null] | compact", - result: Some(vec![ - UntaggedValue::int(1).into(), - UntaggedValue::int(2).into(), - UntaggedValue::int(3).into(), - ]), - }, - Example { - description: "Filter out all directory entries having no 'target'", - example: "ls -la | compact target", - result: None, - }, - ] + vec![Example { + description: "Filter out all directory entries having no 'target'", + example: "ls -la | compact target", + result: None, + }] } } diff --git a/crates/nu-cli/src/commands/do_.rs b/crates/nu-cli/src/commands/do_.rs index 8e2868028d..388fc4b0d4 100644 --- a/crates/nu-cli/src/commands/do_.rs +++ b/crates/nu-cli/src/commands/do_.rs @@ -95,15 +95,7 @@ async fn do_( block.set_redirect(block_redirection); - let result = run_block( - &block, - &mut context, - input, - &scope.it, - &scope.vars, - &scope.env, - ) - .await; + let result = run_block(&block, &mut context, input, scope).await; if ignore_errors { // To properly ignore errors we need to redirect stderr, consume it, and remove diff --git a/crates/nu-cli/src/commands/each/command.rs b/crates/nu-cli/src/commands/each/command.rs index 22c44dd0b3..834ac5b9b5 100644 --- a/crates/nu-cli/src/commands/each/command.rs +++ b/crates/nu-cli/src/commands/each/command.rs @@ -97,9 +97,7 @@ pub async fn process_row( &block, Arc::make_mut(&mut context), input_stream, - &input, - &scope.vars, - &scope.env, + Scope::append_it(scope, input), ) .await? .to_output_stream()) @@ -119,7 +117,7 @@ async fn each( ) -> Result { let registry = registry.clone(); let head = Arc::new(raw_args.call_info.args.head.clone()); - let scope = Arc::new(raw_args.call_info.scope.clone()); + let scope = raw_args.call_info.scope.clone(); let context = Arc::new(EvaluationContext::from_raw(&raw_args, ®istry)); let (each_args, input): (EachArgs, _) = raw_args.process(®istry).await?; let block = Arc::new(each_args.block); diff --git a/crates/nu-cli/src/commands/each/group.rs b/crates/nu-cli/src/commands/each/group.rs index 9d7a97b889..2af3d63864 100644 --- a/crates/nu-cli/src/commands/each/group.rs +++ b/crates/nu-cli/src/commands/each/group.rs @@ -53,7 +53,7 @@ impl WholeStreamCommand for EachGroup { ) -> Result { let registry = registry.clone(); let head = Arc::new(raw_args.call_info.args.head.clone()); - let scope = Arc::new(raw_args.call_info.scope.clone()); + let scope = raw_args.call_info.scope.clone(); let context = Arc::new(EvaluationContext::from_raw(&raw_args, ®istry)); let (each_args, input): (EachGroupArgs, _) = raw_args.process(®istry).await?; let block = Arc::new(each_args.block); diff --git a/crates/nu-cli/src/commands/each/window.rs b/crates/nu-cli/src/commands/each/window.rs index 167003d32b..22630b0457 100644 --- a/crates/nu-cli/src/commands/each/window.rs +++ b/crates/nu-cli/src/commands/each/window.rs @@ -57,7 +57,7 @@ impl WholeStreamCommand for EachWindow { ) -> Result { let registry = registry.clone(); let head = Arc::new(raw_args.call_info.args.head.clone()); - let scope = Arc::new(raw_args.call_info.scope.clone()); + let scope = raw_args.call_info.scope.clone(); let context = Arc::new(EvaluationContext::from_raw(&raw_args, ®istry)); let (each_args, mut input): (EachWindowArgs, _) = raw_args.process(®istry).await?; let block = Arc::new(each_args.block); diff --git a/crates/nu-cli/src/commands/format.rs b/crates/nu-cli/src/commands/format.rs index 942b13e4f1..f676c1fa2e 100644 --- a/crates/nu-cli/src/commands/format.rs +++ b/crates/nu-cli/src/commands/format.rs @@ -3,7 +3,7 @@ use crate::commands::WholeStreamCommand; use crate::evaluate::evaluate_baseline_expr; use crate::prelude::*; use nu_errors::ShellError; -use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue}; +use nu_protocol::{ReturnSuccess, Scope, Signature, SyntaxShape, UntaggedValue}; use nu_source::Tagged; use std::borrow::Borrow; @@ -54,7 +54,7 @@ async fn format_command( registry: &CommandRegistry, ) -> Result { let registry = Arc::new(registry.clone()); - let scope = Arc::new(args.call_info.scope.clone()); + let scope = args.call_info.scope.clone(); let (FormatArgs { pattern }, input) = args.process(®istry).await?; let format_pattern = format(&pattern); @@ -83,9 +83,7 @@ async fn format_command( let result = evaluate_baseline_expr( &full_column_path.0, ®istry, - &value, - &scope.vars, - &scope.env, + Scope::append_it(scope.clone(), value.clone()), ) .await; diff --git a/crates/nu-cli/src/commands/group_by.rs b/crates/nu-cli/src/commands/group_by.rs index 36cc0b8a53..9dfd072c7c 100644 --- a/crates/nu-cli/src/commands/group_by.rs +++ b/crates/nu-cli/src/commands/group_by.rs @@ -96,7 +96,7 @@ pub async fn group_by( let name = args.call_info.name_tag.clone(); let registry = registry.clone(); let head = Arc::new(args.call_info.args.head.clone()); - let scope = Arc::new(args.call_info.scope.clone()); + let scope = args.call_info.scope.clone(); let context = Arc::new(EvaluationContext::from_raw(&args, ®istry)); let (GroupByArgs { grouper }, input) = args.process(®istry).await?; diff --git a/crates/nu-cli/src/commands/if_.rs b/crates/nu-cli/src/commands/if_.rs index a757439118..81afa0ede8 100644 --- a/crates/nu-cli/src/commands/if_.rs +++ b/crates/nu-cli/src/commands/if_.rs @@ -4,7 +4,9 @@ use crate::commands::WholeStreamCommand; use crate::evaluate::evaluate_baseline_expr; use crate::prelude::*; use nu_errors::ShellError; -use nu_protocol::{hir::Block, hir::ClassifiedCommand, Signature, SyntaxShape, UntaggedValue}; +use nu_protocol::{ + hir::Block, hir::ClassifiedCommand, Scope, Signature, SyntaxShape, UntaggedValue, +}; pub struct If; @@ -72,7 +74,7 @@ async fn if_command( registry: &CommandRegistry, ) -> Result { let registry = Arc::new(registry.clone()); - let scope = Arc::new(raw_args.call_info.scope.clone()); + let scope = raw_args.call_info.scope.clone(); let tag = raw_args.call_info.name_tag.clone(); let context = Arc::new(EvaluationContext::from_raw(&raw_args, ®istry)); @@ -119,14 +121,12 @@ async fn if_command( let then_case = then_case.clone(); let else_case = else_case.clone(); let registry = registry.clone(); - let scope = scope.clone(); + let scope = Scope::append_it(scope.clone(), input); let mut context = context.clone(); async move { //FIXME: should we use the scope that's brought in as well? - let condition = - evaluate_baseline_expr(&condition, &*registry, &input, &scope.vars, &scope.env) - .await; + let condition = evaluate_baseline_expr(&condition, &*registry, scope.clone()).await; match condition { Ok(condition) => match condition.as_bool() { @@ -136,9 +136,7 @@ async fn if_command( &then_case, Arc::make_mut(&mut context), InputStream::empty(), - &input, - &scope.vars, - &scope.env, + scope, ) .await { @@ -151,9 +149,7 @@ async fn if_command( &else_case, Arc::make_mut(&mut context), InputStream::empty(), - &input, - &scope.vars, - &scope.env, + scope, ) .await { diff --git a/crates/nu-cli/src/commands/insert.rs b/crates/nu-cli/src/commands/insert.rs index 7754bd6f1e..69526ed0c8 100644 --- a/crates/nu-cli/src/commands/insert.rs +++ b/crates/nu-cli/src/commands/insert.rs @@ -63,15 +63,9 @@ async fn process_row( let for_block = input.clone(); let input_stream = once(async { Ok(for_block) }).to_input_stream(); - let result = run_block( - &block, - Arc::make_mut(&mut context), - input_stream, - &input, - &scope.vars, - &scope.env, - ) - .await; + let scope = Scope::append_it(scope, input.clone()); + + let result = run_block(&block, Arc::make_mut(&mut context), input_stream, scope).await; match result { Ok(mut stream) => { @@ -118,7 +112,11 @@ async fn process_row( Value { value: UntaggedValue::Primitive(Primitive::Nothing), .. - } => match scope.it.insert_data_at_column_path(&field, value.clone()) { + } => match scope + .it() + .unwrap_or_else(|| UntaggedValue::nothing().into_untagged_value()) + .insert_data_at_column_path(&field, value.clone()) + { Ok(v) => OutputStream::one(ReturnSuccess::value(v)), Err(e) => OutputStream::one(Err(e)), }, @@ -135,7 +133,7 @@ async fn insert( registry: &CommandRegistry, ) -> Result { let registry = registry.clone(); - let scope = Arc::new(raw_args.call_info.scope.clone()); + let scope = raw_args.call_info.scope.clone(); let context = Arc::new(EvaluationContext::from_raw(&raw_args, ®istry)); let (InsertArgs { column, value }, input) = raw_args.process(®istry).await?; let value = Arc::new(value); diff --git a/crates/nu-cli/src/commands/keep/until.rs b/crates/nu-cli/src/commands/keep/until.rs index a7b46fce49..db2efb21d1 100644 --- a/crates/nu-cli/src/commands/keep/until.rs +++ b/crates/nu-cli/src/commands/keep/until.rs @@ -3,7 +3,7 @@ use crate::evaluate::evaluate_baseline_expr; use crate::prelude::*; use log::trace; use nu_errors::ShellError; -use nu_protocol::{hir::ClassifiedCommand, Signature, SyntaxShape, UntaggedValue, Value}; +use nu_protocol::{hir::ClassifiedCommand, Scope, Signature, SyntaxShape, UntaggedValue, Value}; pub struct SubCommand; @@ -33,7 +33,7 @@ impl WholeStreamCommand for SubCommand { registry: &CommandRegistry, ) -> Result { let registry = Arc::new(registry.clone()); - let scope = Arc::new(args.call_info.scope.clone()); + let scope = args.call_info.scope.clone(); let call_info = args.evaluate_once(®istry).await?; @@ -85,19 +85,11 @@ impl WholeStreamCommand for SubCommand { .take_while(move |item| { let condition = condition.clone(); let registry = registry.clone(); - let scope = scope.clone(); - let item = item.clone(); + let scope = Scope::append_it(scope.clone(), item.clone()); trace!("ITEM = {:?}", item); async move { - let result = evaluate_baseline_expr( - &*condition, - ®istry, - &item, - &scope.vars, - &scope.env, - ) - .await; + let result = evaluate_baseline_expr(&*condition, ®istry, scope).await; trace!("RESULT = {:?}", result); !matches!(result, Ok(ref v) if v.is_true()) diff --git a/crates/nu-cli/src/commands/keep/while_.rs b/crates/nu-cli/src/commands/keep/while_.rs index bc60788552..c41e8b0c53 100644 --- a/crates/nu-cli/src/commands/keep/while_.rs +++ b/crates/nu-cli/src/commands/keep/while_.rs @@ -3,7 +3,7 @@ use crate::evaluate::evaluate_baseline_expr; use crate::prelude::*; use log::trace; use nu_errors::ShellError; -use nu_protocol::{hir::ClassifiedCommand, Signature, SyntaxShape, UntaggedValue, Value}; +use nu_protocol::{hir::ClassifiedCommand, Scope, Signature, SyntaxShape, UntaggedValue, Value}; pub struct SubCommand; @@ -33,7 +33,7 @@ impl WholeStreamCommand for SubCommand { registry: &CommandRegistry, ) -> Result { let registry = Arc::new(registry.clone()); - let scope = Arc::new(args.call_info.scope.clone()); + let scope = args.call_info.scope.clone(); let call_info = args.evaluate_once(®istry).await?; let block = call_info.args.expect_nth(0)?.clone(); @@ -84,20 +84,11 @@ impl WholeStreamCommand for SubCommand { .take_while(move |item| { let condition = condition.clone(); let registry = registry.clone(); - let scope = scope.clone(); - let item = item.clone(); - + let scope = Scope::append_it(scope.clone(), item.clone()); trace!("ITEM = {:?}", item); async move { - let result = evaluate_baseline_expr( - &*condition, - ®istry, - &item, - &scope.vars, - &scope.env, - ) - .await; + let result = evaluate_baseline_expr(&*condition, ®istry, scope).await; trace!("RESULT = {:?}", result); matches!(result, Ok(ref v) if v.is_true()) diff --git a/crates/nu-cli/src/commands/merge.rs b/crates/nu-cli/src/commands/merge.rs index 9eee098bb3..64b11e9712 100644 --- a/crates/nu-cli/src/commands/merge.rs +++ b/crates/nu-cli/src/commands/merge.rs @@ -60,21 +60,13 @@ async fn merge( let (merge_args, input): (MergeArgs, _) = raw_args.process(®istry).await?; let block = merge_args.block; - let table: Option> = match run_block( - &block, - &mut context, - InputStream::empty(), - &scope.it, - &scope.vars, - &scope.env, - ) - .await - { - Ok(mut stream) => Some(stream.drain_vec().await), - Err(err) => { - return Err(err); - } - }; + let table: Option> = + match run_block(&block, &mut context, InputStream::empty(), scope).await { + Ok(mut stream) => Some(stream.drain_vec().await), + Err(err) => { + return Err(err); + } + }; let table = table.unwrap_or_else(|| { vec![Value { diff --git a/crates/nu-cli/src/commands/reduce.rs b/crates/nu-cli/src/commands/reduce.rs index bca0025739..c85b7d3186 100644 --- a/crates/nu-cli/src/commands/reduce.rs +++ b/crates/nu-cli/src/commands/reduce.rs @@ -87,15 +87,9 @@ async fn process_row( let row_clone = row.clone(); let input_stream = once(async { Ok(row_clone) }).to_input_stream(); - Ok(run_block( - &block, - Arc::make_mut(&mut context), - input_stream, - &row, - &scope.vars, - &scope.env, - ) - .await?) + let scope = Scope::append_it(scope, row); + + Ok(run_block(&block, Arc::make_mut(&mut context), input_stream, scope).await?) } async fn reduce( @@ -133,7 +127,7 @@ async fn reduce( .enumerate() .fold(initial, move |acc, input| { let block = Arc::clone(&block); - let mut scope = base_scope.clone(); + let scope = base_scope.clone(); let context = Arc::clone(&context); let row = each::make_indexed_item(input.0 + ioffset, input.1); @@ -151,8 +145,8 @@ async fn reduce( UntaggedValue::table(&values).into_untagged_value() }; - scope.vars.insert(String::from("$acc"), f); - process_row(block, Arc::new(scope), context, row).await + let scope = Scope::append_var(scope, "$acc".into(), f); + process_row(block, scope, context, row).await } }) .await? @@ -162,7 +156,7 @@ async fn reduce( Ok(input .fold(initial, move |acc, row| { let block = Arc::clone(&block); - let mut scope = base_scope.clone(); + let scope = base_scope.clone(); let context = Arc::clone(&context); async { @@ -179,8 +173,8 @@ async fn reduce( UntaggedValue::table(&values).into_untagged_value() }; - scope.vars.insert(String::from("$acc"), f); - process_row(block, Arc::new(scope), context, row).await + let scope = Scope::append_var(scope, "$acc".into(), f); + process_row(block, scope, context, row).await } }) .await? diff --git a/crates/nu-cli/src/commands/run_alias.rs b/crates/nu-cli/src/commands/run_alias.rs index 3c5b9bd567..efbf288478 100644 --- a/crates/nu-cli/src/commands/run_alias.rs +++ b/crates/nu-cli/src/commands/run_alias.rs @@ -4,7 +4,7 @@ use crate::prelude::*; use derive_new::new; use nu_errors::ShellError; -use nu_protocol::{hir::Block, Signature, SyntaxShape}; +use nu_protocol::{hir::Block, Scope, Signature, SyntaxShape}; #[derive(new, Clone)] pub struct AliasCommand { @@ -47,26 +47,21 @@ impl WholeStreamCommand for AliasCommand { let mut context = EvaluationContext::from_args(&args, ®istry); let input = args.input; - let mut scope = call_info.scope.clone(); + let scope = call_info.scope.clone(); let evaluated = call_info.evaluate(®istry).await?; + + let mut vars = IndexMap::new(); if let Some(positional) = &evaluated.args.positional { for (pos, arg) in positional.iter().enumerate() { - scope - .vars - .insert(alias_command.args[pos].0.to_string(), arg.clone()); + vars.insert(alias_command.args[pos].0.to_string(), arg.clone()); } } + let scope = Scope::append_vars(scope, vars); + // FIXME: we need to patch up the spans to point at the top-level error - Ok(run_block( - &block, - &mut context, - input, - &scope.it, - &scope.vars, - &scope.env, - ) - .await? - .to_output_stream()) + Ok(run_block(&block, &mut context, input, scope) + .await? + .to_output_stream()) } } diff --git a/crates/nu-cli/src/commands/run_external.rs b/crates/nu-cli/src/commands/run_external.rs index e6c0040076..9bc9b41891 100644 --- a/crates/nu-cli/src/commands/run_external.rs +++ b/crates/nu-cli/src/commands/run_external.rs @@ -133,7 +133,7 @@ impl WholeStreamCommand for RunExternalCommand { command, &mut external_context, input, - &scope, + scope, external_redirection, ) .await; diff --git a/crates/nu-cli/src/commands/skip/until.rs b/crates/nu-cli/src/commands/skip/until.rs index d55fa17637..4e2bd0810d 100644 --- a/crates/nu-cli/src/commands/skip/until.rs +++ b/crates/nu-cli/src/commands/skip/until.rs @@ -3,7 +3,7 @@ use crate::evaluate::evaluate_baseline_expr; use crate::prelude::*; use log::trace; use nu_errors::ShellError; -use nu_protocol::{hir::ClassifiedCommand, Signature, SyntaxShape, UntaggedValue, Value}; +use nu_protocol::{hir::ClassifiedCommand, Scope, Signature, SyntaxShape, UntaggedValue, Value}; pub struct SubCommand; @@ -33,7 +33,7 @@ impl WholeStreamCommand for SubCommand { registry: &CommandRegistry, ) -> Result { let registry = Arc::new(registry.clone()); - let scope = Arc::new(args.call_info.scope.clone()); + let scope = args.call_info.scope.clone(); let call_info = args.evaluate_once(®istry).await?; let block = call_info.args.expect_nth(0)?.clone(); @@ -84,19 +84,11 @@ impl WholeStreamCommand for SubCommand { .skip_while(move |item| { let condition = condition.clone(); let registry = registry.clone(); - let scope = scope.clone(); - let item = item.clone(); + let scope = Scope::append_it(scope.clone(), item.clone()); trace!("ITEM = {:?}", item); async move { - let result = evaluate_baseline_expr( - &*condition, - ®istry, - &item, - &scope.vars, - &scope.env, - ) - .await; + let result = evaluate_baseline_expr(&*condition, ®istry, scope).await; trace!("RESULT = {:?}", result); !matches!(result, Ok(ref v) if v.is_true()) diff --git a/crates/nu-cli/src/commands/skip/while_.rs b/crates/nu-cli/src/commands/skip/while_.rs index 57ee93562a..b10c660943 100644 --- a/crates/nu-cli/src/commands/skip/while_.rs +++ b/crates/nu-cli/src/commands/skip/while_.rs @@ -3,7 +3,7 @@ use crate::evaluate::evaluate_baseline_expr; use crate::prelude::*; use log::trace; use nu_errors::ShellError; -use nu_protocol::{hir::ClassifiedCommand, Signature, SyntaxShape, UntaggedValue, Value}; +use nu_protocol::{hir::ClassifiedCommand, Scope, Signature, SyntaxShape, UntaggedValue, Value}; pub struct SubCommand; @@ -33,7 +33,7 @@ impl WholeStreamCommand for SubCommand { registry: &CommandRegistry, ) -> Result { let registry = Arc::new(registry.clone()); - let scope = Arc::new(args.call_info.scope.clone()); + let scope = args.call_info.scope.clone(); let call_info = args.evaluate_once(®istry).await?; let block = call_info.args.expect_nth(0)?.clone(); @@ -85,18 +85,11 @@ impl WholeStreamCommand for SubCommand { let item = item.clone(); let condition = condition.clone(); let registry = registry.clone(); - let scope = scope.clone(); + let scope = Scope::append_it(scope.clone(), item.clone()); trace!("ITEM = {:?}", item); async move { - let result = evaluate_baseline_expr( - &*condition, - ®istry, - &item, - &scope.vars, - &scope.env, - ) - .await; + let result = evaluate_baseline_expr(&*condition, ®istry, scope).await; trace!("RESULT = {:?}", result); matches!(result, Ok(ref v) if v.is_true()) diff --git a/crates/nu-cli/src/commands/update.rs b/crates/nu-cli/src/commands/update.rs index 9a5a1e2dc0..3983d976ed 100644 --- a/crates/nu-cli/src/commands/update.rs +++ b/crates/nu-cli/src/commands/update.rs @@ -70,15 +70,9 @@ async fn process_row( let for_block = input.clone(); let input_stream = once(async { Ok(for_block) }).to_input_stream(); - let result = run_block( - &block, - Arc::make_mut(&mut context), - input_stream, - &input, - &scope.vars, - &scope.env, - ) - .await; + let scope = Scope::append_it(scope, input.clone()); + + let result = run_block(&block, Arc::make_mut(&mut context), input_stream, scope).await; match result { Ok(mut stream) => { @@ -130,7 +124,8 @@ async fn process_row( value: UntaggedValue::Primitive(Primitive::Nothing), .. } => match scope - .it + .it() + .unwrap_or_else(|| UntaggedValue::nothing().into_untagged_value()) .replace_data_at_column_path(&field, replacement.clone()) { Some(v) => OutputStream::one(ReturnSuccess::value(v)), @@ -160,7 +155,7 @@ async fn update( ) -> Result { let registry = registry.clone(); let name_tag = Arc::new(raw_args.call_info.name_tag.clone()); - let scope = Arc::new(raw_args.call_info.scope.clone()); + let scope = raw_args.call_info.scope.clone(); let context = Arc::new(EvaluationContext::from_raw(&raw_args, ®istry)); let (UpdateArgs { field, replacement }, input) = raw_args.process(®istry).await?; let replacement = Arc::new(replacement); diff --git a/crates/nu-cli/src/commands/where_.rs b/crates/nu-cli/src/commands/where_.rs index e3e02c38a5..577b0fab81 100644 --- a/crates/nu-cli/src/commands/where_.rs +++ b/crates/nu-cli/src/commands/where_.rs @@ -3,7 +3,9 @@ use crate::commands::WholeStreamCommand; use crate::evaluate::evaluate_baseline_expr; use crate::prelude::*; use nu_errors::ShellError; -use nu_protocol::{hir::Block, hir::ClassifiedCommand, ReturnSuccess, Signature, SyntaxShape}; +use nu_protocol::{ + hir::Block, hir::ClassifiedCommand, ReturnSuccess, Scope, Signature, SyntaxShape, +}; pub struct Where; @@ -68,7 +70,7 @@ async fn where_command( registry: &CommandRegistry, ) -> Result { let registry = Arc::new(registry.clone()); - let scope = Arc::new(raw_args.call_info.scope.clone()); + let scope = raw_args.call_info.scope.clone(); let tag = raw_args.call_info.name_tag.clone(); let (WhereArgs { block }, input) = raw_args.process(®istry).await?; let condition = { @@ -104,13 +106,11 @@ async fn where_command( .filter_map(move |input| { let condition = condition.clone(); let registry = registry.clone(); - let scope = scope.clone(); + let scope = Scope::append_it(scope.clone(), input.clone()); async move { //FIXME: should we use the scope that's brought in as well? - let condition = - evaluate_baseline_expr(&condition, &*registry, &input, &scope.vars, &scope.env) - .await; + let condition = evaluate_baseline_expr(&condition, &*registry, scope).await; match condition { Ok(condition) => match condition.as_bool() { diff --git a/crates/nu-cli/src/commands/with_env.rs b/crates/nu-cli/src/commands/with_env.rs index 556ca7fb12..dac4149747 100644 --- a/crates/nu-cli/src/commands/with_env.rs +++ b/crates/nu-cli/src/commands/with_env.rs @@ -2,7 +2,9 @@ use crate::commands::classified::block::run_block; use crate::commands::WholeStreamCommand; use crate::prelude::*; use nu_errors::ShellError; -use nu_protocol::{hir::Block, Signature, SpannedTypeName, SyntaxShape, UntaggedValue, Value}; +use nu_protocol::{ + hir::Block, Scope, Signature, SpannedTypeName, SyntaxShape, UntaggedValue, Value, +}; pub struct WithEnv; @@ -77,23 +79,23 @@ async fn with_env( let registry = registry.clone(); let mut context = EvaluationContext::from_raw(&raw_args, ®istry); - let mut scope = raw_args.call_info.scope.clone(); + let scope = raw_args.call_info.scope.clone(); let (WithEnvArgs { variable, block }, input) = raw_args.process(®istry).await?; + let mut env = IndexMap::new(); + match &variable.value { UntaggedValue::Table(table) => { if table.len() == 1 { // single row([[X W]; [Y Z]]) for (k, v) in table[0].row_entries() { - scope.env.insert(k.clone(), v.convert_to_string()); + env.insert(k.clone(), v.convert_to_string()); } } else { // primitive values([X Y W Z]) for row in table.chunks(2) { if row.len() == 2 && row[0].is_primitive() && row[1].is_primitive() { - scope - .env - .insert(row[0].convert_to_string(), row[1].convert_to_string()); + env.insert(row[0].convert_to_string(), row[1].convert_to_string()); } } } @@ -101,7 +103,7 @@ async fn with_env( // when get object by `open x.json` or `from json` UntaggedValue::Row(row) => { for (k, v) in &row.entries { - scope.env.insert(k.clone(), v.convert_to_string()); + env.insert(k.clone(), v.convert_to_string()); } } _ => { @@ -112,15 +114,9 @@ async fn with_env( } }; - let result = run_block( - &block, - &mut context, - input, - &scope.it, - &scope.vars, - &scope.env, - ) - .await; + let scope = Scope::append_env(scope, env); + + let result = run_block(&block, &mut context, input, scope).await; result.map(|x| x.to_output_stream()) } diff --git a/crates/nu-cli/src/evaluate/evaluate_args.rs b/crates/nu-cli/src/evaluate/evaluate_args.rs index cd8fe42789..84fec45e86 100644 --- a/crates/nu-cli/src/evaluate/evaluate_args.rs +++ b/crates/nu-cli/src/evaluate/evaluate_args.rs @@ -4,18 +4,18 @@ use crate::evaluate::evaluate_baseline_expr; use indexmap::IndexMap; use nu_errors::ShellError; use nu_protocol::{hir, EvaluatedArgs, Scope, UntaggedValue, Value}; +use std::sync::Arc; pub(crate) async fn evaluate_args( call: &hir::Call, registry: &CommandRegistry, - scope: &Scope, + scope: Arc, ) -> Result { let mut positional_args: Vec = vec![]; if let Some(positional) = &call.positional { for pos in positional { - let result = - evaluate_baseline_expr(pos, registry, &scope.it, &scope.vars, &scope.env).await?; + let result = evaluate_baseline_expr(pos, registry, scope.clone()).await?; positional_args.push(result); } } @@ -37,8 +37,7 @@ pub(crate) async fn evaluate_args( hir::NamedValue::Value(_, expr) => { named_args.insert( name.clone(), - evaluate_baseline_expr(expr, registry, &scope.it, &scope.vars, &scope.env) - .await?, + evaluate_baseline_expr(expr, registry, scope.clone()).await?, ); } _ => {} diff --git a/crates/nu-cli/src/evaluate/evaluator.rs b/crates/nu-cli/src/evaluate/evaluator.rs index b57ec497b8..84005a71e6 100644 --- a/crates/nu-cli/src/evaluate/evaluator.rs +++ b/crates/nu-cli/src/evaluate/evaluator.rs @@ -7,16 +7,14 @@ use log::trace; use nu_errors::{ArgumentError, ShellError}; use nu_protocol::hir::{self, Expression, ExternalRedirection, RangeOperator, SpannedExpression}; use nu_protocol::{ - ColumnPath, Primitive, RangeInclusion, UnspannedPathMember, UntaggedValue, Value, + ColumnPath, Primitive, RangeInclusion, Scope, UnspannedPathMember, UntaggedValue, Value, }; #[async_recursion] pub(crate) async fn evaluate_baseline_expr( expr: &SpannedExpression, registry: &CommandRegistry, - it: &Value, - vars: &IndexMap, - env: &IndexMap, + scope: Arc, ) -> Result { let tag = Tag { span: expr.span, @@ -33,14 +31,14 @@ pub(crate) async fn evaluate_baseline_expr( Expression::Synthetic(hir::Synthetic::String(s)) => { Ok(UntaggedValue::string(s).into_untagged_value()) } - Expression::Variable(var) => evaluate_reference(&var, it, vars, env, tag), + Expression::Variable(var) => evaluate_reference(&var, scope, tag), Expression::Command => unimplemented!(), - Expression::Invocation(block) => evaluate_invocation(block, registry, it, vars, env).await, + Expression::Invocation(block) => evaluate_invocation(block, registry, scope).await, Expression::ExternalCommand(_) => unimplemented!(), Expression::Binary(binary) => { // TODO: If we want to add short-circuiting, we'll need to move these down - let left = evaluate_baseline_expr(&binary.left, registry, it, vars, env).await?; - let right = evaluate_baseline_expr(&binary.right, registry, it, vars, env).await?; + let left = evaluate_baseline_expr(&binary.left, registry, scope.clone()).await?; + let right = evaluate_baseline_expr(&binary.right, registry, scope).await?; trace!("left={:?} right={:?}", left.value, right.value); @@ -62,13 +60,13 @@ pub(crate) async fn evaluate_baseline_expr( } Expression::Range(range) => { let left = if let Some(left) = &range.left { - evaluate_baseline_expr(&left, registry, it, vars, env).await? + evaluate_baseline_expr(&left, registry, scope.clone()).await? } else { Value::nothing() }; let right = if let Some(right) = &range.right { - evaluate_baseline_expr(&right, registry, it, vars, env).await? + evaluate_baseline_expr(&right, registry, scope).await? } else { Value::nothing() }; @@ -94,7 +92,7 @@ pub(crate) async fn evaluate_baseline_expr( let mut output_headers = vec![]; for expr in headers { - let val = evaluate_baseline_expr(&expr, registry, it, vars, env).await?; + let val = evaluate_baseline_expr(&expr, registry, scope.clone()).await?; let header = val.as_string()?; output_headers.push(header); @@ -122,7 +120,7 @@ pub(crate) async fn evaluate_baseline_expr( let mut row_output = IndexMap::new(); for cell in output_headers.iter().zip(row.iter()) { - let val = evaluate_baseline_expr(&cell.1, registry, it, vars, env).await?; + let val = evaluate_baseline_expr(&cell.1, registry, scope.clone()).await?; row_output.insert(cell.0.clone(), val); } output_table.push(UntaggedValue::row(row_output).into_value(tag.clone())); @@ -134,7 +132,7 @@ pub(crate) async fn evaluate_baseline_expr( let mut exprs = vec![]; for expr in list { - let expr = evaluate_baseline_expr(&expr, registry, it, vars, env).await?; + let expr = evaluate_baseline_expr(&expr, registry, scope.clone()).await?; exprs.push(expr); } @@ -142,7 +140,7 @@ pub(crate) async fn evaluate_baseline_expr( } Expression::Block(block) => Ok(UntaggedValue::Block(block.clone()).into_value(&tag)), Expression::Path(path) => { - let value = evaluate_baseline_expr(&path.head, registry, it, vars, env).await?; + let value = evaluate_baseline_expr(&path.head, registry, scope).await?; let mut item = value; for member in &path.tail { @@ -208,15 +206,20 @@ fn evaluate_literal(literal: &hir::Literal, span: Span) -> Value { fn evaluate_reference( name: &hir::Variable, - it: &Value, - vars: &IndexMap, - env: &IndexMap, + scope: Arc, tag: Tag, ) -> Result { match name { - hir::Variable::It(_) => Ok(it.clone()), + hir::Variable::It(_) => match scope.it() { + Some(v) => Ok(v), + None => Err(ShellError::labeled_error( + "$it variable not in scope", + "not in scope (are you missing an 'each'?)", + tag.span, + )), + }, hir::Variable::Other(name, _) => match name { - x if x == "$nu" => crate::evaluate::variables::nu(env, tag), + x if x == "$nu" => crate::evaluate::variables::nu(&scope.env(), tag), x if x == "$true" => Ok(Value { value: UntaggedValue::boolean(true), tag, @@ -225,10 +228,14 @@ fn evaluate_reference( value: UntaggedValue::boolean(false), tag, }), - x => Ok(vars - .get(x) - .cloned() - .unwrap_or_else(|| UntaggedValue::nothing().into_value(tag))), + x => match scope.var(x) { + Some(v) => Ok(v), + None => Err(ShellError::labeled_error( + "Variable not in scope", + "unknown variable", + tag.span, + )), + }, }, } } @@ -236,20 +243,21 @@ fn evaluate_reference( async fn evaluate_invocation( block: &hir::Block, registry: &CommandRegistry, - it: &Value, - vars: &IndexMap, - env: &IndexMap, + scope: Arc, ) -> Result { // FIXME: we should use a real context here let mut context = EvaluationContext::basic()?; context.registry = registry.clone(); - let input = InputStream::one(it.clone()); + let input = match scope.it() { + Some(it) => InputStream::one(it), + None => InputStream::empty(), + }; let mut block = block.clone(); block.set_redirect(ExternalRedirection::Stdout); - let result = run_block(&block, &mut context, input, it, vars, env).await?; + let result = run_block(&block, &mut context, input, scope).await?; let output = result.into_vec().await; diff --git a/crates/nu-cli/src/evaluation_context.rs b/crates/nu-cli/src/evaluation_context.rs index 49cc0231f6..6809ebc04b 100644 --- a/crates/nu-cli/src/evaluation_context.rs +++ b/crates/nu-cli/src/evaluation_context.rs @@ -152,18 +152,18 @@ impl EvaluationContext { command: Command, name_tag: Tag, args: hir::Call, - scope: &Scope, + scope: Arc, input: InputStream, ) -> Result { let command_args = self.command_args(args, input, name_tag, scope); command.run(command_args, self.registry()).await } - fn call_info(&self, args: hir::Call, name_tag: Tag, scope: &Scope) -> UnevaluatedCallInfo { + fn call_info(&self, args: hir::Call, name_tag: Tag, scope: Arc) -> UnevaluatedCallInfo { UnevaluatedCallInfo { args, name_tag, - scope: scope.clone(), + scope, } } @@ -172,7 +172,7 @@ impl EvaluationContext { args: hir::Call, input: InputStream, name_tag: Tag, - scope: &Scope, + scope: Arc, ) -> CommandArgs { CommandArgs { host: self.host.clone(), diff --git a/crates/nu-cli/src/examples.rs b/crates/nu-cli/src/examples.rs index ea349f990f..6a2a3676b5 100644 --- a/crates/nu-cli/src/examples.rs +++ b/crates/nu-cli/src/examples.rs @@ -1,9 +1,8 @@ use futures::executor::block_on; -use crate::prelude::*; use nu_errors::ShellError; use nu_protocol::hir::ClassifiedBlock; -use nu_protocol::{ShellTypeName, Value}; +use nu_protocol::{Scope, ShellTypeName, Value}; use crate::commands::classified::block::run_block; use crate::commands::{whole_stream_command, BuildString, Each, Echo, StrCollect}; @@ -83,17 +82,12 @@ async fn evaluate_block( let input_stream = InputStream::empty(); let env = ctx.get_env(); - Ok(run_block( - &block.block, - ctx, - input_stream, - &Value::nothing(), - &IndexMap::new(), - &env, - ) - .await? - .into_vec() - .await) + let scope = Scope::from_env(env); + + Ok(run_block(&block.block, ctx, input_stream, scope) + .await? + .into_vec() + .await) } // TODO probably something already available to do this diff --git a/crates/nu-protocol/src/value/evaluate.rs b/crates/nu-protocol/src/value/evaluate.rs index e400d19358..bac9e119ae 100644 --- a/crates/nu-protocol/src/value/evaluate.rs +++ b/crates/nu-protocol/src/value/evaluate.rs @@ -2,30 +2,121 @@ use crate::value::Value; use indexmap::IndexMap; use serde::{Deserialize, Serialize}; use std::fmt::Debug; +use std::sync::Arc; /// An evaluation scope. Scopes map variable names to Values and aid in evaluating blocks and expressions. /// Additionally, holds the value for the special $it variable, a variable used to refer to the value passing /// through the pipeline at that moment #[derive(Deserialize, Serialize, Debug, Clone)] pub struct Scope { - pub it: Value, - pub vars: IndexMap, - pub env: IndexMap, + vars: IndexMap, + env: IndexMap, + parent: Option>, } impl Scope { - /// Create an empty scope - pub fn new() -> Scope { - Scope { - it: Value::nothing(), - vars: IndexMap::new(), - env: IndexMap::new(), + pub fn vars(&self) -> IndexMap { + //FIXME: should this be an interator? + + let mut output = IndexMap::new(); + + for v in &self.vars { + output.insert(v.0.clone(), v.1.clone()); + } + + if let Some(parent) = &self.parent { + for v in parent.vars() { + if !output.contains_key(&v.0) { + output.insert(v.0.clone(), v.1.clone()); + } + } + } + + output + } + + pub fn env(&self) -> IndexMap { + //FIXME: should this be an interator? + + let mut output = IndexMap::new(); + + for v in &self.env { + output.insert(v.0.clone(), v.1.clone()); + } + + if let Some(parent) = &self.parent { + for v in parent.env() { + if !output.contains_key(&v.0) { + output.insert(v.0.clone(), v.1.clone()); + } + } + } + + output + } + + pub fn var(&self, name: &str) -> Option { + if let Some(value) = self.vars().get(name) { + Some(value.clone()) + } else { + None } } -} -impl Default for Scope { - fn default() -> Scope { - Scope::new() + pub fn it(&self) -> Option { + self.var("$it") + } + + pub fn from_env(env: IndexMap) -> Arc { + Arc::new(Scope { + vars: IndexMap::new(), + env, + parent: None, + }) + } + + pub fn append_it(this: Arc, it: Value) -> Arc { + let mut vars = IndexMap::new(); + vars.insert("$it".into(), it); + Arc::new(Scope { + vars, + env: IndexMap::new(), + parent: Some(this), + }) + } + + pub fn append_var(this: Arc, name: String, value: Value) -> Arc { + let mut vars = IndexMap::new(); + vars.insert(name, value); + Arc::new(Scope { + vars, + env: IndexMap::new(), + parent: Some(this), + }) + } + + pub fn append_vars(this: Arc, vars: IndexMap) -> Arc { + Arc::new(Scope { + vars, + env: IndexMap::new(), + parent: Some(this), + }) + } + + pub fn append_env(this: Arc, env: IndexMap) -> Arc { + Arc::new(Scope { + vars: IndexMap::new(), + env, + parent: Some(this), + }) + } + + /// Create an empty scope + pub fn create() -> Arc { + Arc::new(Scope { + vars: IndexMap::new(), + env: IndexMap::new(), + parent: None, + }) } }