FEATURE: Proper precendence for math operators.

Using nested combinators for enforce parsing precedence.
This commit is contained in:
Jeremy Wall 2018-05-14 21:39:29 -05:00
parent 00c1e1751a
commit 05f6597f00
2 changed files with 131 additions and 14 deletions

View File

@ -1193,7 +1193,8 @@ mod compile_test {
#[test] #[test]
fn test_binary_operator_precedence() { fn test_binary_operator_precedence() {
assert_build("let result = 2 * 2 + 1;", "result == 6;"); //assert_build("let result = 2 * 2 + 1;", "result == 6;");
assert_build("let result = 2 + 2 * 1;", "result == 4;");
assert_build("let result = (2 * 2) + 1;", "result == 5;"); assert_build("let result = (2 * 2) + 1;", "result == 5;");
} }
} }

View File

@ -249,14 +249,25 @@ fn tuple_to_binary_expression(
})) }))
} }
/// do_binary_expr implements precedence based parsing where the more tightly bound parsers
/// are passed in as lowerrule parsers. We default to grouped_expression and simple_expression as
/// the most tightly bound expressions.
macro_rules! do_binary_expr { macro_rules! do_binary_expr {
($i:expr, $subrule:ident!( $($args:tt)* ), $typ:expr) => { ($i:expr, $oprule:ident!( $($args:tt)* ), $typ:expr) => {
do_binary_expr!($i, $oprule!($($args)*), $typ, alt!(grouped_expression | simple_expression))
};
($i:expr, $oprule:ident!( $($args:tt)* ), $typ:expr, $lowerrule:ident) => {
do_binary_expr!($i, $oprule!($($args)*), $typ, call!($lowerrule))
};
($i:expr, $oprule:ident!( $($args:tt)* ), $typ:expr, $lowerrule:ident!( $($lowerargs:tt)* )) => {
map_res!($i, map_res!($i,
do_parse!( do_parse!(
pos: pos >> pos: pos >>
left: alt!(simple_expression | grouped_expression) >> left: $lowerrule!($($lowerargs)*) >>
$subrule!($($args)*) >> $oprule!($($args)*) >>
right: expression >> right: $lowerrule!($($lowerargs)*) >>
(pos, $typ, left, right) (pos, $typ, left, right)
), ),
tuple_to_binary_expression tuple_to_binary_expression
@ -264,14 +275,26 @@ macro_rules! do_binary_expr {
}; };
} }
named!(math_expression<TokenIter, Expression, error::Error>,
alt!(sum_expression | product_expression)
);
named!(sum_expression<TokenIter, Expression, error::Error>,
alt!(add_expression | sub_expression)
);
// trace_macros!(true); // trace_macros!(true);
named!(add_expression<TokenIter, Expression, error::Error>, named!(add_expression<TokenIter, Expression, error::Error>,
do_binary_expr!(punct!("+"), BinaryExprType::Add) do_binary_expr!(punct!("+"), BinaryExprType::Add, alt!(product_expression | simple_expression | grouped_expression))
); );
// trace_macros!(false); // trace_macros!(false);
named!(sub_expression<TokenIter, Expression, error::Error>, named!(sub_expression<TokenIter, Expression, error::Error>,
do_binary_expr!(punct!("-"), BinaryExprType::Sub) do_binary_expr!(punct!("-"), BinaryExprType::Sub, alt!(product_expression | simple_expression | grouped_expression))
);
named!(product_expression<TokenIter, Expression, error::Error>,
alt!(mul_expression | div_expression)
); );
named!(mul_expression<TokenIter, Expression, error::Error>, named!(mul_expression<TokenIter, Expression, error::Error>,
@ -282,6 +305,7 @@ named!(div_expression<TokenIter, Expression, error::Error>,
do_binary_expr!(punct!("/"), BinaryExprType::Div) do_binary_expr!(punct!("/"), BinaryExprType::Div)
); );
// TODO(jwall): Change comparison operators to use the do_binary_expr! with precedence?
fn tuple_to_compare_expression( fn tuple_to_compare_expression(
tpl: (Position, CompareType, Expression, Expression), tpl: (Position, CompareType, Expression, Expression),
) -> ParseResult<Expression> { ) -> ParseResult<Expression> {
@ -298,7 +322,7 @@ macro_rules! do_compare_expr {
map_res!($i, map_res!($i,
do_parse!( do_parse!(
pos: pos >> pos: pos >>
left: alt!(simple_expression | grouped_expression) >> left: alt!(simple_expression | grouped_expression | math_expression) >>
$subrule!($($args)*) >> $subrule!($($args)*) >>
right: expression >> right: expression >>
(pos, $typ, left, right) (pos, $typ, left, right)
@ -694,22 +718,19 @@ named!(expression<TokenIter, Expression, error::Error>,
do_parse!( do_parse!(
expr: alt!( expr: alt!(
complete!(list_op_expression) | complete!(list_op_expression) |
complete!(add_expression) | complete!(math_expression) |
complete!(sub_expression) |
complete!(mul_expression) |
complete!(div_expression) |
complete!(eqeq_expression) | complete!(eqeq_expression) |
complete!(not_eqeq_expression) | complete!(not_eqeq_expression) |
complete!(lt_eqeq_expression) | complete!(lt_eqeq_expression) |
complete!(gt_eqeq_expression) | complete!(gt_eqeq_expression) |
complete!(gt_expression) | complete!(gt_expression) |
complete!(lt_expression) | complete!(lt_expression) |
complete!(grouped_expression) |
complete!(macro_expression) | complete!(macro_expression) |
complete!(format_expression) | complete!(format_expression) |
complete!(select_expression) | complete!(select_expression) |
complete!(call_expression) | complete!(call_expression) |
complete!(copy_expression) | complete!(copy_expression) |
complete!(grouped_expression) |
simple_expression simple_expression
) >> ) >>
(expr) (expr)
@ -973,6 +994,62 @@ mod test {
}) })
); );
assert_parse!(
statement("let foo = 1 + 1 * 2;"),
Statement::Let(LetDef {
name: make_tok!("foo", 1, 5),
value: Expression::Binary(BinaryOpDef {
kind: BinaryExprType::Add,
left: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 11)))),
right: Box::new(Expression::Binary(BinaryOpDef {
kind: BinaryExprType::Mul,
left: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 15)))),
right: Box::new(Expression::Simple(Value::Int(value_node!(2, 1, 19)))),
pos: Position::new(1, 15),
})),
pos: Position::new(1, 11),
}),
})
);
assert_parse!(
statement("let foo = (1 + 1) * 2;"),
Statement::Let(LetDef {
name: make_tok!("foo", 1, 5),
value: Expression::Binary(BinaryOpDef {
kind: BinaryExprType::Mul,
left: Box::new(Expression::Grouped(Box::new(Expression::Binary(
BinaryOpDef {
kind: BinaryExprType::Add,
left: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 12)))),
right: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 16)))),
pos: Position::new(1, 12),
},
)))),
right: Box::new(Expression::Simple(Value::Int(value_node!(2, 1, 21)))),
pos: Position::new(1, 11),
}),
})
);
assert_parse!(
statement("let foo = 1 * 1 + 2;"),
Statement::Let(LetDef {
name: make_tok!("foo", 1, 5),
value: Expression::Binary(BinaryOpDef {
kind: BinaryExprType::Add,
left: Box::new(Expression::Binary(BinaryOpDef {
kind: BinaryExprType::Mul,
left: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 11)))),
right: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 15)))),
pos: Position::new(1, 11),
})),
right: Box::new(Expression::Simple(Value::Int(value_node!(2, 1, 19)))),
pos: Position::new(1, 11),
}),
})
);
assert_parse!( assert_parse!(
statement("// comment\nlet foo = 1.0 ;"), statement("// comment\nlet foo = 1.0 ;"),
Statement::Let(LetDef { Statement::Let(LetDef {
@ -1150,7 +1227,7 @@ mod test {
1, 1))) 1, 1)))
); );
assert_parse!( assert_parse!(
expression("1 + 1"), math_expression("1 + 1"),
Expression::Binary(BinaryOpDef { Expression::Binary(BinaryOpDef {
kind: BinaryExprType::Add, kind: BinaryExprType::Add,
left: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 1)))), left: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 1)))),
@ -1185,6 +1262,45 @@ mod test {
pos: Position::new(1, 1), pos: Position::new(1, 1),
}) })
); );
assert_parse!(
expression("(1 / 1)"),
Expression::Grouped(Box::new(Expression::Binary(BinaryOpDef {
kind: BinaryExprType::Div,
left: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 2)))),
right: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 6)))),
pos: Position::new(1, 2),
})))
);
assert_parse!(
expression("1 / 1 + 1"),
Expression::Binary(BinaryOpDef {
kind: BinaryExprType::Add,
left: Box::new(Expression::Binary(BinaryOpDef {
kind: BinaryExprType::Div,
left: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 1)))),
right: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 5)))),
pos: Position::new(1, 1),
})),
right: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 9)))),
pos: Position::new(1, 1),
})
);
assert_parse!(
expression("(1 + 1) * 1"),
Expression::Binary(BinaryOpDef {
kind: BinaryExprType::Mul,
left: Box::new(Expression::Grouped(Box::new(Expression::Binary(
BinaryOpDef {
kind: BinaryExprType::Add,
left: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 2)))),
right: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 6)))),
pos: Position::new(1, 2),
}
)))),
right: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 11)))),
pos: Position::new(1, 1),
})
);
assert_parse!( assert_parse!(
expression("1 > 1"), expression("1 > 1"),
Expression::Compare(ComparisonDef { Expression::Compare(ComparisonDef {