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.
This commit is contained in:
Bruno BELANYI 2020-10-30 18:56:36 +01:00
parent 88b7f50a3a
commit b5912f508e
1 changed files with 24 additions and 6 deletions

View File

@ -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