summaryrefslogtreecommitdiff
path: root/Src/math.c
diff options
context:
space:
mode:
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);
}
}