mirror of
https://github.com/nushell/nushell.git
synced 2025-08-09 08:45:45 +02:00
@ -6,7 +6,7 @@ mod shapes;
|
||||
mod signature;
|
||||
|
||||
pub use crate::files::Files;
|
||||
pub use crate::lite_parse::{lite_parse, LitePipeline};
|
||||
pub use crate::parse::{classify_pipeline, garbage};
|
||||
pub use crate::lite_parse::{lite_parse, LiteBlock};
|
||||
pub use crate::parse::{classify_block, garbage};
|
||||
pub use crate::shapes::shapes;
|
||||
pub use crate::signature::{Signature, SignatureRegistry};
|
||||
|
@ -29,11 +29,16 @@ impl LiteCommand {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct LitePipeline {
|
||||
pub commands: Vec<LiteCommand>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct LiteBlock {
|
||||
pub block: Vec<LitePipeline>,
|
||||
}
|
||||
|
||||
fn skip_whitespace(src: &mut Input) {
|
||||
while let Some((_, x)) = src.peek() {
|
||||
if x.is_whitespace() {
|
||||
@ -58,7 +63,8 @@ fn bare(src: &mut Input, span_offset: usize) -> Result<Spanned<String>, ParseErr
|
||||
let mut inside_quote = false;
|
||||
let mut block_level = vec![];
|
||||
|
||||
for (_, c) in src {
|
||||
while let Some((_, c)) = src.peek() {
|
||||
let c = *c;
|
||||
if inside_quote {
|
||||
if c == delimiter {
|
||||
inside_quote = false;
|
||||
@ -84,10 +90,11 @@ fn bare(src: &mut Input, span_offset: usize) -> Result<Spanned<String>, ParseErr
|
||||
if let Some('(') = block_level.last() {
|
||||
let _ = block_level.pop();
|
||||
}
|
||||
} else if block_level.is_empty() && c.is_whitespace() {
|
||||
} else if block_level.is_empty() && (c.is_whitespace() || c == '|' || c == ';') {
|
||||
break;
|
||||
}
|
||||
bare.push(c);
|
||||
let _ = src.next();
|
||||
}
|
||||
|
||||
let span = Span::new(
|
||||
@ -139,16 +146,14 @@ fn quoted(
|
||||
fn command(src: &mut Input, span_offset: usize) -> Result<LiteCommand, ParseError> {
|
||||
let command = bare(src, span_offset)?;
|
||||
if command.item.is_empty() {
|
||||
Err(ParseError::unexpected_eof(
|
||||
"unexpected end of input",
|
||||
command.span,
|
||||
))
|
||||
Err(ParseError::unexpected_eof("command", command.span))
|
||||
} else {
|
||||
Ok(LiteCommand::new(command))
|
||||
}
|
||||
}
|
||||
|
||||
fn pipeline(src: &mut Input, span_offset: usize) -> Result<LitePipeline, ParseError> {
|
||||
fn pipeline(src: &mut Input, span_offset: usize) -> Result<LiteBlock, ParseError> {
|
||||
let mut block = vec![];
|
||||
let mut commands = vec![];
|
||||
|
||||
skip_whitespace(src);
|
||||
@ -167,6 +172,10 @@ fn pipeline(src: &mut Input, span_offset: usize) -> Result<LitePipeline, ParseEr
|
||||
if let Some((_, c)) = src.peek() {
|
||||
// The first character tells us a lot about each argument
|
||||
match c {
|
||||
';' => {
|
||||
// this is the end of the command and the end of the pipeline
|
||||
break;
|
||||
}
|
||||
'|' => {
|
||||
let _ = src.next();
|
||||
if let Some((pos, next_c)) = src.peek() {
|
||||
@ -202,21 +211,35 @@ fn pipeline(src: &mut Input, span_offset: usize) -> Result<LitePipeline, ParseEr
|
||||
}
|
||||
commands.push(cmd);
|
||||
skip_whitespace(src);
|
||||
|
||||
if let Some((_, ';')) = src.peek() {
|
||||
let _ = src.next();
|
||||
|
||||
if !commands.is_empty() {
|
||||
block.push(LitePipeline { commands });
|
||||
commands = vec![];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(LitePipeline { commands })
|
||||
if !commands.is_empty() {
|
||||
block.push(LitePipeline { commands });
|
||||
}
|
||||
|
||||
Ok(LiteBlock { block })
|
||||
}
|
||||
|
||||
pub fn lite_parse(src: &str, span_offset: usize) -> Result<LitePipeline, ParseError> {
|
||||
pub fn lite_parse(src: &str, span_offset: usize) -> Result<LiteBlock, ParseError> {
|
||||
pipeline(&mut src.char_indices().peekable(), span_offset)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lite_simple_1() -> Result<(), ParseError> {
|
||||
let result = lite_parse("foo", 0)?;
|
||||
assert_eq!(result.commands.len(), 1);
|
||||
assert_eq!(result.commands[0].name.span.start(), 0);
|
||||
assert_eq!(result.commands[0].name.span.end(), 3);
|
||||
assert_eq!(result.block.len(), 1);
|
||||
assert_eq!(result.block[0].commands.len(), 1);
|
||||
assert_eq!(result.block[0].commands[0].name.span.start(), 0);
|
||||
assert_eq!(result.block[0].commands[0].name.span.end(), 3);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -224,9 +247,10 @@ fn lite_simple_1() -> Result<(), ParseError> {
|
||||
#[test]
|
||||
fn lite_simple_offset() -> Result<(), ParseError> {
|
||||
let result = lite_parse("foo", 10)?;
|
||||
assert_eq!(result.commands.len(), 1);
|
||||
assert_eq!(result.commands[0].name.span.start(), 10);
|
||||
assert_eq!(result.commands[0].name.span.end(), 13);
|
||||
assert_eq!(result.block.len(), 1);
|
||||
assert_eq!(result.block[0].commands.len(), 1);
|
||||
assert_eq!(result.block[0].commands[0].name.span.start(), 10);
|
||||
assert_eq!(result.block[0].commands[0].name.span.end(), 13);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -1,13 +1,14 @@
|
||||
use std::path::Path;
|
||||
|
||||
use crate::lite_parse::{lite_parse, LiteCommand, LitePipeline};
|
||||
use crate::lite_parse::{lite_parse, LiteBlock, LiteCommand, LitePipeline};
|
||||
use crate::path::expand_path;
|
||||
use crate::signature::SignatureRegistry;
|
||||
use log::trace;
|
||||
use nu_errors::{ArgumentError, ParseError};
|
||||
use nu_protocol::hir::{
|
||||
self, Binary, ClassifiedCommand, ClassifiedPipeline, Commands, Expression, Flag, FlagKind,
|
||||
InternalCommand, Member, NamedArguments, Operator, SpannedExpression, Unit,
|
||||
self, Binary, Block, ClassifiedBlock, 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};
|
||||
@ -416,11 +417,19 @@ fn parse_arg(
|
||||
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,
|
||||
let lite_block = match lite_parse(&string, lite_arg.span.start() + 1) {
|
||||
Ok(lb) => lb,
|
||||
Err(e) => return (garbage(lite_arg.span), Some(e)),
|
||||
};
|
||||
|
||||
if lite_block.block.len() != 1 {
|
||||
return (
|
||||
garbage(lite_arg.span),
|
||||
Some(ParseError::mismatch("table", lite_arg.clone())),
|
||||
);
|
||||
}
|
||||
|
||||
let lite_pipeline = lite_block.block[0].clone();
|
||||
let mut output = vec![];
|
||||
for lite_inner in &lite_pipeline.commands {
|
||||
let (arg, err) = parse_arg(SyntaxShape::Any, registry, &lite_inner.name);
|
||||
@ -462,17 +471,17 @@ fn parse_arg(
|
||||
let string: String = chars.collect();
|
||||
|
||||
// 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) {
|
||||
let lite_block = match lite_parse(&string, lite_arg.span.start() + 1) {
|
||||
Ok(lp) => lp,
|
||||
Err(e) => return (garbage(lite_arg.span), Some(e)),
|
||||
};
|
||||
|
||||
let classified_block = classify_pipeline(&lite_pipeline, registry);
|
||||
let classified_block = classify_block(&lite_block, registry);
|
||||
let error = classified_block.failed;
|
||||
|
||||
(
|
||||
SpannedExpression::new(
|
||||
Expression::Block(classified_block.commands),
|
||||
Expression::Block(classified_block.block),
|
||||
lite_arg.span,
|
||||
),
|
||||
error,
|
||||
@ -586,11 +595,20 @@ fn parse_parenthesized_expression(
|
||||
let string: String = chars.collect();
|
||||
|
||||
// We haven't done much with the inner string, so let's go ahead and work with it
|
||||
let mut lite_pipeline = match lite_parse(&string, lite_arg.span.start() + 1) {
|
||||
Ok(lp) => lp,
|
||||
let lite_block = match lite_parse(&string, lite_arg.span.start() + 1) {
|
||||
Ok(lb) => lb,
|
||||
Err(e) => return (garbage(lite_arg.span), Some(e)),
|
||||
};
|
||||
|
||||
if lite_block.block.len() != 1 {
|
||||
return (
|
||||
garbage(lite_arg.span),
|
||||
Some(ParseError::mismatch("math expression", lite_arg.clone())),
|
||||
);
|
||||
}
|
||||
|
||||
let mut lite_pipeline = lite_block.block[0].clone();
|
||||
|
||||
let mut collection = vec![];
|
||||
for lite_cmd in lite_pipeline.commands.iter_mut() {
|
||||
collection.push(lite_cmd.name.clone());
|
||||
@ -796,8 +814,10 @@ fn parse_positional_argument(
|
||||
let span = arg.span;
|
||||
let mut commands = hir::Commands::new(span);
|
||||
commands.push(ClassifiedCommand::Expr(Box::new(arg)));
|
||||
let mut block = hir::Block::new(span);
|
||||
block.push(commands);
|
||||
|
||||
let arg = SpannedExpression::new(Expression::Block(commands), span);
|
||||
let arg = SpannedExpression::new(Expression::Block(block), span);
|
||||
|
||||
idx = new_idx;
|
||||
if error.is_none() {
|
||||
@ -971,10 +991,10 @@ fn parse_internal_command(
|
||||
/// Convert a lite-ly parsed pipeline into a fully classified pipeline, ready to be evaluated.
|
||||
/// This conversion does error-recovery, so the result is allowed to be lossy. A lossy unit is designated as garbage.
|
||||
/// Errors are returned as part of a side-car error rather than a Result to allow both error and lossy result simultaneously.
|
||||
pub fn classify_pipeline(
|
||||
fn classify_pipeline(
|
||||
lite_pipeline: &LitePipeline,
|
||||
registry: &dyn SignatureRegistry,
|
||||
) -> ClassifiedPipeline {
|
||||
) -> (ClassifiedPipeline, Option<ParseError>) {
|
||||
// FIXME: fake span
|
||||
let mut commands = Commands::new(Span::new(0, 0));
|
||||
let mut error = None;
|
||||
@ -1054,7 +1074,23 @@ pub fn classify_pipeline(
|
||||
}
|
||||
}
|
||||
|
||||
ClassifiedPipeline::new(commands, error)
|
||||
(ClassifiedPipeline::new(commands), error)
|
||||
}
|
||||
|
||||
pub fn classify_block(lite_block: &LiteBlock, registry: &dyn SignatureRegistry) -> ClassifiedBlock {
|
||||
// FIXME: fake span
|
||||
let mut block = Block::new(Span::new(0, 0));
|
||||
|
||||
let mut error = None;
|
||||
for lite_pipeline in &lite_block.block {
|
||||
let (pipeline, err) = classify_pipeline(lite_pipeline, registry);
|
||||
block.push(pipeline.commands);
|
||||
if error.is_none() {
|
||||
error = err;
|
||||
}
|
||||
}
|
||||
|
||||
ClassifiedBlock::new(block, error)
|
||||
}
|
||||
|
||||
/// Parse out arguments from spanned expressions
|
||||
|
@ -62,43 +62,45 @@ pub fn expression_to_flat_shape(e: &SpannedExpression) -> Vec<Spanned<FlatShape>
|
||||
}
|
||||
|
||||
/// Converts a series of commands into a vec of spanned shapes ready for color-highlighting
|
||||
pub fn shapes(commands: &Commands) -> Vec<Spanned<FlatShape>> {
|
||||
pub fn shapes(commands: &Block) -> Vec<Spanned<FlatShape>> {
|
||||
let mut output = vec![];
|
||||
|
||||
for command in &commands.list {
|
||||
match command {
|
||||
ClassifiedCommand::Internal(internal) => {
|
||||
output.append(&mut expression_to_flat_shape(&internal.args.head));
|
||||
for pipeline in &commands.block {
|
||||
for command in &pipeline.list {
|
||||
match command {
|
||||
ClassifiedCommand::Internal(internal) => {
|
||||
output.append(&mut expression_to_flat_shape(&internal.args.head));
|
||||
|
||||
if let Some(positionals) = &internal.args.positional {
|
||||
for positional_arg in positionals {
|
||||
output.append(&mut expression_to_flat_shape(positional_arg));
|
||||
if let Some(positionals) = &internal.args.positional {
|
||||
for positional_arg in positionals {
|
||||
output.append(&mut expression_to_flat_shape(positional_arg));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(named) = &internal.args.named {
|
||||
for (_, named_arg) in named.iter() {
|
||||
match named_arg {
|
||||
NamedValue::PresentSwitch(span) => {
|
||||
output.push(FlatShape::Flag.spanned(*span));
|
||||
if let Some(named) = &internal.args.named {
|
||||
for (_, named_arg) in named.iter() {
|
||||
match named_arg {
|
||||
NamedValue::PresentSwitch(span) => {
|
||||
output.push(FlatShape::Flag.spanned(*span));
|
||||
}
|
||||
NamedValue::Value(span, expr) => {
|
||||
output.push(FlatShape::Flag.spanned(*span));
|
||||
output.append(&mut expression_to_flat_shape(expr));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
NamedValue::Value(span, expr) => {
|
||||
output.push(FlatShape::Flag.spanned(*span));
|
||||
output.append(&mut expression_to_flat_shape(expr));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ClassifiedCommand::External(external) => {
|
||||
output.push(FlatShape::ExternalCommand.spanned(external.name_tag.span));
|
||||
for arg in external.args.iter() {
|
||||
output.push(FlatShape::ExternalWord.spanned(arg.tag.span));
|
||||
ClassifiedCommand::External(external) => {
|
||||
output.push(FlatShape::ExternalCommand.spanned(external.name_tag.span));
|
||||
for arg in external.args.iter() {
|
||||
output.push(FlatShape::ExternalWord.spanned(arg.tag.span));
|
||||
}
|
||||
}
|
||||
ClassifiedCommand::Expr(expr) => output.append(&mut expression_to_flat_shape(expr)),
|
||||
_ => {}
|
||||
}
|
||||
ClassifiedCommand::Expr(expr) => output.append(&mut expression_to_flat_shape(expr)),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user