about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog7
-rw-r--r--Doc/Zsh/arith.yo27
-rw-r--r--Doc/Zsh/options.yo10
-rw-r--r--Src/math.c346
-rw-r--r--Src/options.c1
-rw-r--r--Src/zsh.h1
-rw-r--r--Test/C01arith.ztst10
7 files changed, 283 insertions, 119 deletions
diff --git a/ChangeLog b/ChangeLog
index 82796996f..698f6b2d1 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@
+2008-06-12  Peter Stephenson  <pws@csr.com>
+
+	* 25149: Doc/Zsh/arith.yo, Doc/Zsh/options.yo, Src/math.c,
+	Src/options.c, Src/zsh.h, Test/C01arith.ztst:  add
+	C_PRECEDENCES option to make arithmetic operators have
+	C (or, where necessary, Perl) precedences.
+
 2008-06-11  Peter Stephenson  <pws@csr.com>
 
 	* 25145: Src/exec.c, Test/E02xtrace.ztst: make sure XTRACE output
diff --git a/Doc/Zsh/arith.yo b/Doc/Zsh/arith.yo
index d7f1ae608..423e6c1bc 100644
--- a/Doc/Zsh/arith.yo
+++ b/Doc/Zsh/arith.yo
@@ -91,8 +91,8 @@ taken for a parameter name.
 
 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 @@ short-circuiting, and only one of the latter two expressions in a ternary
 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
diff --git a/Doc/Zsh/options.yo b/Doc/Zsh/options.yo
index e2e36dc5f..eec951d4f 100644
--- a/Doc/Zsh/options.yo
+++ b/Doc/Zsh/options.yo
@@ -1036,6 +1036,16 @@ on the choice of the output base, nor on the output of bases other than
 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)
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)
@@ -1017,91 +1214,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;
diff --git a/Src/options.c b/Src/options.c
index 9fb3f3388..e59736153 100644
--- a/Src/options.c
+++ b/Src/options.c
@@ -96,6 +96,7 @@ static struct optname optns[] = {
 {{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},
diff --git a/Src/zsh.h b/Src/zsh.h
index b5de54bc9..da9664ef5 100644
--- a/Src/zsh.h
+++ b/Src/zsh.h
@@ -1797,6 +1797,7 @@ enum {
     COMPLETEINWORD,
     CORRECT,
     CORRECTALL,
+    CPRECEDENCES,
     CSHJUNKIEHISTORY,
     CSHJUNKIELOOPS,
     CSHJUNKIEQUOTES,
diff --git a/Test/C01arith.ztst b/Test/C01arith.ztst
index 4e3585fd4..5179948bd 100644
--- a/Test/C01arith.ztst
+++ b/Test/C01arith.ztst
@@ -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