mirror of
https://github.com/nushell/nushell.git
synced 2024-11-22 00:13:21 +01:00
Pipeline blocks (#1579)
* Making Commands match what UntaggedValue needs * WIP * WIP * WIP * Moved to expressions for conditions * Add 'each' command to use command blocks * More cleanup * Add test for 'each' * Instead use an expression block
This commit is contained in:
parent
85d6b24be3
commit
08a09e2273
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -2192,6 +2192,7 @@ dependencies = [
|
||||
"getset",
|
||||
"indexmap",
|
||||
"language-reporting",
|
||||
"log",
|
||||
"natural",
|
||||
"nu-build",
|
||||
"nu-errors",
|
||||
|
@ -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),
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -1,5 +1,5 @@
|
||||
use derive_new::new;
|
||||
use nu_parser::hir;
|
||||
use nu_protocol::hir;
|
||||
|
||||
#[derive(new, Debug)]
|
||||
pub(crate) struct Command {
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
}
|
||||
|
89
crates/nu-cli/src/commands/each.rs
Normal file
89
crates/nu-cli/src/commands/each.rs
Normal file
@ -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<OutputStream, ShellError> {
|
||||
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<Value> = 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())
|
||||
}
|
||||
}
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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<OutputStream, ShellError> {
|
||||
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<OutputStream, ShellError> {
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
@ -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<OutputStream, ShellError> {
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
@ -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<Arc<Command>> {
|
||||
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<Context, Box<dyn Error>> {
|
||||
let registry = CommandRegistry::new();
|
||||
|
||||
|
@ -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<hir::SpannedExpression>,
|
||||
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<Value, ShellError> {
|
||||
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::<Vec<_>>()
|
||||
);
|
||||
|
||||
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,
|
||||
|
@ -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<Number>) -> Primitive {
|
||||
let number = number.into();
|
||||
|
@ -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};
|
||||
|
||||
|
@ -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::<Evaluate, _>(block, name, fields, visitor);
|
||||
return visit::<Commands, _>(block, name, fields, visitor);
|
||||
}
|
||||
|
||||
if name == "ColumnPath" {
|
||||
|
@ -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,
|
||||
|
@ -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<V
|
||||
}
|
||||
}
|
||||
|
||||
fn evaluate_external(external: &hir::ExternalCommand, _scope: &Scope) -> Result<Value, ShellError> {
|
||||
fn evaluate_external(
|
||||
external: &hir::ExternalStringCommand,
|
||||
_scope: &Scope,
|
||||
) -> Result<Value, ShellError> {
|
||||
Err(ShellError::syntax_error(
|
||||
"Unexpected external command".spanned(external.name.span),
|
||||
))
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
@ -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};
|
||||
|
13
crates/nu-cli/tests/commands/each.rs
Normal file
13
crates/nu-cli/tests/commands/each.rs
Normal file
@ -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]");
|
||||
}
|
@ -4,6 +4,7 @@ mod cd;
|
||||
mod compact;
|
||||
mod cp;
|
||||
mod default;
|
||||
mod each;
|
||||
mod edit;
|
||||
mod enter;
|
||||
mod first;
|
||||
|
@ -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,
|
||||
|
@ -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};
|
||||
|
@ -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<SpannedExpression>,
|
||||
Box<SpannedExpression>,
|
||||
Box<SpannedExpression>,
|
||||
),
|
||||
#[allow(unused)]
|
||||
Dynamic(crate::hir::Call),
|
||||
Internal(InternalCommand),
|
||||
External(crate::ExternalCommand),
|
||||
Error(ParseError),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Commands {
|
||||
pub list: Vec<ClassifiedCommand>,
|
||||
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<ParseError>,
|
||||
}
|
||||
|
||||
impl ClassifiedPipeline {
|
||||
pub fn new(commands: Commands, failed: Option<ParseError>) -> 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<String>) -> (SpannedExpression, Option<ParseError>) {
|
||||
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 <shorthand path> <operator> <any>, 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 <arg> <operator> <arg>
|
||||
// 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;
|
||||
}
|
||||
|
@ -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<Spanned<FlatShape>> {
|
||||
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<Spanned<FlatShape>> {
|
||||
output.push(FlatShape::ExternalWord.spanned(arg.tag.span));
|
||||
}
|
||||
}
|
||||
ClassifiedCommand::Expr(expr) => output.append(&mut expression_to_flat_shape(expr)),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
@ -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<ExternalArg>,
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
impl ExternalArgs {
|
||||
pub fn iter(&self) -> impl Iterator<Item = &ExternalArg> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -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"
|
||||
|
@ -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<ParseError>,
|
||||
}
|
||||
|
||||
impl ClassifiedPipeline {
|
||||
pub fn new(commands: Commands, failed: Option<ParseError>) -> ClassifiedPipeline {
|
||||
ClassifiedPipeline { commands, failed }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)]
|
||||
pub enum ClassifiedCommand {
|
||||
Expr(Box<SpannedExpression>),
|
||||
#[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<ClassifiedCommand>,
|
||||
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<String>,
|
||||
pub args: Vec<Spanned<String>>,
|
||||
}
|
||||
|
||||
#[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<ExternalArg>,
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
impl ExternalArgs {
|
||||
pub fn iter(&self) -> impl Iterator<Item = &ExternalArg> {
|
||||
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<Binary>),
|
||||
Range(Box<Range>),
|
||||
Block(Vec<SpannedExpression>),
|
||||
Block(hir::Commands),
|
||||
List(Vec<SpannedExpression>),
|
||||
Path(Box<Path>),
|
||||
|
||||
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<SpannedExpression>,
|
||||
pub positional: Option<Vec<SpannedExpression>>,
|
||||
@ -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<String, NamedValue>,
|
||||
}
|
||||
|
||||
#[allow(clippy::derive_hash_xor_eq)]
|
||||
impl Hash for NamedArguments {
|
||||
/// Create the hash function to allow the Hash trait for dictionaries
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
let mut entries = self.named.clone();
|
||||
entries.sort_keys();
|
||||
entries.keys().collect::<Vec<&String>>().hash(state);
|
||||
entries.values().collect::<Vec<&NamedValue>>().hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for NamedArguments {
|
||||
/// Compare two dictionaries for sort ordering
|
||||
fn partial_cmp(&self, other: &NamedArguments) -> Option<Ordering> {
|
||||
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()
|
@ -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};
|
||||
|
@ -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",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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<Value, ShellError>;
|
||||
fn clone_box(&self) -> Evaluate;
|
||||
}
|
||||
|
||||
interfaces!(Evaluate: dyn ObjectHash);
|
||||
|
||||
#[typetag::serde]
|
||||
impl EvaluateTrait for Evaluate {
|
||||
fn invoke(&self, scope: &Scope) -> Result<Value, ShellError> {
|
||||
self.expr.invoke(scope)
|
||||
}
|
||||
|
||||
fn clone_box(&self) -> Evaluate {
|
||||
self.expr.clone_box()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct Evaluate {
|
||||
expr: Box<dyn EvaluateTrait>,
|
||||
}
|
||||
|
||||
impl Evaluate {
|
||||
pub fn new(evaluate: impl EvaluateTrait) -> Evaluate {
|
||||
Evaluate {
|
||||
expr: Box::new(evaluate),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::hash::Hash for Evaluate {
|
||||
fn hash<H: std::hash::Hasher>(&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<Ordering> {
|
||||
Some(Ordering::Equal)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Evaluate {
|
||||
fn eq(&self, _: &Evaluate) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Evaluate {}
|
||||
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user