diff --git a/crates/nu-parser/src/parser.rs b/crates/nu-parser/src/parser.rs index eed3795f5f..288ad4e2b3 100644 --- a/crates/nu-parser/src/parser.rs +++ b/crates/nu-parser/src/parser.rs @@ -3025,6 +3025,14 @@ pub fn expand_to_cell_path( *expression = new_expression; } + + if let Expression { + expr: Expr::UnaryNot(inner), + .. + } = expression + { + expand_to_cell_path(working_set, inner, var_id); + } } pub fn parse_input_output_types( @@ -4846,6 +4854,8 @@ pub fn parse_math_expression( let first_span = working_set.get_span_contents(spans[0]); + let mut not_start_spans = vec![]; + if first_span == b"if" || first_span == b"match" { // If expression if spans.len() > 1 { @@ -4858,24 +4868,40 @@ pub fn parse_math_expression( return garbage(spans[0]); } } else if first_span == b"not" { - if spans.len() > 1 { - let remainder = parse_math_expression(working_set, &spans[1..], lhs_row_var_id); - return Expression { - expr: Expr::UnaryNot(Box::new(remainder)), - span: span(spans), - ty: Type::Bool, - custom_completion: None, - }; - } else { + not_start_spans.push(spans[idx].start); + idx += 1; + while idx < spans.len() { + let next_value = working_set.get_span_contents(spans[idx]); + + if next_value == b"not" { + not_start_spans.push(spans[idx].start); + idx += 1; + } else { + break; + } + } + + if idx == spans.len() { working_set.error(ParseError::Expected( "expression", - Span::new(spans[0].end, spans[0].end), + Span::new(spans[idx - 1].end, spans[idx - 1].end), )); - return garbage(spans[0]); + return garbage(spans[idx - 1]); } } - let mut lhs = parse_value(working_set, spans[0], &SyntaxShape::Any); + let mut lhs = parse_value(working_set, spans[idx], &SyntaxShape::Any); + + for not_start_span in not_start_spans.iter().rev() { + lhs = Expression { + expr: Expr::UnaryNot(Box::new(lhs)), + span: Span::new(*not_start_span, spans[idx].end), + ty: Type::Bool, + custom_completion: None, + }; + } + not_start_spans.clear(); + idx += 1; if idx >= spans.len() { @@ -4906,13 +4932,45 @@ pub fn parse_math_expression( let content = working_set.get_span_contents(spans[idx]); // allow `if` to be a special value for assignment. + if content == b"if" || content == b"match" { let rhs = parse_call(working_set, &spans[idx..], spans[0], false); expr_stack.push(op); expr_stack.push(rhs); break; + } else if content == b"not" { + not_start_spans.push(spans[idx].start); + idx += 1; + while idx < spans.len() { + let next_value = working_set.get_span_contents(spans[idx]); + + if next_value == b"not" { + not_start_spans.push(spans[idx].start); + idx += 1; + } else { + break; + } + } + + if idx == spans.len() { + working_set.error(ParseError::Expected( + "expression", + Span::new(spans[idx - 1].end, spans[idx - 1].end), + )); + return garbage(spans[idx - 1]); + } } - let rhs = parse_value(working_set, spans[idx], &SyntaxShape::Any); + let mut rhs = parse_value(working_set, spans[idx], &SyntaxShape::Any); + + for not_start_span in not_start_spans.iter().rev() { + rhs = Expression { + expr: Expr::UnaryNot(Box::new(rhs)), + span: Span::new(*not_start_span, spans[idx].end), + ty: Type::Bool, + custom_completion: None, + }; + } + not_start_spans.clear(); while op_prec <= last_prec && expr_stack.len() > 1 { // Collapse the right associated operations first diff --git a/src/tests/test_math.rs b/src/tests/test_math.rs index 930742bb31..ed372b198a 100644 --- a/src/tests/test_math.rs +++ b/src/tests/test_math.rs @@ -105,6 +105,26 @@ fn not_contains() -> TestResult { run_test("'testme' !~ 'test'", "false") } +#[test] +fn not_precedence() -> TestResult { + run_test("not false and false", "false") +} + +#[test] +fn not_precedence2() -> TestResult { + run_test("(not false) and false", "false") +} + +#[test] +fn not_precedence3() -> TestResult { + run_test("not not true and true", "true") +} + +#[test] +fn not_precedence4() -> TestResult { + run_test("not not true and not not true", "true") +} + #[test] fn floating_add() -> TestResult { run_test("10.1 + 0.8", "10.9")