diff -r 000000000000 -r 2e3d3ce01487 openenvutils/commandshell/shell/src/math.c --- /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 + +#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); + } +}