summary refs log tree commit diff
path: root/Src/math.c
diff options
context:
space:
mode:
Diffstat (limited to 'Src/math.c')
-rw-r--r--Src/math.c346
1 files changed, 229 insertions, 117 deletions
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;