From 0794ebf5fae7d5fb3c4ffab8c6a67946f1d844ac Mon Sep 17 00:00:00 2001 From: Fernando Herrera Date: Fri, 10 Sep 2021 08:28:43 +0100 Subject: [PATCH 01/13] error parsing for def, alias and let --- crates/nu-cli/src/errors.rs | 5 +- crates/nu-parser/src/parser.rs | 134 +++++++++++++++++----------- crates/nu-protocol/src/ast/block.rs | 12 +++ 3 files changed, 98 insertions(+), 53 deletions(-) diff --git a/crates/nu-cli/src/errors.rs b/crates/nu-cli/src/errors.rs index 77ddfd8536..1c25e5d2e4 100644 --- a/crates/nu-cli/src/errors.rs +++ b/crates/nu-cli/src/errors.rs @@ -208,8 +208,9 @@ pub fn report_parsing_error( let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; Diagnostic::error() .with_message("Unknown state") - .with_labels(vec![Label::primary(diag_file_id, diag_range) - .with_message(format!("unknown state {}", name))]) + .with_labels(vec![ + Label::primary(diag_file_id, diag_range).with_message(format!("{}", name)) + ]) } ParseError::NonUtf8(span) => { let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; diff --git a/crates/nu-parser/src/parser.rs b/crates/nu-parser/src/parser.rs index 57506b7fd2..119f14c3e5 100644 --- a/crates/nu-parser/src/parser.rs +++ b/crates/nu-parser/src/parser.rs @@ -26,6 +26,10 @@ fn garbage(span: Span) -> Expression { Expression::garbage(span) } +fn garbage_statement(spans: &[Span]) -> Statement { + Statement::Pipeline(Pipeline::from_vec(vec![garbage(span(spans))])) +} + fn is_identifier_byte(b: u8) -> bool { b != b'.' && b != b'[' && b != b'(' && b != b'{' } @@ -59,6 +63,22 @@ fn check_call(command: Span, sig: &Signature, call: &Call) -> Option } } +fn check_name(working_set: &mut StateWorkingSet, spans: &[Span]) -> Option { + if spans[1..].len() < 2 { + Some(ParseError::UnknownState( + "missing definition name".into(), + span(spans), + )) + } else if working_set.get_span_contents(spans[2]) != b"=" { + Some(ParseError::UnknownState( + "missing equal sign in definition".into(), + spans[2], + )) + } else { + None + } +} + pub fn parse_external_call( working_set: &mut StateWorkingSet, spans: &[Span], @@ -2325,6 +2345,13 @@ pub fn parse_def( error = error.or(err); working_set.exit_scope(); + if error.is_some() { + return ( + Statement::Pipeline(Pipeline::from_vec(vec![garbage(span(spans))])), + error, + ); + } + let name = name_expr.as_string(); let signature = sig.as_signature(); @@ -2364,23 +2391,15 @@ pub fn parse_def( ) } _ => ( - Statement::Pipeline(Pipeline::from_vec(vec![Expression { - expr: Expr::Garbage, - span: span(spans), - ty: Type::Unknown, - }])), + Statement::Pipeline(Pipeline::from_vec(vec![garbage(span(spans))])), error, ), } } else { ( - Statement::Pipeline(Pipeline::from_vec(vec![Expression { - expr: Expr::Garbage, - span: span(spans), - ty: Type::Unknown, - }])), + garbage_statement(spans), Some(ParseError::UnknownState( - "internal error: definition unparseable".into(), + "definition unparseable. Expected structure: def [] {}".into(), span(spans), )), ) @@ -2394,6 +2413,13 @@ pub fn parse_alias( let name = working_set.get_span_contents(spans[0]); if name == b"alias" { + if let Some(err) = check_name(working_set, spans) { + return ( + Statement::Pipeline(Pipeline::from_vec(vec![garbage(span(spans))])), + Some(err), + ); + } + if let Some(decl_id) = working_set.find_decl(b"alias") { let (call, call_span, _) = parse_internal_call(working_set, spans[0], &spans[1..], decl_id); @@ -2430,11 +2456,7 @@ pub fn parse_alias( } ( - Statement::Pipeline(Pipeline::from_vec(vec![Expression { - expr: Expr::Garbage, - span: span(spans), - ty: Type::Unknown, - }])), + garbage_statement(spans), Some(ParseError::UnknownState( "internal error: let statement unparseable".into(), span(spans), @@ -2449,6 +2471,13 @@ pub fn parse_let( let name = working_set.get_span_contents(spans[0]); if name == b"let" { + if let Some(err) = check_name(working_set, spans) { + return ( + Statement::Pipeline(Pipeline::from_vec(vec![garbage(span(spans))])), + Some(err), + ); + } + if let Some(decl_id) = working_set.find_decl(b"let") { let (call, call_span, err) = parse_internal_call(working_set, spans[0], &spans[1..], decl_id); @@ -2474,11 +2503,7 @@ pub fn parse_let( } } ( - Statement::Pipeline(Pipeline::from_vec(vec![Expression { - expr: Expr::Garbage, - span: span(spans), - ty: Type::Unknown, - }])), + garbage_statement(spans), Some(ParseError::UnknownState( "internal error: let statement unparseable".into(), span(spans), @@ -2490,16 +2515,16 @@ pub fn parse_statement( working_set: &mut StateWorkingSet, spans: &[Span], ) -> (Statement, Option) { - // FIXME: improve errors by checking keyword first - if let (decl, None) = parse_def(working_set, spans) { - (decl, None) - } else if let (stmt, None) = parse_let(working_set, spans) { - (stmt, None) - } else if let (stmt, None) = parse_alias(working_set, spans) { - (stmt, None) - } else { - let (expr, err) = parse_expression(working_set, spans); - (Statement::Pipeline(Pipeline::from_vec(vec![expr])), err) + let name = working_set.get_span_contents(spans[0]); + + match name { + b"def" => parse_def(working_set, spans), + b"let" => parse_let(working_set, spans), + b"alias" => parse_alias(working_set, spans), + _ => { + let (expr, err) = parse_expression(working_set, spans); + (Statement::Pipeline(Pipeline::from_vec(vec![expr])), err) + } } } @@ -2508,13 +2533,10 @@ pub fn parse_block( lite_block: &LiteBlock, scoped: bool, ) -> (Block, Option) { - let mut error = None; if scoped { working_set.enter_scope(); } - let mut block = Block::new(); - // Pre-declare any definition so that definitions // that share the same block can see each other for pipeline in &lite_block.block { @@ -2523,25 +2545,35 @@ pub fn parse_block( } } - for pipeline in &lite_block.block { - if pipeline.commands.len() > 1 { - let mut output = vec![]; - for command in &pipeline.commands { - let (expr, err) = parse_expression(working_set, &command.parts); - error = error.or(err); + let mut error = None; - output.push(expr); + let block: Block = lite_block + .block + .iter() + .map(|pipeline| { + if pipeline.commands.len() > 1 { + let output = pipeline + .commands + .iter() + .map(|command| { + let (expr, err) = parse_expression(working_set, &command.parts); + error = err.map(|err| err); + + expr + }) + .collect::>(); + + Statement::Pipeline(Pipeline { + expressions: output, + }) + } else { + let (stmt, err) = parse_statement(working_set, &pipeline.commands[0].parts); + error = err.map(|err| err); + + stmt } - block.stmts.push(Statement::Pipeline(Pipeline { - expressions: output, - })); - } else { - let (stmt, err) = parse_statement(working_set, &pipeline.commands[0].parts); - error = error.or(err); - - block.stmts.push(stmt); - } - } + }) + .into(); if scoped { working_set.exit_scope(); diff --git a/crates/nu-protocol/src/ast/block.rs b/crates/nu-protocol/src/ast/block.rs index d36e43d8ec..e5db26da36 100644 --- a/crates/nu-protocol/src/ast/block.rs +++ b/crates/nu-protocol/src/ast/block.rs @@ -48,3 +48,15 @@ impl Block { } } } + +impl From for Block +where + T: Iterator, +{ + fn from(stmts: T) -> Self { + Self { + signature: Box::new(Signature::new("")), + stmts: stmts.collect(), + } + } +} From 9a16a8fd0698fea63349710bfcea3dfbdc4cbe29 Mon Sep 17 00:00:00 2001 From: Fernando Herrera Date: Fri, 10 Sep 2021 08:44:31 +0100 Subject: [PATCH 02/13] corrected error check --- crates/nu-parser/src/parser.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/crates/nu-parser/src/parser.rs b/crates/nu-parser/src/parser.rs index fd8260c2d0..46978a162d 100644 --- a/crates/nu-parser/src/parser.rs +++ b/crates/nu-parser/src/parser.rs @@ -2610,7 +2610,10 @@ pub fn parse_block( .iter() .map(|command| { let (expr, err) = parse_expression(working_set, &command.parts); - error = err.map(|err| err); + + if error.is_none() { + error = err; + } expr }) @@ -2621,7 +2624,10 @@ pub fn parse_block( }) } else { let (stmt, err) = parse_statement(working_set, &pipeline.commands[0].parts); - error = err.map(|err| err); + + if error.is_none() { + error = err; + } stmt } From 198c884158c301e76becc7d631d5ff8e9404f66c Mon Sep 17 00:00:00 2001 From: Fernando Herrera Date: Sat, 11 Sep 2021 08:22:41 +0100 Subject: [PATCH 03/13] change name in error --- crates/nu-parser/src/parser.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/nu-parser/src/parser.rs b/crates/nu-parser/src/parser.rs index 46978a162d..9fba1a2d87 100644 --- a/crates/nu-parser/src/parser.rs +++ b/crates/nu-parser/src/parser.rs @@ -2511,7 +2511,7 @@ pub fn parse_alias( ( garbage_statement(spans), Some(ParseError::UnknownState( - "internal error: let statement unparseable".into(), + "internal error: alias statement unparseable".into(), span(spans), )), ) From 4b8ba29cdb66039e48aa6bfa46ae935e62686c17 Mon Sep 17 00:00:00 2001 From: Fernando Herrera Date: Sat, 11 Sep 2021 13:07:19 +0100 Subject: [PATCH 04/13] check for = before internal parsing --- crates/nu-parser/src/parser.rs | 39 ++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/crates/nu-parser/src/parser.rs b/crates/nu-parser/src/parser.rs index fcfbe7e34f..16ee5aca75 100644 --- a/crates/nu-parser/src/parser.rs +++ b/crates/nu-parser/src/parser.rs @@ -72,7 +72,7 @@ fn check_name(working_set: &mut StateWorkingSet, spans: &[Span]) -> Option 1 { + let test_equal = working_set.get_span_contents(spans[1]); + + if test_equal == &[b'='] { + return ( + garbage(Span::new(0, 0)), + Some(ParseError::UnknownState( + "internal error: incomplete statement".into(), + span(spans), + )), + ); + } + } + // parse internal command let (call, _, err) = parse_internal_call(working_set, span(&spans[0..pos]), &spans[pos..], decl_id); @@ -2568,16 +2585,16 @@ pub fn parse_statement( working_set: &mut StateWorkingSet, spans: &[Span], ) -> (Statement, Option) { - let name = working_set.get_span_contents(spans[0]); - - match name { - b"def" => parse_def(working_set, spans), - b"let" => parse_let(working_set, spans), - b"alias" => parse_alias(working_set, spans), - _ => { - let (expr, err) = parse_expression(working_set, spans); - (Statement::Pipeline(Pipeline::from_vec(vec![expr])), err) - } + // FIXME: improve errors by checking keyword first + if let (decl, None) = parse_def(working_set, spans) { + (decl, None) + } else if let (stmt, None) = parse_let(working_set, spans) { + (stmt, None) + } else if let (stmt, None) = parse_alias(working_set, spans) { + (stmt, None) + } else { + let (expr, err) = parse_expression(working_set, spans); + (Statement::Pipeline(Pipeline::from_vec(vec![expr])), err) } } From 9c98783917ab43a6707d09b18ceb64bb71fce995 Mon Sep 17 00:00:00 2001 From: Fernando Herrera Date: Sat, 11 Sep 2021 13:16:40 +0100 Subject: [PATCH 05/13] clippy correcgtions --- crates/nu-cli/src/errors.rs | 2 +- crates/nu-parser/src/parser.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/nu-cli/src/errors.rs b/crates/nu-cli/src/errors.rs index 1c25e5d2e4..b5470ead13 100644 --- a/crates/nu-cli/src/errors.rs +++ b/crates/nu-cli/src/errors.rs @@ -209,7 +209,7 @@ pub fn report_parsing_error( Diagnostic::error() .with_message("Unknown state") .with_labels(vec![ - Label::primary(diag_file_id, diag_range).with_message(format!("{}", name)) + Label::primary(diag_file_id, diag_range).with_message(name.to_string()) ]) } ParseError::NonUtf8(span) => { diff --git a/crates/nu-parser/src/parser.rs b/crates/nu-parser/src/parser.rs index 16ee5aca75..5c03e08ddb 100644 --- a/crates/nu-parser/src/parser.rs +++ b/crates/nu-parser/src/parser.rs @@ -606,7 +606,7 @@ pub fn parse_call( if spans.len() > 1 { let test_equal = working_set.get_span_contents(spans[1]); - if test_equal == &[b'='] { + if test_equal == [b'='] { return ( garbage(Span::new(0, 0)), Some(ParseError::UnknownState( From 2f04c172fe74981be82149823d96574f7ff4383f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20=C5=BD=C3=A1dn=C3=ADk?= Date: Sun, 12 Sep 2021 14:12:53 +0300 Subject: [PATCH 06/13] Add floating point support for ranges --- crates/nu-protocol/src/value/range.rs | 41 +++++++++++++++++++-------- 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/crates/nu-protocol/src/value/range.rs b/crates/nu-protocol/src/value/range.rs index ff138e4af6..2832d09b2b 100644 --- a/crates/nu-protocol/src/value/range.rs +++ b/crates/nu-protocol/src/value/range.rs @@ -1,3 +1,5 @@ +use std::cmp::Ordering; + use crate::{ ast::{RangeInclusion, RangeOperator}, *, @@ -150,31 +152,46 @@ impl RangeIterator { } } +// Compare two floating point numbers. The decision interval for equality is dynamically scaled +// based on the value being compared. +fn compare_floats(val: f64, other: f64) -> Option { + let prec = val * f64::EPSILON; + + if (other - val).abs() < prec.abs() { + return Some(Ordering::Equal); + } + + val.partial_cmp(&other) +} + impl Iterator for RangeIterator { type Item = Value; fn next(&mut self) -> Option { - use std::cmp::Ordering; if self.done { return None; } let ordering = if matches!(self.end, Value::Nothing { .. }) { - Ordering::Less + Some(Ordering::Less) } else { match (&self.curr, &self.end) { - (Value::Int { val: curr, .. }, Value::Int { val: end, .. }) => curr.cmp(end), - // (Value::Float { val: curr, .. }, Value::Float { val: end, .. }) => curr.cmp(end), - // (Value::Float { val: curr, .. }, Value::Int { val: end, .. }) => curr.cmp(end), - // (Value::Int { val: curr, .. }, Value::Float { val: end, .. }) => curr.cmp(end), - _ => { - self.done = true; - return Some(Value::Error { - error: ShellError::CannotCreateRange(self.span), - }); - } + (Value::Int { val: curr, .. }, Value::Int { val: end, .. }) => Some(curr.cmp(end)), + (Value::Float { val: curr, .. }, Value::Float { val: end, .. }) => compare_floats(*curr, *end), + (Value::Float { val: curr, .. }, Value::Int { val: end, .. }) => compare_floats(*curr, *end as f64), + (Value::Int { val: curr, .. }, Value::Float { val: end, .. }) => compare_floats(*curr as f64, *end), + _ => None, } }; + let ordering = if let Some(ord) = ordering { + ord + } else { + self.done = true; + return Some(Value::Error { + error: ShellError::CannotCreateRange(self.span), + }); + }; + let desired_ordering = if self.moves_up { Ordering::Less } else { From 013b12a86451556903ea1866010864d234afdbfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20=C5=BD=C3=A1dn=C3=ADk?= Date: Sun, 12 Sep 2021 14:55:11 +0300 Subject: [PATCH 07/13] Do not allow precision interval to rach < epsilon --- crates/nu-protocol/src/value/range.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/nu-protocol/src/value/range.rs b/crates/nu-protocol/src/value/range.rs index 2832d09b2b..44a97d7b59 100644 --- a/crates/nu-protocol/src/value/range.rs +++ b/crates/nu-protocol/src/value/range.rs @@ -153,11 +153,11 @@ impl RangeIterator { } // Compare two floating point numbers. The decision interval for equality is dynamically scaled -// based on the value being compared. +// as the value being compared increases in magnitude. fn compare_floats(val: f64, other: f64) -> Option { - let prec = val * f64::EPSILON; + let prec = f64::EPSILON.max(val.abs() * f64::EPSILON); - if (other - val).abs() < prec.abs() { + if (other - val).abs() < prec { return Some(Ordering::Equal); } From 9936946eb57306ccfae5bdc70b61f8b20ee38f9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20=C5=BD=C3=A1dn=C3=ADk?= Date: Sun, 12 Sep 2021 14:58:32 +0300 Subject: [PATCH 08/13] Fmt --- crates/nu-protocol/src/value/range.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/crates/nu-protocol/src/value/range.rs b/crates/nu-protocol/src/value/range.rs index 44a97d7b59..80eb568827 100644 --- a/crates/nu-protocol/src/value/range.rs +++ b/crates/nu-protocol/src/value/range.rs @@ -176,9 +176,15 @@ impl Iterator for RangeIterator { } else { match (&self.curr, &self.end) { (Value::Int { val: curr, .. }, Value::Int { val: end, .. }) => Some(curr.cmp(end)), - (Value::Float { val: curr, .. }, Value::Float { val: end, .. }) => compare_floats(*curr, *end), - (Value::Float { val: curr, .. }, Value::Int { val: end, .. }) => compare_floats(*curr, *end as f64), - (Value::Int { val: curr, .. }, Value::Float { val: end, .. }) => compare_floats(*curr as f64, *end), + (Value::Float { val: curr, .. }, Value::Float { val: end, .. }) => { + compare_floats(*curr, *end) + } + (Value::Float { val: curr, .. }, Value::Int { val: end, .. }) => { + compare_floats(*curr, *end as f64) + } + (Value::Int { val: curr, .. }, Value::Float { val: end, .. }) => { + compare_floats(*curr as f64, *end) + } _ => None, } }; From ce0b5bf4ab43cb60f360873d1a0fece0b06d2750 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20=C5=BD=C3=A1dn=C3=ADk?= Date: Sun, 12 Sep 2021 15:36:54 +0300 Subject: [PATCH 09/13] Add test for float ranges --- crates/nu-parser/tests/test_parser.rs | 32 +++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/crates/nu-parser/tests/test_parser.rs b/crates/nu-parser/tests/test_parser.rs index 2015f356c0..e0359378c2 100644 --- a/crates/nu-parser/tests/test_parser.rs +++ b/crates/nu-parser/tests/test_parser.rs @@ -403,6 +403,38 @@ mod range { } } + #[test] + fn parse_float_range() { + let engine_state = EngineState::new(); + let mut working_set = StateWorkingSet::new(&engine_state); + + let (block, err) = parse(&mut working_set, None, b"2.0..4.0..10.0", true); + + assert!(err.is_none()); + assert!(block.len() == 1); + match &block[0] { + Statement::Pipeline(Pipeline { expressions }) => { + assert!(expressions.len() == 1); + assert!(matches!( + expressions[0], + Expression { + expr: Expr::Range( + Some(_), + Some(_), + Some(_), + RangeOperator { + inclusion: RangeInclusion::Inclusive, + .. + } + ), + .. + } + )) + } + _ => panic!("No match"), + } + } + #[test] fn bad_parse_does_crash() { let engine_state = EngineState::new(); From 78054a53525901c2a36b1bb0f9ae4ba6716572bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20=C5=BD=C3=A1dn=C3=ADk?= Date: Sun, 12 Sep 2021 15:29:27 +0300 Subject: [PATCH 10/13] Allow parsing left-unbounded range (..10) It is implemented as a preliminary check when parsing a call and relies on a fact that a token that successfully parses as a range is unlikely to be a valid path or command name. --- crates/nu-parser/src/parser.rs | 9 ++++++++ crates/nu-parser/tests/test_parser.rs | 31 +++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/crates/nu-parser/src/parser.rs b/crates/nu-parser/src/parser.rs index 6199e87ee1..839f43db88 100644 --- a/crates/nu-parser/src/parser.rs +++ b/crates/nu-parser/src/parser.rs @@ -469,6 +469,15 @@ pub fn parse_call( spans: &[Span], expand_aliases: bool, ) -> (Expression, Option) { + // We might be parsing left-unbounded range ("..10") + let bytes = working_set.get_span_contents(spans[0]); + if let (Some(b'.'), Some(b'.')) = (bytes.get(0), bytes.get(1)) { + let (range_expr, range_err) = parse_range(working_set, spans[0]); + if range_err.is_none() { + return (range_expr, range_err); + } + } + // assume spans.len() > 0? let mut pos = 0; let mut shorthand = vec![]; diff --git a/crates/nu-parser/tests/test_parser.rs b/crates/nu-parser/tests/test_parser.rs index 2015f356c0..11c8189f78 100644 --- a/crates/nu-parser/tests/test_parser.rs +++ b/crates/nu-parser/tests/test_parser.rs @@ -371,6 +371,37 @@ mod range { } } + #[test] + fn parse_left_unbounded_range() { + let engine_state = EngineState::new(); + let mut working_set = StateWorkingSet::new(&engine_state); + + let (block, err) = parse(&mut working_set, None, b"..10", true); + + assert!(err.is_none()); + assert!(block.len() == 1); + match &block[0] { + Statement::Pipeline(Pipeline { expressions }) => { + assert!(expressions.len() == 1); + assert!(matches!( + expressions[0], + Expression { + expr: Expr::Range( + None, + Some(_), + RangeOperator { + inclusion: RangeInclusion::Inclusive, + .. + } + ), + .. + } + )) + } + _ => panic!("No match"), + } + } + #[test] fn parse_negative_range() { let engine_state = EngineState::new(); From 8577d3ff413e6cecf8f84e03f89080c99995632f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20=C5=BD=C3=A1dn=C3=ADk?= Date: Sun, 12 Sep 2021 15:48:19 +0300 Subject: [PATCH 11/13] Check for left-unbounded range before external cmd --- crates/nu-parser/src/parser.rs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/crates/nu-parser/src/parser.rs b/crates/nu-parser/src/parser.rs index 839f43db88..27acdeca6b 100644 --- a/crates/nu-parser/src/parser.rs +++ b/crates/nu-parser/src/parser.rs @@ -469,15 +469,6 @@ pub fn parse_call( spans: &[Span], expand_aliases: bool, ) -> (Expression, Option) { - // We might be parsing left-unbounded range ("..10") - let bytes = working_set.get_span_contents(spans[0]); - if let (Some(b'.'), Some(b'.')) = (bytes.get(0), bytes.get(1)) { - let (range_expr, range_err) = parse_range(working_set, spans[0]); - if range_err.is_none() { - return (range_expr, range_err); - } - } - // assume spans.len() > 0? let mut pos = 0; let mut shorthand = vec![]; @@ -601,6 +592,14 @@ pub fn parse_call( err, ) } else { + // We might be parsing left-unbounded range ("..10") + let bytes = working_set.get_span_contents(spans[0]); + if let (Some(b'.'), Some(b'.')) = (bytes.get(0), bytes.get(1)) { + let (range_expr, range_err) = parse_range(working_set, spans[0]); + if range_err.is_none() { + return (range_expr, range_err); + } + } parse_external_call(working_set, spans) } } From e6a2e27e3360be80cd4e86f6bccc1fdade1db922 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20=C5=BD=C3=A1dn=C3=ADk?= Date: Sun, 12 Sep 2021 15:57:49 +0300 Subject: [PATCH 12/13] Fix failing compilation after rebase --- crates/nu-parser/tests/test_parser.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/nu-parser/tests/test_parser.rs b/crates/nu-parser/tests/test_parser.rs index 11c8189f78..ca5f3d13ca 100644 --- a/crates/nu-parser/tests/test_parser.rs +++ b/crates/nu-parser/tests/test_parser.rs @@ -387,6 +387,7 @@ mod range { expressions[0], Expression { expr: Expr::Range( + None, None, Some(_), RangeOperator { From 66c58217af28d652c8fba519830afa1b92abcb27 Mon Sep 17 00:00:00 2001 From: Fernando Herrera Date: Sun, 12 Sep 2021 16:36:16 +0100 Subject: [PATCH 13/13] change message --- crates/nu-parser/src/parser.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/nu-parser/src/parser.rs b/crates/nu-parser/src/parser.rs index 5c03e08ddb..f4fd1348fd 100644 --- a/crates/nu-parser/src/parser.rs +++ b/crates/nu-parser/src/parser.rs @@ -610,7 +610,7 @@ pub fn parse_call( return ( garbage(Span::new(0, 0)), Some(ParseError::UnknownState( - "internal error: incomplete statement".into(), + "Incomplete statement".into(), span(spans), )), );