mirror of
git://git.code.sf.net/p/zsh/code
synced 2024-09-22 03:40:47 +02:00
25149: add C_PRECEDENCES option to make arithmetic have C/Perl precedence
This commit is contained in:
parent
c17b4c8d77
commit
8a4af211c3
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
348
Src/math.c
348
Src/math.c
@ -159,25 +159,128 @@ static int unary = 1;
|
||||
#define FUNC 52
|
||||
#define TOKCOUNT 53
|
||||
|
||||
/* precedences */
|
||||
|
||||
static int prec[TOKCOUNT] =
|
||||
/*
|
||||
* 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] =
|
||||
{
|
||||
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, 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
|
||||
};
|
||||
|
||||
#define TOPPREC 18
|
||||
#define ARGPREC 16
|
||||
/*
|
||||
* 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] =
|
||||
{
|
||||
/* 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
|
||||
};
|
||||
|
||||
/* 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;
|
||||
|
@ -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},
|
||||
|
@ -1797,6 +1797,7 @@ enum {
|
||||
COMPLETEINWORD,
|
||||
CORRECT,
|
||||
CORRECTALL,
|
||||
CPRECEDENCES,
|
||||
CSHJUNKIEHISTORY,
|
||||
CSHJUNKIELOOPS,
|
||||
CSHJUNKIEQUOTES,
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user