From d0f024f1527bdae5ad40e0b1076723e94e752459 Mon Sep 17 00:00:00 2001 From: Tanaka Akira Date: Fri, 17 Sep 1999 15:12:01 +0000 Subject: manual/7915 --- Src/math.c | 590 +++++++++++++++++++++++++++++++++++-------------------------- 1 file changed, 337 insertions(+), 253 deletions(-) (limited to 'Src/math.c') diff --git a/Src/math.c b/Src/math.c index 10821b284..f91a58ea8 100644 --- a/Src/math.c +++ b/Src/math.c @@ -30,6 +30,8 @@ #include "zsh.mdh" #include "math.pro" +#include + /* nonzero means we are not evaluating, just parsing */ /**/ @@ -42,7 +44,7 @@ int lastbase; static char *ptr; -static zlong yyval; +static mnumber yyval; static LV yylval; static int mlevel = 0; @@ -53,12 +55,27 @@ 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 MTYPE(x) ((x) & 3) + +/* + * OP_A2 2 argument + * OP_A2IR 2 argument with return type integer + * OP_A2IO 2 arguments, must be integer, returning integer + * OP_E2 2 argument with assignment + * OP_E2IO 2 arguments with assignment, must be integer, return integer + */ +#define OP_A2 4 +#define OP_A2IR 8 +#define OP_A2IO 16 +#define OP_E2 32 +#define OP_E2IO 64 + #define M_INPAR 0 #define M_OUTPAR 1 #define NOT 2 @@ -135,17 +152,17 @@ static int prec[TOKCOUNT] = 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, RL, RL, RL, +/* 5 */ RL, 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, RL, 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, +/* 45 */ RL, RL, LR, LR, RL|OP_A2, +/* 50 */ LR, RL|OP_E2 }; #define LVCOUNT 32 @@ -162,6 +179,7 @@ zzlex(void) { int cct = 0; + yyval.type = MN_INTEGER; for (;; cct = 0) switch (*ptr++) { case '+': @@ -302,11 +320,11 @@ zzlex(void) return EQ; case '$': unary = 0; - yyval = mypid; + yyval.u.l = mypid; return NUM; case '?': if (unary) { - yyval = lastval; + yyval.u.l = lastval; unary = 0; return NUM; } @@ -329,7 +347,7 @@ zzlex(void) if (*ptr == ']') ptr++; - yyval = zstrtol(ptr, &ptr, lastbase = base); + yyval.u.l = zstrtol(ptr, &ptr, lastbase = base); return NUM; } case ' ': @@ -340,18 +358,33 @@ zzlex(void) 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)) { + if (idigit(*--ptr) || *ptr == '.') { + char *nptr; unary = 0; - yyval = zstrtol(ptr, &ptr, 10); - - if (*ptr == '#') { - ptr++; - yyval = zstrtol(ptr, &ptr, lastbase = yyval); + for (nptr = ptr; idigit(*nptr); nptr++); + + if (*nptr == '.' || *nptr == 'e' || *nptr == 'E') { + /* it's a float */ + yyval.type = MN_FLOAT; + yyval.u.d = strtod(ptr, &nptr); + 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; } @@ -361,7 +394,7 @@ zzlex(void) ptr++; ptr = getkeystring(ptr, NULL, 6, &v); - yyval = v; + yyval.u.l = v; unary = 0; return NUM; } @@ -395,7 +428,7 @@ zzlex(void) return cct ? CID : ID; } else if (cct) { - yyval = poundgetfn(NULL); + yyval.u.l = poundgetfn(NULL); unary = 0; return NUM; } @@ -411,14 +444,14 @@ static int sp = -1; /* stack pointer */ struct mathvalue { LV lval; - zlong val; + mnumber val; }; static struct mathvalue *stack; /**/ static void -push(zlong val, LV lval) +push(mnumber val, LV lval) { if (sp == STACKSZ - 1) zerr("stack overflow", NULL, 0); @@ -430,275 +463,308 @@ push(zlong val, LV lval) /**/ -static zlong +static mnumber getcvar(LV s) { char *t; + mnumber mn; + mn.type = MN_INTEGER; if (!(t = getsparam(lvals[s]))) - return 0; - return STOUC(*t == Meta ? t[1] ^ 32 : *t); + mn.u.l = 0; + else + mn.u.l = STOUC(*t == Meta ? t[1] ^ 32 : *t); + return mn; } /**/ -static zlong -setvar(LV s, zlong v) +static mnumber +setvar(LV s, mnumber v) { if (s == -1 || s >= lvc) { zerr("lvalue required", NULL, 0); - return 0; + v.type = MN_INTEGER; + v.u.l = 0; } if (noeval) return v; - setiparam(lvals[s], v); + setnparam(lvals[s], v); return v; } /**/ static int -notzero(zlong 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) { - zlong a, b, c; + mnumber a, b, c, *spval; LV lv; + int tp = type[what]; if (sp < 0) { zerr("bad math expression: stack empty", NULL, 0); return; } + + if (tp & (OP_A2|OP_A2IR|OP_A2IO|OP_E2|OP_E2IO)) { + if (sp < 1) { + zerr("bad math expression: unbalanced stack", NULL, 0); \ + return; + } + 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; + } + } + /* + * 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,-1); + 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 = -1; 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 = -1; 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 = -1; 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((zlong)(a < b)); - break; - case LEQ: - pop2(); - pushv((zlong)(a <= b)); - break; - case GRE: - pop2(); - pushv((zlong)(a > b)); - break; - case GEQ: - pop2(); - pushv((zlong)(a >= b)); - break; - case DEQ: - pop2(); - pushv((zlong)(a == b)); - break; - case NEQ: - pop2(); - pushv((zlong)(a != b)); - break; - case DAND: - pop2(); - pushv((zlong)(a && b)); - break; - case DOR: - pop2(); - pushv((zlong)(a || b)); - break; - case DXOR: - pop2(); - pushv((zlong)((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 = -1; break; case QUEST: - pop3(); - pushv((a) ? b : c); + if (sp < 2) { + zerr("bad math expression: unbalanced stack", NULL, 0); + return; + } + 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, -1); 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((zlong)(a && b)); - break; - case DOREQ: - pop2lv(); - set((zlong)(a || b)); - break; - case DXOREQ: - pop2lv(); - set((zlong)((a && !b) || (!a && b))); - break; - case COMMA: - pop2(); - pushv(b); - 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); @@ -711,15 +777,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; }; @@ -727,21 +796,19 @@ bop(int tk) /**/ -static zlong +static mnumber mathevall(char *s, int prek, char **ep) { int t0; int xlastbase, xnoeval, xunary, xlvc; char *xptr; - zlong xyyval; + mnumber xyyval; LV xyylval; char **xlvals = 0, *nlvals[LVCOUNT]; int xsp; struct mathvalue *xstack = 0, nstack[STACKSZ]; - zlong ret; + mnumber ret; - xlastbase = xnoeval = xunary = xlvc = xyyval = xyylval = xsp = 0; - xptr = NULL; if (mlevel++) { xlastbase = lastbase; xnoeval = noeval; @@ -754,6 +821,11 @@ mathevall(char *s, int prek, char **ep) xsp = sp; xstack = stack; + } else { + xlastbase = xnoeval = xunary = xlvc = xyylval = xsp = 0; + xyyval.type = MN_INTEGER; + xyyval.u.l = 0; + xptr = NULL; } stack = nstack; lastbase = -1; @@ -790,15 +862,18 @@ mathevall(char *s, int prek, char **ep) /**/ -zlong +mnumber matheval(char *s) { char *junk; - zlong 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) @@ -806,19 +881,27 @@ matheval(char *s) return x; } +/**/ +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) { - zlong 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; } @@ -842,7 +925,7 @@ mathparse(int pc) push(yyval, -1); break; case ID: - push(getiparam(lvals[yylval]), yylval); + push(getnparam(lvals[yylval]), yylval); break; case CID: push(getcvar(yylval), yylval); @@ -856,7 +939,8 @@ 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++; @@ -873,9 +957,9 @@ 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; -- cgit 1.4.1