home *** CD-ROM | disk | FTP | other *** search
- /* Copyright (c) 1992 Regents of the University of California */
-
- #ifndef lint
- static char SCCSid[] = "@(#)calexpr.c 2.10 11/10/92 LBL";
- #endif
-
- /*
- * Compute data values using expression parser
- *
- * 7/1/85 Greg Ward
- *
- * 11/11/85 Made channel input conditional with (INCHAN) compiles.
- *
- * 4/2/86 Added conditional compiles for function definitions (FUNCTION).
- *
- * 1/29/87 Made variables conditional (VARIABLE)
- *
- * 5/19/88 Added constant subexpression elimination (RCONST)
- */
-
- #include <stdio.h>
-
- #include <ctype.h>
-
- #include <errno.h>
-
- #include <math.h>
-
- #include "calcomp.h"
-
- #define MAXLINE 256 /* maximum line length */
-
- #define newnode() (EPNODE *)ecalloc(1, sizeof(EPNODE))
-
- #define isdecimal(c) (isdigit(c) || (c) == '.')
-
- #ifndef atof
- extern double atof();
- #endif
- extern char *fgets(), *savestr();
- extern char *emalloc(), *ecalloc();
- extern EPNODE *curfunc;
- extern double efunc(), evariable();
- static double euminus(), echannel(), eargument(), enumber();
- static double eadd(), esubtr(), emult(), edivi(), epow();
- static double ebotch();
-
- int nextc; /* lookahead character */
-
- double (*eoper[])() = { /* expression operations */
- ebotch,
- #ifdef VARIABLE
- evariable,
- #else
- ebotch,
- #endif
- enumber,
- euminus,
- #ifdef INCHAN
- echannel,
- #else
- ebotch,
- #endif
- #ifdef FUNCTION
- efunc,
- eargument,
- #else
- ebotch,
- ebotch,
- #endif
- ebotch,
- ebotch,
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- emult,
- eadd,
- 0,
- esubtr,
- 0,
- edivi,
- 0,0,0,0,0,0,0,0,0,0,
- ebotch,
- 0,0,
- ebotch,
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- epow,
- };
-
- static FILE *infp; /* input file pointer */
- static char *linbuf; /* line buffer */
- static char *infile; /* input file name */
- static int lineno; /* input line number */
- static int linepos; /* position in buffer */
-
-
- EPNODE *
- eparse(expr) /* parse an expression string */
- char *expr;
- {
- EPNODE *ep;
-
- initstr(expr, NULL, 0);
- #if defined(VARIABLE) && defined(FUNCTION)
- curfunc = NULL;
- #endif
- ep = getE1();
- if (nextc != EOF)
- syntax("unexpected character");
- return(ep);
- }
-
-
- double
- eval(expr) /* evaluate an expression string */
- char *expr;
- {
- register EPNODE *ep;
- double rval;
-
- ep = eparse(expr);
- rval = evalue(ep);
- epfree(ep);
- return(rval);
- }
-
-
- epfree(epar) /* free a parse tree */
- register EPNODE *epar;
- {
- register EPNODE *ep, *epn;
-
- switch (epar->type) {
-
- #if defined(VARIABLE) || defined(FUNCTION)
- case VAR:
- varfree(epar->v.ln);
- break;
-
- case SYM:
- freestr(epar->v.name);
- break;
- #endif
-
- case NUM:
- case CHAN:
- case ARG:
- case TICK:
- break;
-
- default:
- for (ep = epar->v.kid; ep != NULL; ep = epn) {
- epn = ep->sibling;
- epfree(ep);
- }
- break;
-
- }
-
- efree((char *)epar);
- }
-
- /* the following used to be a switch */
- #ifdef FUNCTION
- static double
- eargument(ep)
- EPNODE *ep;
- {
- return(argument(ep->v.chan));
- }
- #endif
-
- static double
- enumber(ep)
- EPNODE *ep;
- {
- return(ep->v.num);
- }
-
- static double
- euminus(ep)
- EPNODE *ep;
- {
- register EPNODE *ep1 = ep->v.kid;
-
- return(-evalue(ep1));
- }
-
- #ifdef INCHAN
- static double
- echannel(ep)
- EPNODE *ep;
- {
- return(chanvalue(ep->v.chan));
- }
- #endif
-
- static double
- eadd(ep)
- EPNODE *ep;
- {
- register EPNODE *ep1 = ep->v.kid;
-
- return(evalue(ep1) + evalue(ep1->sibling));
- }
-
- static double
- esubtr(ep)
- EPNODE *ep;
- {
- register EPNODE *ep1 = ep->v.kid;
-
- return(evalue(ep1) - evalue(ep1->sibling));
- }
-
- static double
- emult(ep)
- EPNODE *ep;
- {
- register EPNODE *ep1 = ep->v.kid;
-
- return(evalue(ep1) * evalue(ep1->sibling));
- }
-
- static double
- edivi(ep)
- EPNODE *ep;
- {
- register EPNODE *ep1 = ep->v.kid;
- double d;
-
- d = evalue(ep1->sibling);
- if (d == 0.0) {
- wputs("Division by zero\n");
- errno = ERANGE;
- return(0.0);
- }
- return(evalue(ep1) / d);
- }
-
- static double
- epow(ep)
- EPNODE *ep;
- {
- register EPNODE *ep1 = ep->v.kid;
- double d;
- int lasterrno;
-
- lasterrno = errno;
- errno = 0;
- d = pow(evalue(ep1), evalue(ep1->sibling));
- #ifdef IEEE
- if (!finite(d))
- errno = EDOM;
- #endif
- if (errno) {
- wputs("Illegal power\n");
- return(0.0);
- }
- errno = lasterrno;
- return(d);
- }
-
- static double
- ebotch(ep)
- EPNODE *ep;
- {
- eputs("Bad expression!\n");
- quit(1);
- }
-
-
- EPNODE *
- ekid(ep, n) /* return pointer to a node's nth kid */
- register EPNODE *ep;
- register int n;
- {
-
- for (ep = ep->v.kid; ep != NULL; ep = ep->sibling)
- if (--n < 0)
- break;
-
- return(ep);
- }
-
-
- int
- nekids(ep) /* return # of kids for node ep */
- register EPNODE *ep;
- {
- register int n = 0;
-
- for (ep = ep->v.kid; ep != NULL; ep = ep->sibling)
- n++;
-
- return(n);
- }
-
-
- initfile(fp, fn, ln) /* prepare input file */
- FILE *fp;
- char *fn;
- int ln;
- {
- static char inpbuf[MAXLINE];
-
- infp = fp;
- linbuf = inpbuf;
- infile = fn;
- lineno = ln;
- linepos = 0;
- inpbuf[0] = '\0';
- scan();
- }
-
-
- initstr(s, fn, ln) /* prepare input string */
- char *s;
- char *fn;
- int ln;
- {
- infp = NULL;
- infile = fn;
- lineno = ln;
- linbuf = s;
- linepos = 0;
- scan();
- }
-
-
- getscanpos(fnp, lnp, spp, fpp) /* return current scan position */
- char **fnp;
- int *lnp;
- char **spp;
- FILE **fpp;
- {
- if (fnp != NULL) *fnp = infile;
- if (lnp != NULL) *lnp = lineno;
- if (spp != NULL) *spp = linbuf+linepos;
- if (fpp != NULL) *fpp = infp;
- }
-
-
- int
- scan() /* scan next character, return literal next */
- {
- register int lnext = 0;
-
- do {
- if (linbuf[linepos] == '\0')
- if (infp == NULL || fgets(linbuf, MAXLINE, infp) == NULL)
- nextc = EOF;
- else {
- nextc = linbuf[0];
- lineno++;
- linepos = 1;
- }
- else
- nextc = linbuf[linepos++];
- if (!lnext)
- lnext = nextc;
- if (nextc == '{') {
- scan();
- while (nextc != '}')
- if (nextc == EOF)
- syntax("'}' expected");
- else
- scan();
- scan();
- }
- } while (isspace(nextc));
- return(lnext);
- }
-
-
- char *
- long2ascii(l) /* convert long to ascii */
- long l;
- {
- static char buf[16];
- register char *cp;
- int neg = 0;
-
- if (l == 0)
- return("0");
- if (l < 0) {
- l = -l;
- neg++;
- }
- cp = buf + sizeof(buf);
- *--cp = '\0';
- while (l) {
- *--cp = l % 10 + '0';
- l /= 10;
- }
- if (neg)
- *--cp = '-';
- return(cp);
- }
-
-
- syntax(err) /* report syntax error and quit */
- char *err;
- {
- register int i;
-
- if (infile != NULL || lineno != 0) {
- if (infile != NULL) eputs(infile);
- if (lineno != 0) {
- eputs(infile != NULL ? ", line " : "line ");
- eputs(long2ascii((long)lineno));
- }
- eputs(":\n");
- }
- eputs(linbuf);
- if (linbuf[strlen(linbuf)-1] != '\n')
- eputs("\n");
- for (i = 0; i < linepos-1; i++)
- eputs(linbuf[i] == '\t' ? "\t" : " ");
- eputs("^ ");
- eputs(err);
- eputs("\n");
- quit(1);
- }
-
-
- addekid(ep, ekid) /* add a child to ep */
- register EPNODE *ep;
- EPNODE *ekid;
- {
- if (ep->v.kid == NULL)
- ep->v.kid = ekid;
- else {
- for (ep = ep->v.kid; ep->sibling != NULL; ep = ep->sibling)
- ;
- ep->sibling = ekid;
- }
- ekid->sibling = NULL;
- }
-
-
- #if defined(VARIABLE) || defined(FUNCTION)
- char *
- getname() /* scan an identifier */
- {
- static char str[MAXWORD+1];
- register int i, lnext;
-
- lnext = nextc;
- for (i = 0; i < MAXWORD && isid(lnext); i++, lnext = scan())
- str[i] = lnext;
- str[i] = '\0';
- while (isid(lnext)) /* skip rest of name */
- lnext = scan();
-
- return(str);
- }
- #endif
-
-
- int
- getinum() /* scan a positive integer */
- {
- register int n, lnext;
-
- n = 0;
- lnext = nextc;
- while (isdigit(lnext)) {
- n = n * 10 + lnext - '0';
- lnext = scan();
- }
- return(n);
- }
-
-
- double
- getnum() /* scan a positive float */
- {
- register int i, lnext;
- char str[MAXWORD+1];
-
- i = 0;
- lnext = nextc;
- while (isdigit(lnext) && i < MAXWORD) {
- str[i++] = lnext;
- lnext = scan();
- }
- if (lnext == '.' && i < MAXWORD) {
- str[i++] = lnext;
- lnext = scan();
- while (isdigit(lnext) && i < MAXWORD) {
- str[i++] = lnext;
- lnext = scan();
- }
- }
- if ((lnext == 'e' || lnext == 'E') && i < MAXWORD) {
- str[i++] = lnext;
- lnext = scan();
- if ((lnext == '-' || lnext == '+') && i < MAXWORD) {
- str[i++] = lnext;
- lnext = scan();
- }
- while (isdigit(lnext) && i < MAXWORD) {
- str[i++] = lnext;
- lnext = scan();
- }
- }
- str[i] = '\0';
-
- return(atof(str));
- }
-
-
- EPNODE *
- getE1() /* E1 -> E1 ADDOP E2 */
- /* E2 */
- {
- register EPNODE *ep1, *ep2;
-
- ep1 = getE2();
- while (nextc == '+' || nextc == '-') {
- ep2 = newnode();
- ep2->type = nextc;
- scan();
- addekid(ep2, ep1);
- addekid(ep2, getE2());
- #ifdef RCONST
- if (ep1->type == NUM && ep1->sibling->type == NUM)
- ep2 = rconst(ep2);
- #endif
- ep1 = ep2;
- }
- return(ep1);
- }
-
-
- EPNODE *
- getE2() /* E2 -> E2 MULOP E3 */
- /* E3 */
- {
- register EPNODE *ep1, *ep2;
-
- ep1 = getE3();
- while (nextc == '*' || nextc == '/') {
- ep2 = newnode();
- ep2->type = nextc;
- scan();
- addekid(ep2, ep1);
- addekid(ep2, getE3());
- #ifdef RCONST
- if (ep1->type == NUM && ep1->sibling->type == NUM)
- ep2 = rconst(ep2);
- #endif
- ep1 = ep2;
- }
- return(ep1);
- }
-
-
- EPNODE *
- getE3() /* E3 -> E4 ^ E3 */
- /* E4 */
- {
- register EPNODE *ep1, *ep2;
-
- ep1 = getE4();
- if (nextc == '^') {
- ep2 = newnode();
- ep2->type = nextc;
- scan();
- addekid(ep2, ep1);
- addekid(ep2, getE3());
- #ifdef RCONST
- if (ep1->type == NUM && ep1->sibling->type == NUM)
- ep2 = rconst(ep2);
- #endif
- return(ep2);
- }
- return(ep1);
- }
-
-
- EPNODE *
- getE4() /* E4 -> ADDOP E5 */
- /* E5 */
- {
- register EPNODE *ep1, *ep2;
-
- if (nextc == '-') {
- scan();
- ep2 = getE5();
- if (ep2->type == NUM) {
- ep2->v.num = -ep2->v.num;
- return(ep2);
- }
- if (ep2->type == UMINUS) { /* don't generate -(-E5) */
- efree((char *)ep2);
- return(ep2->v.kid);
- }
- ep1 = newnode();
- ep1->type = UMINUS;
- addekid(ep1, ep2);
- return(ep1);
- }
- if (nextc == '+')
- scan();
- return(getE5());
- }
-
-
- EPNODE *
- getE5() /* E5 -> (E1) */
- /* VAR */
- /* NUM */
- /* $N */
- /* FUNC(E1,..) */
- /* ARG */
- {
- int i;
- char *nam;
- register EPNODE *ep1, *ep2;
-
- if (nextc == '(') {
- scan();
- ep1 = getE1();
- if (nextc != ')')
- syntax("')' expected");
- scan();
- return(ep1);
- }
-
- #ifdef INCHAN
- if (nextc == '$') {
- scan();
- ep1 = newnode();
- ep1->type = CHAN;
- ep1->v.chan = getinum();
- return(ep1);
- }
- #endif
-
- #if defined(VARIABLE) || defined(FUNCTION)
- if (isalpha(nextc) || nextc == CNTXMARK) {
- nam = getname();
- #if defined(VARIABLE) && defined(FUNCTION)
- ep1 = NULL;
- if (curfunc != NULL)
- for (i = 1, ep2 = curfunc->v.kid->sibling;
- ep2 != NULL; i++, ep2 = ep2->sibling)
- if (!strcmp(ep2->v.name, nam)) {
- ep1 = newnode();
- ep1->type = ARG;
- ep1->v.chan = i;
- break;
- }
- if (ep1 == NULL)
- #endif
- {
- ep1 = newnode();
- ep1->type = VAR;
- ep1->v.ln = varinsert(nam);
- }
- #ifdef FUNCTION
- if (nextc == '(') {
- ep2 = newnode();
- ep2->type = FUNC;
- addekid(ep2, ep1);
- ep1 = ep2;
- do {
- scan();
- addekid(ep1, getE1());
- } while (nextc == ',');
- if (nextc != ')')
- syntax("')' expected");
- scan();
- }
- #ifndef VARIABLE
- else
- syntax("'(' expected");
- #endif
- #endif
- #ifdef RCONST
- if (isconstvar(ep1))
- ep1 = rconst(ep1);
- #endif
- return(ep1);
- }
- #endif
-
- if (isdecimal(nextc)) {
- ep1 = newnode();
- ep1->type = NUM;
- ep1->v.num = getnum();
- return(ep1);
- }
- syntax("unexpected character");
- }
-
-
- #ifdef RCONST
- EPNODE *
- rconst(epar) /* reduce a constant expression */
- register EPNODE *epar;
- {
- register EPNODE *ep;
-
- ep = newnode();
- ep->type = NUM;
- errno = 0;
- ep->v.num = evalue(epar);
- if (errno)
- syntax("bad constant expression");
- epfree(epar);
-
- return(ep);
- }
-
-
- isconstvar(ep) /* is ep linked to a constant expression? */
- register EPNODE *ep;
- {
- #ifdef VARIABLE
- register EPNODE *ep1;
- #ifdef FUNCTION
-
- if (ep->type == FUNC) {
- if (!isconstfun(ep->v.kid))
- return(0);
- for (ep1 = ep->v.kid->sibling; ep1 != NULL; ep1 = ep1->sibling)
- if (ep1->type != NUM && !isconstfun(ep1))
- return(0);
- return(1);
- }
- #endif
- if (ep->type != VAR)
- return(0);
- ep1 = ep->v.ln->def;
- if (ep1 == NULL || ep1->type != ':')
- return(0);
- #ifdef FUNCTION
- if (ep1->v.kid->type != SYM)
- return(0);
- #endif
- return(1);
- #else
- return(ep->type == FUNC);
- #endif
- }
-
-
- #if defined(FUNCTION) && defined(VARIABLE)
- isconstfun(ep) /* is ep linked to a constant function? */
- register EPNODE *ep;
- {
- register EPNODE *dp;
- register LIBR *lp;
-
- if (ep->type != VAR)
- return(0);
- if ((dp = ep->v.ln->def) != NULL && dp->v.kid->type == FUNC)
- return(dp->type == ':');
- if ((lp = ep->v.ln->lib) != NULL)
- return(lp->atyp == ':');
- return(0);
- }
- #endif
- #endif
-