mirror of
https://github.com/nushell/nushell
synced 2024-12-27 21:43:09 +00:00
Merge branch 'main' into block_param_types
This commit is contained in:
commit
4dfde7393b
5 changed files with 218 additions and 55 deletions
|
@ -208,8 +208,9 @@ pub fn report_parsing_error(
|
||||||
let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?;
|
let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?;
|
||||||
Diagnostic::error()
|
Diagnostic::error()
|
||||||
.with_message("Unknown state")
|
.with_message("Unknown state")
|
||||||
.with_labels(vec![Label::primary(diag_file_id, diag_range)
|
.with_labels(vec![
|
||||||
.with_message(format!("unknown state {}", name))])
|
Label::primary(diag_file_id, diag_range).with_message(name.to_string())
|
||||||
|
])
|
||||||
}
|
}
|
||||||
ParseError::NonUtf8(span) => {
|
ParseError::NonUtf8(span) => {
|
||||||
let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?;
|
let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?;
|
||||||
|
|
|
@ -26,6 +26,10 @@ fn garbage(span: Span) -> Expression {
|
||||||
Expression::garbage(span)
|
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 {
|
fn is_identifier_byte(b: u8) -> bool {
|
||||||
b != b'.' && b != b'[' && b != b'(' && b != b'{'
|
b != b'.' && b != b'[' && b != b'(' && b != b'{'
|
||||||
}
|
}
|
||||||
|
@ -59,6 +63,22 @@ fn check_call(command: Span, sig: &Signature, call: &Call) -> Option<ParseError>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn check_name(working_set: &mut StateWorkingSet, spans: &[Span]) -> Option<ParseError> {
|
||||||
|
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(),
|
||||||
|
span(spans),
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn parse_external_call(
|
pub fn parse_external_call(
|
||||||
_working_set: &mut StateWorkingSet,
|
_working_set: &mut StateWorkingSet,
|
||||||
spans: &[Span],
|
spans: &[Span],
|
||||||
|
@ -580,6 +600,23 @@ pub fn parse_call(
|
||||||
name = new_name;
|
name = new_name;
|
||||||
pos += 1;
|
pos += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Before the internal parsing we check if there is no let or alias declarations
|
||||||
|
// that are missing their name, e.g.: let = 1 or alias = 2
|
||||||
|
if spans.len() > 1 {
|
||||||
|
let test_equal = working_set.get_span_contents(spans[1]);
|
||||||
|
|
||||||
|
if test_equal == [b'='] {
|
||||||
|
return (
|
||||||
|
garbage(Span::new(0, 0)),
|
||||||
|
Some(ParseError::UnknownState(
|
||||||
|
"Incomplete statement".into(),
|
||||||
|
span(spans),
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// parse internal command
|
// parse internal command
|
||||||
let (call, _, err) =
|
let (call, _, err) =
|
||||||
parse_internal_call(working_set, span(&spans[0..pos]), &spans[pos..], decl_id);
|
parse_internal_call(working_set, span(&spans[0..pos]), &spans[pos..], decl_id);
|
||||||
|
@ -592,6 +629,14 @@ pub fn parse_call(
|
||||||
err,
|
err,
|
||||||
)
|
)
|
||||||
} else {
|
} 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)
|
parse_external_call(working_set, spans)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2391,6 +2436,13 @@ pub fn parse_def(
|
||||||
error = error.or(err);
|
error = error.or(err);
|
||||||
working_set.exit_scope();
|
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 name = name_expr.as_string();
|
||||||
|
|
||||||
let signature = sig.as_signature();
|
let signature = sig.as_signature();
|
||||||
|
@ -2430,23 +2482,15 @@ pub fn parse_def(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
_ => (
|
_ => (
|
||||||
Statement::Pipeline(Pipeline::from_vec(vec![Expression {
|
Statement::Pipeline(Pipeline::from_vec(vec![garbage(span(spans))])),
|
||||||
expr: Expr::Garbage,
|
|
||||||
span: span(spans),
|
|
||||||
ty: Type::Unknown,
|
|
||||||
}])),
|
|
||||||
error,
|
error,
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
(
|
(
|
||||||
Statement::Pipeline(Pipeline::from_vec(vec![Expression {
|
garbage_statement(spans),
|
||||||
expr: Expr::Garbage,
|
|
||||||
span: span(spans),
|
|
||||||
ty: Type::Unknown,
|
|
||||||
}])),
|
|
||||||
Some(ParseError::UnknownState(
|
Some(ParseError::UnknownState(
|
||||||
"internal error: definition unparseable".into(),
|
"definition unparseable. Expected structure: def <name> [] {}".into(),
|
||||||
span(spans),
|
span(spans),
|
||||||
)),
|
)),
|
||||||
)
|
)
|
||||||
|
@ -2460,6 +2504,13 @@ pub fn parse_alias(
|
||||||
let name = working_set.get_span_contents(spans[0]);
|
let name = working_set.get_span_contents(spans[0]);
|
||||||
|
|
||||||
if name == b"alias" {
|
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") {
|
if let Some(decl_id) = working_set.find_decl(b"alias") {
|
||||||
let (call, call_span, _) =
|
let (call, call_span, _) =
|
||||||
parse_internal_call(working_set, spans[0], &spans[1..], decl_id);
|
parse_internal_call(working_set, spans[0], &spans[1..], decl_id);
|
||||||
|
@ -2496,13 +2547,9 @@ pub fn parse_alias(
|
||||||
}
|
}
|
||||||
|
|
||||||
(
|
(
|
||||||
Statement::Pipeline(Pipeline::from_vec(vec![Expression {
|
garbage_statement(spans),
|
||||||
expr: Expr::Garbage,
|
|
||||||
span: span(spans),
|
|
||||||
ty: Type::Unknown,
|
|
||||||
}])),
|
|
||||||
Some(ParseError::UnknownState(
|
Some(ParseError::UnknownState(
|
||||||
"internal error: let statement unparseable".into(),
|
"internal error: alias statement unparseable".into(),
|
||||||
span(spans),
|
span(spans),
|
||||||
)),
|
)),
|
||||||
)
|
)
|
||||||
|
@ -2515,6 +2562,13 @@ pub fn parse_let(
|
||||||
let name = working_set.get_span_contents(spans[0]);
|
let name = working_set.get_span_contents(spans[0]);
|
||||||
|
|
||||||
if name == b"let" {
|
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") {
|
if let Some(decl_id) = working_set.find_decl(b"let") {
|
||||||
let (call, call_span, err) =
|
let (call, call_span, err) =
|
||||||
parse_internal_call(working_set, spans[0], &spans[1..], decl_id);
|
parse_internal_call(working_set, spans[0], &spans[1..], decl_id);
|
||||||
|
@ -2540,11 +2594,7 @@ pub fn parse_let(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(
|
(
|
||||||
Statement::Pipeline(Pipeline::from_vec(vec![Expression {
|
garbage_statement(spans),
|
||||||
expr: Expr::Garbage,
|
|
||||||
span: span(spans),
|
|
||||||
ty: Type::Unknown,
|
|
||||||
}])),
|
|
||||||
Some(ParseError::UnknownState(
|
Some(ParseError::UnknownState(
|
||||||
"internal error: let statement unparseable".into(),
|
"internal error: let statement unparseable".into(),
|
||||||
span(spans),
|
span(spans),
|
||||||
|
@ -2574,13 +2624,10 @@ pub fn parse_block(
|
||||||
lite_block: &LiteBlock,
|
lite_block: &LiteBlock,
|
||||||
scoped: bool,
|
scoped: bool,
|
||||||
) -> (Block, Option<ParseError>) {
|
) -> (Block, Option<ParseError>) {
|
||||||
let mut error = None;
|
|
||||||
if scoped {
|
if scoped {
|
||||||
working_set.enter_scope();
|
working_set.enter_scope();
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut block = Block::new();
|
|
||||||
|
|
||||||
// Pre-declare any definition so that definitions
|
// Pre-declare any definition so that definitions
|
||||||
// that share the same block can see each other
|
// that share the same block can see each other
|
||||||
for pipeline in &lite_block.block {
|
for pipeline in &lite_block.block {
|
||||||
|
@ -2589,25 +2636,41 @@ pub fn parse_block(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for pipeline in &lite_block.block {
|
let mut error = None;
|
||||||
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);
|
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
|
if error.is_none() {
|
||||||
|
error = err;
|
||||||
|
}
|
||||||
|
|
||||||
|
expr
|
||||||
|
})
|
||||||
|
.collect::<Vec<Expression>>();
|
||||||
|
|
||||||
|
Statement::Pipeline(Pipeline {
|
||||||
|
expressions: output,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
let (stmt, err) = parse_statement(working_set, &pipeline.commands[0].parts);
|
||||||
|
|
||||||
|
if error.is_none() {
|
||||||
|
error = err;
|
||||||
|
}
|
||||||
|
|
||||||
|
stmt
|
||||||
}
|
}
|
||||||
block.stmts.push(Statement::Pipeline(Pipeline {
|
})
|
||||||
expressions: output,
|
.into();
|
||||||
}));
|
|
||||||
} else {
|
|
||||||
let (stmt, err) = parse_statement(working_set, &pipeline.commands[0].parts);
|
|
||||||
error = error.or(err);
|
|
||||||
|
|
||||||
block.stmts.push(stmt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if scoped {
|
if scoped {
|
||||||
working_set.exit_scope();
|
working_set.exit_scope();
|
||||||
|
|
|
@ -371,6 +371,38 @@ 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,
|
||||||
|
None,
|
||||||
|
Some(_),
|
||||||
|
RangeOperator {
|
||||||
|
inclusion: RangeInclusion::Inclusive,
|
||||||
|
..
|
||||||
|
}
|
||||||
|
),
|
||||||
|
..
|
||||||
|
}
|
||||||
|
))
|
||||||
|
}
|
||||||
|
_ => panic!("No match"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_negative_range() {
|
fn parse_negative_range() {
|
||||||
let engine_state = EngineState::new();
|
let engine_state = EngineState::new();
|
||||||
|
@ -403,6 +435,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]
|
#[test]
|
||||||
fn bad_parse_does_crash() {
|
fn bad_parse_does_crash() {
|
||||||
let engine_state = EngineState::new();
|
let engine_state = EngineState::new();
|
||||||
|
|
|
@ -48,3 +48,15 @@ impl Block {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T> From<T> for Block
|
||||||
|
where
|
||||||
|
T: Iterator<Item = Statement>,
|
||||||
|
{
|
||||||
|
fn from(stmts: T) -> Self {
|
||||||
|
Self {
|
||||||
|
signature: Box::new(Signature::new("")),
|
||||||
|
stmts: stmts.collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::{RangeInclusion, RangeOperator},
|
ast::{RangeInclusion, RangeOperator},
|
||||||
*,
|
*,
|
||||||
|
@ -150,31 +152,52 @@ impl RangeIterator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Compare two floating point numbers. The decision interval for equality is dynamically scaled
|
||||||
|
// as the value being compared increases in magnitude.
|
||||||
|
fn compare_floats(val: f64, other: f64) -> Option<Ordering> {
|
||||||
|
let prec = f64::EPSILON.max(val.abs() * f64::EPSILON);
|
||||||
|
|
||||||
|
if (other - val).abs() < prec {
|
||||||
|
return Some(Ordering::Equal);
|
||||||
|
}
|
||||||
|
|
||||||
|
val.partial_cmp(&other)
|
||||||
|
}
|
||||||
|
|
||||||
impl Iterator for RangeIterator {
|
impl Iterator for RangeIterator {
|
||||||
type Item = Value;
|
type Item = Value;
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
use std::cmp::Ordering;
|
|
||||||
if self.done {
|
if self.done {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let ordering = if matches!(self.end, Value::Nothing { .. }) {
|
let ordering = if matches!(self.end, Value::Nothing { .. }) {
|
||||||
Ordering::Less
|
Some(Ordering::Less)
|
||||||
} else {
|
} else {
|
||||||
match (&self.curr, &self.end) {
|
match (&self.curr, &self.end) {
|
||||||
(Value::Int { val: curr, .. }, Value::Int { val: end, .. }) => curr.cmp(end),
|
(Value::Int { val: curr, .. }, Value::Int { val: end, .. }) => Some(curr.cmp(end)),
|
||||||
// (Value::Float { val: curr, .. }, Value::Float { val: end, .. }) => curr.cmp(end),
|
(Value::Float { val: curr, .. }, Value::Float { val: end, .. }) => {
|
||||||
// (Value::Float { val: curr, .. }, Value::Int { val: end, .. }) => curr.cmp(end),
|
compare_floats(*curr, *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::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 {
|
let desired_ordering = if self.moves_up {
|
||||||
Ordering::Less
|
Ordering::Less
|
||||||
} else {
|
} else {
|
||||||
|
|
Loading…
Reference in a new issue