From 05f6597f00318b2e9f958ac62cf728325f5946ee Mon Sep 17 00:00:00 2001 From: Jeremy Wall Date: Mon, 14 May 2018 21:39:29 -0500 Subject: [PATCH] FEATURE: Proper precendence for math operators. Using nested combinators for enforce parsing precedence. --- src/build.rs | 3 +- src/parse.rs | 142 ++++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 131 insertions(+), 14 deletions(-) diff --git a/src/build.rs b/src/build.rs index 857a194..8cd5daa 100644 --- a/src/build.rs +++ b/src/build.rs @@ -1193,7 +1193,8 @@ mod compile_test { #[test] 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;"); } } diff --git a/src/parse.rs b/src/parse.rs index b055ca5..d99562a 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -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 { - ($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, do_parse!( pos: pos >> - left: alt!(simple_expression | grouped_expression) >> - $subrule!($($args)*) >> - right: expression >> + left: $lowerrule!($($lowerargs)*) >> + $oprule!($($args)*) >> + right: $lowerrule!($($lowerargs)*) >> (pos, $typ, left, right) ), tuple_to_binary_expression @@ -264,14 +275,26 @@ macro_rules! do_binary_expr { }; } +named!(math_expression, + alt!(sum_expression | product_expression) +); + +named!(sum_expression, + alt!(add_expression | sub_expression) +); + // trace_macros!(true); named!(add_expression, - do_binary_expr!(punct!("+"), BinaryExprType::Add) + do_binary_expr!(punct!("+"), BinaryExprType::Add, alt!(product_expression | simple_expression | grouped_expression)) ); // trace_macros!(false); named!(sub_expression, - do_binary_expr!(punct!("-"), BinaryExprType::Sub) + do_binary_expr!(punct!("-"), BinaryExprType::Sub, alt!(product_expression | simple_expression | grouped_expression)) +); + +named!(product_expression, + alt!(mul_expression | div_expression) ); named!(mul_expression, @@ -282,6 +305,7 @@ named!(div_expression, do_binary_expr!(punct!("/"), BinaryExprType::Div) ); +// TODO(jwall): Change comparison operators to use the do_binary_expr! with precedence? fn tuple_to_compare_expression( tpl: (Position, CompareType, Expression, Expression), ) -> ParseResult { @@ -298,7 +322,7 @@ macro_rules! do_compare_expr { map_res!($i, do_parse!( pos: pos >> - left: alt!(simple_expression | grouped_expression) >> + left: alt!(simple_expression | grouped_expression | math_expression) >> $subrule!($($args)*) >> right: expression >> (pos, $typ, left, right) @@ -694,22 +718,19 @@ named!(expression, do_parse!( expr: alt!( complete!(list_op_expression) | - complete!(add_expression) | - complete!(sub_expression) | - complete!(mul_expression) | - complete!(div_expression) | + complete!(math_expression) | complete!(eqeq_expression) | complete!(not_eqeq_expression) | complete!(lt_eqeq_expression) | complete!(gt_eqeq_expression) | complete!(gt_expression) | complete!(lt_expression) | - complete!(grouped_expression) | complete!(macro_expression) | complete!(format_expression) | complete!(select_expression) | complete!(call_expression) | complete!(copy_expression) | + complete!(grouped_expression) | simple_expression ) >> (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!( statement("// comment\nlet foo = 1.0 ;"), Statement::Let(LetDef { @@ -1150,7 +1227,7 @@ mod test { 1, 1))) ); assert_parse!( - expression("1 + 1"), + math_expression("1 + 1"), Expression::Binary(BinaryOpDef { kind: BinaryExprType::Add, left: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 1)))), @@ -1185,6 +1262,45 @@ mod test { 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!( expression("1 > 1"), Expression::Compare(ComparisonDef {