From b5912f508e90a80e4e549f277c344ed9d58a5ff8 Mon Sep 17 00:00:00 2001 From: Bruno BELANYI Date: Fri, 30 Oct 2020 18:56:36 +0100 Subject: [PATCH] evalexpr: parse: improve precedence climbing The previous version of the algorithm did not take into account the fact that the main loop should only run while we have postfix or infix operators. This happened to work because of the small amount of operators used in the grammar. The previous version also had prefix operators hard-coded in the operand parsing function. --- src/parse/climbing_parse.c | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/src/parse/climbing_parse.c b/src/parse/climbing_parse.c index d267aaf..4b4111a 100644 --- a/src/parse/climbing_parse.c +++ b/src/parse/climbing_parse.c @@ -64,12 +64,28 @@ static bool is_binop(char c) return false; } -static bool prec_between(const char **input, int min, int max) +static bool is_prefix(char c) { - skip_whitespace(input); - for (size_t i = 0; i < ARR_SIZE(ops); ++i) - if (*input[0] == ops[i].op[0]) + if (ops[i].fix == OP_PREFIX && c == ops[i].op[0]) + return true; + + return false; +} + +static bool is_postfix(char c) +{ + for (size_t i = 0; i < ARR_SIZE(ops); ++i) + if (ops[i].fix == OP_POSTFIX && c == ops[i].op[0]) + return true; + + return false; +} + +static bool prec_between(char c, int min, int max) +{ + for (size_t i = 0; i < ARR_SIZE(ops); ++i) + if (c == ops[i].op[0] && ops[i].fix != OP_PREFIX) return min <= ops[i].prio && ops[i].prio <= max; return false; @@ -136,7 +152,9 @@ static struct ast_node *climbing_parse_internal(const char **input, int prec) struct ast_node *ast = parse_operand(input); int r = INT_MAX; - while (/* infix_or_postfix(input) && */ prec_between(input, prec, r) && ast) + while ((skip_whitespace(input), true) && // We need to skip the whitespace + (is_binop(*input[0]) || is_postfix(*input[0])) + && prec_between(*input[0], prec, r) && ast) { const char c = *input[0]; eat_char(input); @@ -190,7 +208,7 @@ static struct ast_node *parse_operand(const char **input) struct ast_node *ast = NULL; int val = 0; - if (*input[0] == '+' || *input[0] == '-') + if (is_prefix(*input[0])) { enum unop_kind op = char_to_unop(*input[0]); // Remove the parenthesis