forked from extern/nushell
Move external closer to internal (#1611)
* Refactor InputStream and affected commands. First, making `values` private and leaning on the `Stream` implementation makes consumes of `InputStream` less likely to have to change in the future, if we change what an `InputStream` is internally. Second, we're dropping `Option<InputStream>` as the input to pipelines, internals, and externals. Instead, `InputStream.is_empty` can be used to check for "emptiness". Empty streams are typically only ever used as the first input to a pipeline. * Add run_external internal command. We want to push external commands closer to internal commands, eventually eliminating the concept of "external" completely. This means we can consolidate a couple of things: - Variable evaluation (for example, `$it`, `$nu`, alias vars) - Behaviour of whole stream vs per-item external execution It should also make it easier for us to start introducing argument signatures for external commands, * Update run_external.rs * Update run_external.rs * Update run_external.rs * Update run_external.rs Co-authored-by: Jonathan Turner <jonathandturner@users.noreply.github.com>
This commit is contained in:
@ -6,12 +6,11 @@ use crate::signature::SignatureRegistry;
|
||||
use log::trace;
|
||||
use nu_errors::{ArgumentError, ParseError};
|
||||
use nu_protocol::hir::{
|
||||
self, Binary, ClassifiedCommand, ClassifiedPipeline, Commands, Expression, ExternalArg,
|
||||
ExternalArgs, ExternalCommand, Flag, FlagKind, InternalCommand, Member, NamedArguments,
|
||||
Operator, SpannedExpression, Unit,
|
||||
self, Binary, ClassifiedCommand, ClassifiedPipeline, Commands, Expression, Flag, FlagKind,
|
||||
InternalCommand, Member, NamedArguments, Operator, SpannedExpression, Unit,
|
||||
};
|
||||
use nu_protocol::{NamedType, PositionalType, Signature, SyntaxShape, UnspannedPathMember};
|
||||
use nu_source::{Span, Spanned, SpannedItem, Tag};
|
||||
use nu_source::{Span, Spanned, SpannedItem};
|
||||
use num_bigint::BigInt;
|
||||
|
||||
/// Parses a simple column path, one without a variable (implied or explicit) at the head
|
||||
@ -980,66 +979,76 @@ pub fn classify_pipeline(
|
||||
let mut commands = Commands::new(Span::new(0, 0));
|
||||
let mut error = None;
|
||||
|
||||
for lite_cmd in lite_pipeline.commands.iter() {
|
||||
let mut iter = lite_pipeline.commands.iter().peekable();
|
||||
while let Some(lite_cmd) = iter.next() {
|
||||
if lite_cmd.name.item.starts_with('^') {
|
||||
let cmd_name: String = lite_cmd.name.item.chars().skip(1).collect();
|
||||
// This is an external command we should allow arguments to pass through with minimal parsing
|
||||
commands.push(ClassifiedCommand::External(ExternalCommand {
|
||||
name: cmd_name,
|
||||
name_tag: Tag::unknown_anchor(lite_cmd.name.span),
|
||||
args: ExternalArgs {
|
||||
list: lite_cmd
|
||||
.args
|
||||
.iter()
|
||||
.map(|x| ExternalArg {
|
||||
arg: x.item.clone(),
|
||||
tag: Tag::unknown_anchor(x.span),
|
||||
})
|
||||
.collect(),
|
||||
span: Span::new(0, 0),
|
||||
let name = lite_cmd
|
||||
.name
|
||||
.clone()
|
||||
.map(|v| v.chars().skip(1).collect::<String>());
|
||||
|
||||
// TODO this is the same as the `else` branch below, only the name differs. Find a way
|
||||
// to share this functionality.
|
||||
let name_iter = std::iter::once(name);
|
||||
let args = name_iter.chain(lite_cmd.args.clone().into_iter());
|
||||
let args = arguments_from_string_iter(args);
|
||||
|
||||
commands.push(ClassifiedCommand::Internal(InternalCommand {
|
||||
name: "run_external".to_string(),
|
||||
name_span: Span::unknown(),
|
||||
args: hir::Call {
|
||||
head: Box::new(SpannedExpression {
|
||||
expr: Expression::string("run_external".to_string()),
|
||||
span: Span::unknown(),
|
||||
}),
|
||||
positional: Some(args),
|
||||
named: None,
|
||||
span: Span::unknown(),
|
||||
is_last: iter.peek().is_none(),
|
||||
},
|
||||
}))
|
||||
} else if lite_cmd.name.item == "=" {
|
||||
let expr = if !lite_cmd.args.is_empty() {
|
||||
let (_, expr, err) = parse_math_expression(0, &lite_cmd.args[0..], registry, false);
|
||||
if error.is_none() {
|
||||
error = err;
|
||||
}
|
||||
error = error.or(err);
|
||||
expr
|
||||
} else {
|
||||
if error.is_none() {
|
||||
error = Some(ParseError::argument_error(
|
||||
error = error.or_else(|| {
|
||||
Some(ParseError::argument_error(
|
||||
lite_cmd.name.clone(),
|
||||
ArgumentError::MissingMandatoryPositional("an expression".into()),
|
||||
))
|
||||
}
|
||||
});
|
||||
garbage(lite_cmd.span())
|
||||
};
|
||||
commands.push(ClassifiedCommand::Expr(Box::new(expr)))
|
||||
} else if let Some(signature) = registry.get(&lite_cmd.name.item) {
|
||||
let (internal_command, err) = parse_internal_command(&lite_cmd, registry, &signature);
|
||||
|
||||
if error.is_none() {
|
||||
error = err;
|
||||
}
|
||||
error = error.or(err);
|
||||
commands.push(ClassifiedCommand::Internal(internal_command))
|
||||
} else {
|
||||
let trimmed = trim_quotes(&lite_cmd.name.item);
|
||||
let name = expand_path(&trimmed);
|
||||
// This is an external command we should allow arguments to pass through with minimal parsing
|
||||
commands.push(ClassifiedCommand::External(ExternalCommand {
|
||||
name,
|
||||
name_tag: Tag::unknown_anchor(lite_cmd.name.span),
|
||||
args: ExternalArgs {
|
||||
list: lite_cmd
|
||||
.args
|
||||
.iter()
|
||||
.map(|x| ExternalArg {
|
||||
arg: x.item.clone(),
|
||||
tag: Tag::unknown_anchor(x.span),
|
||||
})
|
||||
.collect(),
|
||||
span: Span::new(0, 0),
|
||||
let name = lite_cmd.name.clone().map(|v| {
|
||||
let trimmed = trim_quotes(&v);
|
||||
expand_path(&trimmed)
|
||||
});
|
||||
|
||||
let name_iter = std::iter::once(name);
|
||||
let args = name_iter.chain(lite_cmd.args.clone().into_iter());
|
||||
let args = arguments_from_string_iter(args);
|
||||
|
||||
commands.push(ClassifiedCommand::Internal(InternalCommand {
|
||||
name: "run_external".to_string(),
|
||||
name_span: Span::unknown(),
|
||||
args: hir::Call {
|
||||
head: Box::new(SpannedExpression {
|
||||
expr: Expression::string("run_external".to_string()),
|
||||
span: Span::unknown(),
|
||||
}),
|
||||
positional: Some(args),
|
||||
named: None,
|
||||
span: Span::unknown(),
|
||||
is_last: iter.peek().is_none(),
|
||||
},
|
||||
}))
|
||||
}
|
||||
@ -1048,6 +1057,20 @@ pub fn classify_pipeline(
|
||||
ClassifiedPipeline::new(commands, error)
|
||||
}
|
||||
|
||||
/// Parse out arguments from spanned expressions
|
||||
pub fn arguments_from_string_iter(
|
||||
iter: impl Iterator<Item = Spanned<String>>,
|
||||
) -> Vec<SpannedExpression> {
|
||||
iter.map(|v| {
|
||||
// TODO parse_full_column_path
|
||||
SpannedExpression {
|
||||
expr: Expression::string(v.to_string()),
|
||||
span: v.span,
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
/// Easy shorthand function to create a garbage expression at the given span
|
||||
pub fn garbage(span: Span) -> SpannedExpression {
|
||||
SpannedExpression::new(Expression::Garbage, span)
|
||||
|
Reference in New Issue
Block a user