Refactor scope (#2602)

* Refactor scope to have parents

* Refactor scope to have parents

* Refactor scope to have parents

* Clippy

Co-authored-by: Jonathan Turner <jonathan@pop-os.localdomain>
This commit is contained in:
Jonathan Turner 2020-09-26 11:40:02 +12:00 committed by GitHub
parent 9dc88f8a95
commit cb7723f423
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 288 additions and 423 deletions

View File

@ -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<dyn Error>> {
&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<S
&classified_block.block,
ctx,
input_stream,
&Value::nothing(),
&IndexMap::new(),
&env,
Scope::from_env(env),
)
.await?
.collect_string(Tag::unknown())
@ -1021,9 +1017,7 @@ pub async fn process_line(
&classified_block.block,
ctx,
input_stream,
&Value::nothing(),
&IndexMap::new(),
&env,
Scope::from_env(env),
)
.await
{

View File

@ -318,7 +318,7 @@ fn create_default_command_args(context: &RunnableContextWithoutInput) -> RawComm
external_redirection: ExternalRedirection::Stdout,
},
name_tag: context.name.clone(),
scope: Scope::new(),
scope: Scope::create(),
},
}
}

View File

@ -83,16 +83,18 @@ async fn benchmark(
let scope = raw_args.call_info.scope.clone();
let (BenchmarkArgs { block, passthrough }, input) = raw_args.process(&registry).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<T, Output>(
passthrough: Option<Block>,
tag: T,
context: &mut EvaluationContext,
scope: &Scope,
scope: Arc<Scope>,
) -> Result<OutputStream, ShellError>
where
T: Into<Tag> + 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())

View File

@ -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<String, Value>,
env: &IndexMap<String, String>,
scope: Arc<Scope>,
) -> Result<InputStream, ShellError> {
let mut output: Result<InputStream, ShellError> = 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<String, Value>,
env: &IndexMap<String, String>,
scope: Arc<Scope>,
) -> Result<InputStream, ShellError> {
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?
}
};
}

View File

@ -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<String, Value>,
env: &IndexMap<String, String>,
scope: Arc<Scope>,
) -> Result<InputStream, ShellError> {
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, &registry, it, vars, env).await?;
let output = evaluate_baseline_expr(&expr, &registry, scope).await?;
Ok(once(async { Ok(output) }).to_input_stream())
}

View File

@ -21,7 +21,7 @@ pub(crate) async fn run_external_command(
command: ExternalCommand,
context: &mut EvaluationContext,
input: InputStream,
scope: &Scope,
scope: Arc<Scope>,
external_redirection: ExternalRedirection,
) -> Result<InputStream, ShellError> {
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<Scope>,
external_redirection: ExternalRedirection,
) -> Result<InputStream, ShellError> {
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<Scope>,
) -> Result<InputStream, ShellError> {
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

View File

@ -11,20 +11,13 @@ pub(crate) async fn run_internal_command(
command: InternalCommand,
context: &mut EvaluationContext,
input: InputStream,
it: &Value,
vars: &IndexMap<String, Value>,
env: &IndexMap<String, String>,
scope: Arc<Scope>,
) -> Result<InputStream, ShellError> {
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

View File

@ -130,7 +130,7 @@ async fn run_filter(
UntaggedValue::Primitive(Primitive::EndOfStream).into_untagged_value()
]);
let args = args.evaluate_once_with_scope(&registry, &scope).await?;
let args = args.evaluate_once_with_scope(&registry, scope).await?;
let real_path = Path::new(&path);
let ext = real_path.extension();

View File

@ -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<Scope>,
}
impl UnevaluatedCallInfo {
pub async fn evaluate(self, registry: &CommandRegistry) -> Result<CallInfo, ShellError> {
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<CallInfo, ShellError> {
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<Scope>,
) -> Result<EvaluatedWholeStreamCommandArgs, ShellError> {
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<parking_lot::Mutex<dyn Host>>,
ctrl_c: Arc<AtomicBool>,
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<OutputStream, ShellError>,
}
#[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<OutputStream, ShellError> {
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))
}

View File

@ -37,22 +37,11 @@ impl WholeStreamCommand for Compact {
}
fn examples(&self) -> Vec<Example> {
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,
}]
}
}

View File

@ -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

View File

@ -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<OutputStream, ShellError> {
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, &registry));
let (each_args, input): (EachArgs, _) = raw_args.process(&registry).await?;
let block = Arc::new(each_args.block);

View File

@ -53,7 +53,7 @@ impl WholeStreamCommand for EachGroup {
) -> Result<OutputStream, ShellError> {
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, &registry));
let (each_args, input): (EachGroupArgs, _) = raw_args.process(&registry).await?;
let block = Arc::new(each_args.block);

View File

@ -57,7 +57,7 @@ impl WholeStreamCommand for EachWindow {
) -> Result<OutputStream, ShellError> {
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, &registry));
let (each_args, mut input): (EachWindowArgs, _) = raw_args.process(&registry).await?;
let block = Arc::new(each_args.block);

View File

@ -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<OutputStream, ShellError> {
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(&registry).await?;
let format_pattern = format(&pattern);
@ -83,9 +83,7 @@ async fn format_command(
let result = evaluate_baseline_expr(
&full_column_path.0,
&registry,
&value,
&scope.vars,
&scope.env,
Scope::append_it(scope.clone(), value.clone()),
)
.await;

View File

@ -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, &registry));
let (GroupByArgs { grouper }, input) = args.process(&registry).await?;

View File

@ -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<OutputStream, ShellError> {
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, &registry));
@ -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
{

View File

@ -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<OutputStream, ShellError> {
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, &registry));
let (InsertArgs { column, value }, input) = raw_args.process(&registry).await?;
let value = Arc::new(value);

View File

@ -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<OutputStream, ShellError> {
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(&registry).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,
&registry,
&item,
&scope.vars,
&scope.env,
)
.await;
let result = evaluate_baseline_expr(&*condition, &registry, scope).await;
trace!("RESULT = {:?}", result);
!matches!(result, Ok(ref v) if v.is_true())

View File

@ -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<OutputStream, ShellError> {
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(&registry).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,
&registry,
&item,
&scope.vars,
&scope.env,
)
.await;
let result = evaluate_baseline_expr(&*condition, &registry, scope).await;
trace!("RESULT = {:?}", result);
matches!(result, Ok(ref v) if v.is_true())

View File

@ -60,21 +60,13 @@ async fn merge(
let (merge_args, input): (MergeArgs, _) = raw_args.process(&registry).await?;
let block = merge_args.block;
let table: Option<Vec<Value>> = 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<Vec<Value>> =
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 {

View File

@ -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?

View File

@ -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, &registry);
let input = args.input;
let mut scope = call_info.scope.clone();
let scope = call_info.scope.clone();
let evaluated = call_info.evaluate(&registry).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())
}
}

View File

@ -133,7 +133,7 @@ impl WholeStreamCommand for RunExternalCommand {
command,
&mut external_context,
input,
&scope,
scope,
external_redirection,
)
.await;

View File

@ -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<OutputStream, ShellError> {
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(&registry).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,
&registry,
&item,
&scope.vars,
&scope.env,
)
.await;
let result = evaluate_baseline_expr(&*condition, &registry, scope).await;
trace!("RESULT = {:?}", result);
!matches!(result, Ok(ref v) if v.is_true())

View File

@ -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<OutputStream, ShellError> {
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(&registry).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,
&registry,
&item,
&scope.vars,
&scope.env,
)
.await;
let result = evaluate_baseline_expr(&*condition, &registry, scope).await;
trace!("RESULT = {:?}", result);
matches!(result, Ok(ref v) if v.is_true())

View File

@ -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<OutputStream, ShellError> {
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, &registry));
let (UpdateArgs { field, replacement }, input) = raw_args.process(&registry).await?;
let replacement = Arc::new(replacement);

View File

@ -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<OutputStream, ShellError> {
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(&registry).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() {

View File

@ -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, &registry);
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(&registry).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())
}

View File

@ -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<Scope>,
) -> Result<EvaluatedArgs, ShellError> {
let mut positional_args: Vec<Value> = 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?,
);
}
_ => {}

View File

@ -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<String, Value>,
env: &IndexMap<String, String>,
scope: Arc<Scope>,
) -> Result<Value, ShellError> {
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<String, Value>,
env: &IndexMap<String, String>,
scope: Arc<Scope>,
tag: Tag,
) -> Result<Value, ShellError> {
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<String, Value>,
env: &IndexMap<String, String>,
scope: Arc<Scope>,
) -> Result<Value, ShellError> {
// 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;

View File

@ -152,18 +152,18 @@ impl EvaluationContext {
command: Command,
name_tag: Tag,
args: hir::Call,
scope: &Scope,
scope: Arc<Scope>,
input: InputStream,
) -> Result<OutputStream, ShellError> {
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<Scope>) -> 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<Scope>,
) -> CommandArgs {
CommandArgs {
host: self.host.clone(),

View File

@ -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

View File

@ -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<String, Value>,
pub env: IndexMap<String, String>,
vars: IndexMap<String, Value>,
env: IndexMap<String, String>,
parent: Option<Arc<Scope>>,
}
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<String, Value> {
//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<String, String> {
//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<Value> {
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<Value> {
self.var("$it")
}
pub fn from_env(env: IndexMap<String, String>) -> Arc<Scope> {
Arc::new(Scope {
vars: IndexMap::new(),
env,
parent: None,
})
}
pub fn append_it(this: Arc<Self>, it: Value) -> Arc<Scope> {
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<Self>, name: String, value: Value) -> Arc<Scope> {
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<Self>, vars: IndexMap<String, Value>) -> Arc<Scope> {
Arc::new(Scope {
vars,
env: IndexMap::new(),
parent: Some(this),
})
}
pub fn append_env(this: Arc<Self>, env: IndexMap<String, String>) -> Arc<Scope> {
Arc::new(Scope {
vars: IndexMap::new(),
env,
parent: Some(this),
})
}
/// Create an empty scope
pub fn create() -> Arc<Scope> {
Arc::new(Scope {
vars: IndexMap::new(),
env: IndexMap::new(),
parent: None,
})
}
}