/* * File: strexpr.c * Purpose: calculate value from algebraic string expression * (Stripped down from expr.c from J.T. Conklin .) * Version: 1.0 (Nov 1995) * Author: Piet Tutelaers */ #include /* sprintf() */ #include /* free(), malloc() */ #include /* strchr(), strcpy() */ #include /* isdigit(), isspace() */ #include "basics.h" /* fatal() */ const char *OPERATORS = "+-*/%()"; enum token { ADD, SUB, MUL, DIV, MOD, LP, RP, OPERAND, EOI }; struct val { enum { integer, string } type; union { char *s; int i; } u; }; enum token token; struct val *tokval; char *pexpr; struct val * make_int(int i) { struct val *vp; vp = (struct val *) malloc(sizeof(*vp)); if (vp == NULL) fatal("expression error\n"); vp->type = integer; vp->u.i = i; return vp; } struct val * make_str(char *s) { char operand[32]; int i; struct val *vp; vp = (struct val *) malloc(sizeof(*vp)); if (vp == NULL) fatal("expression error\n"); pexpr = s; i = 0; while (1) { if (isspace(*pexpr) || *pexpr == '\0') break; if (strchr(OPERATORS, *pexpr) != NULL) break; if (i == 30) fatal("operand too large\n"); operand[i++] = *pexpr++; } if (i == 0) fatal("invalid operand\n"); operand[i] = '\0'; vp->u.s = malloc(i+1); if (vp->u.s == NULL) fatal("Out of memory\n"); strcpy(vp->u.s, operand); vp->type = string; return vp; } void free_value(struct val *vp) { if (vp->type == string) free(vp->u.s); free(vp); } /* determine if vp is an integer; if so, return it's value in *r */ int is_integer(struct val *vp, int *r) { char *s; int neg; int i; if (vp->type == integer) { *r = vp->u.i; return 1; } /* * POSIX.2 defines an "integer" as an optional unary minus * followed by digits. */ s = vp->u.s; i = 0; neg = (*s == '-'); if (neg) s++; while (*s) { if (!isdigit(*s)) return 0; i *= 10; i += *s - '0'; s++; } if (neg) i *= -1; *r = i; return 1; } /* coerce to vp to an integer */ int to_integer(struct val *vp) { int r; if (vp->type == integer) return 1; if (is_integer(vp, &r)) { free(vp->u.s); vp->u.i = r; vp->type = integer; return 1; } return 0; } /* coerce to vp to an string */ void to_string(struct val *vp) { char *tmp; if (vp->type == string) return; tmp = malloc(25); if (tmp == NULL) fatal("Out of memory\n"); sprintf(tmp, "%d", vp->u.i); vp->type = string; vp->u.s = tmp; } void nexttoken() { char *i; /* index */ while (isspace(*pexpr)) pexpr++; if (*pexpr == '\0') { token = EOI; return; } if ((i = strchr(OPERATORS, *pexpr)) != NULL) { pexpr++; token = i - OPERATORS; return; } tokval = make_str(pexpr); token = OPERAND; return; } struct val * eval6() { struct val *eval3(void); struct val *v; if (token == OPERAND) { nexttoken(); return tokval; } else if (token == LP) { nexttoken(); v = eval3(); if (token != RP) fatal("missing parenthesis in expression\n"); nexttoken(); return v; } else fatal("expression error\n"); /* NOTREACHED */ } /* Parse and evaluate multiplication and division expressions */ struct val * eval4() { struct val *l, *r; enum token op; l = eval6(); while ((op = token) == MUL || op == DIV || op == MOD) { nexttoken(); r = eval6(); if (!to_integer(l) || !to_integer(r)) { fatal("non-numeric argument\n"); } if (op == MUL) { l->u.i *= r->u.i; } else { if (r->u.i == 0) { fatal("division by zero\n"); } if (op == DIV) { l->u.i /= r->u.i; } else { l->u.i %= r->u.i; } } free_value(r); } return l; } /* Parse and evaluate addition and subtraction expressions */ struct val * eval3() { struct val *l, *r; enum token op; l = eval4(); while ((op = token) == ADD || op == SUB) { nexttoken(); r = eval4(); if (!to_integer(l) || !to_integer(r)) { fatal("non-numeric argument\n"); } if (op == ADD) { l->u.i += r->u.i; } else { l->u.i -= r->u.i; } free_value(r); } return l; } /* * Evaluate the algebraic expression contained in . The * value will be assigned to . When errors are encountered * non-zero is returned (if program is not aborted) otherwise zero. */ int strexpr(int *result, char *expression) { struct val *vp; pexpr = expression; nexttoken(); vp = eval3(); if (token != EOI) return 1; if (vp->type == integer || to_integer(vp)) { *result = vp->u.i; return 0; } return 1; }