openenvutils/commandshell/shell/src/math.c
changeset 0 2e3d3ce01487
child 1 0fdb7f6b0309
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/openenvutils/commandshell/shell/src/math.c	Tue Feb 02 10:12:00 2010 +0200
@@ -0,0 +1,1188 @@
+// math.c - mathematical expression evaluation
+//
+// © Portions Copyright (c) Symbian Software Ltd 2007. All rights reserved.
+//
+/*
+ * This file is part of zsh, the Z shell.
+ *
+ * Copyright (c) 1992-1997 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.mdh"
+#include "math.pro"
+
+#include <math.h>
+
+#ifdef __SYMBIAN32__
+#ifdef __WINSCW__
+#pragma warn_possunwant off
+#endif//__WINSCW__
+#endif//__SYMBIAN32__
+
+/* nonzero means we are not evaluating, just parsing */
+ 
+/**/
+int noeval;
+ 
+/* integer zero */
+
+/**/
+mod_export mnumber zero_mnumber;
+
+/* last input base we used */
+
+/**/
+int lastbase;
+ 
+static char *ptr;
+
+static mnumber yyval;
+static char *yylval;
+
+#define MAX_MLEVEL 256
+
+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 *
+ * BOOL = short-circuiting boolean   */
+
+#define LR   0x0000
+#define RL   0x0001
+#define BOOL 0x0002
+
+#define MTYPE(x)  ((x) & 3)
+
+/*
+ * OP_A2    2 arguments
+ * OP_A2IR  2 arguments, return integer
+ * OP_A2IO  2 arguments, must be integer, return integer
+ * OP_E2    2 arguments with assignment
+ * OP_E2IO  2 arguments with assignment, must be integer, return integer
+ * OP_OP    None of the above, but occurs where we are expecting an operator
+ *          rather than an operand.
+ * OP_OPF   Followed by an operator, not an operand.
+ *
+ * OP_A2*, OP_E2*, OP_OP*:
+ *   Occur when we need an operator; the next object must be an operand,
+ *   unless OP_OPF is also supplied.
+ *
+ * Others:
+ *   Occur when we need an operand; the next object must also be an operand,
+ *   unless OP_OPF is also supplied.
+ */
+#define OP_A2   0x0004
+#define OP_A2IR 0x0008
+#define OP_A2IO 0x0010
+#define OP_E2   0x0020
+#define OP_E2IO 0x0040
+#define OP_OP   0x0080
+#define OP_OPF  0x0100
+
+#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 FUNC 52
+#define TOKCOUNT 53
+
+/* 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, 15,  16,
+    16,  16, 16, 16,  16,
+    16,  16, 16, 16,  16,
+    16,  16, 16, 17, 200,
+     2,   2,  0,  0,   7,
+     0,  16, 0
+};
+
+#define TOPPREC 18
+#define ARGPREC 16
+
+static int type[TOKCOUNT] =
+{
+/*  0 */  LR, LR|OP_OP|OP_OPF, RL, RL, RL|OP_OP|OP_OPF,
+/*  5 */  RL|OP_OP|OP_OPF, RL, RL, LR|OP_A2IO, LR|OP_A2IO,
+/* 10 */  LR|OP_A2IO, LR|OP_A2, LR|OP_A2, LR|OP_A2IO, LR|OP_A2,
+/* 15 */  LR|OP_A2, LR|OP_A2IO, LR|OP_A2IO, LR|OP_A2IR, LR|OP_A2IR,
+/* 20 */  LR|OP_A2IR, LR|OP_A2IR, LR|OP_A2IR, LR|OP_A2IR, BOOL|OP_A2IO,
+/* 25 */  BOOL|OP_A2IO, LR|OP_A2IO, RL|OP_OP, RL|OP_OP, RL|OP_E2,
+/* 30 */  RL|OP_E2, RL|OP_E2, RL|OP_E2, RL|OP_E2, RL|OP_E2IO,
+/* 35 */  RL|OP_E2IO, RL|OP_E2IO, RL|OP_E2IO, RL|OP_E2IO, RL|OP_E2IO,
+/* 40 */  BOOL|OP_E2IO, BOOL|OP_E2IO, RL|OP_A2IO, RL|OP_A2, RL|OP_OP,
+/* 45 */  RL, RL, LR|OP_OPF, LR|OP_OPF, RL|OP_A2,
+/* 50 */  LR|OP_OPF, RL|OP_E2, LR|OP_OPF
+};
+
+static int
+lexconstant(void)
+{
+#ifdef USE_LOCALE
+    char *prev_locale;
+#endif
+    char *nptr;
+
+    nptr = ptr;
+    if (*nptr == '-')
+	nptr++;
+
+    if (*nptr == '0')
+    {
+	nptr++;
+	if (*nptr == 'x' || *nptr == 'X') {
+	    /* Let zstrtol parse number with base */
+	    yyval.u.l = zstrtol(ptr, &ptr, 0);
+	    /* Should we set lastbase here? */
+	    lastbase = 16;
+	    return NUM;
+	}
+	else if (isset(OCTALZEROES) &&
+		 (memchr(nptr, '.', strlen(nptr)) == NULL) &&
+		 idigit(*nptr)) {
+	    yyval.u.l = zstrtol(ptr, &ptr, 0);
+	    lastbase = 8;
+	    return NUM;
+	}
+    }
+
+    while (idigit(*nptr))
+	nptr++;
+
+    if (*nptr == '.' || *nptr == 'e' || *nptr == 'E') {
+	/* it's a float */
+	yyval.type = MN_FLOAT;
+#ifdef USE_LOCALE
+	prev_locale = dupstring(setlocale(LC_NUMERIC, NULL));
+	setlocale(LC_NUMERIC, "POSIX");
+#endif
+	yyval.u.d = strtod(ptr, &nptr);
+#ifdef USE_LOCALE
+	if (prev_locale) setlocale(LC_NUMERIC, prev_locale);
+#endif
+	if (ptr == nptr || *nptr == '.') {
+	    zerr("bad floating point constant", NULL, 0);
+	    return EOI;
+	}
+	ptr = nptr;
+    } else {
+	/* it's an integer */
+	yyval.u.l = zstrtol(ptr, &ptr, 10);
+
+	if (*ptr == '#') {
+	    ptr++;
+	    yyval.u.l = zstrtol(ptr, &ptr, lastbase = yyval.u.l);
+	}
+    }
+    return NUM;
+}
+
+/**/
+int outputradix;
+
+/**/
+static int
+zzlex(void)
+{
+    int cct = 0;
+    yyval.type = MN_INTEGER;
+
+    for (;; cct = 0)
+	switch (*ptr++) {
+	case '+':
+	    if (*ptr == '+' && (unary || !ialnum(*ptr))) {
+		ptr++;
+		return (unary) ? PREPLUS : POSTPLUS;
+	    }
+	    if (*ptr == '=') {
+		ptr++;
+		return PLUSEQ;
+	    }
+	    return (unary) ? UPLUS : PLUS;
+	case '-':
+	    if (*ptr == '-' && (unary || !ialnum(*ptr))) {
+		ptr++;
+		return (unary) ? PREMINUS : POSTMINUS;
+	    }
+	    if (*ptr == '=') {
+		ptr++;
+		return MINUSEQ;
+	    }
+	    if (unary) {
+		if (idigit(*ptr) || *ptr == '.') {
+		    ptr--;
+		    return lexconstant();
+		} else
+		    return UMINUS;
+	    } else
+		return MINUS;
+	case '(':
+	    return M_INPAR;
+	case ')':
+	    return M_OUTPAR;
+	case '!':
+	    if (*ptr == '=') {
+		ptr++;
+		return NEQ;
+	    }
+	    return NOT;
+	case '~':
+	    return COMP;
+	case '&':
+	    if (*ptr == '&') {
+		if (*++ptr == '=') {
+		    ptr++;
+		    return DANDEQ;
+		}
+		return DAND;
+	    } else if (*ptr == '=') {
+		ptr++;
+		return ANDEQ;
+	    }
+	    return AND;
+	case '|':
+	    if (*ptr == '|') {
+		if (*++ptr == '=') {
+		    ptr++;
+		    return DOREQ;
+		}
+		return DOR;
+	    } else if (*ptr == '=') {
+		ptr++;
+		return OREQ;
+	    }
+	    return OR;
+	case '^':
+	    if (*ptr == '^') {
+		if (*++ptr == '=') {
+		    ptr++;
+		    return DXOREQ;
+		}
+		return DXOR;
+	    } else if (*ptr == '=') {
+		ptr++;
+		return XOREQ;
+	    }
+	    return XOR;
+	case '*':
+	    if (*ptr == '*') {
+		if (*++ptr == '=') {
+		    ptr++;
+		    return POWEREQ;
+		}
+		return POWER;
+	    }
+	    if (*ptr == '=') {
+		ptr++;
+		return MULEQ;
+	    }
+	    return MUL;
+	case '/':
+	    if (*ptr == '=') {
+		ptr++;
+		return DIVEQ;
+	    }
+	    return DIV;
+	case '%':
+	    if (*ptr == '=') {
+		ptr++;
+		return MODEQ;
+	    }
+	    return MOD;
+	case '<':
+	    if (*ptr == '<') {
+		if (*++ptr == '=') {
+		    ptr++;
+		    return SHLEFTEQ;
+		}
+		return SHLEFT;
+	    } else if (*ptr == '=') {
+		ptr++;
+		return LEQ;
+	    }
+	    return LES;
+	case '>':
+	    if (*ptr == '>') {
+		if (*++ptr == '=') {
+		    ptr++;
+		    return SHRIGHTEQ;
+		}
+		return SHRIGHT;
+	    } else if (*ptr == '=') {
+		ptr++;
+		return GEQ;
+	    }
+	    return GRE;
+	case '=':
+	    if (*ptr == '=') {
+		ptr++;
+		return DEQ;
+	    }
+	    return EQ;
+	case '$':
+	    yyval.u.l = mypid;
+	    return NUM;
+	case '?':
+	    if (unary) {
+		yyval.u.l = lastval;
+		return NUM;
+	    }
+	    return QUEST;
+	case ':':
+	    return COLON;
+	case ',':
+	    return COMMA;
+	case '\0':
+	    ptr--;
+	    return EOI;
+	case '[':
+	    {
+		int n;
+
+		if (idigit(*ptr)) {
+		    n = zstrtol(ptr, &ptr, 10);
+		    if (*ptr != ']' || !idigit(*++ptr)) {
+			zerr("bad base syntax", NULL, 0);
+			return EOI;
+		    }
+		    yyval.u.l = zstrtol(ptr, &ptr, lastbase = n);
+		    return NUM;
+		}
+		if (*ptr == '#') {
+		    n = 1;
+		    if (*++ptr == '#') {
+			n = -1;
+			ptr++;
+		    }
+		    if (!idigit(*ptr))
+			goto bofs;
+		    outputradix = n * zstrtol(ptr, &ptr, 10);
+		} else {
+		    bofs:
+		    zerr("bad output format specification", NULL, 0);
+		    return EOI;
+		}
+		if(*ptr != ']')
+			goto bofs;
+		ptr++;
+		break;
+	    }
+	case ' ':
+	case '\t':
+	case '\n':
+	    break;
+	/* Fall through! */
+	default:
+	    if (idigit(*--ptr) || *ptr == '.')
+		return lexconstant();
+	    if (*ptr == '#') {
+		if (*++ptr == '\\' || *ptr == '#') {
+		    int v;
+
+		    ptr++;
+		    if (!*ptr) {
+			zerr("character missing after ##", NULL, 0);
+			return EOI;
+		    }
+		    ptr = getkeystring(ptr, NULL, 6, &v);
+		    yyval.u.l = v;
+		    return NUM;
+		}
+		cct = 1;
+	    }
+	    if (iident(*ptr)) {
+		int func = 0;
+		char *p;
+
+		p = ptr;
+		while (iident(*++ptr));
+		if (*ptr == '[' || (!cct && *ptr == '(')) {
+		    char op = *ptr, cp = ((*ptr == '[') ? ']' : ')');
+		    int l;
+		    func = (op == '(');
+		    for (ptr++, l = 1; *ptr && l; ptr++) {
+			if (*ptr == op)
+			    l++;
+			if (*ptr == cp)
+			    l--;
+			if (*ptr == '\\' && ptr[1])
+			    ptr++;
+		    }
+		}
+		yylval = dupstrpfx(p, ptr - p);
+		return (func ? FUNC : (cct ? CID : ID));
+	    }
+	    else if (cct) {
+		yyval.u.l = poundgetfn(NULL);
+		return NUM;
+	    }
+	    return EOI;
+	}
+}
+
+/* the value stack */
+
+#define STACKSZ 100
+static int mtok;			/* last token */
+static int sp = -1;			/* stack pointer */
+
+struct mathvalue {
+    char *lval;
+    mnumber val;
+};
+
+static struct mathvalue *stack;
+
+/**/
+static void
+push(mnumber val, char *lval, int getme)
+{
+    if (sp == STACKSZ - 1)
+	zerr("stack overflow", NULL, 0);
+    else
+	sp++;
+    stack[sp].val = val;
+    stack[sp].lval = lval;
+    if (getme)
+	stack[sp].val.type = MN_UNSET;
+}
+
+/**/
+static mnumber
+pop(int noget)
+{
+    struct mathvalue *mv = stack+sp;
+
+    if (mv->val.type == MN_UNSET && !noget)
+	mv->val = getnparam(mv->lval);
+    sp--;
+    return errflag ? zero_mnumber : mv->val;
+}
+
+/**/
+static mnumber
+getcvar(char *s)
+{
+    char *t;
+    mnumber mn;
+    mn.type = MN_INTEGER;
+
+    queue_signals();
+    if (!(t = getsparam(s)))
+	mn.u.l = 0;
+    else
+        mn.u.l = STOUC(*t == Meta ? t[1] ^ 32 : *t);
+    unqueue_signals();
+    return mn;
+}
+
+
+/**/
+static mnumber
+setvar(char *s, mnumber v)
+{
+    if (!s) {
+	zerr("lvalue required", NULL, 0);
+	v.type = MN_INTEGER;
+	v.u.l = 0;
+	return v;
+    }
+    if (noeval)
+	return v;
+    untokenize(s);
+    setnparam(s, v);
+    return v;
+}
+
+
+/**/
+static mnumber
+callmathfunc(char *o)
+{
+    MathFunc f;
+    char *a, *n;
+    static mnumber dummy;
+
+    n = a = dupstring(o);
+
+    while (*a != '(')
+	a++;
+    *a++ = '\0';
+    a[strlen(a) - 1] = '\0';
+
+    if ((f = getmathfunc(n, 1))) {
+	if (f->flags & MFF_STR)
+	    return f->sfunc(n, a, f->funcid);
+	else {
+	    int argc = 0;
+	    mnumber *argv = NULL, *q;
+	    LinkList l = newlinklist();
+	    LinkNode node;
+
+	    while (iblank(*a))
+		a++;
+	    while (*a) {
+		if (*a) {
+		    argc++;
+ 		    q = (mnumber *) zhalloc(sizeof(mnumber));
+		    *q = mathevall(a, ARGPREC, &a);
+		    addlinknode(l, q);
+		    if (errflag || mtok != COMMA)
+			break;
+		}
+	    }
+	    if (*a && !errflag)
+		zerr("bad math expression: illegal character: %c",
+		     NULL, *a);
+	    if (!errflag) {
+		if (argc >= f->minargs && (f->maxargs < 0 ||
+					   argc <= f->maxargs)) {
+		    if (argc) {
+			q = argv = (mnumber *)zhalloc(argc * sizeof(mnumber));
+			for (node = firstnode(l); node; incnode(node))
+			    *q++ = *(mnumber *)getdata(node);
+		    }
+		    return f->nfunc(n, argc, argv, f->funcid);
+		} else
+		    zerr("wrong number of arguments: %s", o, 0);
+	    }
+	}
+    } else
+	zerr("unknown function: %s", n, 0);
+
+    dummy.type = MN_INTEGER;
+    dummy.u.l = 0;
+
+    return dummy;
+}
+
+/**/
+static int
+notzero(mnumber a)
+{
+    if ((a.type & MN_INTEGER) ? a.u.l == 0 : a.u.d == 0.0) {
+	zerr("division by zero", NULL, 0);
+	return 0;
+    }
+    return 1;
+}
+
+/* macro to pop three values off the value stack */
+
+/**/
+void
+op(int what)
+{
+    mnumber a, b, c, *spval;
+    char *lv;
+    int tp = type[what];
+
+    if (errflag)
+	return;
+    if (sp < 0) {
+	zerr("bad math expression: stack empty", NULL, 0);
+	return;
+    }
+
+    if (tp & (OP_A2|OP_A2IR|OP_A2IO|OP_E2|OP_E2IO)) {
+	/* Make sure anyone seeing this message reports it. */
+	DPUTS(sp < 1, "BUG: math: not enough wallabies in outback.");
+	b = pop(0);
+	a = pop(what == EQ);
+	if (errflag)
+	    return;
+
+	if (tp & (OP_A2IO|OP_E2IO)) {
+	    /* coerce to integers */
+	    if (a.type & MN_FLOAT) {
+		a.type = MN_INTEGER;
+		a.u.l = (zlong)a.u.d;
+	    }
+	    if (b.type & MN_FLOAT) {
+		b.type = MN_INTEGER;
+		b.u.l = (zlong)b.u.d;
+	    }
+	} else if (a.type != b.type && what != COMMA &&
+		   (a.type != MN_UNSET || what != EQ)) {
+	    /*
+	     * Different types, so coerce to float.
+	     * It may happen during an assignment that the LHS
+	     * variable is actually an integer, but there's still
+	     * no harm in doing the arithmetic in floating point;
+	     * the assignment will do the correct conversion.
+	     * This way, if the parameter is actually a scalar, but
+	     * used to contain an integer, we can write a float into it.
+	     */
+	    if (a.type & MN_INTEGER) {
+		a.type = MN_FLOAT;
+		a.u.d = (double)a.u.l;
+	    }
+	    if (b.type & MN_INTEGER) {
+		b.type = MN_FLOAT;
+		b.u.d = (double)b.u.l;
+	    }
+	}
+
+	if (noeval) {
+	    c.type = MN_INTEGER;
+	    c.u.l = 0;
+	} else {
+	    /*
+	     * type for operation: usually same as operands, but e.g.
+	     * (a == b) returns int.
+	     */
+	    c.type = (tp & OP_A2IR) ? MN_INTEGER : a.type;
+
+	    switch(what) {
+	    case AND:
+	    case ANDEQ:
+		c.u.l = a.u.l & b.u.l;
+		break;
+	    case XOR:
+	    case XOREQ:
+		c.u.l = a.u.l ^ b.u.l;
+		break;
+	    case OR:
+	    case OREQ:
+		c.u.l = a.u.l | b.u.l;
+		break;
+	    case MUL:
+	    case MULEQ:
+		if (c.type == MN_FLOAT)
+		    c.u.d = a.u.d * b.u.d;
+		else
+		    c.u.l = a.u.l * b.u.l;
+		break;
+	    case DIV:
+	    case DIVEQ:
+		if (!notzero(b))
+		    return;
+		if (c.type == MN_FLOAT)
+		    c.u.d = a.u.d / b.u.d;
+		else
+		    c.u.l = a.u.l / b.u.l;
+		break;
+	    case MOD:
+	    case MODEQ:
+		if (!notzero(b))
+		    return;
+		c.u.l = a.u.l % b.u.l;
+		break;
+	    case PLUS:
+	    case PLUSEQ:
+		if (c.type == MN_FLOAT)
+		    c.u.d = a.u.d + b.u.d;
+		else
+		    c.u.l = a.u.l + b.u.l;
+		break;
+	    case MINUS:
+	    case MINUSEQ:
+		if (c.type == MN_FLOAT)
+		    c.u.d = a.u.d - b.u.d;
+		else
+		    c.u.l = a.u.l - b.u.l;
+		break;
+	    case SHLEFT:
+	    case SHLEFTEQ:
+		c.u.l = a.u.l << b.u.l;
+		break;
+	    case SHRIGHT:
+	    case SHRIGHTEQ:
+		c.u.l = a.u.l >> b.u.l;
+		break;
+	    case LES:
+		c.u.l = (zlong)
+		    (a.type == MN_FLOAT ? (a.u.d < b.u.d) : (a.u.l < b.u.l));
+		break;
+	    case LEQ:
+		c.u.l = (zlong)
+		    (a.type == MN_FLOAT ? (a.u.d <= b.u.d) : (a.u.l <= b.u.l));
+		break;
+	    case GRE:
+		c.u.l = (zlong)
+		    (a.type == MN_FLOAT ? (a.u.d > b.u.d) : (a.u.l > b.u.l));
+		break;
+	    case GEQ:
+		c.u.l = (zlong)
+		    (a.type == MN_FLOAT ? (a.u.d >= b.u.d) : (a.u.l >= b.u.l));
+		break;
+	    case DEQ:
+		c.u.l = (zlong)
+		    (a.type == MN_FLOAT ? (a.u.d == b.u.d) : (a.u.l == b.u.l));
+		break;
+	    case NEQ:
+		c.u.l = (zlong)
+		    (a.type == MN_FLOAT ? (a.u.d != b.u.d) : (a.u.l != b.u.l));
+		break;
+	    case DAND:
+	    case DANDEQ:
+		c.u.l = (zlong)(a.u.l && b.u.l);
+		break;
+	    case DOR:
+	    case DOREQ:
+		c.u.l = (zlong)(a.u.l || b.u.l);
+		break;
+	    case DXOR:
+	    case DXOREQ:
+		c.u.l = (zlong)((a.u.l && !b.u.l) || (!a.u.l && b.u.l));
+		break;
+	    case COMMA:
+		c = b;
+		break;
+	    case POWER:
+	    case POWEREQ:
+		if (c.type == MN_INTEGER && b.u.l < 0) {
+		    /* produces a real result, so cast to real. */
+		    a.type = b.type = c.type = MN_FLOAT;
+		    a.u.d = (double) a.u.l;
+		    b.u.d = (double) b.u.l;
+		}
+		if (c.type == MN_INTEGER) {
+		    for (c.u.l = 1; b.u.l--; c.u.l *= a.u.l);
+		} else {
+		    if (b.u.d <= 0 && !notzero(a))
+			return;
+		    if (a.u.d < 0) {
+			/* Error if (-num ** b) and b is not an integer */
+			double tst = (double)(zlong)b.u.d;
+			if (tst != b.u.d) {
+			    zerr("imaginary power", NULL, 0);
+			    return;
+			}
+		    }
+		    c.u.d = pow(a.u.d, b.u.d);
+		}
+		break;
+	    case EQ:
+		c = b;
+		break;
+	    }
+	}
+	if (tp & (OP_E2|OP_E2IO)) {
+	    lv = stack[sp+1].lval;
+	    push(setvar(lv,c), lv, 0);
+	} else
+	    push(c,NULL, 0);
+	return;
+    }
+
+    spval = &stack[sp].val;
+    if (stack[sp].val.type == MN_UNSET)
+	*spval = getnparam(stack[sp].lval);
+    switch (what) {
+    case NOT:
+	if (spval->type & MN_FLOAT) {
+	    spval->u.l = !spval->u.d;
+	    spval->type = MN_INTEGER;
+	} else
+	    spval->u.l = !spval->u.l;
+	stack[sp].lval = NULL;
+	break;
+    case COMP:
+	if (spval->type & MN_FLOAT) {
+	    spval->u.l = ~((zlong)spval->u.d);
+	    spval->type = MN_INTEGER;
+	} else
+	    spval->u.l = ~spval->u.l;
+	stack[sp].lval = NULL;
+	break;
+    case POSTPLUS:
+	a = *spval;
+	if (spval->type & MN_FLOAT)
+	    a.u.d++;
+	else
+	    a.u.l++;
+	(void)setvar(stack[sp].lval, a);
+	break;
+    case POSTMINUS:
+	a = *spval;
+	if (spval->type & MN_FLOAT)
+	    a.u.d--;
+	else
+	    a.u.l--;
+	(void)setvar(stack[sp].lval, a);
+	break;
+    case UPLUS:
+	stack[sp].lval = NULL;
+	break;
+    case UMINUS:
+	if (spval->type & MN_FLOAT)
+	    spval->u.d = -spval->u.d;
+	else
+	    spval->u.l = -spval->u.l;
+	stack[sp].lval = NULL;
+	break;
+    case QUEST:
+	DPUTS(sp < 2, "BUG: math: three shall be the number of the counting.");
+	c = pop(0);
+	b = pop(0);
+	a = pop(0);
+	if (errflag)
+	    return;
+	/* b and c can stay different types in this case. */
+	push(((a.type & MN_FLOAT) ? a.u.d : a.u.l) ? b : c, NULL, 0);
+	break;
+    case COLON:
+	zerr("':' without '?'", NULL, 0);
+	break;
+    case PREPLUS:
+	if (spval->type & MN_FLOAT)
+	    spval->u.d++;
+	else
+	    spval->u.l++;
+	setvar(stack[sp].lval, *spval);
+	break;
+    case PREMINUS:
+	if (spval->type & MN_FLOAT)
+	    spval->u.d--;
+	else
+	    spval->u.l--;
+	setvar(stack[sp].lval, *spval);
+	break;
+    default:
+	zerr("out of integers", NULL, 0);
+	return;
+    }
+}
+
+
+/**/
+static void
+bop(int tk)
+{
+    mnumber *spval = &stack[sp].val;
+    int tst;
+
+    if (stack[sp].val.type == MN_UNSET)
+	*spval = getnparam(stack[sp].lval);
+    tst = (spval->type & MN_FLOAT) ? (zlong)spval->u.d : spval->u.l; 
+
+    switch (tk) {
+    case DAND:
+    case DANDEQ:
+	if (!tst)
+	    noeval++;
+	break;
+    case DOR:
+    case DOREQ:
+	if (tst)
+	    noeval++;
+	break;
+    };
+}
+
+
+/**/
+static mnumber
+mathevall(char *s, int prek, char **ep)
+{
+    int xlastbase, xnoeval, xunary;
+    char *xptr;
+    mnumber xyyval;
+    char *xyylval;
+    int xsp;
+    struct mathvalue *xstack = 0, nstack[STACKSZ];
+    mnumber ret;
+
+    if (mlevel >= MAX_MLEVEL) {
+	xyyval.type = MN_INTEGER;
+	xyyval.u.l = 0;
+
+	zerr("math recursion limit exceeded", NULL, 0);
+
+	return xyyval;
+    }
+    if (mlevel++) {
+	xlastbase = lastbase;
+	xnoeval = noeval;
+	xunary = unary;
+	xptr = ptr;
+	xyyval = yyval;
+	xyylval = yylval;
+
+	xsp = sp;
+	xstack = stack;
+    } else {
+	xlastbase = xnoeval = xunary = xsp = 0;
+	xyyval.type = MN_INTEGER;
+	xyyval.u.l = 0;
+	xyylval = NULL;
+	xptr = NULL;
+    }
+    stack = nstack;
+    lastbase = -1;
+    ptr = s;
+    sp = -1;
+    unary = 1;
+    stack[0].val.type = MN_INTEGER;
+    stack[0].val.u.l = 0;
+    mathparse(prek);
+    *ep = ptr;
+    DPUTS(!errflag && sp > 0,
+	  "BUG: math: wallabies roaming too freely in outback");
+
+    if (errflag) {
+	ret.type = MN_INTEGER;
+	ret.u.l = errflag;
+    } else {
+	if (stack[0].val.type == MN_UNSET)
+	    ret = getnparam(stack[0].lval);
+	else
+	    ret = stack[0].val;
+    }
+
+    if (--mlevel) {
+	lastbase = xlastbase;
+	noeval = xnoeval;
+	unary = xunary;
+	ptr = xptr;
+	yyval = xyyval;
+	yylval = xyylval;
+
+	sp = xsp;
+	stack = xstack;
+    }
+    return ret;
+}
+
+
+/**/
+mod_export mnumber
+matheval(char *s)
+{
+    char *junk;
+    mnumber x;
+    int xmtok = mtok;
+    /* maintain outputradix across levels of evaluation */
+    if (!mlevel)
+	outputradix = 0;
+
+    if (!*s) {
+	x.type = MN_INTEGER;
+	x.u.l = 0;
+	return x;
+    }
+    x = mathevall(s, TOPPREC, &junk);
+    mtok = xmtok;
+    if (*junk)
+	zerr("bad math expression: illegal character: %c", NULL, *junk);
+    return x;
+}
+
+/**/
+mod_export zlong
+mathevali(char *s)
+{
+    mnumber x = matheval(s);
+    return (x.type & MN_FLOAT) ? (zlong)x.u.d : x.u.l;
+}
+
+
+/**/
+zlong
+mathevalarg(char *s, char **ss)
+{
+    mnumber x;
+    int xmtok = mtok;
+
+    x = mathevall(s, ARGPREC, ss);
+    if (mtok == COMMA)
+	(*ss)--;
+    mtok = xmtok;
+    return (x.type & MN_FLOAT) ? (zlong)x.u.d : x.u.l;
+}
+
+/*
+ * Make sure we have an operator or an operand, whatever is expected.
+ * For this purpose, unary operators constitute part of an operand.
+ */
+
+/**/
+static void
+checkunary(int mtokc, char *mptr)
+{
+    int errmsg = 0;
+    int tp = type[mtokc];
+    if (tp & (OP_A2|OP_A2IR|OP_A2IO|OP_E2|OP_E2IO|OP_OP)) {
+	if (unary)
+	    errmsg = 1;
+    } else {
+	if (!unary)
+	    errmsg = 2;
+    }
+    if (errmsg) {
+	char errbuf[80];
+	int len, over = 0;
+	while (inblank(*mptr))
+	    mptr++;
+	len = ztrlen(mptr);
+	if (len > 10) {
+	    len = 10;
+	    over = 1;
+	}
+	sprintf(errbuf, "bad math expression: %s expected at `%%l%s'",
+		errmsg == 2 ? "operator" : "operand",
+		over ? "..." : ""); 
+	zerr(errbuf, mptr, len);
+    }
+    unary = !(tp & OP_OPF);
+}
+
+/* operator-precedence parse the string and execute */
+
+/**/
+static void
+mathparse(int pc)
+{
+    zlong q;
+    int otok, onoeval;
+    char *optr = ptr;
+
+    if (errflag)
+	return;
+    mtok = zzlex();
+    /* Handle empty input */
+    if (pc == TOPPREC && mtok == EOI)
+	return;
+    checkunary(mtok, optr);
+    while (prec[mtok] <= pc) {
+	if (errflag)
+	    return;
+	switch (mtok) {
+	case NUM:
+	    push(yyval, NULL, 0);
+	    break;
+	case ID:
+	    push(zero_mnumber, yylval, !noeval);
+	    break;
+	case CID:
+	    push((noeval ? zero_mnumber : getcvar(yylval)), yylval, 0);
+	    break;
+	case FUNC:
+	    push((noeval ? zero_mnumber : callmathfunc(yylval)), yylval, 0);
+	    break;
+	case M_INPAR:
+	    mathparse(TOPPREC);
+	    if (mtok != M_OUTPAR) {
+		if (!errflag)
+		    zerr("')' expected", NULL, 0);
+		return;
+	    }
+	    break;
+	case QUEST:
+	    if (stack[sp].val.type == MN_UNSET)
+		stack[sp].val = getnparam(stack[sp].lval);
+	    q = (stack[sp].val.type == MN_FLOAT) ? (zlong)stack[sp].val.u.d :
+		stack[sp].val.u.l;
+
+	    if (!q)
+		noeval++;
+	    mathparse(prec[COLON] - 1);
+	    if (!q)
+		noeval--;
+	    if (mtok != COLON) {
+		if (!errflag)
+		    zerr("':' expected", NULL, 0);
+		return;
+	    }
+	    if (q)
+		noeval++;
+	    mathparse(prec[QUEST]);
+	    if (q)
+		noeval--;
+	    op(QUEST);
+	    continue;
+	default:
+	    otok = mtok;
+	    onoeval = noeval;
+	    if (MTYPE(type[otok]) == BOOL)
+		bop(otok);
+	    mathparse(prec[otok] - (MTYPE(type[otok]) != RL));
+	    noeval = onoeval;
+	    op(otok);
+	    continue;
+	}
+	optr = ptr;
+	mtok = zzlex();
+	checkunary(mtok, optr);
+    }
+}