Mercurial > hg-old > index.cgi
diff lwlib/lw_expr.c @ 346:a82c55070624
Added expression parsing infrastructure and misc fixes
author | lost@starbug |
---|---|
date | Sat, 27 Mar 2010 19:04:03 -0600 |
parents | 7b4123dce741 |
children | 1649bc7bda5a |
line wrap: on
line diff
--- a/lwlib/lw_expr.c Thu Mar 25 23:17:54 2010 -0600 +++ b/lwlib/lw_expr.c Sat Mar 27 19:04:03 2010 -0600 @@ -33,6 +33,12 @@ static lw_expr_fn_t *evaluate_special = NULL; static lw_expr_fn2_t *evaluate_var = NULL; +static lw_expr_fn3_t *parse_term = NULL; + +void lw_expr_set_term_parser(lw_expr_fn3_t *fn) +{ + parse_term = fn; +} void lw_expr_set_special_handler(lw_expr_fn_t *fn) { @@ -353,7 +359,7 @@ return 1; } -void lw_expr_simplify(lw_expr_t E) +void lw_expr_simplify(lw_expr_t E, void *priv) { struct lw_expr_opers *o; @@ -363,7 +369,7 @@ { lw_expr_t te; - te = evaluate_special(E -> value, E -> value2); + te = evaluate_special(E -> value, E -> value2, priv); if (te) { for (o = E -> operands; o; o = o -> next) @@ -389,7 +395,7 @@ { lw_expr_t te; - te = evaluate_var(E -> value2); + te = evaluate_var(E -> value2, priv); if (te) { for (o = E -> operands; o; o = o -> next) @@ -420,7 +426,7 @@ // simplify operands for (o = E -> operands; o; o = o -> next) - lw_expr_simplify(o -> p); + lw_expr_simplify(o -> p, priv); for (o = E -> operands; o; o = o -> next) { @@ -637,3 +643,187 @@ return; } } + +/* + +The following two functions are co-routines which evaluate an infix +expression. lw_expr_parse_term checks for unary prefix operators then, if +none found, passes the string off the the defined helper function to +determine what the term really is. It also handles parentheses. + +lw_expr_parse_expr evaluates actual expressions with infix operators. It +respects the order of operations. + +The end of an expression is determined by the presence of any of the +following conditions: + +1. a NUL character +2. a whitespace character +3. a ) +4. a , +5. any character that is not recognized as a term + +lw_expr_parse_term returns NULL if there is no term (end of expr, etc.) + +lw_expr_parse_expr returns NULL if there is no expression or on a syntax +error. + +*/ + +lw_expr_t lw_expr_parse_expr(char **p, void *priv, int prec); + +lw_expr_t lw_expr_parse_term(char **p, void *priv) +{ + lw_expr_t term, term2; + +eval_next: + if (!**p || isspace(**p) || **p == ')' || **p == ']') + return NULL; + + // parentheses + if (**p == '(') + { + (*p)++; + term = lw_expr_parse_expr(p, priv, 0); + if (**p != ')') + { + lw_expr_destroy(term); + return NULL; + } + (*p)++; + return term; + } + + // unary + + if (**p == '+') + { + (*p)++; + goto eval_next; + } + + // unary - (prec 200) + if (**p == '-') + { + (*p)++; + term = lw_expr_parse_expr(p, priv, 200); + if (!term) + return NULL; + + term2 = lw_expr_build(lw_expr_type_oper, lw_expr_oper_neg, term); + lw_expr_destroy(term); + return term2; + } + + // unary ^ or ~ (complement, prec 200) + if (**p == '^' || **p == '~') + { + (*p)++; + term = lw_expr_parse_expr(p, priv, 200); + if (!term) + return NULL; + + term2 = lw_expr_build(lw_expr_type_oper, lw_expr_oper_com, term); + lw_expr_destroy(term); + return term2; + } + + // non-operator - pass to caller + return parse_term(p, priv); +} + +lw_expr_t lw_expr_parse_expr(char **p, void *priv, int prec) +{ + static const struct operinfo + { + int opernum; + char *operstr; + int operprec; + } operators[] = + { + { lw_expr_oper_plus, "+", 100 }, + { lw_expr_oper_minus, "-", 100 }, + { lw_expr_oper_times, "*", 100 }, + { lw_expr_oper_divide, "/", 150 }, + { lw_expr_oper_mod, "%", 150 }, + { lw_expr_oper_intdiv, "\\", 150 }, + + { lw_expr_oper_and, "&&", 25 }, + { lw_expr_oper_or, "||", 25 }, + + { lw_expr_oper_bwand, "&", 50 }, + { lw_expr_oper_bwor, "|", 50 }, + { lw_expr_oper_bwxor, "^", 50 }, + + { lw_expr_oper_none, "", 0 } + }; + + int opern, i; + lw_expr_t term1, term2, term3; + + if (!**p || isspace(**p) || **p == ')' || **p == ',' || **p == ']') + return NULL; + + term1 = lw_expr_parse_term(p, priv); + if (!term1) + return NULL; + +eval_next: + if (!**p || isspace(**p) || **p == ')' || **p == ',' || **p == ']') + return term1; + + // expecting an operator here + for (opern = 0; operators[opern].opernum != lw_expr_oper_none; opern++) + { + for (i = 0; (*p)[i] && operators[opern].operstr[i] && ((*p)[i] == operators[opern].operstr[i]); i++) + /* do nothing */; + if (operators[opern].operstr[i] == '\0') + break; + } + + if (operators[opern].opernum == lw_expr_oper_none) + { + // unrecognized operator + lw_expr_destroy(term1); + return NULL; + } + + // operator number is in opern, length of oper in i + + // logic: + // if the precedence of this operation is <= to the "prec" flag, + // we simply return without advancing the input pointer; the operator + // will be evaluated again in the enclosing function call + if (operators[opern].operprec <= prec) + return term1; + + // logic: + // we have a higher precedence operator here so we will advance the + // input pointer to the next term and let the expression evaluator + // loose on it after which time we will push our operator onto the + // stack and then go on with the expression evaluation + (*p) += i; + + // evaluate next expression(s) of higher precedence + term2 = lw_expr_parse_expr(p, priv, operators[opern].operprec); + if (!term2) + { + lw_expr_destroy(term1); + return NULL; + } + + // now create operator + term3 = lw_expr_build(lw_expr_type_oper, operators[opern].opernum, term1, term2); + lw_expr_destroy(term1); + lw_expr_destroy(term2); + + // the new "expression" is the next "left operand" + term1 = term3; + + // continue evaluating + goto eval_next; +} + +lw_expr_t lw_expr_parse(char **p, void *priv) +{ + return lw_expr_parse_expr(p, priv, 0); +}