diff --git a/modcc/parser.cpp b/modcc/parser.cpp index bebef194dca3d5ca5233de67051a83e16dc1fbe5..7bf11c37f9140b6800240eca05256a44c1ed1146 100644 --- a/modcc/parser.cpp +++ b/modcc/parser.cpp @@ -1095,27 +1095,34 @@ expression_ptr Parser::parse_conserve_expression() { return make_expression<ConserveExpression>(here, std::move(lhs), std::move(rhs)); } -expression_ptr Parser::parse_expression() { +expression_ptr Parser::parse_expression(int prec) { auto lhs = parse_unaryop(); + if(lhs==nullptr) return nullptr; - if(lhs==nullptr) { // error - return nullptr; - } - - // we parse a binary expression if followed by an operator - if( binop_precedence(token_.type)>0 ) { + // Combine all sub-expressions with precedence greater than prec. + for (;;) { if(token_.type==tok::eq) { error("assignment '"+yellow("=")+"' not allowed in sub-expression"); return nullptr; } - Token op = token_; // save the operator - get_token(); // consume the operator - return parse_binop(std::move(lhs), op); + + auto op = token_; + auto p_op = binop_precedence(op.type); + + if(p_op<=prec) return lhs; + get_token(); + + lhs = parse_binop(std::move(lhs), op); + if(!lhs) return nullptr; } return lhs; } +expression_ptr Parser::parse_expression() { + return parse_expression(0); +} + /// Parse a unary expression. /// If called when the current node in the AST is not a unary expression the call /// will be forwarded to parse_primary. This mechanism makes it possible to parse @@ -1215,44 +1222,35 @@ expression_ptr Parser::parse_integer() { } expression_ptr Parser::parse_binop(expression_ptr&& lhs, Token op_left) { - // only way out of the loop below is by return: - // :: return with nullptr on error - // :: return when loop runs out of operators - // i.e. if(pp<0) - // :: return when recursion applied to remainder of expression - // i.e. if(p_op>p_left) - while(1) { - // get precedence of the left operator - auto p_left = binop_precedence(op_left.type); + auto p_op_left = binop_precedence(op_left.type); + auto rhs = parse_expression(p_op_left); + if(!rhs) return nullptr; - auto e = parse_unaryop(); - if(!e) return nullptr; + auto op_right = token_; + auto p_op_right = binop_precedence(op_right.type); + bool right_assoc = operator_associativity(op_right.type)==associativityKind::right; - auto op = token_; - auto p_op = binop_precedence(op.type); - if(operator_associativity(op.type)==associativityKind::right) { - p_op += 1; - } + if(p_op_right>p_op_left) { + throw compiler_exception( + "parse_binop() : encountered operator of higher precedence", + location_); + } - // if no binop, parsing of expression is finished with (op_left lhs e) - if(p_op < 0) { - return binary_expression(op_left.location, op_left.type, std::move(lhs), std::move(e)); - } + if(p_op_right<p_op_left) { + return binary_expression(op_left.location, op_left.type, std::move(lhs), std::move(rhs)); + } - get_token(); // consume op - if(p_op > p_left) { - auto rhs = parse_binop(std::move(e), op); - if(!rhs) return nullptr; - return binary_expression(op_left.location, op_left.type, std::move(lhs), std::move(rhs)); - } + get_token(); // consume op_right + if(right_assoc) { + rhs = parse_binop(std::move(rhs), op_right); + if(!rhs) return nullptr; - lhs = binary_expression(op_left.location, op_left.type, std::move(lhs), std::move(e)); - op_left = op; + return binary_expression(op_left.location, op_left.type, std::move(lhs), std::move(rhs)); + } + else { + lhs = binary_expression(op_left.location, op_left.type, std::move(lhs), std::move(rhs)); + return parse_binop(std::move(lhs), op_right); } - throw compiler_exception( - "parse_binop() : fell out of recursive parse descent", - location_); - return nullptr; } /// parse a local variable definition diff --git a/modcc/parser.hpp b/modcc/parser.hpp index dc673e9d7b9f9d1084f8d024801ec4eaf87b9d88..10a33e598c4d9e1ddcc954496c9a44ed587e78e6 100644 --- a/modcc/parser.hpp +++ b/modcc/parser.hpp @@ -20,6 +20,7 @@ public: expression_ptr parse_integer(); expression_ptr parse_real(); expression_ptr parse_call(); + expression_ptr parse_expression(int prec); expression_ptr parse_expression(); expression_ptr parse_primary(); expression_ptr parse_parenthesis_expression(); diff --git a/tests/modcc/test_kinetic_rewriter.cpp b/tests/modcc/test_kinetic_rewriter.cpp index 53a32d919ff654fe700b63347991016acd974743..4dc1017c1612b0fe25a563a50b63cea65ec9cf15 100644 --- a/tests/modcc/test_kinetic_rewriter.cpp +++ b/tests/modcc/test_kinetic_rewriter.cpp @@ -43,7 +43,7 @@ static const char* derivative_abc = " a' = -3*a + b*v \n" " LOCAL rev2 \n" " rev2 = c*b^3*sin(4) \n" - " b' = 3*a - (v*b) + 8*b - 2*rev2\n" + " b' = 3*a - v*b + 8*b - 2*rev2\n" " c' = 4*b - rev2 \n" "} \n"; diff --git a/tests/modcc/test_parser.cpp b/tests/modcc/test_parser.cpp index 8cab1b8bd9208c508068e9ca50eb8a278bd3e62b..853b2136511d3c73705111d2062946605e6c12bc 100644 --- a/tests/modcc/test_parser.cpp +++ b/tests/modcc/test_parser.cpp @@ -513,13 +513,15 @@ long double eval(Expression *e) { // test parsing of expressions for correctness // by parsing rvalue expressions with numeric atoms, which can be evalutated using eval TEST(Parser, parse_binop) { + using std::pow; + std::pair<const char*, double> tests[] = { // simple {"2+3", 2.+3.}, {"2-3", 2.-3.}, {"2*3", 2.*3.}, {"2/3", 2./3.}, - {"2^3", std::pow(2., 3.)}, + {"2^3", pow(2., 3.)}, // more complicated {"2+3*2", 2.+(3*2)}, @@ -530,12 +532,16 @@ TEST(Parser, parse_binop) { {"2 * 7 - 3 * 11 + 4 * 13", 2.*7.-3.*11.+4.*13.}, // right associative - {"2^3^1.5", std::pow(2.,std::pow(3.,1.5))}, - {"2^3^1.5^2", std::pow(2.,std::pow(3.,std::pow(1.5,2.)))}, - {"2^2^3", std::pow(2.,std::pow(2.,3.))}, - {"(2^2)^3", std::pow(std::pow(2.,2.),3.)}, - {"3./2^7.", 3./std::pow(2.,7.)}, - {"3^2*5.", std::pow(3.,2.)*5.}, + {"2^3^1.5", pow(2.,pow(3.,1.5))}, + {"2^3^1.5^2", pow(2.,pow(3.,pow(1.5,2.)))}, + {"2^2^3", pow(2.,pow(2.,3.))}, + {"(2^2)^3", pow(pow(2.,2.),3.)}, + {"3./2^7.", 3./pow(2.,7.)}, + {"3^2*5.", pow(3.,2.)*5.}, + + // multilevel + {"1-2*3^4*5^2^3-3^2^3/4/8-5", + 1.-2*pow(3.,4.)*pow(5.,pow(2.,3.))-pow(3,pow(2.,3.))/4./8.-5} }; for (const auto& test_case: tests) {