diff --git a/Cargo.lock b/Cargo.lock index f2c4783660..633dba5418 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2192,6 +2192,7 @@ dependencies = [ "getset", "indexmap", "language-reporting", + "log", "natural", "nu-build", "nu-errors", diff --git a/crates/nu-cli/src/cli.rs b/crates/nu-cli/src/cli.rs index 19f3cb8a90..9b4b9f992f 100644 --- a/crates/nu-cli/src/cli.rs +++ b/crates/nu-cli/src/cli.rs @@ -10,7 +10,7 @@ use crate::prelude::*; use futures_codec::FramedRead; use nu_errors::ShellError; -use nu_parser::{ClassifiedCommand, ExternalCommand}; +use nu_protocol::hir::{ClassifiedCommand, ExternalCommand}; use nu_protocol::{Primitive, ReturnSuccess, Signature, UntaggedValue, Value}; use log::{debug, trace}; @@ -304,6 +304,7 @@ pub fn create_default_context( whole_stream_command(Range), whole_stream_command(Rename), whole_stream_command(Uniq), + per_item_command(Each), // Table manipulation whole_stream_command(Shuffle), whole_stream_command(Wrap), diff --git a/crates/nu-cli/src/commands.rs b/crates/nu-cli/src/commands.rs index 03b9a826cc..88e392399f 100644 --- a/crates/nu-cli/src/commands.rs +++ b/crates/nu-cli/src/commands.rs @@ -20,6 +20,7 @@ pub(crate) mod date; pub(crate) mod debug; pub(crate) mod default; pub(crate) mod du; +pub(crate) mod each; pub(crate) mod echo; pub(crate) mod edit; pub(crate) mod enter; @@ -124,6 +125,7 @@ pub(crate) use date::Date; pub(crate) use debug::Debug; pub(crate) use default::Default; pub(crate) use du::Du; +pub(crate) use each::Each; pub(crate) use echo::Echo; pub(crate) use edit::Edit; pub(crate) mod kill; diff --git a/crates/nu-cli/src/commands/autoview.rs b/crates/nu-cli/src/commands/autoview.rs index 6017edd779..06d5f7e6fd 100644 --- a/crates/nu-cli/src/commands/autoview.rs +++ b/crates/nu-cli/src/commands/autoview.rs @@ -2,7 +2,7 @@ use crate::commands::UnevaluatedCallInfo; use crate::commands::WholeStreamCommand; use crate::prelude::*; use nu_errors::ShellError; -use nu_parser::{hir, hir::Expression, hir::Literal, hir::SpannedExpression}; +use nu_protocol::{hir, hir::Expression, hir::Literal, hir::SpannedExpression}; use nu_protocol::{Primitive, ReturnSuccess, Signature, UntaggedValue, Value}; use std::sync::atomic::AtomicBool; use std::sync::atomic::Ordering; diff --git a/crates/nu-cli/src/commands/classified/dynamic.rs b/crates/nu-cli/src/commands/classified/dynamic.rs index ed933bb6a5..3b912a11e4 100644 --- a/crates/nu-cli/src/commands/classified/dynamic.rs +++ b/crates/nu-cli/src/commands/classified/dynamic.rs @@ -1,5 +1,5 @@ use derive_new::new; -use nu_parser::hir; +use nu_protocol::hir; #[derive(new, Debug)] pub(crate) struct Command { diff --git a/crates/nu-cli/src/commands/classified/external.rs b/crates/nu-cli/src/commands/classified/external.rs index 8c77bcc335..9984f85c0a 100644 --- a/crates/nu-cli/src/commands/classified/external.rs +++ b/crates/nu-cli/src/commands/classified/external.rs @@ -6,8 +6,7 @@ use futures::stream::StreamExt; use futures_codec::FramedRead; use log::trace; use nu_errors::ShellError; -use nu_parser::ExternalArg; -use nu_parser::ExternalCommand; +use nu_protocol::hir::{ExternalArg, ExternalCommand}; use nu_protocol::{ColumnPath, Primitive, ShellTypeName, UntaggedValue, Value}; use nu_source::{Tag, Tagged}; use nu_value_ext::as_column_path; diff --git a/crates/nu-cli/src/commands/classified/internal.rs b/crates/nu-cli/src/commands/classified/internal.rs index 0e3bcb63c4..f14fa29268 100644 --- a/crates/nu-cli/src/commands/classified/internal.rs +++ b/crates/nu-cli/src/commands/classified/internal.rs @@ -2,7 +2,7 @@ use crate::commands::UnevaluatedCallInfo; use crate::prelude::*; use log::{log_enabled, trace}; use nu_errors::ShellError; -use nu_parser::InternalCommand; +use nu_protocol::hir::InternalCommand; use nu_protocol::{CommandAction, Primitive, ReturnSuccess, UntaggedValue, Value}; pub(crate) fn run_internal_command( @@ -61,7 +61,7 @@ pub(crate) fn run_internal_command( ctrl_c: context.ctrl_c.clone(), shell_manager: context.shell_manager.clone(), call_info: UnevaluatedCallInfo { - args: nu_parser::hir::Call { + args: nu_protocol::hir::Call { head: command.args.head, positional: None, named: None, diff --git a/crates/nu-cli/src/commands/classified/pipeline.rs b/crates/nu-cli/src/commands/classified/pipeline.rs index a61725b4e0..f99bd3c443 100644 --- a/crates/nu-cli/src/commands/classified/pipeline.rs +++ b/crates/nu-cli/src/commands/classified/pipeline.rs @@ -3,7 +3,7 @@ use crate::commands::classified::internal::run_internal_command; use crate::context::Context; use crate::stream::InputStream; use nu_errors::ShellError; -use nu_parser::{ClassifiedCommand, ClassifiedPipeline}; +use nu_protocol::hir::{ClassifiedCommand, ClassifiedPipeline}; pub(crate) async fn run_pipeline( pipeline: ClassifiedPipeline, diff --git a/crates/nu-cli/src/commands/command.rs b/crates/nu-cli/src/commands/command.rs index d0d141fc83..4995e9178e 100644 --- a/crates/nu-cli/src/commands/command.rs +++ b/crates/nu-cli/src/commands/command.rs @@ -6,7 +6,7 @@ use crate::prelude::*; use derive_new::new; use getset::Getters; use nu_errors::ShellError; -use nu_parser::hir; +use nu_protocol::hir; use nu_protocol::{CallInfo, EvaluatedArgs, ReturnValue, Scope, Signature, Value}; use serde::{Deserialize, Serialize}; use std::ops::Deref; @@ -364,6 +364,14 @@ impl EvaluatedCommandArgs { self.call_info.args.nth(pos) } + /// Get the nth positional argument, error if not possible + pub fn expect_nth(&self, pos: usize) -> Result<&Value, ShellError> { + match self.call_info.args.nth(pos) { + None => Err(ShellError::unimplemented("Better error: expect_nth")), + Some(item) => Ok(item), + } + } + pub fn get(&self, name: &str) -> Option<&Value> { self.call_info.args.get(name) } diff --git a/crates/nu-cli/src/commands/each.rs b/crates/nu-cli/src/commands/each.rs new file mode 100644 index 0000000000..cb8f0669be --- /dev/null +++ b/crates/nu-cli/src/commands/each.rs @@ -0,0 +1,89 @@ +use crate::commands::classified::pipeline::run_pipeline; + +use crate::commands::PerItemCommand; +use crate::context::CommandRegistry; +use crate::prelude::*; +use nu_errors::ShellError; +use nu_protocol::{ + hir::ClassifiedPipeline, CallInfo, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value, +}; + +pub struct Each; + +impl PerItemCommand for Each { + fn name(&self) -> &str { + "each" + } + + fn signature(&self) -> Signature { + Signature::build("each").required( + "block", + SyntaxShape::Block, + "the block to run on each row", + ) + } + + fn usage(&self) -> &str { + "Run a block on each row of the table." + } + + fn run( + &self, + call_info: &CallInfo, + registry: &CommandRegistry, + raw_args: &RawCommandArgs, + input: Value, + ) -> Result { + let call_info = call_info.clone(); + let registry = registry.clone(); + let raw_args = raw_args.clone(); + let stream = async_stream! { + match call_info.args.expect_nth(0)? { + Value { + value: UntaggedValue::Block(block), + tag + } => { + let mut context = Context::from_raw(&raw_args, ®istry); + let input_stream = async_stream! { + yield Ok(input.clone()) + }.to_input_stream(); + + let result = run_pipeline( + ClassifiedPipeline::new(block.clone(), None), + &mut context, + Some(input_stream), + ).await; + + match result { + Ok(Some(v)) => { + let results: Vec = v.collect().await; + + for result in results { + yield Ok(ReturnSuccess::Value(result)); + } + } + Ok(None) => { + yield Err(ShellError::labeled_error( + "Expected a block", + "each needs a block", + tag, + )); + } + Err(e) => { + yield Err(e); + } + } + } + Value { tag, .. } => { + yield Err(ShellError::labeled_error( + "Expected a block", + "each needs a block", + tag, + )) + } + }; + }; + + Ok(stream.to_output_stream()) + } +} diff --git a/crates/nu-cli/src/commands/enter.rs b/crates/nu-cli/src/commands/enter.rs index aebad74322..4b056bfaaa 100644 --- a/crates/nu-cli/src/commands/enter.rs +++ b/crates/nu-cli/src/commands/enter.rs @@ -97,7 +97,7 @@ impl PerItemCommand for Enter { ctrl_c: raw_args.ctrl_c, shell_manager: raw_args.shell_manager, call_info: UnevaluatedCallInfo { - args: nu_parser::hir::Call { + args: nu_protocol::hir::Call { head: raw_args.call_info.args.head, positional: None, named: None, diff --git a/crates/nu-cli/src/commands/save.rs b/crates/nu-cli/src/commands/save.rs index 660be78ca6..895344e1ab 100644 --- a/crates/nu-cli/src/commands/save.rs +++ b/crates/nu-cli/src/commands/save.rs @@ -228,7 +228,7 @@ fn save( ctrl_c, shell_manager, call_info: UnevaluatedCallInfo { - args: nu_parser::hir::Call { + args: nu_protocol::hir::Call { head: raw_args.call_info.args.head, positional: None, named: None, diff --git a/crates/nu-cli/src/commands/skip_while.rs b/crates/nu-cli/src/commands/skip_while.rs index 2407ffe5a6..7493a90572 100644 --- a/crates/nu-cli/src/commands/skip_while.rs +++ b/crates/nu-cli/src/commands/skip_while.rs @@ -1,16 +1,12 @@ use crate::commands::WholeStreamCommand; +use crate::evaluate::evaluate_baseline_expr; use crate::prelude::*; use log::trace; use nu_errors::ShellError; -use nu_protocol::{Evaluate, Scope, Signature, SyntaxShape}; +use nu_protocol::{hir::ClassifiedCommand, Scope, Signature, SyntaxShape, UntaggedValue, Value}; pub struct SkipWhile; -#[derive(Deserialize)] -pub struct SkipWhileArgs { - condition: Evaluate, -} - impl WholeStreamCommand for SkipWhile { fn name(&self) -> &str { "skip-while" @@ -20,7 +16,7 @@ impl WholeStreamCommand for SkipWhile { Signature::build("skip-while") .required( "condition", - SyntaxShape::Block, + SyntaxShape::Condition, "the condition that must be met to continue skipping", ) .filter() @@ -35,26 +31,57 @@ impl WholeStreamCommand for SkipWhile { args: CommandArgs, registry: &CommandRegistry, ) -> Result { - args.process(registry, skip_while)?.run() - } -} + let registry = registry.clone(); + let call_info = args.evaluate_once(®istry)?; -pub fn skip_while( - SkipWhileArgs { condition }: SkipWhileArgs, - RunnableContext { input, .. }: RunnableContext, -) -> Result { - let objects = input.values.skip_while(move |item| { - trace!("ITEM = {:?}", item); - let result = condition.invoke(&Scope::new(item.clone())); - trace!("RESULT = {:?}", result); + let block = call_info.args.expect_nth(0)?.clone(); - let return_value = match result { - Ok(ref v) if v.is_true() => true, - _ => false, + let condition = match block { + Value { + value: UntaggedValue::Block(block), + tag, + } => match block.list.get(0) { + Some(item) => match item { + ClassifiedCommand::Expr(expr) => expr.clone(), + _ => { + return Err(ShellError::labeled_error( + "Expected a condition", + "expected a condition", + tag, + )) + } + }, + None => { + return Err(ShellError::labeled_error( + "Expected a condition", + "expected a condition", + tag, + )); + } + }, + Value { tag, .. } => { + return Err(ShellError::labeled_error( + "Expected a condition", + "expected a condition", + tag, + )); + } }; - futures::future::ready(return_value) - }); + let objects = call_info.input.values.skip_while(move |item| { + let condition = condition.clone(); + trace!("ITEM = {:?}", item); + let result = evaluate_baseline_expr(&*condition, ®istry, &Scope::new(item.clone())); + trace!("RESULT = {:?}", result); - Ok(objects.from_input_stream()) + let return_value = match result { + Ok(ref v) if v.is_true() => true, + _ => false, + }; + + futures::future::ready(return_value) + }); + + Ok(objects.from_input_stream()) + } } diff --git a/crates/nu-cli/src/commands/where_.rs b/crates/nu-cli/src/commands/where_.rs index d43101cd8b..14567831ac 100644 --- a/crates/nu-cli/src/commands/where_.rs +++ b/crates/nu-cli/src/commands/where_.rs @@ -1,8 +1,12 @@ use crate::commands::PerItemCommand; use crate::context::CommandRegistry; +use crate::evaluate::evaluate_baseline_expr; use crate::prelude::*; use nu_errors::ShellError; -use nu_protocol::{CallInfo, ReturnSuccess, Scope, Signature, SyntaxShape, UntaggedValue, Value}; +use nu_protocol::{ + hir::ClassifiedCommand, CallInfo, ReturnSuccess, Scope, Signature, SyntaxShape, UntaggedValue, + Value, +}; pub struct Where; @@ -14,7 +18,7 @@ impl PerItemCommand for Where { fn signature(&self) -> Signature { Signature::build("where").required( "condition", - SyntaxShape::Block, + SyntaxShape::Condition, "the condition that must match", ) } @@ -26,37 +30,57 @@ impl PerItemCommand for Where { fn run( &self, call_info: &CallInfo, - _registry: &CommandRegistry, + registry: &CommandRegistry, _raw_args: &RawCommandArgs, input: Value, ) -> Result { - let condition = call_info.args.expect_nth(0)?; - let stream = match condition { + let block = call_info.args.expect_nth(0)?.clone(); + + let condition = match block { Value { value: UntaggedValue::Block(block), - .. - } => { - let result = block.invoke(&Scope::new(input.clone())); - match result { - Ok(v) => { - if v.is_true() { - VecDeque::from(vec![Ok(ReturnSuccess::Value(input))]) - } else { - VecDeque::new() - } + tag, + } => match block.list.get(0) { + Some(item) => match item { + ClassifiedCommand::Expr(expr) => expr.clone(), + _ => { + return Err(ShellError::labeled_error( + "Expected a condition", + "expected a condition", + tag, + )) } - Err(e) => return Err(e), + }, + None => { + return Err(ShellError::labeled_error( + "Expected a condition", + "expected a condition", + tag, + )); } - } + }, Value { tag, .. } => { return Err(ShellError::labeled_error( "Expected a condition", - "where needs a condition", + "expected a condition", tag, - )) + )); } }; + let condition = evaluate_baseline_expr(&condition, registry, &Scope::new(input.clone()))?; + + let stream = match condition.as_bool() { + Ok(b) => { + if b { + VecDeque::from(vec![Ok(ReturnSuccess::Value(input))]) + } else { + VecDeque::new() + } + } + Err(e) => return Err(e), + }; + Ok(stream.into()) } } diff --git a/crates/nu-cli/src/context.rs b/crates/nu-cli/src/context.rs index 739ae2ea65..f73ec98b2d 100644 --- a/crates/nu-cli/src/context.rs +++ b/crates/nu-cli/src/context.rs @@ -1,11 +1,13 @@ -use crate::commands::{command::CommandArgs, Command, UnevaluatedCallInfo}; +use crate::commands::{ + command::CommandArgs, command::RawCommandArgs, Command, UnevaluatedCallInfo, +}; use crate::env::host::Host; use crate::shell::shell_manager::ShellManager; use crate::stream::{InputStream, OutputStream}; use indexmap::IndexMap; use nu_errors::ShellError; -use nu_parser::{hir, SignatureRegistry}; -use nu_protocol::Signature; +use nu_parser::SignatureRegistry; +use nu_protocol::{hir, Signature}; use nu_source::{Tag, Text}; use parking_lot::Mutex; use std::error::Error; @@ -40,12 +42,6 @@ impl CommandRegistry { } impl CommandRegistry { - pub(crate) fn empty() -> CommandRegistry { - CommandRegistry { - registry: Arc::new(Mutex::new(IndexMap::default())), - } - } - pub(crate) fn get_command(&self, name: &str) -> Option> { let registry = self.registry.lock(); @@ -92,6 +88,30 @@ impl Context { &self.registry } + pub(crate) fn from_raw(raw_args: &RawCommandArgs, registry: &CommandRegistry) -> Context { + #[cfg(windows)] + { + Context { + registry: registry.clone(), + host: raw_args.host.clone(), + current_errors: Arc::new(Mutex::new(vec![])), + ctrl_c: raw_args.ctrl_c.clone(), + shell_manager: raw_args.shell_manager.clone(), + windows_drives_previous_cwd: Arc::new(Mutex::new(std::collections::HashMap::new())), + } + } + #[cfg(not(windows))] + { + Context { + registry: registry.clone(), + host: raw_args.host.clone(), + current_errors: Arc::new(Mutex::new(vec![])), + ctrl_c: raw_args.ctrl_c.clone(), + shell_manager: raw_args.shell_manager.clone(), + } + } + } + pub(crate) fn basic() -> Result> { let registry = CommandRegistry::new(); diff --git a/crates/nu-cli/src/data/base.rs b/crates/nu-cli/src/data/base.rs index 24d6522ab5..d6cbd73305 100644 --- a/crates/nu-cli/src/data/base.rs +++ b/crates/nu-cli/src/data/base.rs @@ -1,16 +1,11 @@ pub(crate) mod shape; -use crate::context::CommandRegistry; -use crate::evaluate::evaluate_baseline_expr; use bigdecimal::BigDecimal; use chrono::{DateTime, Utc}; use derive_new::new; -use log::trace; use nu_errors::ShellError; -use nu_parser::hir; use nu_protocol::{ - Evaluate, EvaluateTrait, Primitive, Scope, ShellTypeName, SpannedTypeName, TaggedDictBuilder, - UntaggedValue, Value, + hir, Primitive, ShellTypeName, SpannedTypeName, TaggedDictBuilder, UntaggedValue, Value, }; use nu_source::Tag; use nu_value_ext::ValueExt; @@ -29,42 +24,12 @@ pub struct Operation { #[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone, Hash, Serialize, Deserialize, new)] pub struct Block { - pub(crate) expressions: Vec, + pub(crate) commands: hir::Commands, pub(crate) tag: Tag, } interfaces!(Block: dyn ObjectHash); -#[typetag::serde] -impl EvaluateTrait for Block { - fn invoke(&self, scope: &Scope) -> Result { - if self.expressions.is_empty() { - return Ok(UntaggedValue::nothing().into_value(&self.tag)); - } - - let mut last = Ok(UntaggedValue::nothing().into_value(&self.tag)); - - trace!( - "EXPRS = {:?}", - self.expressions - .iter() - .map(|e| format!("{:?}", e)) - .collect::>() - ); - - for expr in self.expressions.iter() { - last = evaluate_baseline_expr(&expr, &CommandRegistry::empty(), &scope) - } - - last - } - - fn clone_box(&self) -> Evaluate { - let block = self.clone(); - Evaluate::new(block) - } -} - #[derive(Serialize, Deserialize)] pub enum Switch { Present, diff --git a/crates/nu-cli/src/data/primitive.rs b/crates/nu-cli/src/data/primitive.rs index 34ce8e5fb6..c352fa3e76 100644 --- a/crates/nu-cli/src/data/primitive.rs +++ b/crates/nu-cli/src/data/primitive.rs @@ -1,5 +1,4 @@ -use nu_parser::hir::Number; -use nu_protocol::Primitive; +use nu_protocol::{hir::Number, Primitive}; pub fn number(number: impl Into) -> Primitive { let number = number.into(); diff --git a/crates/nu-cli/src/data/value.rs b/crates/nu-cli/src/data/value.rs index baba7997a9..ee1d4351e9 100644 --- a/crates/nu-cli/src/data/value.rs +++ b/crates/nu-cli/src/data/value.rs @@ -3,7 +3,7 @@ use crate::data::base::shape::{Column, InlineShape}; use crate::data::primitive::style_primitive; use chrono::DateTime; use nu_errors::ShellError; -use nu_parser::hir::CompareOperator; +use nu_protocol::hir::CompareOperator; use nu_protocol::{Primitive, Type, UntaggedValue}; use nu_source::{DebugDocBuilder, PrettyDebug, Tagged}; diff --git a/crates/nu-cli/src/deserializer.rs b/crates/nu-cli/src/deserializer.rs index 882c9e8b22..66406a5999 100644 --- a/crates/nu-cli/src/deserializer.rs +++ b/crates/nu-cli/src/deserializer.rs @@ -1,7 +1,8 @@ use log::trace; use nu_errors::{CoerceInto, ShellError}; use nu_protocol::{ - CallInfo, ColumnPath, Evaluate, Primitive, RangeInclusion, ShellTypeName, UntaggedValue, Value, + hir::Commands, CallInfo, ColumnPath, Primitive, RangeInclusion, ShellTypeName, UntaggedValue, + Value, }; use nu_source::{HasSpan, Spanned, SpannedItem, Tagged, TaggedItem}; use nu_value_ext::ValueExt; @@ -368,7 +369,7 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut ConfigDeserializer<'de> { )) } }; - return visit::(block, name, fields, visitor); + return visit::(block, name, fields, visitor); } if name == "ColumnPath" { diff --git a/crates/nu-cli/src/evaluate/evaluate_args.rs b/crates/nu-cli/src/evaluate/evaluate_args.rs index 632033e97e..493036a4a2 100644 --- a/crates/nu-cli/src/evaluate/evaluate_args.rs +++ b/crates/nu-cli/src/evaluate/evaluate_args.rs @@ -3,8 +3,7 @@ use crate::context::CommandRegistry; use crate::evaluate::evaluate_baseline_expr; use indexmap::IndexMap; use nu_errors::ShellError; -use nu_parser::hir; -use nu_protocol::{EvaluatedArgs, Scope, UntaggedValue, Value}; +use nu_protocol::{hir, EvaluatedArgs, Scope, UntaggedValue, Value}; pub(crate) fn evaluate_args( call: &hir::Call, diff --git a/crates/nu-cli/src/evaluate/evaluator.rs b/crates/nu-cli/src/evaluate/evaluator.rs index e451b3c1a2..0650451027 100644 --- a/crates/nu-cli/src/evaluate/evaluator.rs +++ b/crates/nu-cli/src/evaluate/evaluator.rs @@ -1,13 +1,11 @@ use crate::context::CommandRegistry; -use crate::data::base::Block; use crate::evaluate::operator::apply_operator; use crate::prelude::*; use log::trace; use nu_errors::{ArgumentError, ShellError}; -use nu_parser::hir::{self, Expression, SpannedExpression}; +use nu_protocol::hir::{self, Expression, SpannedExpression}; use nu_protocol::{ - ColumnPath, Evaluate, Primitive, RangeInclusion, Scope, UnspannedPathMember, UntaggedValue, - Value, + ColumnPath, Primitive, RangeInclusion, Scope, UnspannedPathMember, UntaggedValue, Value, }; pub(crate) fn evaluate_baseline_expr( @@ -81,11 +79,7 @@ pub(crate) fn evaluate_baseline_expr( Ok(UntaggedValue::Table(exprs).into_value(tag)) } - Expression::Block(block) => Ok(UntaggedValue::Block(Evaluate::new(Block::new( - block.clone(), - tag.clone(), - ))) - .into_value(&tag)), + Expression::Block(block) => Ok(UntaggedValue::Block(block.clone()).into_value(&tag)), Expression::Path(path) => { let value = evaluate_baseline_expr(&path.head, registry, scope)?; let mut item = value; @@ -138,8 +132,8 @@ fn evaluate_literal(literal: &hir::Literal, span: Span) -> Value { .into_value(span) } hir::Literal::Number(int) => match int { - nu_parser::hir::Number::Int(i) => UntaggedValue::int(i.clone()).into_value(span), - nu_parser::hir::Number::Decimal(d) => { + nu_protocol::hir::Number::Int(i) => UntaggedValue::int(i.clone()).into_value(span), + nu_protocol::hir::Number::Decimal(d) => { UntaggedValue::decimal(d.clone()).into_value(span) } }, @@ -166,7 +160,10 @@ fn evaluate_reference(name: &hir::Variable, scope: &Scope, tag: Tag) -> Result Result { +fn evaluate_external( + external: &hir::ExternalStringCommand, + _scope: &Scope, +) -> Result { Err(ShellError::syntax_error( "Unexpected external command".spanned(external.name.span), )) diff --git a/crates/nu-cli/src/evaluate/operator.rs b/crates/nu-cli/src/evaluate/operator.rs index 52e0a0ec33..3cfe102752 100644 --- a/crates/nu-cli/src/evaluate/operator.rs +++ b/crates/nu-cli/src/evaluate/operator.rs @@ -1,5 +1,5 @@ use crate::data::value; -use nu_parser::hir::CompareOperator; +use nu_protocol::hir::CompareOperator; use nu_protocol::{Primitive, ShellTypeName, UntaggedValue, Value}; use std::ops::Not; diff --git a/crates/nu-cli/src/prelude.rs b/crates/nu-cli/src/prelude.rs index 53b5ea3e4a..e6fe212a9d 100644 --- a/crates/nu-cli/src/prelude.rs +++ b/crates/nu-cli/src/prelude.rs @@ -91,7 +91,7 @@ pub(crate) use async_stream::stream as async_stream; pub(crate) use bigdecimal::BigDecimal; pub(crate) use futures::stream::BoxStream; pub(crate) use futures::{FutureExt, Stream, StreamExt}; -pub(crate) use nu_protocol::{EvaluateTrait, MaybeOwned}; +pub(crate) use nu_protocol::MaybeOwned; pub(crate) use nu_source::{ b, AnchorLocation, DebugDocBuilder, PrettyDebug, PrettyDebugWithSource, Span, SpannedItem, Tag, TaggedItem, Text, diff --git a/crates/nu-cli/src/shell/completer.rs b/crates/nu-cli/src/shell/completer.rs index 8b0fad169c..3145614c78 100644 --- a/crates/nu-cli/src/shell/completer.rs +++ b/crates/nu-cli/src/shell/completer.rs @@ -112,9 +112,9 @@ impl NuCompleter { let result = nu_parser::classify_pipeline(&lite_parse, &self.commands); for command in result.commands.list { - if let nu_parser::ClassifiedCommand::Internal(nu_parser::InternalCommand { - args, .. - }) = command + if let nu_protocol::hir::ClassifiedCommand::Internal( + nu_protocol::hir::InternalCommand { args, .. }, + ) = command { if replace_pos >= args.span.start() && replace_pos <= args.span.end() { if let Some(named) = args.named { diff --git a/crates/nu-cli/src/shell/helper.rs b/crates/nu-cli/src/shell/helper.rs index 8552466a23..3a9e6faf63 100644 --- a/crates/nu-cli/src/shell/helper.rs +++ b/crates/nu-cli/src/shell/helper.rs @@ -1,7 +1,7 @@ use crate::context::Context; use ansi_term::{Color, Style}; -use nu_parser::hir::FlatShape; use nu_parser::SignatureRegistry; +use nu_protocol::hir::FlatShape; use nu_source::{Span, Spanned, Tag, Tagged}; use rustyline::completion::Completer; use rustyline::error::ReadlineError; diff --git a/crates/nu-cli/src/utils/data_processing.rs b/crates/nu-cli/src/utils/data_processing.rs index b8edda1f62..d4b4e45863 100644 --- a/crates/nu-cli/src/utils/data_processing.rs +++ b/crates/nu-cli/src/utils/data_processing.rs @@ -2,7 +2,7 @@ use crate::data::value::compare_values; use crate::data::TaggedListBuilder; use chrono::{DateTime, NaiveDate, Utc}; use nu_errors::ShellError; -use nu_parser::hir::CompareOperator; +use nu_protocol::hir::CompareOperator; use nu_protocol::{Primitive, TaggedDictBuilder, UntaggedValue, Value}; use nu_source::{SpannedItem, Tag, Tagged, TaggedItem}; use nu_value_ext::{get_data_by_key, ValueExt}; diff --git a/crates/nu-cli/tests/commands/each.rs b/crates/nu-cli/tests/commands/each.rs new file mode 100644 index 0000000000..fed8abfff2 --- /dev/null +++ b/crates/nu-cli/tests/commands/each.rs @@ -0,0 +1,13 @@ +use nu_test_support::{nu, pipeline}; + +#[test] +fn each_works_separately() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + echo [1 2 3] | each { echo $it 10 | sum } | to-json | echo $it + "# + )); + + assert_eq!(actual, "[11,12,13]"); +} diff --git a/crates/nu-cli/tests/commands/mod.rs b/crates/nu-cli/tests/commands/mod.rs index b2587415fa..ec78cf622c 100644 --- a/crates/nu-cli/tests/commands/mod.rs +++ b/crates/nu-cli/tests/commands/mod.rs @@ -4,6 +4,7 @@ mod cd; mod compact; mod cp; mod default; +mod each; mod edit; mod enter; mod first; diff --git a/crates/nu-errors/src/lib.rs b/crates/nu-errors/src/lib.rs index 56caa8b4f8..cca4e63de0 100644 --- a/crates/nu-errors/src/lib.rs +++ b/crates/nu-errors/src/lib.rs @@ -13,7 +13,7 @@ use std::ops::Range; /// A structured reason for a ParseError. Note that parsing in nu is more like macro expansion in /// other languages, so the kinds of errors that can occur during parsing are more contextual than /// you might expect. -#[derive(Debug, Clone, Eq, PartialEq)] +#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)] pub enum ParseErrorReason { /// The parser encountered an EOF rather than what it was expecting Eof { expected: String, span: Span }, @@ -38,7 +38,7 @@ pub enum ParseErrorReason { } /// A newtype for `ParseErrorReason` -#[derive(Debug, Clone, Eq, PartialEq, Getters)] +#[derive(Debug, Clone, Getters, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)] pub struct ParseError { #[get = "pub"] reason: ParseErrorReason, diff --git a/crates/nu-parser/src/lib.rs b/crates/nu-parser/src/lib.rs index 4dd6675fd5..dec424a2b9 100644 --- a/crates/nu-parser/src/lib.rs +++ b/crates/nu-parser/src/lib.rs @@ -1,5 +1,4 @@ mod files; -pub mod hir; mod lite_parse; mod parse; mod shapes; @@ -7,10 +6,6 @@ mod signature; pub use crate::files::Files; pub use crate::lite_parse::{lite_parse, LitePipeline}; -pub use crate::parse::{ - classify_pipeline, garbage, ClassifiedCommand, ClassifiedPipeline, Commands, InternalCommand, -}; +pub use crate::parse::{classify_pipeline, garbage}; pub use crate::shapes::shapes; -pub use crate::signature::{ - ExternalArg, ExternalArgs, ExternalCommand, Signature, SignatureRegistry, -}; +pub use crate::signature::{Signature, SignatureRegistry}; diff --git a/crates/nu-parser/src/parse.rs b/crates/nu-parser/src/parse.rs index e5344829a2..c0275e6cf1 100644 --- a/crates/nu-parser/src/parse.rs +++ b/crates/nu-parser/src/parse.rs @@ -1,83 +1,17 @@ use std::path::Path; -use nu_errors::{ArgumentError, ParseError}; -//use crate::hir::*; -use crate::hir::{ - Binary, CompareOperator, Expression, Flag, FlagKind, Member, NamedArguments, SpannedExpression, - Unit, -}; use crate::lite_parse::{lite_parse, LiteCommand, LitePipeline}; use crate::signature::SignatureRegistry; -use crate::{ExternalArg, ExternalArgs, ExternalCommand}; +use nu_errors::{ArgumentError, ParseError}; +use nu_protocol::hir::{ + self, Binary, ClassifiedCommand, ClassifiedPipeline, Commands, CompareOperator, Expression, + ExternalArg, ExternalArgs, ExternalCommand, Flag, FlagKind, InternalCommand, Member, + NamedArguments, SpannedExpression, Unit, +}; use nu_protocol::{NamedType, PositionalType, Signature, SyntaxShape, UnspannedPathMember}; use nu_source::{Span, Spanned, SpannedItem, Tag}; use num_bigint::BigInt; -#[derive(Debug, Clone)] -pub struct InternalCommand { - pub name: String, - pub name_span: Span, - pub args: crate::hir::Call, -} - -impl InternalCommand { - pub fn new(name: String, name_span: Span, full_span: Span) -> InternalCommand { - InternalCommand { - name: name.clone(), - name_span, - args: crate::hir::Call::new( - Box::new(SpannedExpression::new(Expression::string(name), name_span)), - full_span, - ), - } - } -} - -#[derive(Debug, Clone)] -pub enum ClassifiedCommand { - #[allow(unused)] - Comparison( - Box, - Box, - Box, - ), - #[allow(unused)] - Dynamic(crate::hir::Call), - Internal(InternalCommand), - External(crate::ExternalCommand), - Error(ParseError), -} - -#[derive(Debug, Clone)] -pub struct Commands { - pub list: Vec, - pub span: Span, -} - -impl Commands { - pub fn new(span: Span) -> Commands { - Commands { list: vec![], span } - } - - pub fn push(&mut self, command: ClassifiedCommand) { - self.list.push(command); - } -} - -#[derive(Debug, Clone)] -pub struct ClassifiedPipeline { - pub commands: Commands, - // this is not a Result to make it crystal clear that these shapes - // aren't intended to be used directly with `?` - pub failed: Option, -} - -impl ClassifiedPipeline { - pub fn new(commands: Commands, failed: Option) -> ClassifiedPipeline { - ClassifiedPipeline { commands, failed } - } -} - /// Parses a simple column path, one without a variable (implied or explicit) at the head fn parse_simple_column_path(lite_arg: &Spanned) -> (SpannedExpression, Option) { let mut delimiter = '.'; @@ -534,53 +468,23 @@ fn parse_arg( (Some('{'), Some('}')) => { // We have a literal block let string: String = chars.collect(); - let mut error = None; // We haven't done much with the inner string, so let's go ahead and work with it let lite_pipeline = match lite_parse(&string, lite_arg.span.start() + 1) { Ok(lp) => lp, Err(e) => return (garbage(lite_arg.span), Some(e)), }; - //let pipeline = parse(&lite_pipeline, definitions)?; - // For now, just take the first command - if let Some(lite_cmd) = lite_pipeline.commands.first() { - if lite_cmd.args.len() != 2 { - return ( - garbage(lite_arg.span), - Some(ParseError::mismatch("block", lite_arg.clone())), - ); - } - let (lhs, err) = - parse_arg(SyntaxShape::FullColumnPath, registry, &lite_cmd.name); - if error.is_none() { - error = err; - } - let (op, err) = - parse_arg(SyntaxShape::Operator, registry, &lite_cmd.args[0]); - if error.is_none() { - error = err; - } - let (rhs, err) = parse_arg(SyntaxShape::Any, registry, &lite_cmd.args[1]); - if error.is_none() { - error = err; - } + let classified_block = classify_pipeline(&lite_pipeline, registry); + let error = classified_block.failed; - let span = Span::new(lhs.span.start(), rhs.span.end()); - let binary = SpannedExpression::new( - Expression::Binary(Box::new(Binary::new(lhs, op, rhs))), - span, - ); - ( - SpannedExpression::new(Expression::Block(vec![binary]), span), - error, - ) - } else { - ( - garbage(lite_arg.span), - Some(ParseError::mismatch("block", lite_arg.clone())), - ) - } + ( + SpannedExpression::new( + Expression::Block(classified_block.commands), + lite_arg.span, + ), + error, + ) } _ => { // We have an implied block, but we can't parse this here @@ -592,6 +496,14 @@ fn parse_arg( } } } + SyntaxShape::Condition => { + // We have an implied condition, but we can't parse this here + // it needed to have been parsed up higher where we have control over more than one arg + ( + garbage(lite_arg.span), + Some(ParseError::mismatch("condition", lite_arg.clone())), + ) + } } } @@ -664,13 +576,10 @@ fn classify_positional_arg( let mut idx = idx; let mut error = None; let arg = match positional_type { - PositionalType::Mandatory(_, SyntaxShape::Block) - | PositionalType::Optional(_, SyntaxShape::Block) => { - // We may have an implied block, so let's try to parse it here - // The only implied block format we currently support is , though - // we may want to revisit this in the future - - // TODO: only do this step if it's not a literal block + PositionalType::Mandatory(_, SyntaxShape::Condition) + | PositionalType::Optional(_, SyntaxShape::Condition) => { + // A condition can take up multiple arguments, as we build the operation as + // We need to do this here because in parse_arg, we have access to only one arg at a time if (idx + 2) < lite_cmd.args.len() { let (lhs, err) = parse_arg(SyntaxShape::FullColumnPath, registry, &lite_cmd.args[idx]); @@ -691,9 +600,19 @@ fn classify_positional_arg( Expression::Binary(Box::new(Binary::new(lhs, op, rhs))), span, ); - SpannedExpression::new(Expression::Block(vec![binary]), span) + let mut commands = hir::Commands::new(span); + commands.push(ClassifiedCommand::Expr(Box::new(binary))); + SpannedExpression::new(Expression::Block(commands), span) + } else if idx < lite_cmd.args.len() { + let (arg, err) = + parse_arg(SyntaxShape::FullColumnPath, registry, &lite_cmd.args[idx]); + if error.is_none() { + error = err; + } + arg } else { - let (arg, err) = parse_arg(SyntaxShape::Block, registry, &lite_cmd.args[idx]); + // TODO - this needs to get fixed and return an actual error + let (arg, err) = parse_arg(SyntaxShape::Condition, registry, &lite_cmd.args[idx]); if error.is_none() { error = err; } diff --git a/crates/nu-parser/src/shapes.rs b/crates/nu-parser/src/shapes.rs index 1ad3b59205..4fec6b544a 100644 --- a/crates/nu-parser/src/shapes.rs +++ b/crates/nu-parser/src/shapes.rs @@ -1,18 +1,11 @@ -use crate::hir::*; -use crate::parse::{ClassifiedCommand, Commands}; +use nu_protocol::hir::*; use nu_protocol::UnspannedPathMember; use nu_source::{Spanned, SpannedItem}; /// Converts a SpannedExpression into a spanned shape(s) ready for color-highlighting pub fn expression_to_flat_shape(e: &SpannedExpression) -> Vec> { match &e.expr { - Expression::Block(exprs) => { - let mut output = vec![]; - for expr in exprs.iter() { - output.append(&mut expression_to_flat_shape(expr)); - } - output - } + Expression::Block(exprs) => shapes(exprs), Expression::FilePath(_) => vec![FlatShape::Path.spanned(e.span)], Expression::Garbage => vec![FlatShape::Garbage.spanned(e.span)], Expression::List(exprs) => { @@ -106,6 +99,7 @@ pub fn shapes(commands: &Commands) -> Vec> { output.push(FlatShape::ExternalWord.spanned(arg.tag.span)); } } + ClassifiedCommand::Expr(expr) => output.append(&mut expression_to_flat_shape(expr)), _ => {} } } diff --git a/crates/nu-parser/src/signature.rs b/crates/nu-parser/src/signature.rs index 355018667a..5fcc74060e 100644 --- a/crates/nu-parser/src/signature.rs +++ b/crates/nu-parser/src/signature.rs @@ -1,6 +1,6 @@ use std::fmt::Debug; -use nu_source::{b, DebugDocBuilder, HasSpan, PrettyDebug, PrettyDebugWithSource, Span, Tag}; +use nu_source::{DebugDocBuilder, HasSpan, PrettyDebugWithSource, Span}; pub trait SignatureRegistry: Debug { fn has(&self, name: &str) -> bool; @@ -46,99 +46,3 @@ impl PrettyDebugWithSource for Signature { self.unspanned.pretty_debug(source) } } - -#[derive(Debug, Clone, Eq, PartialEq)] -pub struct ExternalArg { - pub arg: String, - pub tag: Tag, -} - -impl ExternalArg { - pub fn has(&self, name: &str) -> bool { - self.arg == name - } - - pub fn is_it(&self) -> bool { - self.has("$it") - } - - pub fn is_nu(&self) -> bool { - self.has("$nu") - } - - pub fn looks_like_it(&self) -> bool { - self.arg.starts_with("$it") && (self.arg.starts_with("$it.") || self.is_it()) - } - - pub fn looks_like_nu(&self) -> bool { - self.arg.starts_with("$nu") && (self.arg.starts_with("$nu.") || self.is_nu()) - } -} - -impl std::ops::Deref for ExternalArg { - type Target = str; - - fn deref(&self) -> &str { - &self.arg - } -} - -#[derive(Debug, Clone, Eq, PartialEq)] -pub struct ExternalArgs { - pub list: Vec, - pub span: Span, -} - -impl ExternalArgs { - pub fn iter(&self) -> impl Iterator { - self.list.iter() - } -} - -impl std::ops::Deref for ExternalArgs { - type Target = [ExternalArg]; - - fn deref(&self) -> &[ExternalArg] { - &self.list - } -} - -#[derive(Debug, Clone, Eq, PartialEq)] -pub struct ExternalCommand { - pub name: String, - - pub name_tag: Tag, - pub args: ExternalArgs, -} - -impl ExternalCommand { - pub fn has_it_argument(&self) -> bool { - self.args.iter().any(|arg| arg.looks_like_it()) - } - - pub fn has_nu_argument(&self) -> bool { - self.args.iter().any(|arg| arg.looks_like_nu()) - } -} - -impl PrettyDebug for ExternalCommand { - fn pretty(&self) -> DebugDocBuilder { - b::typed( - "external command", - b::description(&self.name) - + b::preceded( - b::space(), - b::intersperse( - self.args.iter().map(|a| b::primitive(a.arg.to_string())), - b::space(), - ), - ), - ) - } -} - -impl HasSpan for ExternalCommand { - fn span(&self) -> Span { - self.name_tag.span.until(self.args.span) - } -} diff --git a/crates/nu-protocol/Cargo.toml b/crates/nu-protocol/Cargo.toml index d493614730..ec6e1b03b9 100644 --- a/crates/nu-protocol/Cargo.toml +++ b/crates/nu-protocol/Cargo.toml @@ -28,6 +28,7 @@ typetag = "0.1.4" query_interface = "0.3.5" byte-unit = "3.0.3" natural = "0.5.0" +log = "0.4.8" # implement conversions serde_yaml = "0.8" diff --git a/crates/nu-parser/src/hir.rs b/crates/nu-protocol/src/hir.rs similarity index 82% rename from crates/nu-parser/src/hir.rs rename to crates/nu-protocol/src/hir.rs index a4947b530d..014ca16367 100644 --- a/crates/nu-parser/src/hir.rs +++ b/crates/nu-protocol/src/hir.rs @@ -1,16 +1,20 @@ +use std::cmp::{Ord, Ordering, PartialOrd}; +use std::hash::{Hash, Hasher}; + use std::path::PathBuf; use serde::{Deserialize, Serialize}; +use crate::{hir, Primitive, UntaggedValue}; +use crate::{PathMember, ShellTypeName}; use derive_new::new; -use nu_protocol::{PathMember, ShellTypeName}; -use nu_protocol::{Primitive, UntaggedValue}; use num_traits::ToPrimitive; +use nu_errors::ParseError; use nu_source::{ b, DebugDocBuilder, HasSpan, PrettyDebug, PrettyDebugRefineKind, PrettyDebugWithSource, }; -use nu_source::{IntoSpanned, Span, Spanned, SpannedItem}; +use nu_source::{IntoSpanned, Span, Spanned, SpannedItem, Tag}; use bigdecimal::BigDecimal; use indexmap::IndexMap; @@ -19,12 +23,168 @@ use num_bigint::BigInt; use num_traits::identities::Zero; use num_traits::FromPrimitive; +#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)] +pub struct InternalCommand { + pub name: String, + pub name_span: Span, + pub args: crate::hir::Call, +} + +impl InternalCommand { + pub fn new(name: String, name_span: Span, full_span: Span) -> InternalCommand { + InternalCommand { + name: name.clone(), + name_span, + args: crate::hir::Call::new( + Box::new(SpannedExpression::new(Expression::string(name), name_span)), + full_span, + ), + } + } +} + +#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)] +pub struct ClassifiedPipeline { + pub commands: Commands, + // this is not a Result to make it crystal clear that these shapes + // aren't intended to be used directly with `?` + pub failed: Option, +} + +impl ClassifiedPipeline { + pub fn new(commands: Commands, failed: Option) -> ClassifiedPipeline { + ClassifiedPipeline { commands, failed } + } +} + +#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)] +pub enum ClassifiedCommand { + Expr(Box), + #[allow(unused)] + Dynamic(crate::hir::Call), + Internal(InternalCommand), + External(crate::hir::ExternalCommand), + Error(ParseError), +} + +#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)] +pub struct Commands { + pub list: Vec, + pub span: Span, +} + +impl Commands { + pub fn new(span: Span) -> Commands { + Commands { list: vec![], span } + } + + pub fn push(&mut self, command: ClassifiedCommand) { + self.list.push(command); + } +} + #[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone, Hash, Deserialize, Serialize)] -pub struct ExternalCommand { +pub struct ExternalStringCommand { pub name: Spanned, pub args: Vec>, } +#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)] +pub struct ExternalArg { + pub arg: String, + pub tag: Tag, +} + +impl ExternalArg { + pub fn has(&self, name: &str) -> bool { + self.arg == name + } + + pub fn is_it(&self) -> bool { + self.has("$it") + } + + pub fn is_nu(&self) -> bool { + self.has("$nu") + } + + pub fn looks_like_it(&self) -> bool { + self.arg.starts_with("$it") && (self.arg.starts_with("$it.") || self.is_it()) + } + + pub fn looks_like_nu(&self) -> bool { + self.arg.starts_with("$nu") && (self.arg.starts_with("$nu.") || self.is_nu()) + } +} + +impl std::ops::Deref for ExternalArg { + type Target = str; + + fn deref(&self) -> &str { + &self.arg + } +} + +#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)] +pub struct ExternalArgs { + pub list: Vec, + pub span: Span, +} + +impl ExternalArgs { + pub fn iter(&self) -> impl Iterator { + self.list.iter() + } +} + +impl std::ops::Deref for ExternalArgs { + type Target = [ExternalArg]; + + fn deref(&self) -> &[ExternalArg] { + &self.list + } +} + +#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)] +pub struct ExternalCommand { + pub name: String, + + pub name_tag: Tag, + pub args: ExternalArgs, +} + +impl ExternalCommand { + pub fn has_it_argument(&self) -> bool { + self.args.iter().any(|arg| arg.looks_like_it()) + } + + pub fn has_nu_argument(&self) -> bool { + self.args.iter().any(|arg| arg.looks_like_nu()) + } +} + +impl PrettyDebug for ExternalCommand { + fn pretty(&self) -> DebugDocBuilder { + b::typed( + "external command", + b::description(&self.name) + + b::preceded( + b::space(), + b::intersperse( + self.args.iter().map(|a| b::primitive(a.arg.to_string())), + b::space(), + ), + ), + ) + } +} + +impl HasSpan for ExternalCommand { + fn span(&self) -> Span { + self.name_tag.span.until(self.args.span) + } +} + #[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone, Hash, Copy, Deserialize, Serialize)] pub enum Unit { // Filesize units @@ -558,12 +718,12 @@ pub enum Expression { Variable(Variable), Binary(Box), Range(Box), - Block(Vec), + Block(hir::Commands), List(Vec), Path(Box), FilePath(PathBuf), - ExternalCommand(ExternalCommand), + ExternalCommand(ExternalStringCommand), Command(Span), Boolean(bool), @@ -664,7 +824,7 @@ impl Expression { } } -#[derive(Debug, Clone, Deserialize, Serialize)] +#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)] pub enum NamedValue { AbsentSwitch, PresentSwitch(Span), @@ -695,7 +855,7 @@ impl PrettyDebugWithSource for NamedValue { } } -#[derive(Debug, Clone, Deserialize, Serialize)] +#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)] pub struct Call { pub head: Box, pub positional: Option>, @@ -711,7 +871,7 @@ impl Call { .unwrap_or(false) } - pub fn set_initial_flags(&mut self, signature: &nu_protocol::Signature) { + pub fn set_initial_flags(&mut self, signature: &crate::Signature) { for (named, value) in signature.named.iter() { if self.named.is_none() { self.named = Some(NamedArguments::new()); @@ -719,7 +879,7 @@ impl Call { if let Some(ref mut args) = self.named { match value.0 { - nu_protocol::NamedType::Switch(_) => args.insert_switch(named, None), + crate::NamedType::Switch(_) => args.insert_switch(named, None), _ => args.insert_optional(named, Span::new(0, 0), None), } } @@ -817,11 +977,56 @@ pub enum FlatShape { Size { number: Span, unit: Span }, } -#[derive(Debug, Clone, Default, Deserialize, Serialize)] +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] pub struct NamedArguments { pub named: IndexMap, } +#[allow(clippy::derive_hash_xor_eq)] +impl Hash for NamedArguments { + /// Create the hash function to allow the Hash trait for dictionaries + fn hash(&self, state: &mut H) { + let mut entries = self.named.clone(); + entries.sort_keys(); + entries.keys().collect::>().hash(state); + entries.values().collect::>().hash(state); + } +} + +impl PartialOrd for NamedArguments { + /// Compare two dictionaries for sort ordering + fn partial_cmp(&self, other: &NamedArguments) -> Option { + let this: Vec<&String> = self.named.keys().collect(); + let that: Vec<&String> = other.named.keys().collect(); + + if this != that { + return this.partial_cmp(&that); + } + + let this: Vec<&NamedValue> = self.named.values().collect(); + let that: Vec<&NamedValue> = self.named.values().collect(); + + this.partial_cmp(&that) + } +} + +impl Ord for NamedArguments { + /// Compare two dictionaries for ordering + fn cmp(&self, other: &NamedArguments) -> Ordering { + let this: Vec<&String> = self.named.keys().collect(); + let that: Vec<&String> = other.named.keys().collect(); + + if this != that { + return this.cmp(&that); + } + + let this: Vec<&NamedValue> = self.named.values().collect(); + let that: Vec<&NamedValue> = self.named.values().collect(); + + this.cmp(&that) + } +} + impl NamedArguments { pub fn new() -> NamedArguments { Default::default() diff --git a/crates/nu-protocol/src/lib.rs b/crates/nu-protocol/src/lib.rs index 08d96f58cd..3f65f3fd0f 100644 --- a/crates/nu-protocol/src/lib.rs +++ b/crates/nu-protocol/src/lib.rs @@ -2,6 +2,7 @@ mod macros; mod call_info; +pub mod hir; mod maybe_owned; mod return_value; mod signature; @@ -19,7 +20,7 @@ pub use crate::type_name::{PrettyType, ShellTypeName, SpannedTypeName}; pub use crate::type_shape::{Row as RowType, Type}; pub use crate::value::column_path::{did_you_mean, ColumnPath, PathMember, UnspannedPathMember}; pub use crate::value::dict::{Dictionary, TaggedDictBuilder}; -pub use crate::value::evaluate::{Evaluate, EvaluateTrait, Scope}; +pub use crate::value::evaluate::Scope; pub use crate::value::primitive::Primitive; pub use crate::value::primitive::{format_date, format_duration, format_primitive}; pub use crate::value::range::{Range, RangeInclusion}; diff --git a/crates/nu-protocol/src/syntax_shape.rs b/crates/nu-protocol/src/syntax_shape.rs index 4642f86309..a7029ca522 100644 --- a/crates/nu-protocol/src/syntax_shape.rs +++ b/crates/nu-protocol/src/syntax_shape.rs @@ -30,6 +30,8 @@ pub enum SyntaxShape { Unit, /// An operator Operator, + /// A condition, eg `foo > 1` + Condition, } impl PrettyDebug for SyntaxShape { @@ -49,6 +51,7 @@ impl PrettyDebug for SyntaxShape { SyntaxShape::Table => "table", SyntaxShape::Unit => "unit", SyntaxShape::Operator => "operator", + SyntaxShape::Condition => "condition", }) } } diff --git a/crates/nu-protocol/src/value.rs b/crates/nu-protocol/src/value.rs index d6d8007e40..e7697a019d 100644 --- a/crates/nu-protocol/src/value.rs +++ b/crates/nu-protocol/src/value.rs @@ -8,9 +8,9 @@ pub mod range; mod serde_bigdecimal; mod serde_bigint; +use crate::hir; use crate::type_name::{ShellTypeName, SpannedTypeName}; use crate::value::dict::Dictionary; -use crate::value::evaluate::Evaluate; use crate::value::primitive::Primitive; use crate::value::range::{Range, RangeInclusion}; use crate::{ColumnPath, PathMember}; @@ -38,7 +38,7 @@ pub enum UntaggedValue { Error(ShellError), /// A block of Nu code, eg `{ ls | get name }` - Block(Evaluate), + Block(hir::Commands), } impl UntaggedValue { diff --git a/crates/nu-protocol/src/value/evaluate.rs b/crates/nu-protocol/src/value/evaluate.rs index 2a95db1403..2bb3abb660 100644 --- a/crates/nu-protocol/src/value/evaluate.rs +++ b/crates/nu-protocol/src/value/evaluate.rs @@ -1,9 +1,5 @@ use crate::value::{Primitive, UntaggedValue, Value}; use indexmap::IndexMap; -use nu_errors::ShellError; -use query_interface::{interfaces, vtable_for, Object, ObjectHash}; -use serde::{Deserialize, Serialize}; -use std::cmp::{Ord, Ordering, PartialOrd}; use std::fmt::Debug; /// An evaluation scope. Scopes map variable names to Values and aid in evaluating blocks and expressions. @@ -42,67 +38,3 @@ impl Scope { } } } - -#[typetag::serde(tag = "type")] -pub trait EvaluateTrait: Debug + Send + Sync + Object + ObjectHash + 'static { - fn invoke(&self, scope: &Scope) -> Result; - fn clone_box(&self) -> Evaluate; -} - -interfaces!(Evaluate: dyn ObjectHash); - -#[typetag::serde] -impl EvaluateTrait for Evaluate { - fn invoke(&self, scope: &Scope) -> Result { - self.expr.invoke(scope) - } - - fn clone_box(&self) -> Evaluate { - self.expr.clone_box() - } -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct Evaluate { - expr: Box, -} - -impl Evaluate { - pub fn new(evaluate: impl EvaluateTrait) -> Evaluate { - Evaluate { - expr: Box::new(evaluate), - } - } -} - -impl std::hash::Hash for Evaluate { - fn hash(&self, state: &mut H) { - self.expr.obj_hash(state) - } -} - -impl Clone for Evaluate { - fn clone(&self) -> Evaluate { - self.expr.clone_box() - } -} - -impl Ord for Evaluate { - fn cmp(&self, _: &Self) -> Ordering { - Ordering::Equal - } -} - -impl PartialOrd for Evaluate { - fn partial_cmp(&self, _: &Evaluate) -> Option { - Some(Ordering::Equal) - } -} - -impl PartialEq for Evaluate { - fn eq(&self, _: &Evaluate) -> bool { - true - } -} - -impl Eq for Evaluate {} diff --git a/crates/nu-test-support/src/commands.rs b/crates/nu-test-support/src/commands.rs index 03f1e107d5..41b1ce1a8b 100644 --- a/crates/nu-test-support/src/commands.rs +++ b/crates/nu-test-support/src/commands.rs @@ -1,4 +1,4 @@ -use nu_parser::{ExternalArg, ExternalArgs, ExternalCommand}; +use nu_protocol::hir::{ExternalArg, ExternalArgs, ExternalCommand}; use nu_source::{Span, SpannedItem, Tag, TaggedItem}; pub struct ExternalBuilder {