Fix logical operator precedence in parser.
Whoopsie... || and && were treated with the same precedence, causing very surprising behavior down the line. I noticed this because of the auto-formatter adding parenthesis where it really shouldn't. The problem came actually from the parser and how it constructed the AST.
This commit is contained in:
parent
62fe321ddf
commit
83a86e6dc0
|
@ -1301,13 +1301,9 @@ pub fn expr_parser(
|
||||||
})
|
})
|
||||||
.boxed();
|
.boxed();
|
||||||
|
|
||||||
// Logical
|
// Conjunction
|
||||||
let op = choice((
|
let op = just(Token::AmperAmper).to(BinOp::And);
|
||||||
just(Token::AmperAmper).to(BinOp::And),
|
let conjunction = comparison
|
||||||
just(Token::VbarVbar).to(BinOp::Or),
|
|
||||||
));
|
|
||||||
|
|
||||||
let logical = comparison
|
|
||||||
.clone()
|
.clone()
|
||||||
.then(op.then(comparison).repeated())
|
.then(op.then(comparison).repeated())
|
||||||
.foldl(|a, (op, b)| expr::UntypedExpr::BinOp {
|
.foldl(|a, (op, b)| expr::UntypedExpr::BinOp {
|
||||||
|
@ -1318,10 +1314,23 @@ pub fn expr_parser(
|
||||||
})
|
})
|
||||||
.boxed();
|
.boxed();
|
||||||
|
|
||||||
// Pipeline
|
// Disjunction
|
||||||
logical
|
let op = just(Token::VbarVbar).to(BinOp::Or);
|
||||||
|
let disjunction = conjunction
|
||||||
.clone()
|
.clone()
|
||||||
.then(just(Token::Pipe).ignore_then(logical).repeated())
|
.then(op.then(conjunction).repeated())
|
||||||
|
.foldl(|a, (op, b)| expr::UntypedExpr::BinOp {
|
||||||
|
location: a.location().union(b.location()),
|
||||||
|
name: op,
|
||||||
|
left: Box::new(a),
|
||||||
|
right: Box::new(b),
|
||||||
|
})
|
||||||
|
.boxed();
|
||||||
|
|
||||||
|
// Pipeline
|
||||||
|
disjunction
|
||||||
|
.clone()
|
||||||
|
.then(just(Token::Pipe).ignore_then(disjunction).repeated())
|
||||||
.foldl(|l, r| {
|
.foldl(|l, r| {
|
||||||
let expressions = if let expr::UntypedExpr::PipeLine { mut expressions } = l {
|
let expressions = if let expr::UntypedExpr::PipeLine { mut expressions } = l {
|
||||||
expressions.push(r);
|
expressions.push(r);
|
||||||
|
@ -1421,30 +1430,33 @@ pub fn when_clause_guard_parser() -> impl Parser<Token, ast::ClauseGuard<(), ()>
|
||||||
})
|
})
|
||||||
.boxed();
|
.boxed();
|
||||||
|
|
||||||
let logical_op = choice((
|
let and_op = just(Token::AmperAmper);
|
||||||
just(Token::AmperAmper).to(BinOp::And),
|
let conjunction = comparison
|
||||||
just(Token::VbarVbar).to(BinOp::Or),
|
|
||||||
));
|
|
||||||
|
|
||||||
comparison
|
|
||||||
.clone()
|
.clone()
|
||||||
.then(logical_op.then(comparison).repeated())
|
.then(and_op.then(comparison).repeated())
|
||||||
.foldl(|left, (op, right)| {
|
.foldl(|left, (_tok, right)| {
|
||||||
let location = left.location().union(right.location());
|
let location = left.location().union(right.location());
|
||||||
let left = Box::new(left);
|
let left = Box::new(left);
|
||||||
let right = Box::new(right);
|
let right = Box::new(right);
|
||||||
match op {
|
ast::ClauseGuard::And {
|
||||||
BinOp::And => ast::ClauseGuard::And {
|
|
||||||
location,
|
location,
|
||||||
left,
|
left,
|
||||||
right,
|
right,
|
||||||
},
|
}
|
||||||
BinOp::Or => ast::ClauseGuard::Or {
|
});
|
||||||
|
|
||||||
|
let or_op = just(Token::VbarVbar);
|
||||||
|
conjunction
|
||||||
|
.clone()
|
||||||
|
.then(or_op.then(conjunction).repeated())
|
||||||
|
.foldl(|left, (_tok, right)| {
|
||||||
|
let location = left.location().union(right.location());
|
||||||
|
let left = Box::new(left);
|
||||||
|
let right = Box::new(right);
|
||||||
|
ast::ClauseGuard::Or {
|
||||||
location,
|
location,
|
||||||
left,
|
left,
|
||||||
right,
|
right,
|
||||||
},
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -14,7 +14,7 @@ fn assert_fmt(src: &str, expected: &str) {
|
||||||
let (module2, extra2) = parser::module(&out, ModuleKind::Lib).unwrap();
|
let (module2, extra2) = parser::module(&out, ModuleKind::Lib).unwrap();
|
||||||
let mut out2 = String::new();
|
let mut out2 = String::new();
|
||||||
format::pretty(&mut out2, module2, extra2, &out);
|
format::pretty(&mut out2, module2, extra2, &out);
|
||||||
assert!(out == out2, "formatting isn't idempotent");
|
assert_eq!(out, out2, "formatting isn't idempotent");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -233,7 +233,7 @@ fn test_negate() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_block_expr() {
|
fn test_block_arithmetic_expr() {
|
||||||
let src = indoc! {r#"
|
let src = indoc! {r#"
|
||||||
fn foo() {
|
fn foo() {
|
||||||
( 14 + 42 ) * 1337
|
( 14 + 42 ) * 1337
|
||||||
|
@ -257,6 +257,39 @@ fn test_block_expr() {
|
||||||
assert_fmt(src, expected);
|
assert_fmt(src, expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_block_logical_expr() {
|
||||||
|
let src = indoc! {r#"
|
||||||
|
fn foo() {
|
||||||
|
!(a && b)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bar() {
|
||||||
|
(a || b) && (c || d)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn baz() {
|
||||||
|
a || (b && c) || d
|
||||||
|
}
|
||||||
|
"#};
|
||||||
|
|
||||||
|
let expected = indoc! {r#"
|
||||||
|
fn foo() {
|
||||||
|
!(a && b)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bar() {
|
||||||
|
( a || b ) && ( c || d )
|
||||||
|
}
|
||||||
|
|
||||||
|
fn baz() {
|
||||||
|
a || b && c || d
|
||||||
|
}
|
||||||
|
"#};
|
||||||
|
|
||||||
|
assert_fmt(src, expected);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_format_bytearray_literals() {
|
fn test_format_bytearray_literals() {
|
||||||
let src = indoc! {r#"
|
let src = indoc! {r#"
|
||||||
|
|
|
@ -2166,7 +2166,7 @@ fn clause_guards() {
|
||||||
_ if bar -> Void
|
_ if bar -> Void
|
||||||
_ if True -> Void
|
_ if True -> Void
|
||||||
_ if a || b && c -> Void
|
_ if a || b && c -> Void
|
||||||
_ if a || (b && c) -> Void
|
_ if (a || b) && c -> Void
|
||||||
_ if a <= 42 || b > 14 || "str" -> Void
|
_ if a <= 42 || b > 14 || "str" -> Void
|
||||||
_ if a == 14 && !b -> Void
|
_ if a == 14 && !b -> Void
|
||||||
_ if !!True -> Void
|
_ if !!True -> Void
|
||||||
|
@ -2241,27 +2241,27 @@ fn clause_guards() {
|
||||||
location: Span::new((), 92..93),
|
location: Span::new((), 92..93),
|
||||||
}],
|
}],
|
||||||
alternative_patterns: vec![],
|
alternative_patterns: vec![],
|
||||||
guard: Some(ast::ClauseGuard::And {
|
guard: Some(ast::ClauseGuard::Or {
|
||||||
location: Span::new((), 97..108),
|
location: Span::new((), 97..108),
|
||||||
left: Box::new(ast::ClauseGuard::Or {
|
|
||||||
location: Span::new((), 97..103),
|
|
||||||
left: Box::new(ast::ClauseGuard::Var {
|
left: Box::new(ast::ClauseGuard::Var {
|
||||||
location: Span::new((), 97..98),
|
location: Span::new((), 97..98),
|
||||||
name: "a".to_string(),
|
name: "a".to_string(),
|
||||||
tipo: (),
|
tipo: (),
|
||||||
}),
|
}),
|
||||||
right: Box::new(ast::ClauseGuard::Var {
|
right: Box::new(ast::ClauseGuard::And {
|
||||||
|
location: Span::new((), 102..108),
|
||||||
|
left: Box::new(ast::ClauseGuard::Var {
|
||||||
location: Span::new((), 102..103),
|
location: Span::new((), 102..103),
|
||||||
name: "b".to_string(),
|
name: "b".to_string(),
|
||||||
tipo: (),
|
tipo: (),
|
||||||
}),
|
}),
|
||||||
}),
|
|
||||||
right: Box::new(ast::ClauseGuard::Var {
|
right: Box::new(ast::ClauseGuard::Var {
|
||||||
location: Span::new((), 107..108),
|
location: Span::new((), 107..108),
|
||||||
name: "c".to_string(),
|
name: "c".to_string(),
|
||||||
tipo: (),
|
tipo: (),
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
|
}),
|
||||||
then: expr::UntypedExpr::Var {
|
then: expr::UntypedExpr::Var {
|
||||||
location: Span::new((), 112..116),
|
location: Span::new((), 112..116),
|
||||||
name: "Void".to_string(),
|
name: "Void".to_string(),
|
||||||
|
@ -2274,27 +2274,27 @@ fn clause_guards() {
|
||||||
location: Span::new((), 121..122),
|
location: Span::new((), 121..122),
|
||||||
}],
|
}],
|
||||||
alternative_patterns: vec![],
|
alternative_patterns: vec![],
|
||||||
guard: Some(ast::ClauseGuard::Or {
|
guard: Some(ast::ClauseGuard::And {
|
||||||
location: Span::new((), 126..138),
|
location: Span::new((), 127..139),
|
||||||
|
left: Box::new(ast::ClauseGuard::Or {
|
||||||
|
location: Span::new((), 127..133),
|
||||||
left: Box::new(ast::ClauseGuard::Var {
|
left: Box::new(ast::ClauseGuard::Var {
|
||||||
location: Span::new((), 126..127),
|
location: Span::new((), 127..128),
|
||||||
name: "a".to_string(),
|
name: "a".to_string(),
|
||||||
tipo: (),
|
tipo: (),
|
||||||
}),
|
}),
|
||||||
right: Box::new(ast::ClauseGuard::And {
|
right: Box::new(ast::ClauseGuard::Var {
|
||||||
location: Span::new((), 132..138),
|
|
||||||
left: Box::new(ast::ClauseGuard::Var {
|
|
||||||
location: Span::new((), 132..133),
|
location: Span::new((), 132..133),
|
||||||
name: "b".to_string(),
|
name: "b".to_string(),
|
||||||
tipo: (),
|
tipo: (),
|
||||||
}),
|
}),
|
||||||
|
}),
|
||||||
right: Box::new(ast::ClauseGuard::Var {
|
right: Box::new(ast::ClauseGuard::Var {
|
||||||
location: Span::new((), 137..138),
|
location: Span::new((), 138..139),
|
||||||
name: "c".to_string(),
|
name: "c".to_string(),
|
||||||
tipo: (),
|
tipo: (),
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
}),
|
|
||||||
then: expr::UntypedExpr::Var {
|
then: expr::UntypedExpr::Var {
|
||||||
location: Span::new((), 143..147),
|
location: Span::new((), 143..147),
|
||||||
name: "Void".to_string(),
|
name: "Void".to_string(),
|
||||||
|
@ -2420,3 +2420,98 @@ fn clause_guards() {
|
||||||
})],
|
})],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn scope_logical_expression() {
|
||||||
|
let code = indoc! {r#"
|
||||||
|
fn foo() {
|
||||||
|
let x = !(a && b)
|
||||||
|
let y = a || b && c || d
|
||||||
|
x
|
||||||
|
}
|
||||||
|
"#};
|
||||||
|
assert_definitions(
|
||||||
|
code,
|
||||||
|
vec![ast::Definition::Fn(Function {
|
||||||
|
arguments: vec![],
|
||||||
|
body: expr::UntypedExpr::Sequence {
|
||||||
|
location: Span::new((), 13..61),
|
||||||
|
expressions: vec![
|
||||||
|
expr::UntypedExpr::Assignment {
|
||||||
|
location: Span::new((), 13..30),
|
||||||
|
value: Box::new(expr::UntypedExpr::UnOp {
|
||||||
|
op: ast::UnOp::Not,
|
||||||
|
location: Span::new((), 21..29),
|
||||||
|
value: Box::new(expr::UntypedExpr::BinOp {
|
||||||
|
location: Span::new((), 23..29),
|
||||||
|
name: ast::BinOp::And,
|
||||||
|
left: Box::new(expr::UntypedExpr::Var {
|
||||||
|
location: Span::new((), 23..24),
|
||||||
|
name: "a".to_string(),
|
||||||
|
}),
|
||||||
|
right: Box::new(expr::UntypedExpr::Var {
|
||||||
|
location: Span::new((), 28..29),
|
||||||
|
name: "b".to_string(),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
pattern: ast::Pattern::Var {
|
||||||
|
location: Span::new((), 17..18),
|
||||||
|
name: "x".to_string(),
|
||||||
|
},
|
||||||
|
kind: ast::AssignmentKind::Let,
|
||||||
|
annotation: None,
|
||||||
|
},
|
||||||
|
expr::UntypedExpr::Assignment {
|
||||||
|
location: Span::new((), 33..57),
|
||||||
|
value: Box::new(expr::UntypedExpr::BinOp {
|
||||||
|
location: Span::new((), 41..57),
|
||||||
|
name: ast::BinOp::Or,
|
||||||
|
left: Box::new(expr::UntypedExpr::BinOp {
|
||||||
|
location: Span::new((), 41..52),
|
||||||
|
name: ast::BinOp::Or,
|
||||||
|
left: Box::new(expr::UntypedExpr::Var {
|
||||||
|
location: Span::new((), 41..42),
|
||||||
|
name: "a".to_string(),
|
||||||
|
}),
|
||||||
|
right: Box::new(expr::UntypedExpr::BinOp {
|
||||||
|
location: Span::new((), 46..52),
|
||||||
|
name: ast::BinOp::And,
|
||||||
|
left: Box::new(expr::UntypedExpr::Var {
|
||||||
|
location: Span::new((), 46..47),
|
||||||
|
name: "b".to_string(),
|
||||||
|
}),
|
||||||
|
right: Box::new(expr::UntypedExpr::Var {
|
||||||
|
location: Span::new((), 51..52),
|
||||||
|
name: "c".to_string(),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
right: Box::new(expr::UntypedExpr::Var {
|
||||||
|
location: Span::new((), 56..57),
|
||||||
|
name: "d".to_string(),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
pattern: ast::Pattern::Var {
|
||||||
|
location: Span::new((), 37..38),
|
||||||
|
name: "y".to_string(),
|
||||||
|
},
|
||||||
|
kind: ast::AssignmentKind::Let,
|
||||||
|
annotation: None,
|
||||||
|
},
|
||||||
|
expr::UntypedExpr::Var {
|
||||||
|
location: Span::new((), 60..61),
|
||||||
|
name: "x".to_string(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
doc: None,
|
||||||
|
location: Span::new((), 0..8),
|
||||||
|
name: "foo".to_string(),
|
||||||
|
public: false,
|
||||||
|
return_annotation: None,
|
||||||
|
return_type: (),
|
||||||
|
end_position: 62,
|
||||||
|
})],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue