Mercurial > hg > index.cgi
diff lwlib/lw_expr.c @ 0:2c24602be78f
Initial import from lwtools 3.0.1 version, with new hand built build system and file reorganization
author | lost@l-w.ca |
---|---|
date | Wed, 19 Jan 2011 22:27:17 -0700 |
parents | |
children | 7317fbe024af |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lwlib/lw_expr.c Wed Jan 19 22:27:17 2011 -0700 @@ -0,0 +1,1273 @@ +/* +lwexpr.c + +Copyright © 2010 William Astle + +This file is part of LWTOOLS. + +LWTOOLS is free software: you can redistribute it and/or modify it under the +terms of the GNU General Public License as published by the Free Software +Foundation, either version 3 of the License, or (at your option) any later +version. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +more details. + +You should have received a copy of the GNU General Public License along with +this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <stdarg.h> +#include <stdio.h> +#include <string.h> + +#define ___lw_expr_c_seen___ +#include "lw_alloc.h" +#include "lw_expr.h" +#include "lw_error.h" +#include "lw_string.h" + +static lw_expr_fn_t *evaluate_special = NULL; +static lw_expr_fn2_t *evaluate_var = NULL; +static lw_expr_fn3_t *parse_term = NULL; + +/* Q&D to break out of infinite recursion */ +static int level = 0; +static int bailing = 0; + +int lw_expr_istype(lw_expr_t e, int t) +{ + if (e -> type == t) + return 1; + return 0; +} + +int lw_expr_intval(lw_expr_t e) +{ + if (e -> type == lw_expr_type_int) + return e -> value; + return -1; +} + +int lw_expr_whichop(lw_expr_t e) +{ + if (e -> type == lw_expr_type_oper) + return e -> value; + return -1; +} + +int lw_expr_specint(lw_expr_t e) +{ + if (e -> type == lw_expr_type_special) + return e -> value; + return -1; +} + +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) +{ + evaluate_special = fn; +} + +void lw_expr_set_var_handler(lw_expr_fn2_t *fn) +{ + evaluate_var = fn; +} + +lw_expr_t lw_expr_create(void) +{ + lw_expr_t r; + + r = lw_alloc(sizeof(struct lw_expr_priv)); + r -> operands = NULL; + + return r; +} + +void lw_expr_destroy(lw_expr_t E) +{ + struct lw_expr_opers *o; + if (!E) + return; + while (E -> operands) + { + o = E -> operands; + E -> operands = o -> next; + lw_expr_destroy(o -> p); + lw_free(o); + } + if (E -> type == lw_expr_type_var) + lw_free(E -> value2); + lw_free(E); +} + +/* actually duplicates the entire expression */ +void lw_expr_add_operand(lw_expr_t E, lw_expr_t O); +lw_expr_t lw_expr_copy(lw_expr_t E) +{ + lw_expr_t r, t; + struct lw_expr_opers *o; + + r = lw_alloc(sizeof(struct lw_expr_priv)); + *r = *E; + r -> operands = NULL; + + if (E -> type == lw_expr_type_var) + r -> value2 = lw_strdup(E -> value2); + for (o = E -> operands; o; o = o -> next) + { + lw_expr_add_operand(r, o -> p); + } + + return r; +} + +void lw_expr_add_operand(lw_expr_t E, lw_expr_t O) +{ + struct lw_expr_opers *o, *t; + + o = lw_alloc(sizeof(struct lw_expr_opers)); + o -> p = lw_expr_copy(O); + o -> next = NULL; + for (t = E -> operands; t && t -> next; t = t -> next) + /* do nothing */ ; + + if (t) + t -> next = o; + else + E -> operands = o; +} + +lw_expr_t lw_expr_build_aux(int exprtype, va_list args) +{ + lw_expr_t r; + int t; + void *p; + + lw_expr_t te1, te2; + + r = lw_expr_create(); + + switch (exprtype) + { + case lw_expr_type_int: + t = va_arg(args, int); + r -> type = lw_expr_type_int; + r -> value = t; + break; + + case lw_expr_type_var: + p = va_arg(args, char *); + r -> type = lw_expr_type_var; + r -> value2 = lw_strdup(p); + break; + + case lw_expr_type_special: + t = va_arg(args, int); + p = va_arg(args, char *); + r -> type = lw_expr_type_special; + r -> value = t; + r -> value2 = p; + break; + + case lw_expr_type_oper: + t = va_arg(args, int); + te1 = va_arg(args, lw_expr_t); + if (t != lw_expr_oper_com && t != lw_expr_oper_neg) + te2 = va_arg(args, lw_expr_t); + else + te2 = NULL; + + r -> type = lw_expr_type_oper; + r -> value = t; + lw_expr_add_operand(r, te1); + if (te2) + lw_expr_add_operand(r, te2); + break; + + default: + lw_error("Invalid expression type specified to lw_expr_build"); + } + + return r; +} + +lw_expr_t lw_expr_build(int exprtype, ...) +{ + va_list args; + lw_expr_t r; + + va_start(args, exprtype); + r = lw_expr_build_aux(exprtype, args); + va_end(args); + return r; +} + +void lw_expr_print_aux(lw_expr_t E, char **obuf, int *buflen, int *bufloc) +{ + struct lw_expr_opers *o; + int c = 0; + char buf[256]; + + if (!E) + { + strcpy(buf, "(NULL)"); + return; + } + for (o = E -> operands; o; o = o -> next) + { + c++; + lw_expr_print_aux(o -> p, obuf, buflen, bufloc); + } + + switch (E -> type) + { + case lw_expr_type_int: + if (E -> value < 0) + snprintf(buf, 256, "-%#x ", -(E -> value)); + else + snprintf(buf, 256, "%#x ", E -> value); + break; + + case lw_expr_type_var: + snprintf(buf, 256, "V(%s) ", (char *)(E -> value2)); + break; + + case lw_expr_type_special: + snprintf(buf, 256, "S(%d,%p) ", E -> value, E -> value2); + break; + + case lw_expr_type_oper: + snprintf(buf, 256, "[%d]", c); + switch (E -> value) + { + case lw_expr_oper_plus: + strcat(buf, "+ "); + break; + + case lw_expr_oper_minus: + strcat(buf, "- "); + break; + + case lw_expr_oper_times: + strcat(buf, "* "); + break; + + case lw_expr_oper_divide: + strcat(buf, "/ "); + break; + + case lw_expr_oper_mod: + strcat(buf, "% "); + break; + + case lw_expr_oper_intdiv: + strcat(buf, "\\ "); + break; + + case lw_expr_oper_bwand: + strcat(buf, "BWAND "); + break; + + case lw_expr_oper_bwor: + strcat(buf, "BWOR "); + break; + + case lw_expr_oper_bwxor: + strcat(buf, "BWXOR "); + break; + + case lw_expr_oper_and: + strcat(buf, "AND "); + break; + + case lw_expr_oper_or: + strcat(buf, "OR "); + break; + + case lw_expr_oper_neg: + strcat(buf, "NEG "); + break; + + case lw_expr_oper_com: + strcat(buf, "COM "); + break; + + default: + strcat(buf, "OPER "); + break; + } + break; + default: + snprintf(buf, 256, "ERR "); + break; + } + + c = strlen(buf); + if (*bufloc + c >= *buflen) + { + *buflen += 128; + *obuf = lw_realloc(*obuf, *buflen); + } + strcpy(*obuf + *bufloc, buf); + *bufloc += c; +} + +char *lw_expr_print(lw_expr_t E) +{ + static char *obuf = NULL; + static int obufsize = 0; + + int obufloc = 0; + + lw_expr_print_aux(E, &obuf, &obufsize, &obufloc); + + return obuf; +} + +/* +Return: +nonzero if expressions are the same (identical pointers or matching values) +zero if expressions are not the same + +*/ +int lw_expr_compare(lw_expr_t E1, lw_expr_t E2) +{ + struct lw_expr_opers *o1, *o2; + + if (E1 == E2) + return 1; + + if (!E1 || !E2) + return 0; + + if (!(E1 -> type == E2 -> type && E1 -> value == E2 -> value)) + return 0; + + if (E1 -> type == lw_expr_type_var) + { + if (!strcmp(E1 -> value2, E2 -> value2)) + return 1; + else + return 0; + } + + if (E1 -> type == lw_expr_type_special) + { + if (E1 -> value2 == E2 -> value2) + return 1; + else + return 0; + } + + for (o1 = E1 -> operands, o2 = E2 -> operands; o1 && o2; o1 = o1 -> next, o2 = o2 -> next) + if (lw_expr_compare(o1 -> p, o2 -> p) == 0) + return 0; + if (o1 || o2) + return 0; + + return 1; +} + +/* return true if E is an operator of type oper */ +int lw_expr_isoper(lw_expr_t E, int oper) +{ + if (E -> type == lw_expr_type_oper && E -> value == oper) + return 1; + return 0; +} + + +void lw_expr_simplify_sortconstfirst(lw_expr_t E) +{ + struct lw_expr_opers *o; + + if (E -> type != lw_expr_type_oper) + return; + if (E -> value != lw_expr_oper_times && E -> value != lw_expr_oper_plus) + return; + + for (o = E -> operands; o; o = o -> next) + { + if (o -> p -> type == lw_expr_type_oper && (o -> p -> value == lw_expr_oper_times || o -> p -> value == lw_expr_oper_plus)) + lw_expr_simplify_sortconstfirst(o -> p); + } + + for (o = E -> operands; o; o = o -> next) + { + if (o -> p -> type == lw_expr_type_int && o != E -> operands) + { + struct lw_expr_opers *o2; + for (o2 = E -> operands; o2 -> next != o; o2 = o2 -> next) + /* do nothing */ ; + o2 -> next = o -> next; + o -> next = E -> operands; + E -> operands = o; + o = o2; + } + } +} + +void lw_expr_sortoperandlist(struct lw_expr_opers **o) +{ +// fprintf(stderr, "lw_expr_sortoperandlist() not yet implemented\n"); +} + +// return 1 if the operand lists match, 0 if not +// may re-order the argument lists +int lw_expr_simplify_compareoperandlist(struct lw_expr_opers **ol1, struct lw_expr_opers **ol2) +{ + struct lw_expr_opers *o1, *o2; + + lw_expr_sortoperandlist(ol1); + lw_expr_sortoperandlist(ol2); + + for (o1 = *ol1, o2 = *ol2; o1 && o2; o1 = o1 -> next, o2 = o2 -> next) + { + if (!lw_expr_compare(o1 -> p, o2 -> p)) + return 0; + } + if (o1 || o2) + return 0; + return 1; +} + +int lw_expr_simplify_isliketerm(lw_expr_t e1, lw_expr_t e2) +{ + // first term is a "times" + if (e1 -> type == lw_expr_type_oper && e1 -> value == lw_expr_oper_times) + { + // second term is a "times" + if (e2 -> type == lw_expr_type_oper && e2 -> value == lw_expr_oper_times) + { + // both times - easy check + struct lw_expr_opers *o1, *o2; + for (o1 = e1 -> operands; o1; o1 = o1 -> next) + if (o1 -> p -> type != lw_expr_type_int) + break; + + for (o2 = e2 -> operands; o2; o2 = o2 -> next) + if (o2 -> p -> type != lw_expr_type_int) + break; + + if (lw_expr_simplify_compareoperandlist(&o1, &o2)) + return 1; + return 0; + } + + // not a times - have to assume it's the operand list + // with a "1 *" in front if it + if (!e1 -> operands -> next) + return 0; + if (e1 -> operands -> next -> next) + return 0; + if (!lw_expr_compare(e1 -> operands -> next -> p, e2)) + return 0; + return 1; + } + + // e1 is not a times + if (e2 -> type == lw_expr_type_oper && e2 -> value == lw_expr_oper_times) + { + // e2 is a times + if (e2 -> operands -> next -> next) + return 0; + if (!lw_expr_compare(e1, e2 -> operands -> next -> p)) + return 0; + return 1; + } + + // neither are times + if (!lw_expr_compare(e1, e2)) + return 0; + return 1; +} + +int lw_expr_contains(lw_expr_t E, lw_expr_t E1) +{ + struct lw_expr_opers *o; + + // NULL expr contains nothing :) + if (!E) + return 0; + + if (E1 -> type != lw_expr_type_var && E1 -> type != lw_expr_type_special) + return 0; + + if (lw_expr_compare(E, E1)) + return 1; + + for (o = E -> operands; o; o = o -> next) + { + if (lw_expr_contains(o -> p, E1)) + return 1; + } + return 0; +} + +void lw_expr_simplify_l(lw_expr_t E, void *priv); + +void lw_expr_simplify_go(lw_expr_t E, void *priv) +{ + struct lw_expr_opers *o; + + // replace subtraction with O1 + -1(O2)... + // needed for like term collection + if (E -> type == lw_expr_type_oper && E -> value == lw_expr_oper_minus) + { + for (o = E -> operands -> next; o; o = o -> next) + { + lw_expr_t e1, e2; + + e2 = lw_expr_build(lw_expr_type_int, -1); + e1 = lw_expr_build(lw_expr_type_oper, lw_expr_oper_times, e2, o -> p); + lw_expr_destroy(o -> p); + lw_expr_destroy(e2); + o -> p = e1; + } + E -> value = lw_expr_oper_plus; + } + + // turn "NEG" into -1(O) - needed for like term collection + if (E -> type == lw_expr_type_oper && E -> value == lw_expr_oper_neg) + { + lw_expr_t e1; + + E -> value = lw_expr_oper_times; + e1 = lw_expr_build(lw_expr_type_int, -1); + lw_expr_add_operand(E, e1); + lw_expr_destroy(e1); + } + +again: + // try to resolve non-constant terms to constants here + if (E -> type == lw_expr_type_special && evaluate_special) + { + lw_expr_t te; + + te = evaluate_special(E -> value, E -> value2, priv); + if (lw_expr_contains(te, E)) + lw_expr_destroy(te); + if (te) + { + for (o = E -> operands; o; o = o -> next) + lw_expr_destroy(o -> p); + if (E -> type == lw_expr_type_var) + lw_free(E -> value2); + *E = *te; + E -> operands = NULL; + + if (te -> type == lw_expr_type_var) + E -> value2 = lw_strdup(te -> value2); + for (o = te -> operands; o; o = o -> next) + { + lw_expr_add_operand(E, lw_expr_copy(o -> p)); + } + lw_expr_destroy(te); + goto again; + } + return; + } + + if (E -> type == lw_expr_type_var && evaluate_var) + { + lw_expr_t te; + + te = evaluate_var(E -> value2, priv); + if (lw_expr_contains(te, E)) + lw_expr_destroy(te); + else if (te) + { + for (o = E -> operands; o; o = o -> next) + lw_expr_destroy(o -> p); + if (E -> type == lw_expr_type_var) + lw_free(E -> value2); + *E = *te; + E -> operands = NULL; + + if (te -> type == lw_expr_type_var) + E -> value2 = lw_strdup(te -> value2); + for (o = te -> operands; o; o = o -> next) + { + lw_expr_add_operand(E, lw_expr_copy(o -> p)); + } + lw_expr_destroy(te); + goto again; + } + return; + } + + // non-operators have no simplification to do! + if (E -> type != lw_expr_type_oper) + return; + + // merge plus operations + if (E -> value == lw_expr_oper_plus) + { + lw_expr_t e2; + + tryagainplus: + for (o = E -> operands; o; o = o -> next) + { + if (o -> p -> type == lw_expr_type_oper && o -> p -> value == lw_expr_oper_plus) + { + struct lw_expr_opers *o2, *o3; + // we have a + operation - bring operands up + + for (o2 = E -> operands; o2 && o2 -> next != o; o2 = o2 -> next) + /* do nothing */ ; + if (o2) + o2 -> next = o -> p -> operands; + else + E -> operands = o -> p -> operands; + for (o2 = o -> p -> operands; o2 -> next; o2 = o2 -> next) + /* do nothing */ ; + o2 -> next = o -> next; + o -> p -> operands = NULL; + lw_expr_destroy(o -> p); + lw_free(o); + goto tryagainplus; + } + } + } + + // merge times operations + if (E -> value == lw_expr_oper_times) + { + lw_expr_t e2; + + tryagaintimes: + for (o = E -> operands; o; o = o -> next) + { + if (o -> p -> type == lw_expr_type_oper && o -> p -> value == lw_expr_oper_times) + { + struct lw_expr_opers *o2, *o3; + // we have a + operation - bring operands up + + for (o2 = E -> operands; o2 && o2 -> next != o; o2 = o2 -> next) + /* do nothing */ ; + if (o2) + o2 -> next = o -> p -> operands; + else + E -> operands = o -> p -> operands; + for (o2 = o -> p -> operands; o2 -> next; o2 = o2 -> next) + /* do nothing */ ; + o2 -> next = o -> next; + o -> p -> operands = NULL; + lw_expr_destroy(o -> p); + lw_free(o); + goto tryagaintimes; + } + } + } + + // simplify operands + for (o = E -> operands; o; o = o -> next) + lw_expr_simplify_l(o -> p, priv); + + for (o = E -> operands; o; o = o -> next) + { + if (o -> p -> type != lw_expr_type_int) + break; + } + + if (!o) + { + // we can do the operation here! + int tr = -42424242; + + switch (E -> value) + { + case lw_expr_oper_neg: + tr = -(E -> operands -> p -> value); + break; + + case lw_expr_oper_com: + tr = ~(E -> operands -> p -> value); + break; + + case lw_expr_oper_plus: + tr = E -> operands -> p -> value; + for (o = E -> operands -> next; o; o = o -> next) + tr += o -> p -> value; + break; + + case lw_expr_oper_minus: + tr = E -> operands -> p -> value; + for (o = E -> operands -> next; o; o = o -> next) + tr -= o -> p -> value; + break; + + case lw_expr_oper_times: + tr = E -> operands -> p -> value; + for (o = E -> operands -> next; o; o = o -> next) + tr *= o -> p -> value; + break; + + case lw_expr_oper_divide: + tr = E -> operands -> p -> value / E -> operands -> next -> p -> value; + break; + + case lw_expr_oper_mod: + tr = E -> operands -> p -> value % E -> operands -> next -> p -> value; + break; + + case lw_expr_oper_intdiv: + tr = E -> operands -> p -> value / E -> operands -> next -> p -> value; + break; + + case lw_expr_oper_bwand: + tr = E -> operands -> p -> value & E -> operands -> next -> p -> value; + break; + + case lw_expr_oper_bwor: + tr = E -> operands -> p -> value | E -> operands -> next -> p -> value; + break; + + case lw_expr_oper_bwxor: + tr = E -> operands -> p -> value ^ E -> operands -> next -> p -> value; + break; + + case lw_expr_oper_and: + tr = E -> operands -> p -> value && E -> operands -> next -> p -> value; + break; + + case lw_expr_oper_or: + tr = E -> operands -> p -> value || E -> operands -> next -> p -> value; + break; + + } + + while (E -> operands) + { + o = E -> operands; + E -> operands = o -> next; + lw_expr_destroy(o -> p); + lw_free(o); + } + E -> type = lw_expr_type_int; + E -> value = tr; + return; + } + + if (E -> value == lw_expr_oper_plus) + { + lw_expr_t e1; + int cval = 0; + + e1 = lw_expr_create(); + e1 -> operands = E -> operands; + E -> operands = 0; + + for (o = e1 -> operands; o; o = o -> next) + { + if (o -> p -> type == lw_expr_type_int) + cval += o -> p -> value; + else + lw_expr_add_operand(E, o -> p); + } + lw_expr_destroy(e1); + if (cval) + { + e1 = lw_expr_build(lw_expr_type_int, cval); + lw_expr_add_operand(E, e1); + lw_expr_destroy(e1); + } + } + + if (E -> value == lw_expr_oper_times) + { + lw_expr_t e1; + int cval = 1; + + e1 = lw_expr_create(); + e1 -> operands = E -> operands; + E -> operands = 0; + + for (o = e1 -> operands; o; o = o -> next) + { + if (o -> p -> type == lw_expr_type_int) + cval *= o -> p -> value; + else + lw_expr_add_operand(E, o -> p); + } + lw_expr_destroy(e1); + if (cval != 1) + { + e1 = lw_expr_build(lw_expr_type_int, cval); + lw_expr_add_operand(E, e1); + lw_expr_destroy(e1); + } + } + + if (E -> value == lw_expr_oper_times) + { + for (o = E -> operands; o; o = o -> next) + { + if (o -> p -> type == lw_expr_type_int && o -> p -> value == 0) + { + // one operand of times is 0, replace operation with 0 + while (E -> operands) + { + o = E -> operands; + E -> operands = o -> next; + lw_expr_destroy(o -> p); + lw_free(o); + } + E -> type = lw_expr_type_int; + E -> value = 0; + return; + } + } + } + + // sort "constants" to the start of each operand list for + and * + if (E -> value == lw_expr_oper_plus || E -> value == lw_expr_oper_times) + lw_expr_simplify_sortconstfirst(E); + + // look for like terms and collect them together + if (E -> value == lw_expr_oper_plus) + { + struct lw_expr_opers *o2; + for (o = E -> operands; o; o = o -> next) + { + // skip constants + if (o -> p -> type == lw_expr_type_int) + continue; + + // we have a term to match + // (o -> p) is first term + for (o2 = o -> next; o2; o2 = o2 -> next) + { + lw_expr_t e1, e2; + + if (o2 -> p -> type == lw_expr_type_int) + continue; + + if (lw_expr_simplify_isliketerm(o -> p, o2 -> p)) + { + int coef, coef2; + + // we have a like term here + // do something about it + if (o -> p -> type == lw_expr_type_oper && o -> p -> value == lw_expr_oper_times) + { + if (o -> p -> operands -> p -> type == lw_expr_type_int) + coef = o -> p -> operands -> p -> value; + else + coef = 1; + } + else + coef = 1; + if (o2 -> p -> type == lw_expr_type_oper && o2 -> p -> value == lw_expr_oper_times) + { + if (o2 -> p -> operands -> p -> type == lw_expr_type_int) + coef2 = o2 -> p -> operands -> p -> value; + else + coef2 = 1; + } + else + coef2 = 1; + coef += coef2; + e1 = lw_expr_create(); + e1 -> type = lw_expr_type_oper; + e1 -> value = lw_expr_oper_times; + if (coef != 1) + { + e2 = lw_expr_build(lw_expr_type_int, coef); + lw_expr_add_operand(e1, e2); + lw_expr_destroy(e2); + } + lw_expr_destroy(o -> p); + o -> p = e1; + for (o = o2 -> p -> operands; o; o = o -> next) + { + if (o -> p -> type == lw_expr_type_int) + continue; + lw_expr_add_operand(e1, o -> p); + } + lw_expr_destroy(o2 -> p); + o2 -> p = lw_expr_build(lw_expr_type_int, 0); + goto again; + } + } + } + } + + + if (E -> value == lw_expr_oper_plus) + { + int c = 0, t = 0; + for (o = E -> operands; o; o = o -> next) + { + t++; + if (!(o -> p -> type == lw_expr_type_int && o -> p -> value == 0)) + { + c++; + } + } + if (c == 1) + { + lw_expr_t r; + // find the value and "move it up" + while (E -> operands) + { + o = E -> operands; + if (o -> p -> type != lw_expr_type_int || o -> p -> value != 0) + { + r = lw_expr_copy(o -> p); + } + E -> operands = o -> next; + lw_expr_destroy(o -> p); + lw_free(o); + } + *E = *r; + return; + } + else if (c == 0) + { + // replace with 0 + while (E -> operands) + { + o = E -> operands; + E -> operands = o -> next; + lw_expr_destroy(o -> p); + lw_free(o); + } + E -> type = lw_expr_type_int; + E -> value = 0; + return; + } + else if (c != t) + { + // collapse out zero terms + struct lw_expr_opers *o2; + + for (o = E -> operands; o; o = o -> next) + { + if (o -> p -> type == lw_expr_type_int && o -> p -> value == 0) + { + if (o == E -> operands) + { + E -> operands = o -> next; + lw_expr_destroy(o -> p); + lw_free(o); + o = E -> operands; + } + else + { + for (o2 = E -> operands; o2 -> next == o; o2 = o2 -> next) + /* do nothing */ ; + o2 -> next = o -> next; + lw_expr_destroy(o -> p); + lw_free(o); + o = o2; + } + } + } + } + return; + } + + /* handle <int> times <plus> - expand the terms - only with exactly two operands */ + if (E -> value == lw_expr_oper_times) + { + lw_expr_t t1; + lw_expr_t E2; + lw_expr_t E3; + if (E -> operands && E -> operands -> next && !(E -> operands -> next -> next)) + { + if (E -> operands -> p -> type == lw_expr_type_int) + { + /* <int> TIMES <other> */ + E2 = E -> operands -> next -> p; + E3 = E -> operands -> p; + if (E2 -> type == lw_expr_type_oper && E2 -> value == lw_expr_oper_plus) + { + lw_free(E -> operands -> next); + lw_free(E -> operands); + E -> operands = NULL; + E -> value = lw_expr_oper_plus; + + for (o = E2 -> operands; o; o = o -> next) + { + t1 = lw_expr_build(lw_expr_type_oper, lw_expr_oper_times, E3, o -> p); + lw_expr_add_operand(E, t1); + } + + lw_expr_destroy(E2); + lw_expr_destroy(E3); + } + } + else if (E -> operands -> next -> p -> type == lw_expr_type_int) + { + /* <other> TIMES <int> */ + E2 = E -> operands -> p; + E3 = E -> operands -> next -> p; + if (E2 -> type == lw_expr_type_oper && E2 -> value == lw_expr_oper_plus) + { + lw_free(E -> operands -> next); + lw_free(E -> operands); + E -> operands = NULL; + E -> value = lw_expr_oper_plus; + + for (o = E2 -> operands; o; o = o -> next) + { + t1 = lw_expr_build(lw_expr_type_oper, lw_expr_oper_times, E3, o -> p); + lw_expr_add_operand(E, t1); + } + + lw_expr_destroy(E2); + lw_expr_destroy(E3); + } + } + } + } +} + +void lw_expr_simplify_l(lw_expr_t E, void *priv) +{ + lw_expr_t te; + int c; + + (level)++; + // bail out if the level gets too deep + if (level >= 500 || bailing) + { + bailing = 1; + level--; + if (level == 0) + bailing = 0; + return; + } + do + { + te = lw_expr_copy(E); + lw_expr_simplify_go(E, priv); + c = 0; + if (lw_expr_compare(te, E) == 0) + c = 1; + lw_expr_destroy(te); + } + while (c); + (level)--; +} + +void lw_expr_simplify(lw_expr_t E, void *priv) +{ + lw_expr_simplify_l(E, priv); +} + +/* + +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, "*", 150 }, + { 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); +} + +int lw_expr_testterms(lw_expr_t e, lw_expr_testfn_t *fn, void *priv) +{ + struct lw_expr_opers *o; + int r; + + for (o = e -> operands; o; o = o -> next) + { + r = lw_expr_testterms(o -> p, fn, priv); + if (r) + return r; + } + return (fn)(e, priv); +} + +int lw_expr_type(lw_expr_t e) +{ + return e -> type; +} + +void *lw_expr_specptr(lw_expr_t e) +{ + return e -> value2; +}