* WIP on blocks

* Getting further

* add some tests
This commit is contained in:
Jonathan Turner
2020-04-20 18:41:51 +12:00
committed by GitHub
parent 6412bfd58d
commit eec94e4016
21 changed files with 384 additions and 155 deletions

View File

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

View File

@ -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(())
}

View File

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

View File

@ -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)),
_ => {}
}
}