From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 28707 invoked from network); 12 Jun 2008 13:41:02 -0000 X-Spam-Checker-Version: SpamAssassin 3.2.4 (2008-01-01) on f.primenet.com.au X-Spam-Level: X-Spam-Status: No, score=-2.6 required=5.0 tests=AWL,BAYES_00 autolearn=ham version=3.2.4 Received: from news.dotsrc.org (HELO a.mx.sunsite.dk) (130.225.247.88) by ns1.primenet.com.au with SMTP; 12 Jun 2008 13:41:02 -0000 Received-SPF: none (ns1.primenet.com.au: domain at sunsite.dk does not designate permitted sender hosts) Received: (qmail 19354 invoked from network); 12 Jun 2008 13:40:57 -0000 Received: from sunsite.dk (130.225.247.90) by a.mx.sunsite.dk with SMTP; 12 Jun 2008 13:40:57 -0000 Received: (qmail 11269 invoked by alias); 12 Jun 2008 13:40:54 -0000 Mailing-List: contact zsh-workers-help@sunsite.dk; run by ezmlm Precedence: bulk X-No-Archive: yes X-Seq: 25149 Received: (qmail 11252 invoked from network); 12 Jun 2008 13:40:53 -0000 Received: from bifrost.dotsrc.org (130.225.254.106) by sunsite.dk with SMTP; 12 Jun 2008 13:40:53 -0000 Received: from cluster-g.mailcontrol.com (unknown [208.87.233.190]) by bifrost.dotsrc.org (Postfix) with ESMTPS id 1B4888028AC3 for ; Thu, 12 Jun 2008 15:40:34 +0200 (CEST) Received: from cameurexb01.EUROPE.ROOT.PRI ([193.128.72.68]) by rly26g.srv.mailcontrol.com (MailControl) with ESMTP id m5CDePHE005013 for ; Thu, 12 Jun 2008 14:40:26 +0100 Received: from news01 ([10.103.143.38]) by cameurexb01.EUROPE.ROOT.PRI with Microsoft SMTPSVC(6.0.3790.3959); Thu, 12 Jun 2008 14:40:25 +0100 Date: Thu, 12 Jun 2008 14:40:24 +0100 From: Peter Stephenson To: Zsh hackers list Subject: Re: arithmetic operator precedence Message-ID: <20080612144024.542b53ad@news01> In-Reply-To: <20080612095723.GF5113@sc.homeunix.net> References: <20080612095723.GF5113@sc.homeunix.net> Organization: CSR X-Mailer: Claws Mail 3.4.0 (GTK+ 2.12.8; i386-redhat-linux-gnu) Mime-Version: 1.0 Content-Type: text/plain; charset=US-ASCII Content-Transfer-Encoding: 7bit X-OriginalArrivalTime: 12 Jun 2008 13:40:25.0057 (UTC) FILETIME=[DE379D10:01C8CC91] X-Scanned-By: MailControl A-08-50-03 (www.mailcontrol.com) on 10.71.0.136 X-Virus-Scanned: ClamAV 0.92.1/7449/Thu Jun 12 14:39:39 2008 on bifrost X-Virus-Status: Clean On Thu, 12 Jun 2008 10:57:23 +0100 Stephane Chazelas wrote: > $ zsh -c 'echo $(( 1 & 2 == 2 ))' > 0 > > "==" is meant to have precedence over &, so the above should > give 1 as in C I think. All other shells do. This is a fairly icky incompatibility, given that the manual page claims "an arithmetic expression uses nearly the same syntax, precedence, and associativity of expressions [as] in C". This adds the option C_PRECEDENCES. I'd prefer to be able just to change them, but it's been documented the other way for too long. I imagine we need to set the option for emulating any other shell. (The two long bits of code differene are mostly due to moving mathevall() so it could use a local enum in its argument list without screwing up prototyping.) Index: Doc/Zsh/arith.yo =================================================================== RCS file: /cvsroot/zsh/zsh/Doc/Zsh/arith.yo,v retrieving revision 1.12 diff -u -r1.12 arith.yo --- Doc/Zsh/arith.yo 12 Nov 2007 16:55:12 -0000 1.12 +++ Doc/Zsh/arith.yo 12 Jun 2008 13:35:40 -0000 @@ -91,8 +91,8 @@ cindex(arithmetic operators) cindex(operators, arithmetic) -An arithmetic expression uses nearly the same syntax, precedence, and -associativity of expressions in C. +An arithmetic expression uses nearly the same syntax and +associativity of expressions as in C. The following operators are supported (listed in decreasing order of precedence): @@ -119,6 +119,29 @@ operator is evaluated. Note the precedence of the bitwise AND, OR, and XOR operators. +With the option tt(C_PRECEDENCES) the precedences (but no other +properties) of the operators are altered to be the same as those in +most other languages that support the relevant operators: + +startsitem() +sitem(tt(PLUS() - ! ~ PLUS()PLUS() --))(unary plus/minus, logical NOT, complement, {pre,post}{in,de}crement) +sitem(tt(**))(exponentiation) +sitem(tt(* / %))(multiplication, division, modulus (remainder)) +sitem(tt(PLUS() -))(addition, subtraction) +sitem(tt(<< >>))(bitwise shift left, right) +sitem(tt(< > <= >=))(comparison) +sitem(tt(== !=))(equality and inequality) +sitem(tt(&))(bitwise AND) +sitem(tt(^))(bitwise XOR) +sitem(tt(|))(bitwise OR) +sitem(tt(&&))(logical AND) +sitem(tt(^^))(logical XOR) +sitem(tt(||))(logical OR) +sitem(tt(? :))(ternary operator) +sitem(tt(= PLUS()= -= *= /= %= &= ^= |= <<= >>= &&= ||= ^^= **=))(assignment) +sitem(tt(,))(comma operator) +endsitem() + cindex(mathematical functions, use of) cindex(functions, math, use of) Mathematical functions can be called with the syntax Index: Doc/Zsh/options.yo =================================================================== RCS file: /cvsroot/zsh/zsh/Doc/Zsh/options.yo,v retrieving revision 1.60 diff -u -r1.60 options.yo --- Doc/Zsh/options.yo 10 Jun 2008 08:50:48 -0000 1.60 +++ Doc/Zsh/options.yo 12 Jun 2008 13:35:40 -0000 @@ -1036,6 +1036,16 @@ hexadecimal and octal. Note that these formats will be understood on input irrespective of the setting of tt(C_BASES). ) +pindex(C_PRECEDENCES) +cindex(precedence, operator) +cindex(operator precedence) +item(tt(C_PRECEDENCES))( +This alters the precedence of arithemtic operators to be more +like C and other programming languages; +ifnzman(Arithmetic Evaluation)\ +ifzman(the section ARITHMETIC EVALUATION in zmanref(zshmisc)) +has an explicit list. +) pindex(DEBUG_BEFORE_CMD) cindex(traps, DEBUG, before or after command) cindex(DEBUG trap, before or after command) Index: Src/math.c =================================================================== RCS file: /cvsroot/zsh/zsh/Src/math.c,v retrieving revision 1.32 diff -u -r1.32 math.c --- Src/math.c 10 Jun 2008 08:50:52 -0000 1.32 +++ Src/math.c 12 Jun 2008 13:35:40 -0000 @@ -159,25 +159,128 @@ #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 @@ /* 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 @@ } } -/* 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 @@ 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 @@ 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) @@ -1017,91 +1214,6 @@ /**/ -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 @@ 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 @@ mnumber x; int xmtok = mtok; - x = mathevall(s, ARGPREC, ss); + x = mathevall(s, MPREC_ARG, ss); if (mtok == COMMA) (*ss)--; mtok = xmtok; Index: Src/options.c =================================================================== RCS file: /cvsroot/zsh/zsh/Src/options.c,v retrieving revision 1.41 diff -u -r1.41 options.c --- Src/options.c 17 Apr 2008 10:23:53 -0000 1.41 +++ Src/options.c 12 Jun 2008 13:35:40 -0000 @@ -96,6 +96,7 @@ {{NULL, "caseglob", OPT_ALL}, CASEGLOB}, {{NULL, "casematch", OPT_ALL}, CASEMATCH}, {{NULL, "cbases", 0}, CBASES}, +{{NULL, "cprecedences", OPT_EMULATE|OPT_NONZSH}, CPRECEDENCES}, {{NULL, "cdablevars", OPT_EMULATE}, CDABLEVARS}, {{NULL, "chasedots", OPT_EMULATE}, CHASEDOTS}, {{NULL, "chaselinks", OPT_EMULATE}, CHASELINKS}, Index: Src/zsh.h =================================================================== RCS file: /cvsroot/zsh/zsh/Src/zsh.h,v retrieving revision 1.137 diff -u -r1.137 zsh.h --- Src/zsh.h 8 Jun 2008 17:53:55 -0000 1.137 +++ Src/zsh.h 12 Jun 2008 13:35:40 -0000 @@ -1797,6 +1797,7 @@ COMPLETEINWORD, CORRECT, CORRECTALL, + CPRECEDENCES, CSHJUNKIEHISTORY, CSHJUNKIELOOPS, CSHJUNKIEQUOTES, Index: Test/C01arith.ztst =================================================================== RCS file: /cvsroot/zsh/zsh/Test/C01arith.ztst,v retrieving revision 1.15 diff -u -r1.15 C01arith.ztst --- Test/C01arith.ztst 10 Jun 2008 09:13:01 -0000 1.15 +++ Test/C01arith.ztst 12 Jun 2008 13:35:40 -0000 @@ -43,6 +43,16 @@ 0:precedence (arithmetic) >1591 + fn() { + setopt localoptions c_precedences + integer i + (( i = 4 - - 3 * 7 << 1 & 7 ^ 1 | 16 ** 2 )) + print $i + } + fn +0:precedence (arithmetic, with C_PRECEDENCES) +>259 + print $(( 1 < 2 || 2 < 2 && 3 > 4 )) 0:precedence (logical) >1 -- Peter Stephenson Software Engineer CSR PLC, Churchill House, Cambridge Business Park, Cowley Road Cambridge, CB4 0WZ, UK Tel: +44 (0)1223 692070