mirror of
https://github.com/nushell/nushell
synced 2024-12-27 21:43:09 +00:00
Improve keyword parsing, including for (#747)
* Improve keyword parsing, including for * touchup
This commit is contained in:
parent
21a7278259
commit
f9c0d223c1
5 changed files with 80 additions and 29 deletions
|
@ -61,10 +61,20 @@ pub enum ParseError {
|
||||||
#[diagnostic(
|
#[diagnostic(
|
||||||
code(nu::parser::unexpected_keyword),
|
code(nu::parser::unexpected_keyword),
|
||||||
url(docsrs),
|
url(docsrs),
|
||||||
help("'export' keyword is allowed only in a module.")
|
help("'{0}' keyword is allowed only in a module.")
|
||||||
)]
|
)]
|
||||||
UnexpectedKeyword(String, #[label("unexpected {0}")] Span),
|
UnexpectedKeyword(String, #[label("unexpected {0}")] Span),
|
||||||
|
|
||||||
|
#[error("Statement used in pipeline.")]
|
||||||
|
#[diagnostic(
|
||||||
|
code(nu::parser::unexpected_keyword),
|
||||||
|
url(docsrs),
|
||||||
|
help(
|
||||||
|
"'{0}' keyword is not allowed in pipeline. Use '{0}' by itself, outside of a pipeline."
|
||||||
|
)
|
||||||
|
)]
|
||||||
|
StatementInPipeline(String, #[label("not allowed in pipeline")] Span),
|
||||||
|
|
||||||
#[error("Incorrect value")]
|
#[error("Incorrect value")]
|
||||||
#[diagnostic(code(nu::parser::incorrect_value), url(docsrs), help("{2}"))]
|
#[diagnostic(code(nu::parser::incorrect_value), url(docsrs), help("{2}"))]
|
||||||
IncorrectValue(String, #[label("unexpected {0}")] Span, String),
|
IncorrectValue(String, #[label("unexpected {0}")] Span, String),
|
||||||
|
@ -203,14 +213,6 @@ pub enum ParseError {
|
||||||
#[diagnostic(code(nu::parser::file_not_found), url(docsrs))]
|
#[diagnostic(code(nu::parser::file_not_found), url(docsrs))]
|
||||||
FileNotFound(String, #[label("File not found: {0}")] Span),
|
FileNotFound(String, #[label("File not found: {0}")] Span),
|
||||||
|
|
||||||
#[error("'let' statements can't be part of a pipeline")]
|
|
||||||
#[diagnostic(
|
|
||||||
code(nu::parser::let_not_statement),
|
|
||||||
url(docsrs),
|
|
||||||
help("use parens to assign to a variable\neg) let x = ('hello' | str length)")
|
|
||||||
)]
|
|
||||||
LetNotStatement(#[label = "let statement part of a pipeline"] Span),
|
|
||||||
|
|
||||||
#[error("{0}")]
|
#[error("{0}")]
|
||||||
#[diagnostic()]
|
#[diagnostic()]
|
||||||
LabeledError(String, String, #[label("{1}")] Span),
|
LabeledError(String, String, #[label("{1}")] Span),
|
||||||
|
|
|
@ -60,12 +60,12 @@ pub fn parse_def_predecl(working_set: &mut StateWorkingSet, spans: &[Span]) -> O
|
||||||
pub fn parse_for(
|
pub fn parse_for(
|
||||||
working_set: &mut StateWorkingSet,
|
working_set: &mut StateWorkingSet,
|
||||||
spans: &[Span],
|
spans: &[Span],
|
||||||
) -> (Statement, Option<ParseError>) {
|
) -> (Expression, Option<ParseError>) {
|
||||||
// Checking that the function is used with the correct name
|
// Checking that the function is used with the correct name
|
||||||
// Maybe this is not necessary but it is a sanity check
|
// Maybe this is not necessary but it is a sanity check
|
||||||
if working_set.get_span_contents(spans[0]) != b"for" {
|
if working_set.get_span_contents(spans[0]) != b"for" {
|
||||||
return (
|
return (
|
||||||
garbage_statement(spans),
|
garbage(spans[0]),
|
||||||
Some(ParseError::UnknownState(
|
Some(ParseError::UnknownState(
|
||||||
"internal error: Wrong call name for 'for' function".into(),
|
"internal error: Wrong call name for 'for' function".into(),
|
||||||
span(spans),
|
span(spans),
|
||||||
|
@ -79,7 +79,7 @@ pub fn parse_for(
|
||||||
let (call, call_span) = match working_set.find_decl(b"for") {
|
let (call, call_span) = match working_set.find_decl(b"for") {
|
||||||
None => {
|
None => {
|
||||||
return (
|
return (
|
||||||
garbage_statement(spans),
|
garbage(spans[0]),
|
||||||
Some(ParseError::UnknownState(
|
Some(ParseError::UnknownState(
|
||||||
"internal error: def declaration not found".into(),
|
"internal error: def declaration not found".into(),
|
||||||
span(spans),
|
span(spans),
|
||||||
|
@ -117,12 +117,12 @@ pub fn parse_for(
|
||||||
err = check_call(call_span, &sig, &call).or(err);
|
err = check_call(call_span, &sig, &call).or(err);
|
||||||
if err.is_some() || call.has_flag("help") {
|
if err.is_some() || call.has_flag("help") {
|
||||||
return (
|
return (
|
||||||
Statement::Pipeline(Pipeline::from_vec(vec![Expression {
|
Expression {
|
||||||
expr: Expr::Call(call),
|
expr: Expr::Call(call),
|
||||||
span: call_span,
|
span: call_span,
|
||||||
ty: Type::Unknown,
|
ty: Type::Unknown,
|
||||||
custom_completion: None,
|
custom_completion: None,
|
||||||
}])),
|
},
|
||||||
err,
|
err,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -162,12 +162,12 @@ pub fn parse_for(
|
||||||
}
|
}
|
||||||
|
|
||||||
(
|
(
|
||||||
Statement::Pipeline(Pipeline::from_vec(vec![Expression {
|
Expression {
|
||||||
expr: Expr::Call(call),
|
expr: Expr::Call(call),
|
||||||
span: call_span,
|
span: call_span,
|
||||||
ty: Type::Unknown,
|
ty: Type::Unknown,
|
||||||
custom_completion: None,
|
custom_completion: None,
|
||||||
}])),
|
},
|
||||||
error,
|
error,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -488,6 +488,15 @@ pub fn parse_multispan_value(
|
||||||
|
|
||||||
(arg, error)
|
(arg, error)
|
||||||
}
|
}
|
||||||
|
SyntaxShape::MathExpression => {
|
||||||
|
trace!("parsing: math expression");
|
||||||
|
|
||||||
|
let (arg, err) = parse_math_expression(working_set, &spans[*spans_idx..], None);
|
||||||
|
error = error.or(err);
|
||||||
|
*spans_idx = spans.len() - 1;
|
||||||
|
|
||||||
|
(arg, error)
|
||||||
|
}
|
||||||
SyntaxShape::Expression => {
|
SyntaxShape::Expression => {
|
||||||
trace!("parsing: expression");
|
trace!("parsing: expression");
|
||||||
|
|
||||||
|
@ -3337,7 +3346,49 @@ pub fn parse_expression(
|
||||||
let (output, err) = if is_math_expression_byte(bytes[0]) {
|
let (output, err) = if is_math_expression_byte(bytes[0]) {
|
||||||
parse_math_expression(working_set, &spans[pos..], None)
|
parse_math_expression(working_set, &spans[pos..], None)
|
||||||
} else {
|
} else {
|
||||||
parse_call(working_set, &spans[pos..], expand_aliases, spans[0])
|
// For now, check for special parses of certain keywords
|
||||||
|
match bytes {
|
||||||
|
b"def" => (
|
||||||
|
parse_call(working_set, &spans[pos..], expand_aliases, spans[0]).0,
|
||||||
|
Some(ParseError::StatementInPipeline("def".into(), spans[0])),
|
||||||
|
),
|
||||||
|
b"let" => (
|
||||||
|
parse_call(working_set, &spans[pos..], expand_aliases, spans[0]).0,
|
||||||
|
Some(ParseError::StatementInPipeline("let".into(), spans[0])),
|
||||||
|
),
|
||||||
|
b"alias" => (
|
||||||
|
parse_call(working_set, &spans[pos..], expand_aliases, spans[0]).0,
|
||||||
|
Some(ParseError::StatementInPipeline("alias".into(), spans[0])),
|
||||||
|
),
|
||||||
|
b"module" => (
|
||||||
|
parse_call(working_set, &spans[pos..], expand_aliases, spans[0]).0,
|
||||||
|
Some(ParseError::StatementInPipeline("module".into(), spans[0])),
|
||||||
|
),
|
||||||
|
b"use" => (
|
||||||
|
parse_call(working_set, &spans[pos..], expand_aliases, spans[0]).0,
|
||||||
|
Some(ParseError::StatementInPipeline("use".into(), spans[0])),
|
||||||
|
),
|
||||||
|
b"source" => (
|
||||||
|
parse_call(working_set, &spans[pos..], expand_aliases, spans[0]).0,
|
||||||
|
Some(ParseError::StatementInPipeline("source".into(), spans[0])),
|
||||||
|
),
|
||||||
|
b"export" => (
|
||||||
|
parse_call(working_set, &spans[pos..], expand_aliases, spans[0]).0,
|
||||||
|
Some(ParseError::UnexpectedKeyword("export".into(), spans[0])),
|
||||||
|
),
|
||||||
|
b"hide" => (
|
||||||
|
parse_call(working_set, &spans[pos..], expand_aliases, spans[0]).0,
|
||||||
|
Some(ParseError::StatementInPipeline("hide".into(), spans[0])),
|
||||||
|
),
|
||||||
|
#[cfg(feature = "plugin")]
|
||||||
|
b"register" => (
|
||||||
|
parse_call(working_set, &spans[pos..], expand_aliases, spans[0]).0,
|
||||||
|
Some(ParseError::StatementInPipeline("plugin".into(), spans[0])),
|
||||||
|
),
|
||||||
|
|
||||||
|
b"for" => parse_for(working_set, spans),
|
||||||
|
_ => parse_call(working_set, &spans[pos..], expand_aliases, spans[0]),
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let with_env = working_set.find_decl(b"with-env");
|
let with_env = working_set.find_decl(b"with-env");
|
||||||
|
@ -3425,7 +3476,10 @@ pub fn parse_statement(
|
||||||
match name {
|
match name {
|
||||||
b"def" => parse_def(working_set, spans),
|
b"def" => parse_def(working_set, spans),
|
||||||
b"let" => parse_let(working_set, spans),
|
b"let" => parse_let(working_set, spans),
|
||||||
b"for" => parse_for(working_set, spans),
|
b"for" => {
|
||||||
|
let (expr, err) = parse_for(working_set, spans);
|
||||||
|
(Statement::Pipeline(Pipeline::from_vec(vec![expr])), err)
|
||||||
|
}
|
||||||
b"alias" => parse_alias(working_set, spans),
|
b"alias" => parse_alias(working_set, spans),
|
||||||
b"module" => parse_module(working_set, spans),
|
b"module" => parse_module(working_set, spans),
|
||||||
b"use" => parse_use(working_set, spans),
|
b"use" => parse_use(working_set, spans),
|
||||||
|
@ -3563,16 +3617,6 @@ pub fn parse_block(
|
||||||
})
|
})
|
||||||
.collect::<Vec<Expression>>();
|
.collect::<Vec<Expression>>();
|
||||||
|
|
||||||
if let Some(let_call_id) = working_set.find_decl(b"let") {
|
|
||||||
for expr in output.iter() {
|
|
||||||
if let Expr::Call(x) = &expr.expr {
|
|
||||||
if let_call_id == x.decl_id && output.len() != 1 && error.is_none() {
|
|
||||||
error = Some(ParseError::LetNotStatement(expr.span));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for expr in output.iter_mut().skip(1) {
|
for expr in output.iter_mut().skip(1) {
|
||||||
if expr.has_in_variable(working_set) {
|
if expr.has_in_variable(working_set) {
|
||||||
*expr = wrap_expr_with_collect(working_set, expr);
|
*expr = wrap_expr_with_collect(working_set, expr);
|
||||||
|
|
|
@ -143,3 +143,8 @@ fn proper_variable_captures_with_nesting() -> TestResult {
|
||||||
"102",
|
"102",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn proper_variable_for() -> TestResult {
|
||||||
|
run_test(r#"for x in 1..3 { if $x == 2 { "bob" } } | get 1"#, "bob")
|
||||||
|
}
|
||||||
|
|
|
@ -116,7 +116,7 @@ fn long_flag() -> TestResult {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn let_not_statement() -> TestResult {
|
fn let_not_statement() -> TestResult {
|
||||||
fail_test(r#"let x = "hello" | str length"#, "can't")
|
fail_test(r#"let x = "hello" | str length"#, "used in pipeline")
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
Loading…
Reference in a new issue