home *** CD-ROM | disk | FTP | other *** search
- /*
- * $Id: math.c,v 2.11 1996/10/15 20:16:35 hzoli Exp $
- *
- * math.c - mathematical expression evaluation
- *
- * This file is part of zsh, the Z shell.
- *
- * Copyright (c) 1992-1996 Paul Falstad
- * All rights reserved.
- *
- * Permission is hereby granted, without written agreement and without
- * license or royalty fees, to use, copy, modify, and distribute this
- * software and to distribute modified versions of this software for any
- * purpose, provided that the above copyright notice and the following
- * two paragraphs appear in all copies of this software.
- *
- * In no event shall Paul Falstad or the Zsh Development Group be liable
- * to any party for direct, indirect, special, incidental, or consequential
- * damages arising out of the use of this software and its documentation,
- * even if Paul Falstad and the Zsh Development Group have been advised of
- * the possibility of such damage.
- *
- * Paul Falstad and the Zsh Development Group specifically disclaim any
- * warranties, including, but not limited to, the implied warranties of
- * merchantability and fitness for a particular purpose. The software
- * provided hereunder is on an "as is" basis, and Paul Falstad and the
- * Zsh Development Group have no obligation to provide maintenance,
- * support, updates, enhancements, or modifications.
- *
- */
-
- #include "zsh.h"
-
- static char *ptr;
-
- static long yyval;
- static LV yylval;
-
- static int mlevel = 0;
-
- /* != 0 means recognize unary plus, minus, etc. */
-
- static int unary = 1;
-
- /* LR = left-to-right associativity *
- * RL = right-to-left associativity *
- * BOO = short-circuiting boolean */
-
- #define LR 0
- #define RL 1
- #define BOOL 2
-
- #define M_INPAR 0
- #define M_OUTPAR 1
- #define NOT 2
- #define COMP 3
- #define POSTPLUS 4
- #define POSTMINUS 5
- #define UPLUS 6
- #define UMINUS 7
- #define AND 8
- #define XOR 9
- #define OR 10
- #define MUL 11
- #define DIV 12
- #define MOD 13
- #define PLUS 14
- #define MINUS 15
- #define SHLEFT 16
- #define SHRIGHT 17
- #define LES 18
- #define LEQ 19
- #define GRE 20
- #define GEQ 21
- #define DEQ 22
- #define NEQ 23
- #define DAND 24
- #define DOR 25
- #define DXOR 26
- #define QUEST 27
- #define COLON 28
- #define EQ 29
- #define PLUSEQ 30
- #define MINUSEQ 31
- #define MULEQ 32
- #define DIVEQ 33
- #define MODEQ 34
- #define ANDEQ 35
- #define XOREQ 36
- #define OREQ 37
- #define SHLEFTEQ 38
- #define SHRIGHTEQ 39
- #define DANDEQ 40
- #define DOREQ 41
- #define DXOREQ 42
- #define COMMA 43
- #define EOI 44
- #define PREPLUS 45
- #define PREMINUS 46
- #define NUM 47
- #define ID 48
- #define POWER 49
- #define CID 50
- #define POWEREQ 51
- #define TOKCOUNT 52
-
- /* precedences */
-
- static int prec[TOKCOUNT] =
- {
- 1, 137, 2, 2, 2,
- 2, 2, 2, 4, 5,
- 6, 8, 8, 8, 9,
- 9, 3, 3, 10, 10,
- 10, 10, 11, 11, 12,
- 13, 13, 14, 14, 15,
- 15, 15, 15, 15, 15,
- 15, 15, 15, 15, 15,
- 15, 15, 15, 16, 200,
- 2, 2, 0, 0, 7,
- 0, 15
- };
-
- #define TOPPREC 16
- #define ARGPREC (TOPPREC-1)
-
- static int type[TOKCOUNT] =
- {
- LR, LR, RL, RL, RL,
- RL, RL, RL, LR, LR,
- LR, LR, LR, LR, LR,
- LR, LR, LR, LR, LR,
- LR, LR, LR, LR, BOOL,
- BOOL, LR, RL, RL, RL,
- RL, RL, RL, RL, RL,
- RL, RL, RL, RL, RL,
- BOOL, BOOL, RL, RL, RL,
- RL, RL, LR, LR, RL,
- LR, RL
- };
-
- #define LVCOUNT 32
-
- /* list of lvalues (variables) */
-
- static int lvc;
- static char **lvals;
-
-
- /**/
- int
- zzlex(void)
- {
- int cct = 0;
-
- for (;; cct = 0)
- switch (*ptr++) {
- case '+':
- if (*ptr == '+' && (unary || !ialnum(*ptr))) {
- ptr++;
- return (unary) ? PREPLUS : POSTPLUS;
- }
- if (*ptr == '=') {
- unary = 1;
- ptr++;
- return PLUSEQ;
- }
- return (unary) ? UPLUS : PLUS;
- case '-':
- if (*ptr == '-' && (unary || !ialnum(*ptr))) {
- ptr++;
- return (unary) ? PREMINUS : POSTMINUS;
- }
- if (*ptr == '=') {
- unary = 1;
- ptr++;
- return MINUSEQ;
- }
- return (unary) ? UMINUS : MINUS;
- case '(':
- unary = 1;
- return M_INPAR;
- case ')':
- return M_OUTPAR;
- case '!':
- if (*ptr == '=') {
- unary = 1;
- ptr++;
- return NEQ;
- }
- return NOT;
- case '~':
- return COMP;
- case '&':
- unary = 1;
- if (*ptr == '&') {
- if (*++ptr == '=') {
- ptr++;
- return DANDEQ;
- }
- return DAND;
- } else if (*ptr == '=') {
- ptr++;
- return ANDEQ;
- }
- return AND;
- case '|':
- unary = 1;
- if (*ptr == '|') {
- if (*++ptr == '=') {
- ptr++;
- return DOREQ;
- }
- return DOR;
- } else if (*ptr == '=') {
- ptr++;
- return OREQ;
- }
- return OR;
- case '^':
- unary = 1;
- if (*ptr == '^') {
- if (*++ptr == '=') {
- ptr++;
- return DXOREQ;
- }
- return DXOR;
- } else if (*ptr == '=') {
- ptr++;
- return XOREQ;
- }
- return XOR;
- case '*':
- unary = 1;
- if (*ptr == '*') {
- if (*++ptr == '=') {
- ptr++;
- return POWEREQ;
- }
- return POWER;
- }
- if (*ptr == '=') {
- ptr++;
- return MULEQ;
- }
- return MUL;
- case '/':
- unary = 1;
- if (*ptr == '=') {
- ptr++;
- return DIVEQ;
- }
- return DIV;
- case '%':
- unary = 1;
- if (*ptr == '=') {
- ptr++;
- return MODEQ;
- }
- return MOD;
- case '<':
- unary = 1;
- if (*ptr == '<') {
- if (*++ptr == '=') {
- ptr++;
- return SHLEFTEQ;
- }
- return SHLEFT;
- } else if (*ptr == '=') {
- ptr++;
- return LEQ;
- }
- return LES;
- case '>':
- unary = 1;
- if (*ptr == '>') {
- if (*++ptr == '=') {
- ptr++;
- return SHRIGHTEQ;
- }
- return SHRIGHT;
- } else if (*ptr == '=') {
- ptr++;
- return GEQ;
- }
- return GRE;
- case '=':
- unary = 1;
- if (*ptr == '=') {
- ptr++;
- return DEQ;
- }
- return EQ;
- case '$':
- unary = 0;
- yyval = mypid;
- return NUM;
- case '?':
- if (unary) {
- yyval = lastval;
- unary = 0;
- return NUM;
- }
- unary = 1;
- return QUEST;
- case ':':
- unary = 1;
- return COLON;
- case ',':
- unary = 1;
- return COMMA;
- case '\0':
- unary = 1;
- ptr--;
- return EOI;
- case '[':
- unary = 0;
- {
- int base = zstrtol(ptr, &ptr, 10);
-
- if (*ptr == ']')
- ptr++;
- yyval = zstrtol(ptr, &ptr, lastbase = base);
- return NUM;
- }
- case ' ':
- case '\t':
- case '\n':
- break;
- case '0':
- if (*ptr == 'x' || *ptr == 'X') {
- unary = 0;
- /* Should we set lastbase here? */
- yyval = zstrtol(++ptr, &ptr, lastbase = 16);
- return NUM;
- }
- /* Fall through! */
- default:
- if (idigit(*--ptr)) {
- unary = 0;
- yyval = zstrtol(ptr, &ptr, 10);
-
- if (*ptr == '#') {
- ptr++;
- yyval = zstrtol(ptr, &ptr, lastbase = yyval);
- }
- return NUM;
- }
- if (*ptr == '#') {
- if (*++ptr == '\\') {
- ptr++;
- yyval = *ptr == Meta ? *++ptr ^ 32 : *ptr;
- ptr++;
- unary = 0;
- return NUM;
- }
- cct = 1;
- }
- if (iident(*ptr)) {
- char *p, q;
-
- p = ptr;
- if (lvc == LVCOUNT) {
- zerr("too many identifiers (complain to author)", NULL, 0);
- return EOI;
- }
- unary = 0;
- while (iident(*++ptr));
- if (*ptr == '[') {
- int l;
- for (ptr++, l = 1; *ptr && l; ptr++) {
- if (*ptr == '[')
- l++;
- if (*ptr == ']')
- l--;
- if (*ptr == '\\' && ptr[1])
- ptr++;
- }
- }
- q = *ptr;
- *ptr = '\0';
- lvals[yylval = lvc++] = ztrdup(p);
- *ptr = q;
- return cct ? CID : ID;
- }
- else if (cct) {
- yyval = poundgetfn(NULL);
- unary = 0;
- return NUM;
- }
- return EOI;
- }
- }
-
- /* the value stack */
-
- #define STACKSZ 100
- int mtok; /* last token */
- int sp = -1; /* stack pointer */
- struct mathvalue {
- LV lval;
- long val;
- };
-
- static struct mathvalue *stack;
-
- static void push _((long val, LV lval));
-
- static void
- push(long val, LV lval)
- {
- if (sp == STACKSZ - 1)
- zerr("stack overflow", NULL, 0);
- else
- sp++;
- stack[sp].val = val;
- stack[sp].lval = lval;
- }
-
-
- /**/
- long
- getcvar(LV s)
- {
- char *t;
-
- if (!(t = getsparam(lvals[s])))
- return 0;
- return STOUC(*t == Meta ? t[1] ^ 32 : *t);
- }
-
-
- /**/
- long
- setvar(LV s, long v)
- {
- if (s == -1 || s >= lvc) {
- zerr("lvalue required", NULL, 0);
- return 0;
- }
- if (noeval)
- return v;
- setiparam(lvals[s], v);
- return v;
- }
-
-
- /**/
- int
- notzero(long a)
- {
- if (a == 0) {
- zerr("division by zero", NULL, 0);
- return 0;
- }
- return 1;
- }
-
- /* macro to pop two values off the value stack */
- #define pop2() { \
- if (sp < 1) { \
- zerr("bad math expression: unbalanced stack", NULL, 0); \
- return; \
- } \
- b = stack[sp--].val; \
- a = stack[sp--].val; \
- }
-
- /* macro to pop three values off the value stack */
- #define pop3() { \
- if (sp < 2) { \
- zerr("bad math expression: unbalanced stack", NULL, 0); \
- return; \
- } \
- c = stack[sp--].val; \
- b = stack[sp--].val; \
- a = stack[sp--].val; \
- }
-
- #define nolval() {stack[sp].lval= -1;}
- #define pushv(X) { push(X,-1); }
- #define pop2lv() { pop2() lv = stack[sp+1].lval; }
- #define set(X) { push(setvar(lv,X),lv); }
-
-
- /**/
- void
- op(int what)
- {
- long a, b, c;
- LV lv;
-
- if (sp < 0) {
- zerr("bad math expression: stack empty", NULL, 0);
- return;
- }
- switch (what) {
- case NOT:
- stack[sp].val = !stack[sp].val;
- nolval();
- break;
- case COMP:
- stack[sp].val = ~stack[sp].val;
- nolval();
- break;
- case POSTPLUS:
- (void)setvar(stack[sp].lval, stack[sp].val + 1);
- break;
- case POSTMINUS:
- (void)setvar(stack[sp].lval, stack[sp].val - 1);
- break;
- case UPLUS:
- nolval();
- break;
- case UMINUS:
- stack[sp].val = -stack[sp].val;
- nolval();
- break;
- case AND:
- pop2();
- pushv(a & b);
- break;
- case XOR:
- pop2();
- pushv(a ^ b);
- break;
- case OR:
- pop2();
- pushv(a | b);
- break;
- case MUL:
- pop2();
- pushv(a * b);
- break;
- case DIV:
- pop2();
- if (notzero(b))
- pushv(a / b);
- break;
- case MOD:
- pop2();
- if (notzero(b))
- pushv(a % b);
- break;
- case PLUS:
- pop2();
- pushv(a + b);
- break;
- case MINUS:
- pop2();
- pushv(a - b);
- break;
- case SHLEFT:
- pop2();
- pushv(a << b);
- break;
- case SHRIGHT:
- pop2();
- pushv(a >> b);
- break;
- case LES:
- pop2();
- pushv((long)(a < b));
- break;
- case LEQ:
- pop2();
- pushv((long)(a <= b));
- break;
- case GRE:
- pop2();
- pushv((long)(a > b));
- break;
- case GEQ:
- pop2();
- pushv((long)(a >= b));
- break;
- case DEQ:
- pop2();
- pushv((long)(a == b));
- break;
- case NEQ:
- pop2();
- pushv((long)(a != b));
- break;
- case DAND:
- pop2();
- pushv((long)(a && b));
- break;
- case DOR:
- pop2();
- pushv((long)(a || b));
- break;
- case DXOR:
- pop2();
- pushv((long)((a && !b) || (!a && b)));
- break;
- case QUEST:
- pop3();
- pushv((a) ? b : c);
- break;
- case COLON:
- break;
- case EQ:
- pop2();
- lv = stack[sp + 1].lval;
- set(b);
- break;
- case PLUSEQ:
- pop2lv();
- set(a + b);
- break;
- case MINUSEQ:
- pop2lv();
- set(a - b);
- break;
- case MULEQ:
- pop2lv();
- set(a * b);
- break;
- case DIVEQ:
- pop2lv();
- if (notzero(b))
- set(a / b);
- break;
- case MODEQ:
- pop2lv();
- if (notzero(b))
- set(a % b);
- break;
- case ANDEQ:
- pop2lv();
- set(a & b);
- break;
- case XOREQ:
- pop2lv();
- set(a ^ b);
- break;
- case OREQ:
- pop2lv();
- set(a | b);
- break;
- case SHLEFTEQ:
- pop2lv();
- set(a << b);
- break;
- case SHRIGHTEQ:
- pop2lv();
- set(a >> b);
- break;
- case DANDEQ:
- pop2lv();
- set((long)(a && b));
- break;
- case DOREQ:
- pop2lv();
- set((long)(a || b));
- break;
- case DXOREQ:
- pop2lv();
- set((long)((a && !b) || (!a && b)));
- break;
- case COMMA:
- pop2();
- pushv(b);
- break;
- case PREPLUS:
- stack[sp].val = setvar(stack[sp].lval,
- stack[sp].val + 1);
- break;
- case PREMINUS:
- stack[sp].val = setvar(stack[sp].lval,
- stack[sp].val - 1);
- break;
- case POWER:
- pop2();
- if (b < 0) {
- zerr("can't handle negative exponents", NULL, 0);
- return;
- }
- for (c = 1; b--; c *= a);
- pushv(c);
- break;
- case POWEREQ:
- pop2lv();
- if (b < 0) {
- zerr("can't handle negative exponents", NULL, 0);
- return;
- }
- for (c = 1; b--; c *= a);
- set(c);
- break;
- default:
- zerr("out of integers", NULL, 0);
- return;
- }
- }
-
-
- /**/
- void
- bop(int tk)
- {
- switch (tk) {
- case DAND:
- case DANDEQ:
- if (!stack[sp].val)
- noeval++;
- break;
- case DOR:
- case DOREQ:
- if (stack[sp].val)
- noeval++;
- break;
- };
- }
-
-
- /**/
- long
- mathevall(char *s, int prek, char **ep)
- {
- int t0;
- int xlastbase, xnoeval, xunary, xlvc;
- char *xptr;
- long xyyval;
- LV xyylval;
- char **xlvals = 0;
- int xsp;
- struct mathvalue *xstack = 0;
- long ret;
-
- xlastbase = xnoeval = xunary = xlvc = xyyval = xyylval = xsp = 0;
- xptr = NULL;
- if (mlevel++) {
- xlastbase = lastbase;
- xnoeval = noeval;
- xunary = unary;
- xlvc = lvc;
- xptr = ptr;
- xyyval = yyval;
- xyylval = yylval;
- xlvals = lvals;
-
- xsp = sp;
- xstack = stack;
- }
- stack = (struct mathvalue *)zalloc(STACKSZ*sizeof(struct mathvalue));
- lastbase = -1;
- lvals = (char **)zcalloc(LVCOUNT*sizeof(char *));
- lvc = 0;
- ptr = s;
- sp = -1;
- unary = 1;
- mathparse(prek);
- *ep = ptr;
- if (sp)
- zerr("bad math expression: unbalanced stack", NULL, 0);
- for (t0 = 0; t0 != lvc; t0++)
- zsfree(lvals[t0]);
-
- ret = stack[0].val;
-
- zfree(lvals, LVCOUNT*sizeof(char *));
- zfree(stack, STACKSZ*sizeof(struct mathvalue));
- if (--mlevel) {
- lastbase = xlastbase;
- noeval = xnoeval;
- unary = xunary;
- lvc = xlvc;
- ptr = xptr;
- yyval = xyyval;
- yylval = xyylval;
- lvals = xlvals;
-
- sp = xsp;
- stack = xstack;
- }
- return ret;
- }
-
-
- /**/
- long
- matheval(char *s)
- {
- char *junk;
- long x;
- int xmtok = mtok;
-
- if (!*s)
- return 0;
- x = mathevall(s, TOPPREC, &junk);
- mtok = xmtok;
- if (*junk)
- zerr("bad math expression: illegal character: %c", NULL, *junk);
- return x;
- }
-
-
- /**/
- long
- mathevalarg(char *s, char **ss)
- {
- long x;
- int xmtok = mtok;
-
- x = mathevall(s, ARGPREC, ss);
- if (mtok == COMMA)
- (*ss)--;
- mtok = xmtok;
- return x;
- }
-
-
- /* operator-precedence parse the string and execute */
-
- /**/
- void
- mathparse(int pc)
- {
- int q, otok, onoeval;
-
- if (errflag)
- return;
- mtok = zzlex();
- while (prec[mtok] <= pc) {
- if (errflag)
- return;
- switch (mtok) {
- case NUM:
- push(yyval, -1);
- break;
- case ID:
- push(getiparam(lvals[yylval]), yylval);
- break;
- case CID:
- push(getcvar(yylval), yylval);
- break;
- case M_INPAR:
- mathparse(TOPPREC);
- if (mtok != M_OUTPAR) {
- if (!errflag)
- zerr("')' expected", NULL, 0);
- return;
- }
- break;
- case QUEST:
- q = stack[sp].val;
-
- if (!q)
- noeval++;
- mathparse(prec[QUEST] - 1);
- if (!q)
- noeval--;
- else
- noeval++;
- mathparse(prec[QUEST]);
- if (q)
- noeval--;
- op(QUEST);
- continue;
- default:
- otok = mtok;
- onoeval = noeval;
- if (type[otok] == BOOL)
- bop(otok);
- mathparse(prec[otok] - (type[otok] != RL));
- noeval = onoeval;
- op(otok);
- continue;
- }
- mtok = zzlex();
- }
- }
-