Back to index

tetex-bin  3.0
strexpr.c
Go to the documentation of this file.
00001 /*
00002  * File:    strexpr.c
00003  * Purpose: calculate value from algebraic string expression
00004  *          (Stripped down from expr.c from J.T. Conklin <jtc@netbsd.org>.)
00005  * Version: 1.0 (Nov 1995)
00006  * Author:  Piet Tutelaers
00007  */
00008 
00009 #include <stdio.h>   /* sprintf() */
00010 #include <stdlib.h>  /* free(), malloc() */
00011 #include <string.h>  /* strchr(), strcpy() */
00012 #include <ctype.h>   /* isdigit(), isspace() */
00013 #include "basics.h"  /* fatal() */
00014 
00015 const char *OPERATORS = "+-*/%()";
00016 
00017 enum token {
00018        ADD, SUB, MUL, DIV, MOD, LP, RP, OPERAND, EOI
00019 };
00020 
00021 struct val {
00022        enum {
00023               integer,
00024               string
00025        } type;
00026 
00027        union {
00028               char           *s;
00029               int             i;
00030        } u;
00031 };
00032 
00033 enum token    token;
00034 struct val     *tokval;
00035 char           *pexpr;
00036 
00037 struct val *
00038 make_int(int i)
00039 {
00040        struct val     *vp;
00041 
00042        vp = (struct val *) malloc(sizeof(*vp));
00043        if (vp == NULL) fatal("expression error\n");
00044        vp->type = integer;
00045        vp->u.i = i;
00046        return vp;
00047 }
00048 
00049 
00050 struct val *
00051 make_str(char *s)
00052 {
00053        char operand[32]; int i;
00054 
00055        struct val     *vp;
00056 
00057        vp = (struct val *) malloc(sizeof(*vp));
00058        if (vp == NULL) fatal("expression error\n");
00059 
00060         pexpr = s; i = 0;
00061         while (1) {
00062           if (isspace(*pexpr) || *pexpr == '\0') break;
00063           if (strchr(OPERATORS, *pexpr) != NULL) break;
00064           if (i == 30) fatal("operand too large\n");
00065           operand[i++] = *pexpr++;
00066        }
00067        if (i == 0) fatal("invalid operand\n");
00068        operand[i] = '\0';
00069        vp->u.s = malloc(i+1);
00070        if (vp->u.s == NULL) fatal("Out of memory\n");
00071        strcpy(vp->u.s, operand);
00072        vp->type = string;
00073        return vp;
00074 }
00075 
00076 
00077 void
00078 free_value(struct val *vp)
00079 {
00080        if (vp->type == string)
00081               free(vp->u.s);
00082        free(vp);
00083 }
00084 
00085 
00086 /* determine if vp is an integer; if so, return it's value in *r */
00087 int
00088 is_integer(struct val *vp, int *r)
00089 {
00090        char           *s;
00091        int             neg;
00092        int             i;
00093 
00094        if (vp->type == integer) {
00095               *r = vp->u.i;
00096               return 1;
00097        }
00098 
00099        /*
00100         * POSIX.2 defines an "integer" as an optional unary minus 
00101         * followed by digits.
00102         */
00103        s = vp->u.s;
00104        i = 0;
00105 
00106        neg = (*s == '-');
00107        if (neg)
00108               s++;
00109 
00110        while (*s) {
00111               if (!isdigit(*s))
00112                      return 0;
00113 
00114               i *= 10;
00115               i += *s - '0';
00116 
00117               s++;
00118        }
00119 
00120        if (neg)
00121               i *= -1;
00122 
00123        *r = i;
00124        return 1;
00125 }
00126 
00127 
00128 /* coerce to vp to an integer */
00129 int
00130 to_integer(struct val *vp)
00131 {
00132        int             r;
00133 
00134        if (vp->type == integer)
00135               return 1;
00136 
00137        if (is_integer(vp, &r)) {
00138               free(vp->u.s);
00139               vp->u.i = r;
00140               vp->type = integer;
00141               return 1;
00142        }
00143 
00144        return 0;
00145 }
00146 
00147 
00148 /* coerce to vp to an string */
00149 void
00150 to_string(struct val *vp)
00151 {
00152        char           *tmp;
00153 
00154        if (vp->type == string)
00155               return;
00156 
00157        tmp = malloc(25);
00158        if (tmp == NULL) fatal("Out of memory\n");
00159        sprintf(tmp, "%d", vp->u.i);
00160        vp->type = string;
00161        vp->u.s = tmp;
00162 }
00163 
00164 void
00165 nexttoken()
00166 {
00167        char           *i;   /* index */
00168 
00169        while (isspace(*pexpr)) pexpr++;
00170 
00171        if (*pexpr == '\0') {
00172               token = EOI;
00173               return;
00174        }
00175 
00176        if ((i = strchr(OPERATORS, *pexpr)) != NULL) {
00177               pexpr++;
00178               token = i - OPERATORS;
00179               return;
00180        }
00181        tokval = make_str(pexpr);
00182        token = OPERAND;
00183        return;
00184 }
00185 
00186 struct val *
00187 eval6()
00188 {
00189        struct val *eval3(void);
00190        struct val     *v;
00191 
00192        if (token == OPERAND) {
00193               nexttoken();
00194               return tokval;
00195 
00196        } else if (token == LP) {
00197               nexttoken();
00198               v = eval3();
00199 
00200               if (token != RP)
00201                  fatal("missing parenthesis in expression\n");
00202               nexttoken();
00203               return v;
00204        } else fatal("expression error\n");
00205        /* NOTREACHED */
00206 }
00207 
00208 /* Parse and evaluate multiplication and division expressions */
00209 struct val *
00210 eval4()
00211 {
00212        struct val     *l, *r;
00213        enum token    op;
00214 
00215        l = eval6();
00216        while ((op = token) == MUL || op == DIV || op == MOD) {
00217               nexttoken();
00218               r = eval6();
00219 
00220               if (!to_integer(l) || !to_integer(r)) {
00221                      fatal("non-numeric argument\n");
00222               }
00223 
00224               if (op == MUL) {
00225                      l->u.i *= r->u.i;
00226               } else {
00227                      if (r->u.i == 0) {
00228                             fatal("division by zero\n");
00229                      }
00230                      if (op == DIV) {
00231                             l->u.i /= r->u.i;
00232                      } else {
00233                             l->u.i %= r->u.i;
00234                      }
00235               }
00236 
00237               free_value(r);
00238        }
00239 
00240        return l;
00241 }
00242 
00243 /* Parse and evaluate addition and subtraction expressions */
00244 struct val *
00245 eval3()
00246 {
00247        struct val     *l, *r;
00248        enum token    op;
00249 
00250        l = eval4();
00251        while ((op = token) == ADD || op == SUB) {
00252               nexttoken();
00253               r = eval4();
00254 
00255               if (!to_integer(l) || !to_integer(r)) {
00256                      fatal("non-numeric argument\n");
00257               }
00258 
00259               if (op == ADD) {
00260                      l->u.i += r->u.i;
00261               } else {
00262                      l->u.i -= r->u.i;
00263               }
00264 
00265               free_value(r);
00266        }
00267 
00268        return l;
00269 }
00270 
00271 /*
00272  * Evaluate the algebraic expression contained in <expression>. The
00273  * value will be assigned to <result>. When errors are encountered
00274  * non-zero is returned (if program is not aborted) otherwise zero. 
00275  */
00276 int strexpr(int *result, char *expression) {
00277        struct val     *vp;
00278 
00279        pexpr = expression;
00280 
00281         nexttoken();
00282        vp = eval3();
00283 
00284        if (token != EOI) return 1;
00285 
00286        if (vp->type == integer || to_integer(vp)) {
00287               *result = vp->u.i;
00288               return 0;
00289        }
00290 
00291         return 1;
00292 }