summaryrefslogtreecommitdiff
path: root/src/cmd/ksh93/sh/streval.c
diff options
context:
space:
mode:
authorIgor Pashev <pashev.igor@gmail.com>2012-06-24 22:28:35 +0000
committerIgor Pashev <pashev.igor@gmail.com>2012-06-24 22:28:35 +0000
commit3950ffe2a485479f6561c27364d3d7df5a21d124 (patch)
tree468c6e14449d1b1e279222ec32f676b0311917d2 /src/cmd/ksh93/sh/streval.c
downloadksh-upstream.tar.gz
Imported Upstream version 93u+upstream
Diffstat (limited to 'src/cmd/ksh93/sh/streval.c')
-rw-r--r--src/cmd/ksh93/sh/streval.c1023
1 files changed, 1023 insertions, 0 deletions
diff --git a/src/cmd/ksh93/sh/streval.c b/src/cmd/ksh93/sh/streval.c
new file mode 100644
index 0000000..d13e0f8
--- /dev/null
+++ b/src/cmd/ksh93/sh/streval.c
@@ -0,0 +1,1023 @@
+/***********************************************************************
+* *
+* This software is part of the ast package *
+* Copyright (c) 1982-2011 AT&T Intellectual Property *
+* and is licensed under the *
+* Eclipse Public License, Version 1.0 *
+* by AT&T Intellectual Property *
+* *
+* A copy of the License is available at *
+* http://www.eclipse.org/org/documents/epl-v10.html *
+* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
+* *
+* Information and Software Systems Research *
+* AT&T Research *
+* Florham Park NJ *
+* *
+* David Korn <dgk@research.att.com> *
+* *
+***********************************************************************/
+#pragma prototyped
+
+/*
+ * D. G. Korn
+ * AT&T Labs
+ *
+ * arithmetic expression evaluator
+ *
+ * this version compiles the expression onto a stack
+ * and has a separate executor
+ */
+
+#include "streval.h"
+#include <ctype.h>
+#include <error.h>
+#include <stak.h>
+#include "FEATURE/externs"
+#include "defs.h" /* for sh.decomma */
+
+#ifndef ERROR_dictionary
+# define ERROR_dictionary(s) (s)
+#endif
+#ifndef SH_DICT
+# define SH_DICT "libshell"
+#endif
+
+#define MAXLEVEL 9
+#define SMALL_STACK 12
+
+/*
+ * The following are used with tokenbits() macro
+ */
+#define T_OP 0x3f /* mask for operator number */
+#define T_BINARY 0x40 /* binary operators */
+#define T_NOFLOAT 0x80 /* non floating point operator */
+#define A_LVALUE (2*MAXPREC+2)
+
+#define pow2size(x) ((x)<=2?2:(x)<=4?4:(x)<=8?8:(x)<=16?16:(x)<=32?32:64)
+#define round(x,size) (((x)+(size)-1)&~((size)-1))
+#define stakpush(v,val,type) ((((v)->offset=round(staktell(),pow2size(sizeof(type)))),\
+ stakseek((v)->offset+sizeof(type)), \
+ *((type*)stakptr((v)->offset)) = (val)),(v)->offset)
+#define roundptr(ep,cp,type) (((unsigned char*)(ep))+round(cp-((unsigned char*)(ep)),pow2size(sizeof(type))))
+
+static int level;
+
+struct vars /* vars stacked per invocation */
+{
+ Shell_t *shp;
+ const char *expr; /* current expression */
+ const char *nextchr; /* next char in current expression */
+ const char *errchr; /* next char after error */
+ const char *errstr; /* error string */
+ struct lval errmsg; /* error message text */
+ int offset; /* offset for pushchr macro */
+ int staksize; /* current stack size needed */
+ int stakmaxsize; /* maximum stack size needed */
+ unsigned char paren; /* parenthesis level */
+ char infun; /* incremented by comma inside function */
+ int emode;
+ Sfdouble_t (*convert)(const char**,struct lval*,int,Sfdouble_t);
+};
+
+typedef Sfdouble_t (*Math_f)(Sfdouble_t,...);
+typedef Sfdouble_t (*Math_1f_f)(Sfdouble_t);
+typedef int (*Math_1i_f)(Sfdouble_t);
+typedef Sfdouble_t (*Math_2f_f)(Sfdouble_t,Sfdouble_t);
+typedef Sfdouble_t (*Math_2f_i)(Sfdouble_t,int);
+typedef int (*Math_2i_f)(Sfdouble_t,Sfdouble_t);
+typedef Sfdouble_t (*Math_3f_f)(Sfdouble_t,Sfdouble_t,Sfdouble_t);
+typedef int (*Math_3i_f)(Sfdouble_t,Sfdouble_t,Sfdouble_t);
+
+#define getchr(vp) (*(vp)->nextchr++)
+#define peekchr(vp) (*(vp)->nextchr)
+#define ungetchr(vp) ((vp)->nextchr--)
+
+#if ('a'==97) /* ASCII encodings */
+# define getop(c) (((c) >= sizeof(strval_states))? \
+ ((c)=='|'?A_OR:((c)=='^'?A_XOR:((c)=='~'?A_TILDE:A_REG))):\
+ strval_states[(c)])
+#else
+# define getop(c) (isdigit(c)?A_DIG:((c==' '||c=='\t'||c=='\n'||c=='"')?0: \
+ (c=='<'?A_LT:(c=='>'?A_GT:(c=='='?A_ASSIGN: \
+ (c=='+'?A_PLUS:(c=='-'?A_MINUS:(c=='*'?A_TIMES: \
+ (c=='/'?A_DIV:(c=='%'?A_MOD:(c==','?A_COMMA: \
+ (c=='&'?A_AND:(c=='!'?A_NOT:(c=='('?A_LPAR: \
+ (c==')'?A_RPAR:(c==0?A_EOF:(c==':'?A_COLON: \
+ (c=='?'?A_QUEST:(c=='|'?A_OR:(c=='^'?A_XOR: \
+ (c=='\''?A_LIT: \
+ (c=='.'?A_DOT:(c=='~'?A_TILDE:A_REG)))))))))))))))))))))))
+#endif
+
+#define seterror(v,msg) _seterror(v,ERROR_dictionary(msg))
+#define ERROR(vp,msg) return(seterror((vp),msg))
+
+/*
+ * set error message string and return(0)
+ */
+static int _seterror(struct vars *vp,const char *msg)
+{
+ if(!vp->errmsg.value)
+ vp->errmsg.value = (char*)msg;
+ vp->errchr = vp->nextchr;
+ vp->nextchr = "";
+ level = 0;
+ return(0);
+}
+
+
+static void arith_error(const char *message,const char *expr, int mode)
+{
+ level = 0;
+ mode = (mode&3)!=0;
+ errormsg(SH_DICT,ERROR_exit(mode),message,expr);
+}
+
+#if _ast_no_um2fm
+static Sfdouble_t U2F(Sfulong_t u)
+{
+ Sflong_t s = u;
+ Sfdouble_t f;
+
+ if (s >= 0)
+ return s;
+ s = u / 2;
+ f = s;
+ f *= 2;
+ if (u & 1)
+ f++;
+ return f;
+}
+#else
+#define U2F(x) x
+#endif
+
+Sfdouble_t arith_exec(Arith_t *ep)
+{
+ register Sfdouble_t num=0,*dp,*sp;
+ register unsigned char *cp = ep->code;
+ register int c,type=0;
+ register char *tp;
+ Sfdouble_t small_stack[SMALL_STACK+1],arg[9];
+ const char *ptr = "";
+ char *lastval=0;
+ int lastsub;
+ Math_f fun;
+ struct lval node;
+ Shell_t *shp = ep->shp;
+ node.shp = shp;
+ node.emode = ep->emode;
+ node.expr = ep->expr;
+ node.elen = ep->elen;
+ node.value = 0;
+ node.nosub = 0;
+ node.ptr = 0;
+ node.eflag = 0;
+ if(level++ >=MAXLEVEL)
+ {
+ arith_error(e_recursive,ep->expr,ep->emode);
+ return(0);
+ }
+ if(ep->staksize < SMALL_STACK)
+ sp = small_stack;
+ else
+ sp = (Sfdouble_t*)stakalloc(ep->staksize*(sizeof(Sfdouble_t)+1));
+ tp = (char*)(sp+ep->staksize);
+ tp--,sp--;
+ while(c = *cp++)
+ {
+ if(c&T_NOFLOAT)
+ {
+ if(type==1 || ((c&T_BINARY) && (c&T_OP)!=A_MOD && tp[-1]==1))
+ arith_error(e_incompatible,ep->expr,ep->emode);
+ }
+ switch(c&T_OP)
+ {
+ case A_JMP: case A_JMPZ: case A_JMPNZ:
+ c &= T_OP;
+ cp = roundptr(ep,cp,short);
+ if((c==A_JMPZ && num) || (c==A_JMPNZ &&!num))
+ cp += sizeof(short);
+ else
+ cp = (unsigned char*)ep + *((short*)cp);
+ continue;
+ case A_NOTNOT:
+ num = (num!=0);
+ type=0;
+ break;
+ case A_PLUSPLUS:
+ node.nosub = -1;
+ (*ep->fun)(&ptr,&node,ASSIGN,num+1);
+ break;
+ case A_MINUSMINUS:
+ node.nosub = -1;
+ (*ep->fun)(&ptr,&node,ASSIGN,num-1);
+ break;
+ case A_INCR:
+ num = num+1;
+ node.nosub = -1;
+ num = (*ep->fun)(&ptr,&node,ASSIGN,num);
+ break;
+ case A_DECR:
+ num = num-1;
+ node.nosub = -1;
+ num = (*ep->fun)(&ptr,&node,ASSIGN,num);
+ break;
+ case A_SWAP:
+ num = sp[-1];
+ sp[-1] = *sp;
+ type = tp[-1];
+ tp[-1] = *tp;
+ break;
+ case A_POP:
+ sp--;
+ continue;
+ case A_ASSIGNOP1:
+ node.emode |= ARITH_ASSIGNOP;
+ case A_PUSHV:
+ cp = roundptr(ep,cp,Sfdouble_t*);
+ dp = *((Sfdouble_t**)cp);
+ cp += sizeof(Sfdouble_t*);
+ c = *(short*)cp;
+ cp += sizeof(short);
+ lastval = node.value = (char*)dp;
+ if(node.flag = c)
+ lastval = 0;
+ node.isfloat=0;
+ node.level = level;
+ node.nosub = 0;
+ num = (*ep->fun)(&ptr,&node,VALUE,num);
+ if(node.emode&ARITH_ASSIGNOP)
+ {
+ lastsub = node.nosub;
+ node.nosub = 0;
+ node.emode &= ~ARITH_ASSIGNOP;
+ }
+ if(node.value != (char*)dp)
+ arith_error(node.value,ptr,ep->emode);
+ *++sp = num;
+ type = node.isfloat;
+ if(num > LDBL_ULLONG_MAX || num < LDBL_LLONG_MIN)
+ type = 1;
+ else
+ {
+ Sfdouble_t d=num;
+ if(num > LDBL_LLONG_MAX && num <= LDBL_ULLONG_MAX)
+ {
+ type = 2;
+ d -= LDBL_LLONG_MAX;
+ }
+ if((Sflong_t)d!=d)
+ type = 1;
+ }
+ *++tp = type;
+ c = 0;
+ break;
+ case A_ENUM:
+ node.eflag = 1;
+ continue;
+ case A_ASSIGNOP:
+ node.nosub = lastsub;
+ case A_STORE:
+ cp = roundptr(ep,cp,Sfdouble_t*);
+ dp = *((Sfdouble_t**)cp);
+ cp += sizeof(Sfdouble_t*);
+ c = *(short*)cp;
+ if(c<0)
+ c = 0;
+ cp += sizeof(short);
+ node.value = (char*)dp;
+ node.flag = c;
+ if(lastval)
+ node.eflag = 1;
+ node.ptr = 0;
+ num = (*ep->fun)(&ptr,&node,ASSIGN,num);
+ if(lastval && node.ptr)
+ {
+ Sfdouble_t r;
+ node.flag = 0;
+ node.value = lastval;
+ r = (*ep->fun)(&ptr,&node,VALUE,num);
+ if(r!=num)
+ {
+ node.flag=c;
+ node.value = (char*)dp;
+ num = (*ep->fun)(&ptr,&node,ASSIGN,r);
+ }
+
+ }
+ lastval = 0;
+ c=0;
+ break;
+ case A_PUSHF:
+ cp = roundptr(ep,cp,Math_f);
+ *++sp = (Sfdouble_t)(cp-ep->code);
+ cp += sizeof(Math_f);
+ *++tp = *cp++;
+ continue;
+ case A_PUSHN:
+ cp = roundptr(ep,cp,Sfdouble_t);
+ num = *((Sfdouble_t*)cp);
+ cp += sizeof(Sfdouble_t);
+ *++sp = num;
+ *++tp = type = *cp++;
+ break;
+ case A_NOT:
+ type=0;
+ num = !num;
+ break;
+ case A_UMINUS:
+ num = -num;
+ break;
+ case A_TILDE:
+ num = ~((Sflong_t)(num));
+ break;
+ case A_PLUS:
+ num += sp[-1];
+ break;
+ case A_MINUS:
+ num = sp[-1] - num;
+ break;
+ case A_TIMES:
+ num *= sp[-1];
+ break;
+ case A_POW:
+ num = pow(sp[-1],num);
+ break;
+ case A_MOD:
+ if(!(Sflong_t)num)
+ arith_error(e_divzero,ep->expr,ep->emode);
+ if(type==2 || tp[-1]==2)
+ num = U2F((Sfulong_t)(sp[-1]) % (Sfulong_t)(num));
+ else
+ num = (Sflong_t)(sp[-1]) % (Sflong_t)(num);
+ break;
+ case A_DIV:
+ if(type==1 || tp[-1]==1)
+ {
+ num = sp[-1]/num;
+ type = 1;
+ }
+ else if((Sfulong_t)(num)==0)
+ arith_error(e_divzero,ep->expr,ep->emode);
+ else if(type==2 || tp[-1]==2)
+ num = U2F((Sfulong_t)(sp[-1]) / (Sfulong_t)(num));
+ else
+ num = (Sflong_t)(sp[-1]) / (Sflong_t)(num);
+ break;
+ case A_LSHIFT:
+ if(tp[-1]==2)
+ num = U2F((Sfulong_t)(sp[-1]) << (long)(num));
+ else
+ num = (Sflong_t)(sp[-1]) << (long)(num);
+ break;
+ case A_RSHIFT:
+ if(tp[-1]==2)
+ num = U2F((Sfulong_t)(sp[-1]) >> (long)(num));
+ else
+ num = (Sflong_t)(sp[-1]) >> (long)(num);
+ break;
+ case A_XOR:
+ if(type==2 || tp[-1]==2)
+ num = U2F((Sfulong_t)(sp[-1]) ^ (Sfulong_t)(num));
+ else
+ num = (Sflong_t)(sp[-1]) ^ (Sflong_t)(num);
+ break;
+ case A_OR:
+ if(type==2 || tp[-1]==2)
+ num = U2F((Sfulong_t)(sp[-1]) | (Sfulong_t)(num));
+ else
+ num = (Sflong_t)(sp[-1]) | (Sflong_t)(num);
+ break;
+ case A_AND:
+ if(type==2 || tp[-1]==2)
+ num = U2F((Sfulong_t)(sp[-1]) & (Sfulong_t)(num));
+ else
+ num = (Sflong_t)(sp[-1]) & (Sflong_t)(num);
+ break;
+ case A_EQ:
+ num = (sp[-1]==num);
+ type=0;
+ break;
+ case A_NEQ:
+ num = (sp[-1]!=num);
+ type=0;
+ break;
+ case A_LE:
+ num = (sp[-1]<=num);
+ type=0;
+ break;
+ case A_GE:
+ num = (sp[-1]>=num);
+ type=0;
+ break;
+ case A_GT:
+ num = (sp[-1]>num);
+ type=0;
+ break;
+ case A_LT:
+ num = (sp[-1]<num);
+ type=0;
+ break;
+ case A_CALL1F:
+ sp--,tp--;
+ fun = *((Math_f*)(ep->code+(int)(*sp)));
+ type = *tp;
+ if(c&T_BINARY)
+ {
+ c &= ~T_BINARY;
+ arg[0] = num;
+ arg[1] = 0;
+ num = sh_mathfun(shp,(void*)fun,1,arg);
+ break;
+ }
+ num = (*((Math_1f_f)fun))(num);
+ break;
+ case A_CALL1I:
+ sp--,tp--;
+ fun = *((Math_f*)(ep->code+(int)(*sp)));
+ type = *tp;
+ num = (*((Math_1i_f)fun))(num);
+ break;
+ case A_CALL2F:
+ sp-=2,tp-=2;
+ fun = *((Math_f*)(ep->code+(int)(*sp)));
+ type = *tp;
+ if(c&T_BINARY)
+ {
+ c &= ~T_BINARY;
+ arg[0] = sp[1];
+ arg[1] = num;
+ arg[2] = 0;
+ num = sh_mathfun(shp,(void*)fun,2,arg);
+ break;
+ }
+ if(c&T_NOFLOAT)
+ num = (*((Math_2f_i)fun))(sp[1],(int)num);
+ else
+ num = (*((Math_2f_f)fun))(sp[1],num);
+ break;
+ case A_CALL2I:
+ sp-=2,tp-=2;
+ fun = *((Math_f*)(ep->code+(int)(*sp)));
+ type = *tp;
+ num = (*((Math_2i_f)fun))(sp[1],num);
+ break;
+ case A_CALL3F:
+ sp-=3,tp-=3;
+ fun = *((Math_f*)(ep->code+(int)(*sp)));
+ type = *tp;
+ if(c&T_BINARY)
+ {
+ c &= ~T_BINARY;
+ arg[0] = sp[1];
+ arg[1] = sp[2];
+ arg[2] = num;
+ arg[3] = 0;
+ num = sh_mathfun(shp,(void*)fun,3,arg);
+ break;
+ }
+ num = (*((Math_3f_f)fun))(sp[1],sp[2],num);
+ break;
+ }
+ if(c)
+ lastval = 0;
+ if(c&T_BINARY)
+ {
+ node.ptr = 0;
+ sp--,tp--;
+ type |= (*tp!=0);
+ }
+ *sp = num;
+ *tp = type;
+ }
+ if(level>0)
+ level--;
+ return(num);
+}
+
+/*
+ * This returns operator tokens or A_REG or A_NUM
+ */
+static int gettok(register struct vars *vp)
+{
+ register int c,op;
+ vp->errchr = vp->nextchr;
+ while(1)
+ {
+ c = getchr(vp);
+ switch(op=getop(c))
+ {
+ case 0:
+ vp->errchr = vp->nextchr;
+ continue;
+ case A_EOF:
+ vp->nextchr--;
+ break;
+ case A_COMMA:
+ if(vp->shp->decomma && (c=peekchr(vp))>='0' && c<='9')
+ {
+ op = A_DIG;
+ goto keep;
+ }
+ break;
+ case A_DOT:
+ if((c=peekchr(vp))>='0' && c<='9')
+ op = A_DIG;
+ else
+ op = A_REG;
+ /*FALL THRU*/
+ case A_DIG: case A_REG: case A_LIT:
+ keep:
+ ungetchr(vp);
+ break;
+ case A_QUEST:
+ if(peekchr(vp)==':')
+ {
+ getchr(vp);
+ op = A_QCOLON;
+ }
+ break;
+ case A_LT: case A_GT:
+ if(peekchr(vp)==c)
+ {
+ getchr(vp);
+ op -= 2;
+ break;
+ }
+ /* FALL THRU */
+ case A_NOT: case A_COLON:
+ c = '=';
+ /* FALL THRU */
+ case A_ASSIGN:
+ case A_TIMES:
+ case A_PLUS: case A_MINUS:
+ case A_OR: case A_AND:
+ if(peekchr(vp)==c)
+ {
+ getchr(vp);
+ op--;
+ }
+ }
+ return(op);
+ }
+}
+
+/*
+ * evaluate a subexpression with precedence
+ */
+
+static int expr(register struct vars *vp,register int precedence)
+{
+ register int c, op;
+ int invalid,wasop=0;
+ struct lval lvalue,assignop;
+ const char *pos;
+ Sfdouble_t d;
+
+ lvalue.value = 0;
+ lvalue.nargs = 0;
+ lvalue.fun = 0;
+ lvalue.shp = vp->shp;
+again:
+ op = gettok(vp);
+ c = 2*MAXPREC+1;
+ switch(op)
+ {
+ case A_PLUS:
+ goto again;
+ case A_EOF:
+ if(precedence>2)
+ ERROR(vp,e_moretokens);
+ return(1);
+ case A_MINUS:
+ op = A_UMINUS;
+ goto common;
+ case A_NOT:
+ goto common;
+ case A_MINUSMINUS:
+ c = A_LVALUE;
+ op = A_DECR|T_NOFLOAT;
+ goto common;
+ case A_PLUSPLUS:
+ c = A_LVALUE;
+ op = A_INCR|T_NOFLOAT;
+ /* FALL THRU */
+ case A_TILDE:
+ op |= T_NOFLOAT;
+ common:
+ if(!expr(vp,c))
+ return(0);
+ stakputc(op);
+ break;
+ default:
+ vp->nextchr = vp->errchr;
+ wasop = 1;
+ }
+ invalid = wasop;
+ while(1)
+ {
+ assignop.value = 0;
+ op = gettok(vp);
+ if(op==A_DIG || op==A_REG || op==A_LIT)
+ {
+ if(!wasop)
+ ERROR(vp,e_synbad);
+ goto number;
+ }
+ if(wasop++ && op!=A_LPAR)
+ ERROR(vp,e_synbad);
+ /* check for assignment operation */
+ if(peekchr(vp)== '=' && !(strval_precedence[op]&NOASSIGN))
+ {
+ if((!lvalue.value || precedence > 3))
+ ERROR(vp,e_notlvalue);
+ if(precedence==3)
+ precedence = 2;
+ assignop = lvalue;
+ getchr(vp);
+ c = 3;
+ }
+ else
+ {
+ c = (strval_precedence[op]&PRECMASK);
+ if(c==MAXPREC || op==A_POW)
+ c++;
+ c *= 2;
+ }
+ /* from here on c is the new precedence level */
+ if(lvalue.value && (op!=A_ASSIGN))
+ {
+ if(vp->staksize++>=vp->stakmaxsize)
+ vp->stakmaxsize = vp->staksize;
+ if(op==A_EQ || op==A_NEQ)
+ stakputc(A_ENUM);
+ stakputc(assignop.value?A_ASSIGNOP1:A_PUSHV);
+ stakpush(vp,lvalue.value,char*);
+ if(lvalue.flag<0)
+ lvalue.flag = 0;
+ stakpush(vp,lvalue.flag,short);
+ if(vp->nextchr==0)
+ ERROR(vp,e_badnum);
+ if(!(strval_precedence[op]&SEQPOINT))
+ lvalue.value = 0;
+ invalid = 0;
+ }
+ else if(precedence==A_LVALUE)
+ ERROR(vp,e_notlvalue);
+ if(invalid && op>A_ASSIGN)
+ ERROR(vp,e_synbad);
+ if(precedence >= c)
+ goto done;
+ if(strval_precedence[op]&RASSOC)
+ c--;
+ if((c < (2*MAXPREC+1)) && !(strval_precedence[op]&SEQPOINT))
+ {
+ wasop = 0;
+ if(!expr(vp,c))
+ return(0);
+ }
+ switch(op)
+ {
+ case A_RPAR:
+ if(!vp->paren)
+ ERROR(vp,e_paren);
+ if(invalid)
+ ERROR(vp,e_synbad);
+ goto done;
+
+ case A_COMMA:
+ wasop = 0;
+ if(vp->infun)
+ vp->infun++;
+ else
+ {
+ stakputc(A_POP);
+ vp->staksize--;
+ }
+ if(!expr(vp,c))
+ {
+ stakseek(staktell()-1);
+ return(0);
+ }
+ lvalue.value = 0;
+ break;
+
+ case A_LPAR:
+ {
+ int infun = vp->infun;
+ int userfun=0;
+ Sfdouble_t (*fun)(Sfdouble_t,...);
+ int nargs = lvalue.nargs;
+ if(nargs<0)
+ nargs = -nargs;
+ fun = lvalue.fun;
+ lvalue.fun = 0;
+ if(fun)
+ {
+ if(vp->staksize++>=vp->stakmaxsize)
+ vp->stakmaxsize = vp->staksize;
+ vp->infun=1;
+ if((int)lvalue.nargs<0)
+ userfun = T_BINARY;
+ else if((int)lvalue.nargs&040)
+ userfun = T_NOFLOAT;
+ stakputc(A_PUSHF);
+ stakpush(vp,fun,Math_f);
+ stakputc(1);
+ }
+ else
+ vp->infun = 0;
+ if(!invalid)
+ ERROR(vp,e_synbad);
+ vp->paren++;
+ if(!expr(vp,1))
+ return(0);
+ vp->paren--;
+ if(fun)
+ {
+ int x= (nargs&010)?2:-1;
+ nargs &= 7;
+ if(vp->infun != nargs)
+ ERROR(vp,e_argcount);
+ if((vp->staksize+=nargs)>=vp->stakmaxsize)
+ vp->stakmaxsize = vp->staksize+nargs;
+ stakputc(A_CALL1F+userfun+nargs+x);
+ vp->staksize -= nargs;
+ }
+ vp->infun = infun;
+ if (gettok(vp) != A_RPAR)
+ ERROR(vp,e_paren);
+ wasop = 0;
+ break;
+ }
+
+ case A_PLUSPLUS:
+ case A_MINUSMINUS:
+ wasop=0;
+ op |= T_NOFLOAT;
+ case A_ASSIGN:
+ if(!lvalue.value)
+ ERROR(vp,e_notlvalue);
+ if(op==A_ASSIGN)
+ {
+ stakputc(A_STORE);
+ stakpush(vp,lvalue.value,char*);
+ stakpush(vp,lvalue.flag,short);
+ vp->staksize--;
+ }
+ else
+ stakputc(op);
+ lvalue.value = 0;
+ break;
+
+ case A_QUEST:
+ {
+ int offset1,offset2;
+ stakputc(A_JMPZ);
+ offset1 = stakpush(vp,0,short);
+ stakputc(A_POP);
+ if(!expr(vp,1))
+ return(0);
+ if(gettok(vp)!=A_COLON)
+ ERROR(vp,e_questcolon);
+ stakputc(A_JMP);
+ offset2 = stakpush(vp,0,short);
+ *((short*)stakptr(offset1)) = staktell();
+ stakputc(A_POP);
+ if(!expr(vp,3))
+ return(0);
+ *((short*)stakptr(offset2)) = staktell();
+ lvalue.value = 0;
+ wasop = 0;
+ break;
+ }
+
+ case A_COLON:
+ ERROR(vp,e_badcolon);
+ break;
+
+ case A_QCOLON:
+ case A_ANDAND:
+ case A_OROR:
+ {
+ int offset;
+ if(op==A_ANDAND)
+ op = A_JMPZ;
+ else
+ op = A_JMPNZ;
+ stakputc(op);
+ offset = stakpush(vp,0,short);
+ stakputc(A_POP);
+ if(!expr(vp,c))
+ return(0);
+ *((short*)stakptr(offset)) = staktell();
+ if(op!=A_QCOLON)
+ stakputc(A_NOTNOT);
+ lvalue.value = 0;
+ wasop=0;
+ break;
+ }
+ case A_AND: case A_OR: case A_XOR: case A_LSHIFT:
+ case A_RSHIFT: case A_MOD:
+ op |= T_NOFLOAT;
+ /* FALL THRU */
+ case A_PLUS: case A_MINUS: case A_TIMES: case A_DIV:
+ case A_EQ: case A_NEQ: case A_LT: case A_LE:
+ case A_GT: case A_GE: case A_POW:
+ stakputc(op|T_BINARY);
+ vp->staksize--;
+ break;
+ case A_NOT: case A_TILDE:
+ default:
+ ERROR(vp,e_synbad);
+ number:
+ wasop = 0;
+ if(*vp->nextchr=='L' && vp->nextchr[1]=='\'')
+ {
+ vp->nextchr++;
+ op = A_LIT;
+ }
+ pos = vp->nextchr;
+ lvalue.isfloat = 0;
+ lvalue.expr = vp->expr;
+ lvalue.emode = vp->emode;
+ if(op==A_LIT)
+ {
+ /* character constants */
+ if(pos[1]=='\\' && pos[2]=='\'' && pos[3]!='\'')
+ {
+ d = '\\';
+ vp->nextchr +=2;
+ }
+ else
+ d = chresc(pos+1,(char**)&vp->nextchr);
+ /* posix allows the trailing ' to be optional */
+ if(*vp->nextchr=='\'')
+ vp->nextchr++;
+ }
+ else
+ d = (*vp->convert)(&vp->nextchr, &lvalue, LOOKUP, 0);
+ if (vp->nextchr == pos)
+ {
+ if(vp->errmsg.value = lvalue.value)
+ vp->errstr = pos;
+ ERROR(vp,op==A_LIT?e_charconst:e_synbad);
+ }
+ if(op==A_DIG || op==A_LIT)
+ {
+ stakputc(A_PUSHN);
+ if(vp->staksize++>=vp->stakmaxsize)
+ vp->stakmaxsize = vp->staksize;
+ stakpush(vp,d,Sfdouble_t);
+ stakputc(lvalue.isfloat);
+ }
+
+ /* check for function call */
+ if(lvalue.fun)
+ continue;
+ break;
+ }
+ invalid = 0;
+ if(assignop.value)
+ {
+ if(vp->staksize++>=vp->stakmaxsize)
+ vp->stakmaxsize = vp->staksize;
+ if(assignop.flag<0)
+ assignop.flag = 0;
+ stakputc(c&1?A_ASSIGNOP:A_STORE);
+ stakpush(vp,assignop.value,char*);
+ stakpush(vp,assignop.flag,short);
+ }
+ }
+ done:
+ vp->nextchr = vp->errchr;
+ return(1);
+}
+
+Arith_t *arith_compile(Shell_t *shp,const char *string,char **last,Sfdouble_t(*fun)(const char**,struct lval*,int,Sfdouble_t),int emode)
+{
+ struct vars cur;
+ register Arith_t *ep;
+ int offset;
+ memset((void*)&cur,0,sizeof(cur));
+ cur.shp = shp;
+ cur.expr = cur.nextchr = string;
+ cur.convert = fun;
+ cur.emode = emode;
+ cur.errmsg.value = 0;
+ cur.errmsg.emode = emode;
+ stakseek(sizeof(Arith_t));
+ if(!expr(&cur,0) && cur.errmsg.value)
+ {
+ if(cur.errstr)
+ string = cur.errstr;
+ if((*fun)( &string , &cur.errmsg, MESSAGE, 0) < 0)
+ {
+ stakseek(0);
+ *last = (char*)Empty;
+ return(0);
+ }
+ cur.nextchr = cur.errchr;
+ }
+ stakputc(0);
+ offset = staktell();
+ ep = (Arith_t*)stakfreeze(0);
+ ep->shp = shp;
+ ep->expr = string;
+ ep->elen = strlen(string);
+ ep->code = (unsigned char*)(ep+1);
+ ep->fun = fun;
+ ep->emode = emode;
+ ep->size = offset - sizeof(Arith_t);
+ ep->staksize = cur.stakmaxsize+1;
+ if(last)
+ *last = (char*)(cur.nextchr);
+ return(ep);
+}
+
+/*
+ * evaluate an integer arithmetic expression in s
+ *
+ * (Sfdouble_t)(*convert)(char** end, struct lval* string, int type, Sfdouble_t value)
+ * 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 MESSAGE then string
+ * points to an error message string
+ *
+ * NOTE: (*convert)() may call strval()
+ */
+
+Sfdouble_t strval(Shell_t *shp,const char *s,char **end,Sfdouble_t(*conv)(const char**,struct lval*,int,Sfdouble_t),int emode)
+{
+ Arith_t *ep;
+ Sfdouble_t d;
+ char *sp=0;
+ int offset;
+ if(offset=staktell())
+ sp = stakfreeze(1);
+ ep = arith_compile(shp,s,end,conv,emode);
+ ep->emode = emode;
+ d = arith_exec(ep);
+ stakset(sp?sp:(char*)ep,offset);
+ return(d);
+}
+
+#if _mem_name__exception
+#undef _mem_name_exception
+#define _mem_name_exception 1
+#undef exception
+#define exception _exception
+#undef matherr
+#endif
+
+#if _mem_name_exception
+
+#undef error
+
+#if _BLD_shell && defined(__EXPORT__)
+#define extern __EXPORT__
+#endif
+
+#ifndef DOMAIN
+#define DOMAIN _DOMAIN
+#endif
+#ifndef OVERFLOW
+#define OVERFLOW _OVERFLOW
+#endif
+#ifndef SING
+#define SING _SING
+#endif
+
+ extern int matherr(struct exception *ep)
+ {
+ const char *message;
+ switch(ep->type)
+ {
+#ifdef DOMAIN
+ case DOMAIN:
+ message = ERROR_dictionary(e_domain);
+ break;
+#endif
+#ifdef OVERFLOW
+ case OVERFLOW:
+ message = ERROR_dictionary(e_overflow);
+ break;
+#endif
+#ifdef SING
+ case SING:
+ message = ERROR_dictionary(e_singularity);
+ break;
+#endif
+ default:
+ return(1);
+ }
+ level=0;
+ errormsg(SH_DICT,ERROR_exit(1),message,ep->name);
+ return(0);
+ }
+
+#undef extern
+
+#endif /* _mem_name_exception */