Zsh Mailing List Archive
Messages sorted by:
Reverse Date,
Date,
Thread,
Author
Re: arithmetic operator precedence
- X-seq: zsh-workers 25149
- From: Peter Stephenson <pws@xxxxxxx>
- To: Zsh hackers list <zsh-workers@xxxxxxxxxx>
- Subject: Re: arithmetic operator precedence
- Date: Thu, 12 Jun 2008 14:40:24 +0100
- In-reply-to: <20080612095723.GF5113@xxxxxxxxxxxxxxx>
- Mailing-list: contact zsh-workers-help@xxxxxxxxxx; run by ezmlm
- Organization: CSR
- References: <20080612095723.GF5113@xxxxxxxxxxxxxxx>
On Thu, 12 Jun 2008 10:57:23 +0100
Stephane Chazelas <Stephane_Chazelas@xxxxxxxx> 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 <pws@xxxxxxx> Software Engineer
CSR PLC, Churchill House, Cambridge Business Park, Cowley Road
Cambridge, CB4 0WZ, UK Tel: +44 (0)1223 692070
Messages sorted by:
Reverse Date,
Date,
Thread,
Author