/*
* An evaluation function takes three arguments, as follows: (1) a pointer to
* an element of the precedence table which lists the operators at the current
* level of precedence; (2) a pointer to an integer which will receive the
* value of the expression; and (3) a pointer to a char* that points to the
* expression to be evaluated and that is updated to the end of the expression
* when evaluation is complete. The function returns LT_FALSE if the value of
* the expression is zero, LT_TRUE if it is non-zero, LT_IF if the expression
* depends on an unknown symbol, or LT_ERROR if there is a parse failure.
*/
struct ops;
typedef Linetype eval_fn(const struct ops *, int *, const char **);
static eval_fn eval_table, eval_unary;
/*
* The precedence table. Expressions involving binary operators are evaluated
* in a table-driven way by eval_table. When it evaluates a subexpression it
* calls the inner function with its first argument pointing to the next
* element of the table. Innermost expressions have special non-table-driven
* handling.
*/
static const struct ops {
eval_fn *inner;
struct op {
const char *str;
Linetype (*fn)(int *, Linetype, int, Linetype, int);
} op[5];
} eval_ops[] = {
{ eval_table, { { “||”, op_or } } },
{ eval_table, { { “&&”, op_and } } },
{ eval_table, { { “==”, op_eq },
{ “!=”, op_ne } } },
{ eval_unary, { { “<=”, op_le },
{ “>=”, op_ge },
{ “<”, op_lt },
{ “>”, op_gt } } }
};
/*
* Function for evaluating the innermost parts of expressions,
* viz. !expr (expr) number defined(symbol) symbol
* We reset the constexpr flag in the last two cases.
*/
static Linetype
eval_unary(const struct ops *ops, int *valp, const char **cpp)
{
const char *cp;
char *ep;
int sym;
bool defparen;
Linetype lt;
cp = skipcomment(*cpp);
if (*cp == ‘!’) {
debug(“eval%d !”, ops – eval_ops);
cp++;
lt = eval_unary(ops, valp, &cp);
if (lt == LT_ERROR)
return (LT_ERROR);
if (lt != LT_IF) {
*valp = !*valp;
lt = *valp ? LT_TRUE : LT_FALSE;
}
} else if (*cp == ‘(‘) {
cp++;
debug(“eval%d (“, ops – eval_ops);
lt = eval_table(eval_ops, valp, &cp);
if (lt == LT_ERROR)
return (LT_ERROR);
cp = skipcomment(cp);
if (*cp++ != ‘)’)
return (LT_ERROR);
} else if (isdigit((unsigned char)*cp)) {
debug(“eval%d number”, ops – eval_ops);
*valp = strtol(cp, &ep, 0);
if (ep == cp)
return (LT_ERROR);
lt = *valp ? LT_TRUE : LT_FALSE;
cp = skipsym(cp);
} else if (strncmp(cp, “defined”, 7) == 0 && endsym(cp[7])) {
cp = skipcomment(cp+7);
debug(“eval%d defined”, ops – eval_ops);
if (*cp == ‘(‘) {
cp = skipcomment(cp+1);
defparen = true;
} else {
defparen = false;
}
sym = findsym(cp);
if (sym < 0) {
lt = LT_IF;
} else {
*valp = (value[sym] != NULL);
lt = *valp ? LT_TRUE : LT_FALSE;
}
cp = skipsym(cp);
cp = skipcomment(cp);
if (defparen && *cp++ != ‘)’)
return (LT_ERROR);
constexpr = false;
} else if (!endsym(*cp)) {
debug(“eval%d symbol”, ops – eval_ops);
sym = findsym(cp);
cp = skipsym(cp);
if (sym < 0) {
lt = LT_IF;
cp = skipargs(cp);
} else if (value[sym] == NULL) {
*valp = 0;
lt = LT_FALSE;
} else {
*valp = strtol(value[sym], &ep, 0);
if (*ep != ‘\0′ || ep == value[sym])
return (LT_ERROR);
lt = *valp ? LT_TRUE : LT_FALSE;
cp = skipargs(cp);
}
constexpr = false;
} else {
debug(“eval%d bad expr”, ops – eval_ops);
return (LT_ERROR);
}
*cpp = cp;
debug(“eval%d = %d”, ops – eval_ops, *valp);
return (lt);
}