Files
seta75D 7c4988eac0 Init
2021-10-11 19:38:01 -03:00

476 lines
7.6 KiB
C
Executable File

#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 <ctype.h>
#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 */