#ident "@(#)streval.c 1.6 93/06/08 SMI" /* From AT&T Toolchest */ /* * G. S. Fowler * D. G. Korn * AT&T Bell Laboratories * * arithmetic expression evaluator * * NOTE: all operands are evaluated as both the parse * and evaluation are done on the fly */ #ifdef KSHELL # include "shtype.h" #else # include #endif /* KSHELL */ #include "streval.h" #define MAXLEVEL 9 struct vars /* vars stacked per invocation */ { char* nextchr; /* next char in current expression */ char* errchr; /* next char after error */ struct lval errmsg; /* error message text */ char* errstr; /* error string */ #ifdef FLOAT char isfloat; /* set when floating number */ #endif /* FLOAT */ }; #define getchr() (*cur.nextchr++) #define peekchr() (*cur.nextchr) #define ungetchr() (cur.nextchr--) #define pushchr(s) {struct vars old;old=cur;cur.nextchr=(s);cur.errmsg.value=0;cur.errstr=0 #define popchr() cur=old;} #define error(msg) return(seterror(msg)) extern struct Optable optable[]; static struct vars cur; static char noassign; /* set to skip assignment */ static int level; static number (*convert)(); /* external conversion routine */ static number expr(); /* subexpression evaluator */ static number seterror(); /* set error message string */ static struct Optable *findop(); /* lookup operator */ /* * evaluate an integer arithmetic expression in s * * (number)(*convert)(char** end, struct lval* string, int type) is a user supplied * conversion routine that is called when unknown chars are encountered. * *end points to the part to be converted and must be adjusted by convert to * point to the next non-converted character; if typ is ERRMSG then string * points to an error message string * * NOTE: (*convert)() may call streval() */ number streval(s, end, conv) char* s; char** end; number (*conv)(); { number n; pushchr(s); #ifdef FLOAT cur.isfloat = 0; #endif /* FLOAT */ convert = conv; if(level++ >= MAXLEVEL) (void)seterror(e_recursive); else { n = expr(0); if (peekchr() == ':') (void)seterror(e_badcolon); } if (cur.errmsg.value) { if(cur.errstr) s = cur.errstr; (void)(*convert)( &s , &cur.errmsg, ERRMSG); cur.nextchr = cur.errchr; n = 0; } if (end) *end = cur.nextchr; if(level>0) level--; popchr(); return(n); } /* * evaluate a subexpression with precedence */ static number expr(precedence) register int precedence; { register int c; register number n; register number x; register struct Optable *op; int incr = 0; int wasop = 1; struct lval assignop; struct lval lvalue; char* pos; char invalid=0; while ((c=getchr()) && isspace(c)); switch (c) { case 0: if(precedence>5) error(e_moretokens); return(0); #ifdef future case '-': incr = -2; case '+': incr++; if(c != peekchr()) { /* unary plus or minus */ n = incr*expr(2*MAXPREC-1); incr = 0; } else /* ++ or -- */ { invalid = 1; getchr(); } break; #else case '-': n = -expr(2*MAXPREC-1); break; case '+': n = expr(2*MAXPREC-1); break; #endif case '!': n = !expr(2*MAXPREC-1); break; case '~': #ifdef FLOAT if(cur.isfloat) error(e_incompatible); #endif /* FLOAT */ n = ~(long)expr(2*MAXPREC-1); break; default: ungetchr(); invalid = 1; break; } lvalue.value = 0; while(1) { cur.errchr = cur.nextchr; if((c=getchr()) && isspace(c)) continue; assignop.value = 0; if(isalnum(c)) goto alphanumeric; op = findop(c); wasop++; /* check for assignment operation */ if(c && peekchr()== '=' && !(op->precedence&NOASSIGN)) { if(!noassign && (!lvalue.value || precedence > 2)) error(e_notlvalue); assignop = lvalue; getchr(); c = 3; } else { c = (op->precedence&PRECMASK); c *= 2; } /* from here on c is the new precedence level */ if(lvalue.value && (op->opcode!=ASSIGNMENT)) { n = (*convert)(&cur.nextchr, &lvalue, VALUE); if(cur.nextchr==0) error(e_number); if(!(op->precedence&SEQPOINT)) lvalue.value = 0; invalid = 0; } if(invalid && op->opcode>ASSIGNMENT) error(e_synbad); if(precedence >= c) goto done; if(op->precedence&RASSOC) c--; if(c < 2*MAXPREC && !(op->precedence&SEQPOINT)) x = expr(c); #ifdef FLOAT if((op->precedence&NOFLOAT)&& cur.isfloat) error(e_incompatible); #endif /* FLOAT */ switch(op->opcode) { case RPAREN: error(e_paren); case LPAREN: { #ifdef FLOAT char savefloat = cur.isfloat; cur.isfloat = 0; #endif /* FLOAT */ n = expr(2); #ifdef FLOAT cur.isfloat = savefloat; #endif /* FLOAT */ if (getchr() != ')') error(e_paren); wasop = 0; break; } #ifdef future case PLUSPLUS: incr = 1; goto common; case MINUSMINUS: incr = -1; common: x = n; #endif case ASSIGNMENT: if(!noassign && !lvalue.value) error(e_notlvalue); n = x; assignop = lvalue; lvalue.value = 0; break; #ifdef future case QUEST: { register int nextc; if(n) { x = expr(c); nextc = getchr(); } noassign++; (void)expr(c); noassign--; if(!n) { nextc = getchr(); x = expr(c); } n = x; if (nextc == ':') break; } case COLON: (void)seterror(e_badcolon); break; #endif case OR: n = (long)n | (long)x; break; case QCOLON: case OROR: if(n) { noassign++; expr(c); noassign--; } else n = expr(c); #ifdef future if(op->opcode==OROR) #endif n = (n!=0); lvalue.value = 0; break; case XOR: n = (long)n ^ (long)x; break; case NOT: error(e_synbad); case AND: n = (long)n & (long)x; break; case ANDAND: if(n==0) { noassign++; expr(c); noassign--; } else n = (expr(c)!=0); lvalue.value = 0; break; case EQ: n = n == x; break; case NEQ: n = n != x; break; case LT: n = n < x; break; case LSHIFT: n = (long)n << (long)x; break; case LE: n = n <= x; break; case GT: n = n > x; break; case RSHIFT: n = (long)n >> (long)x; break; case GE: n = n >= x; break; case PLUS: n += x; break; case MINUS: n -= x; break; case TIMES: n *= x; break; case DIVIDE: if(x!=0) { n /= x; break; } case MOD: if(x!=0) n = (long)n % (long)x; else error(e_divzero); break; default: alphanumeric: if(!wasop) error(e_synbad); wasop = 0; pos = --cur.nextchr; n = (*convert)(&cur.nextchr, &lvalue, LOOKUP); if (cur.nextchr == pos) { if(cur.errmsg.value = lvalue.value) cur.errstr = pos; error(e_synbad); } #ifdef future /* this handles ++x and --x */ if(incr) { if(lvalue.value) n = (*convert)(&cur.nextchr, &lvalue, VALUE); n += incr; incr = 0; goto common; } #endif break; } invalid = 0; #ifdef FLOAT if((long)n != n) cur.isfloat++; #endif /* FLOAT */ if(!noassign && assignop.value) (void)(*convert)(&cur.nextchr,&assignop,ASSIGN,n+incr); incr = 0; } done: cur.nextchr = cur.errchr; return(n); } /* * set error message string */ static number seterror(msg) char* msg; { if(!cur.errmsg.value) cur.errmsg.value = msg; cur.errchr = cur.nextchr; cur.nextchr = ""; level = 0; return(0); } /* * look for operator in table */ static struct Optable *findop(c) register int c; { register struct Optable *op = optable; if(c > '|') return(op); while(++op) { if(c > op->name[0]) continue; if(c < op->name[0]) return(optable); if(op->name[1]==0) break; c = getchr(); if(op->name[1]==c) break; if((++op)->name[1]==c) break; if(op->name[1]) op++; ungetchr(); break; } return(op); } #ifdef FLOAT set_float() { cur.isfloat++; } #endif /* FLOAT */