about summary refs log tree commit diff
path: root/Src/math.c
diff options
context:
space:
mode:
authorOliver Kiddle <opk@users.sourceforge.net>2000-04-30 17:58:35 +0000
committerOliver Kiddle <opk@users.sourceforge.net>2000-04-30 17:58:35 +0000
commitddd8614e5149a5aabde1acef1609084003adbc80 (patch)
treecaec96cf9ce80ba08adb3402b819c8d12faf164f /Src/math.c
parent3e43e5099282927fad9844f106b1cd8a361be03c (diff)
downloadzsh-ddd8614e5149a5aabde1acef1609084003adbc80.tar.gz
zsh-ddd8614e5149a5aabde1acef1609084003adbc80.tar.xz
zsh-ddd8614e5149a5aabde1acef1609084003adbc80.zip
AIX dependency fixes
Diffstat (limited to 'Src/math.c')
-rw-r--r--Src/math.c872
1 files changed, 533 insertions, 339 deletions
diff --git a/Src/math.c b/Src/math.c
index 7a0a1f9bd..aee318cfc 100644
--- a/Src/math.c
+++ b/Src/math.c
@@ -30,11 +30,18 @@
 #include "zsh.mdh"
 #include "math.pro"
 
+#include <math.h>
+
 /* nonzero means we are not evaluating, just parsing */
  
 /**/
 int noeval;
  
+/* integer zero */
+
+/**/
+mnumber zero_mnumber;
+
 /* last input base we used */
 
 /**/
@@ -42,8 +49,10 @@ int lastbase;
  
 static char *ptr;
 
-static long yyval;
-static LV yylval;
+static mnumber yyval;
+static char *yylval;
+
+#define MAX_MLEVEL 256
 
 static int mlevel = 0;
 
@@ -53,11 +62,39 @@ static int unary = 1;
 
 /* LR = left-to-right associativity *
  * RL = right-to-left associativity *
- * BOO = short-circuiting boolean   */
+ * BOOL = short-circuiting boolean   */
 
-#define LR 0
-#define RL 1
-#define BOOL 2
+#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
@@ -111,7 +148,8 @@ static int unary = 1;
 #define POWER 49
 #define CID 50
 #define POWEREQ 51
-#define TOKCOUNT 52
+#define FUNC 52
+#define TOKCOUNT 53
 
 /* precedences */
 
@@ -122,45 +160,42 @@ static int prec[TOKCOUNT] =
      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,
+    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,  15
+     0,  16, 0
 };
 
-#define TOPPREC 16
+#define TOPPREC 17
 #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
+/*  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
 };
 
-#define LVCOUNT 32
-
-/* list of lvalues (variables) */
-
-static int lvc;
-static char **lvals;
-
 
 /**/
 static int
 zzlex(void)
 {
+#ifdef USE_LOCALE
+    char *prev_locale;
+#endif
     int cct = 0;
+    yyval.type = MN_INTEGER;
 
     for (;; cct = 0)
 	switch (*ptr++) {
@@ -170,7 +205,6 @@ zzlex(void)
 		return (unary) ? PREPLUS : POSTPLUS;
 	    }
 	    if (*ptr == '=') {
-		unary = 1;
 		ptr++;
 		return PLUSEQ;
 	    }
@@ -181,19 +215,16 @@ zzlex(void)
 		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;
 	    }
@@ -201,7 +232,6 @@ zzlex(void)
 	case '~':
 	    return COMP;
 	case '&':
-	    unary = 1;
 	    if (*ptr == '&') {
 		if (*++ptr == '=') {
 		    ptr++;
@@ -214,7 +244,6 @@ zzlex(void)
 	    }
 	    return AND;
 	case '|':
-	    unary = 1;
 	    if (*ptr == '|') {
 		if (*++ptr == '=') {
 		    ptr++;
@@ -227,7 +256,6 @@ zzlex(void)
 	    }
 	    return OR;
 	case '^':
-	    unary = 1;
 	    if (*ptr == '^') {
 		if (*++ptr == '=') {
 		    ptr++;
@@ -240,7 +268,6 @@ zzlex(void)
 	    }
 	    return XOR;
 	case '*':
-	    unary = 1;
 	    if (*ptr == '*') {
 		if (*++ptr == '=') {
 		    ptr++;
@@ -254,21 +281,18 @@ zzlex(void)
 	    }
 	    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++;
@@ -281,7 +305,6 @@ zzlex(void)
 	    }
 	    return LES;
 	case '>':
-	    unary = 1;
 	    if (*ptr == '>') {
 		if (*++ptr == '=') {
 		    ptr++;
@@ -294,42 +317,34 @@ zzlex(void)
 	    }
 	    return GRE;
 	case '=':
-	    unary = 1;
 	    if (*ptr == '=') {
 		ptr++;
 		return DEQ;
 	    }
 	    return EQ;
 	case '$':
-	    unary = 0;
-	    yyval = mypid;
+	    yyval.u.l = mypid;
 	    return NUM;
 	case '?':
 	    if (unary) {
-		yyval = lastval;
-		unary = 0;
+		yyval.u.l = lastval;
 		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);
+		yyval.u.l = zstrtol(ptr, &ptr, lastbase = base);
 		return NUM;
 	    }
 	case ' ':
@@ -338,63 +353,78 @@ zzlex(void)
 	    break;
 	case '0':
 	    if (*ptr == 'x' || *ptr == 'X') {
-		unary = 0;
 		/* Should we set lastbase here? */
-		yyval = zstrtol(++ptr, &ptr, lastbase = 16);
+		yyval.u.l = zstrtol(++ptr, &ptr, lastbase = 16);
 		return NUM;
 	    }
 	/* Fall through! */
 	default:
-	    if (idigit(*--ptr)) {
-		unary = 0;
-		yyval = zstrtol(ptr, &ptr, 10);
+	    if (idigit(*--ptr) || *ptr == '.') {
+		char *nptr;
+		for (nptr = ptr; idigit(*nptr); nptr++);
 
-		if (*ptr == '#') {
-		    ptr++;
-		    yyval = zstrtol(ptr, &ptr, lastbase = yyval);
+		if (*nptr == '.' || *nptr == 'e' || *nptr == 'E') {
+		    /* it's a float */
+		    yyval.type = MN_FLOAT;
+#ifdef USE_LOCALE
+		    prev_locale = setlocale(LC_NUMERIC, NULL);
+		    setlocale(LC_NUMERIC, "POSIX");
+#endif
+		    yyval.u.d = strtod(ptr, &nptr);
+#ifdef USE_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;
 	    }
 	    if (*ptr == '#') {
-		if (*++ptr == '\\') {
-		    ptr++;
-		    yyval = *ptr == Meta ? *++ptr ^ 32 : *ptr;
+		if (*++ptr == '\\' || *ptr == '#') {
+		    int v;
+
 		    ptr++;
-		    unary = 0;
+		    ptr = getkeystring(ptr, NULL, 6, &v);
+		    yyval.u.l = v;
 		    return NUM;
 		}
 		cct = 1;
 	    }
 	    if (iident(*ptr)) {
-		char *p, q;
+		int func = 0;
+		char *p;
 
 		p = ptr;
-		if (lvc == LVCOUNT) {
-		    zerr("too many identifiers (complain to author)", NULL, 0);
-		    return EOI;
-		}
-		unary = 0;
 		while (iident(*++ptr));
-		if (*ptr == '[') {
+		if (*ptr == '[' || (!cct && *ptr == '(')) {
+		    char op = *ptr, cp = ((*ptr == '[') ? ']' : ')');
 		    int l;
+		    func = (op == '(');
 		    for (ptr++, l = 1; *ptr && l; ptr++) {
-			if (*ptr == '[')
+			if (*ptr == op)
 			    l++;
-			if (*ptr == ']')
+			if (*ptr == cp)
 			    l--;
 			if (*ptr == '\\' && ptr[1])
 			    ptr++;
 		    }
 		}
-		q = *ptr;
-		*ptr = '\0';
-		lvals[yylval = lvc++] = ztrdup(p);
-		*ptr = q;
-		return cct ? CID : ID;
+		yylval = dupstrpfx(p, ptr - p);
+		return (func ? FUNC : (cct ? CID : ID));
 	    }
 	    else if (cct) {
-		yyval = poundgetfn(NULL);
-		unary = 0;
+		yyval.u.l = poundgetfn(NULL);
 		return NUM;
 	    }
 	    return EOI;
@@ -408,15 +438,15 @@ static int mtok;			/* last token */
 static int sp = -1;			/* stack pointer */
 
 struct mathvalue {
-    LV lval;
-    long val;
+    char *lval;
+    mnumber val;
 };
 
 static struct mathvalue *stack;
 
 /**/
 static void
-push(long val, LV lval)
+push(mnumber val, char *lval)
 {
     if (sp == STACKSZ - 1)
 	zerr("stack overflow", NULL, 0);
@@ -428,275 +458,374 @@ push(long val, LV lval)
 
 
 /**/
-static long
-getcvar(LV s)
+static mnumber
+getcvar(char *s)
 {
     char *t;
+    mnumber mn;
+    mn.type = MN_INTEGER;
 
-    if (!(t = getsparam(lvals[s])))
-	return 0;
-    return STOUC(*t == Meta ? t[1] ^ 32 : *t);
+    if (!(t = getsparam(s)))
+	mn.u.l = 0;
+    else
+        mn.u.l = STOUC(*t == Meta ? t[1] ^ 32 : *t);
+    return mn;
 }
 
 
 /**/
-static long
-setvar(LV s, long v)
+static mnumber
+setvar(char *s, mnumber v)
 {
-    if (s == -1 || s >= lvc) {
+    if (!s) {
 	zerr("lvalue required", NULL, 0);
-	return 0;
+	v.type = MN_INTEGER;
+	v.u.l = 0;
+	return v;
     }
     if (noeval)
 	return v;
-    setiparam(lvals[s], v);
+    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(long a)
+notzero(mnumber a)
 {
-    if (a == 0) {
+    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 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;
+    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 = stack[sp--].val;
+	a = stack[sp--].val;
+
+	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) {
+	    /*
+	     * Different types, so coerce to float.
+	     * It may happen during an assigment 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);
+	} else
+	    push(c,NULL);
+	return;
+    }
+
+    spval = &stack[sp].val;
     switch (what) {
     case NOT:
-	stack[sp].val = !stack[sp].val;
-	nolval();
+	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:
-	stack[sp].val = ~stack[sp].val;
-	nolval();
+	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:
-	(void)setvar(stack[sp].lval, stack[sp].val + 1);
+	a = *spval;
+	if (spval->type & MN_FLOAT)
+	    a.u.d++;
+	else
+	    a.u.l++;
+	(void)setvar(stack[sp].lval, a);
 	break;
     case POSTMINUS:
-	(void)setvar(stack[sp].lval, stack[sp].val - 1);
+	a = *spval;
+	if (spval->type & MN_FLOAT)
+	    a.u.d--;
+	else
+	    a.u.l--;
+	(void)setvar(stack[sp].lval, a);
 	break;
     case UPLUS:
-	nolval();
+	stack[sp].lval = NULL;
 	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)));
+	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:
-	pop3();
-	pushv((a) ? b : c);
+	DPUTS(sp < 2, "BUG: math: three shall be the number of the counting.");
+	c = stack[sp--].val;
+	b = stack[sp--].val;
+	a = stack[sp--].val;
+	/* b and c can stay different types in this case. */
+	push(((a.type & MN_FLOAT) ? a.u.d : a.u.l) ? b : c, NULL);
 	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);
+	zerr("':' without '?'", NULL, 0);
 	break;
     case PREPLUS:
-	stack[sp].val = setvar(stack[sp].lval,
-			       stack[sp].val + 1);
+	if (spval->type & MN_FLOAT)
+	    spval->u.d++;
+	else
+	    spval->u.l++;
+	setvar(stack[sp].lval, *spval);
 	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);
+	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);
@@ -709,15 +838,18 @@ op(int what)
 static void
 bop(int tk)
 {
+    mnumber *spval = &stack[sp].val;
+    int tst = (spval->type & MN_FLOAT) ? (zlong)spval->u.d : spval->u.l; 
+
     switch (tk) {
     case DAND:
     case DANDEQ:
-	if (!stack[sp].val)
+	if (!tst)
 	    noeval++;
 	break;
     case DOR:
     case DOREQ:
-	if (stack[sp].val)
+	if (tst)
 	    noeval++;
 	break;
     };
@@ -725,61 +857,63 @@ bop(int tk)
 
 
 /**/
-static long
+static mnumber
 mathevall(char *s, int prek, char **ep)
 {
-    int t0;
-    int xlastbase, xnoeval, xunary, xlvc;
+    int xlastbase, xnoeval, xunary;
     char *xptr;
-    long xyyval;
-    LV xyylval;
-    char **xlvals = 0;
+    mnumber xyyval;
+    char *xyylval;
     int xsp;
-    struct mathvalue *xstack = 0;
-    long ret;
+    struct mathvalue *xstack = 0, nstack[STACKSZ];
+    mnumber ret;
+
+    if (mlevel >= MAX_MLEVEL) {
+	xyyval.type = MN_INTEGER;
+	xyyval.u.l = 0;
 
-    xlastbase = xnoeval = xunary = xlvc = xyyval = xyylval = xsp = 0;
-    xptr = NULL;
+	zerr("math recursion limit exceeded", NULL, 0);
+
+	return xyyval;
+    }
     if (mlevel++) {
 	xlastbase = lastbase;
 	xnoeval = noeval;
 	xunary = unary;
-	xlvc = lvc;
 	xptr = ptr;
 	xyyval = yyval;
 	xyylval = yylval;
-	xlvals = lvals;
 
 	xsp = sp;
 	xstack = stack;
+    } else {
+	xlastbase = xnoeval = xunary = xsp = 0;
+	xyyval.type = MN_INTEGER;
+	xyyval.u.l = 0;
+	xyylval = NULL;
+	xptr = NULL;
     }
-    stack = (struct mathvalue *)zalloc(STACKSZ*sizeof(struct mathvalue));
+    stack = nstack;
     lastbase = -1;
-    lvals = (char **)zcalloc(LVCOUNT*sizeof(char *));
-    lvc = 0;
     ptr = s;
     sp = -1;
     unary = 1;
+    stack[0].val.type = MN_INTEGER;
+    stack[0].val.u.l = 0;
     mathparse(prek);
     *ep = ptr;
-    if (sp)
-	zerr("bad math expression: unbalanced stack", NULL, 0);
-    for (t0 = 0; t0 != lvc; t0++)
-	zsfree(lvals[t0]);
+    DPUTS(!errflag && sp,
+	  "BUG: math: wallabies roaming too freely in outback");
 
     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;
@@ -789,15 +923,18 @@ mathevall(char *s, int prek, char **ep)
 
 
 /**/
-long
+mod_export mnumber
 matheval(char *s)
 {
     char *junk;
-    long x;
+    mnumber x;
     int xmtok = mtok;
 
-    if (!*s)
-	return 0;
+    if (!*s) {
+	x.type = MN_INTEGER;
+	x.u.l = 0;
+	return x;
+    }
     x = mathevall(s, TOPPREC, &junk);
     mtok = xmtok;
     if (*junk)
@@ -805,21 +942,64 @@ matheval(char *s)
     return x;
 }
 
+/**/
+mod_export zlong
+mathevali(char *s)
+{
+    mnumber x = matheval(s);
+    return (x.type & MN_FLOAT) ? (zlong)x.u.d : x.u.l;
+}
+
 
 /**/
-long
+zlong
 mathevalarg(char *s, char **ss)
 {
-    long x;
+    mnumber x;
     int xmtok = mtok;
 
     x = mathevall(s, ARGPREC, ss);
     if (mtok == COMMA)
 	(*ss)--;
     mtok = xmtok;
-    return x;
+    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 */
 
@@ -827,23 +1007,29 @@ mathevalarg(char *s, char **ss)
 static void
 mathparse(int pc)
 {
-    int q, otok, onoeval;
+    zlong q;
+    int otok, onoeval;
+    char *optr = ptr;
 
     if (errflag)
 	return;
     mtok = zzlex();
+    checkunary(mtok, optr);
     while (prec[mtok] <= pc) {
 	if (errflag)
 	    return;
 	switch (mtok) {
 	case NUM:
-	    push(yyval, -1);
+	    push(yyval, NULL);
 	    break;
 	case ID:
-	    push(getiparam(lvals[yylval]), yylval);
+	    push((noeval ? zero_mnumber : getnparam(yylval)), yylval);
 	    break;
 	case CID:
-	    push(getcvar(yylval), yylval);
+	    push((noeval ? zero_mnumber : getcvar(yylval)), yylval);
+	    break;
+	case FUNC:
+	    push((noeval ? zero_mnumber : callmathfunc(yylval)), yylval);
 	    break;
 	case M_INPAR:
 	    mathparse(TOPPREC);
@@ -854,14 +1040,20 @@ mathparse(int pc)
 	    }
 	    break;
 	case QUEST:
-	    q = stack[sp].val;
+	    q = (stack[sp].val.type == MN_FLOAT) ? (zlong)stack[sp].val.u.d :
+		stack[sp].val.u.l;
 
 	    if (!q)
 		noeval++;
-	    mathparse(prec[QUEST] - 1);
+	    mathparse(prec[COLON] - 1);
 	    if (!q)
 		noeval--;
-	    else
+	    if (mtok != COLON) {
+		if (!errflag)
+		    zerr("':' expected", NULL, 0);
+		return;
+	    }
+	    if (q)
 		noeval++;
 	    mathparse(prec[QUEST]);
 	    if (q)
@@ -871,13 +1063,15 @@ mathparse(int pc)
 	default:
 	    otok = mtok;
 	    onoeval = noeval;
-	    if (type[otok] == BOOL)
+	    if (MTYPE(type[otok]) == BOOL)
 		bop(otok);
-	    mathparse(prec[otok] - (type[otok] != RL));
+	    mathparse(prec[otok] - (MTYPE(type[otok]) != RL));
 	    noeval = onoeval;
 	    op(otok);
 	    continue;
 	}
+	optr = ptr;
 	mtok = zzlex();
+	checkunary(mtok, optr);
     }
 }