From 8a4af211c346fd00ded0bb1b1384ca1514ed84e0 Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Thu, 12 Jun 2008 13:45:04 +0000 Subject: 25149: add C_PRECEDENCES option to make arithmetic have C/Perl precedence --- Src/math.c | 346 ++++++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 229 insertions(+), 117 deletions(-) (limited to 'Src/math.c') diff --git a/Src/math.c b/Src/math.c index 5b6941953..1aedcde28 100644 --- a/Src/math.c +++ b/Src/math.c @@ -159,25 +159,128 @@ static int unary = 1; #define FUNC 52 #define TOKCOUNT 53 -/* precedences */ +/* + * Opeator recedences: in reverse order, i.e. lower number, high precedence. + * These are the C precedences. + * + * 0 Non-operators: NUM (numeric constant), ID (identifier), + * CID (identifier with '#'), FUNC (math function) + * 1 Opening parenthesis: M_INPAR '(' (for convenience, not an operator) + * 2 Unary operators: PREPLUS/POSTPLUS '++', PREMINUS/POSTMINUS '--', + * NOT '!', COMP '~', UPLUS '+', UMINUS '-' + * 3 POWER '**' (not in C but at high precedence in Perl) + * 4 MUL '*', DIV '/', MOD '%' + * 5 PLUS '+', MINUS '-' + * 6 SHLEFT '<<', SHRIGHT '>>' + * 7 GRE '>', 'GEQ' '>=', LES '<', LEQ '<=' + * 8 DEQ '==', NEQ '!=' + * 9 AND '&' + * 10 XOR '^' + * 11 OR '|' + * 12 DAND '&&' + * 13 DXOR '^^' (not in C) + * 14 DOR '||' + * 15 QUEST '?' + * 16 COLON ':' + * 17 EQ '=', PLUSEQ '+=', MINUSEQ '-=', MULEQ '*=', DIVEQ '/=', + * MODEQ '%=', ANDEQ '&=', XOREQ '^=', OREQ '|=', + * SHFLEFTEQ '<<=', SHRIGHTEQ '>>=', DANDEQ '&&=', DOREQ '||=', + * DXOREQ '^^=' + * 18 COMMA ',' + * 137 M_OUTPAR ')' (for convenience, not an operator) + * 200 EOI (end of input: for convenience, not an operator) + */ +static int c_prec[TOKCOUNT] = +{ +/* M_INPAR M_OUTPAR NOT COMP POSTPLUS */ +/* 0 */ 1, 137, 2, 2, 2, +/* POSTMINUS UPLUS UMINUS AND XOR */ +/* 5 */ 2, 2, 2, 9, 10, +/* OR MUL DIV MOD PLUS */ +/* 10 */ 11, 4, 4, 4, 5, +/* MINUS SHLEFT SHRIGHT LES LEQ */ +/* 15 */ 5, 6, 6, 7, 7, +/* GRE GEQ DEQ NEQ DAND */ +/* 20 */ 7, 7, 8, 8, 12, +/* DOR DXOR QUEST COLON EQ */ +/* 25 */ 14, 13, 15, 16, 17, +/* PLUSEQ MINUSEQ MULEQ DIVEQ MODEQ */ +/* 30 */ 17, 17, 17, 17, 17, +/* ANDEQ XOREQ OREQ SHLEFTEQ SHRIGHTEQ */ +/* 35 */ 17, 17, 17, 17, 17, +/* DANDEQ DOREQ DXOREQ COMMA EOI */ +/* 40 */ 17, 17, 17, 18, 200, +/* PREPLUS PREMINUS NUM ID POWER */ +/* 45 */ 2, 2, 0, 0, 3, +/* CID POWEREQ FUNC */ +/* 50 */ 0, 17, 0 +}; -static int prec[TOKCOUNT] = +/* + * Opeator recedences: in reverse order, i.e. lower number, high precedence. + * These are the default zsh precedences. + * + * 0 Non-operators: NUM (numeric constant), ID (identifier), + * CID (identifier with '#'), FUNC (math function) + * 1 Opening parenthesis: M_INPAR '(' (for convenience, not an operator) + * 2 Unary operators: PREPLUS/POSTPLUS '++', PREMINUS/POSTMINUS '--', + * NOT '!', COMP '~', UPLUS '+', UMINUS '-' + * 3 SHLEFT '<<', SHRIGHT '>>' + * 4 AND '&' + * 5 XOR '^' + * 6 OR '|' + * 7 POWER '**' (not in C but at high precedence in Perl) + * 8 MUL '*', DIV '/', MOD '%' + * 9 PLUS '+', MINUS '-' + * 10 GRE '>', 'GEQ' '>=', LES '<', LEQ '<=' + * 11 DEQ '==', NEQ '!=' + * 12 DAND '&&' + * 13 DOR '||', DXOR '^^' (not in C) + * 14 QUEST '?' + * 15 COLON ':' + * 16 EQ '=', PLUSEQ '+=', MINUSEQ '-=', MULEQ '*=', DIVEQ '/=', + * MODEQ '%=', ANDEQ '&=', XOREQ '^=', OREQ '|=', + * SHFLEFTEQ '<<=', SHRIGHTEQ '>>=', DANDEQ '&&=', DOREQ '||=', + * DXOREQ '^^=' + * 17 COMMA ',' + * 137 M_OUTPAR ')' (for convenience, not an operator) + * 200 EOI (end of input: for convenience, not an operator) + */ +static int z_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 +/* M_INPAR M_OUTPAR NOT COMP POSTPLUS */ +/* 0 */ 1, 137, 2, 2, 2, +/* POSTMINUS UPLUS UMINUS AND XOR */ +/* 5 */ 2, 2, 2, 4, 5, +/* OR MUL DIV MOD PLUS */ +/* 10 */ 6, 8, 8, 8, 9, +/* MINUS SHLEFT SHRIGHT LES LEQ */ +/* 15 */ 9, 3, 3, 10, 10, +/* GRE GEQ DEQ NEQ DAND */ +/* 20 */ 10, 10, 11, 11, 12, +/* DOR DXOR QUEST COLON EQ */ +/* 25 */ 13, 13, 14, 15, 16, +/* PLUSEQ MINUSEQ MULEQ DIVEQ MODEQ */ +/* 30 */ 16, 16, 16, 16, 16, +/* ANDEQ XOREQ OREQ SHLEFTEQ SHRIGHTEQ */ +/* 35 */ 16, 16, 16, 16, 16, +/* DANDEQ DOREQ DXOREQ COMMA EOI */ +/* 40 */ 16, 16, 16, 17, 200, +/* PREPLUS PREMINUS NUM ID POWER */ +/* 45 */ 2, 2, 0, 0, 7, +/* CID POWEREQ FUNC */ +/* 50 */ 0, 16, 0 }; -#define TOPPREC 18 -#define ARGPREC 16 +/* Option-selectable preference table */ +static int *prec; + +/* + * Precedences for top and argument evaluation. Careful: + * prec needs to be set before we use these. + */ +#define TOPPREC (prec[COMMA]+1) +#define ARGPREC (prec[COMMA]-1) static int type[TOKCOUNT] = { @@ -194,6 +297,113 @@ static int type[TOKCOUNT] = /* 50 */ LR|OP_OPF, RL|OP_E2, LR|OP_OPF }; +/* 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; + +enum prec_type { + /* Evaluating a top-level expression */ + MPREC_TOP, + /* Evaluating a function argument */ + MPREC_ARG +}; + +static mnumber +mathevall(char *s, enum prec_type prec_tp, char **ep) +{ + int xlastbase, xnoeval, xunary, *xprec; + 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"); + + return xyyval; + } + if (mlevel++) { + xlastbase = lastbase; + xnoeval = noeval; + xunary = unary; + xptr = ptr; + xyyval = yyval; + xyylval = yylval; + + xsp = sp; + xstack = stack; + xprec = prec; + } else { + xlastbase = xnoeval = xunary = xsp = 0; + xyyval.type = MN_INTEGER; + xyyval.u.l = 0; + xyylval = NULL; + xptr = NULL; + xprec = NULL; + } + prec = isset(CPRECEDENCES) ? c_prec : z_prec; + stack = nstack; + lastbase = -1; + ptr = s; + sp = -1; + unary = 1; + stack[0].val.type = MN_INTEGER; + stack[0].val.u.l = 0; + mathparse(prec_tp == MPREC_TOP ? TOPPREC : ARGPREC); + *ep = ptr; + DPUTS(!errflag && sp > 0, + "BUG: math: wallabies roaming too freely in outback"); + + if (errflag) { + /* + * This used to set the return value to errflag. + * I don't understand how that could be useful; the + * caller doesn't know that's what's happened and + * may not get a value at all. + * Worse, we reset errflag in execarith() and setting + * this explicitly non-zero means a (( ... )) returns + * status 0 if there's an error. That surely can't + * be right. execarith() now detects an error and returns + * status 2. + */ + ret.type = MN_INTEGER; + ret.u.l = 0; + } 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; + prec = xprec; + } + return lastmathval = ret; +} + static int lexconstant(void) { @@ -521,19 +731,6 @@ zzlex(void) } } -/* 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) @@ -645,7 +842,7 @@ callmathfunc(char *o) if (f->flags & MFF_USERFUNC) { /* need to pass strings */ char *str; - marg = mathevall(a, ARGPREC, &a); + marg = mathevall(a, MPREC_ARG, &a); if (marg.type & MN_FLOAT) { /* convfloat is off the heap */ str = convfloat(marg.u.d, 0, 0, NULL); @@ -657,7 +854,7 @@ callmathfunc(char *o) addlinknode(l, str); } else { q = (mnumber *) zhalloc(sizeof(mnumber)); - *q = mathevall(a, ARGPREC, &a); + *q = mathevall(a, MPREC_ARG, &a); addlinknode(l, q); } if (errflag || mtok != COMMA) @@ -1016,91 +1213,6 @@ bop(int tk) } -/**/ -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"); - - 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) { - /* - * This used to set the return value to errflag. - * I don't understand how that could be useful; the - * caller doesn't know that's what's happened and - * may not get a value at all. - * Worse, we reset errflag in execarith() and setting - * this explicitly non-zero means a (( ... )) returns - * status 0 if there's an error. That surely can't - * be right. execarith() now detects an error and returns - * status 2. - */ - ret.type = MN_INTEGER; - ret.u.l = 0; - } 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 lastmathval = ret; -} - - /**/ mod_export mnumber matheval(char *s) @@ -1117,7 +1229,7 @@ matheval(char *s) x.u.l = 0; return x; } - x = mathevall(s, TOPPREC, &junk); + x = mathevall(s, MPREC_TOP, &junk); mtok = xmtok; if (*junk) zerr("bad math expression: illegal character: %c", *junk); @@ -1140,7 +1252,7 @@ mathevalarg(char *s, char **ss) mnumber x; int xmtok = mtok; - x = mathevall(s, ARGPREC, ss); + x = mathevall(s, MPREC_ARG, ss); if (mtok == COMMA) (*ss)--; mtok = xmtok; -- cgit 1.4.1