1
0
Fork 0
mirror of git://git.code.sf.net/p/zsh/code synced 2025-10-27 16:50:58 +01:00

Merge of 23553: retrieving array as number.

This commit is contained in:
Paul Ackersviller 2007-11-19 00:52:24 +00:00
parent 3ea0ed6cfe
commit 5cceac415d
4 changed files with 193 additions and 73 deletions

View file

@ -22,7 +22,7 @@ matching `tt(RPAR()RPAR())' are treated as a quoted expression and
arithmetic expansion performed as for an argument of tt(let). More arithmetic expansion performed as for an argument of tt(let). More
precisely, `tt(LPAR()LPAR())var(...)tt(RPAR()RPAR())' is equivalent to precisely, `tt(LPAR()LPAR())var(...)tt(RPAR()RPAR())' is equivalent to
`tt(let ")var(...)tt(")'. The return status is 0 if the arithmetic value `tt(let ")var(...)tt(")'. The return status is 0 if the arithmetic value
of the expression is non-zero, and 1 otherwise. of the expression is non-zero, 1 if it is zero, and 2 if an error occurred.
For example, the following statement For example, the following statement

View file

@ -652,9 +652,12 @@ tt(kill -IO) and tt(kill -POLL) have the same effect.
findex(let) findex(let)
item(tt(let) var(arg) ...)( item(tt(let) var(arg) ...)(
Evaluate each var(arg) as an arithmetic expression. Evaluate each var(arg) as an arithmetic expression.
See noderef(Arithmetic Evaluation) for a description See
of arithmetic expressions. The exit status is 0 if the ifzman(the section `Arithmetic Evaluation' in zmanref(zshmisc))\
value of the last expression is nonzero, and 1 otherwise. ifnzman(noderef(Arithmetic Evaluation))
for a description of arithmetic expressions. The exit status is 0 if the
value of the last expression is nonzero, 1 if it is zero, and 2 if
an error occurred.
) )
findex(limit) findex(limit)
cindex(resource limits) cindex(resource limits)

View file

@ -3323,7 +3323,10 @@ execarith(Estate state, UNUSED(int do_exec))
fprintf(xtrerr, " ))\n"); fprintf(xtrerr, " ))\n");
fflush(xtrerr); fflush(xtrerr);
} }
errflag = 0; if (errflag) {
errflag = 0;
return 2;
}
/* should test for fabs(val.u.d) < epsilon? */ /* should test for fabs(val.u.d) < epsilon? */
return (val.type == MN_INTEGER) ? val.u.l == 0 : val.u.d == 0.0; return (val.type == MN_INTEGER) ? val.u.l == 0 : val.u.d == 0.0;
} }

View file

@ -40,7 +40,7 @@ int noeval;
/* integer zero */ /* integer zero */
/**/ /**/
mnumber zero_mnumber; mod_export mnumber zero_mnumber;
/* last input base we used */ /* last input base we used */
@ -168,8 +168,8 @@ static int prec[TOKCOUNT] =
0, 16, 0 0, 16, 0
}; };
#define TOPPREC 17 #define TOPPREC 18
#define ARGPREC (TOPPREC-1) #define ARGPREC 16
static int type[TOKCOUNT] = static int type[TOKCOUNT] =
{ {
@ -186,14 +186,75 @@ static int type[TOKCOUNT] =
/* 50 */ LR|OP_OPF, RL|OP_E2, LR|OP_OPF /* 50 */ LR|OP_OPF, RL|OP_E2, LR|OP_OPF
}; };
static int
lexconstant(void)
{
#ifdef USE_LOCALE
char *prev_locale;
#endif
char *nptr;
nptr = ptr;
if (*nptr == '-')
nptr++;
if (*nptr == '0')
{
nptr++;
if (*nptr == 'x' || *nptr == 'X') {
/* Let zstrtol parse number with base */
yyval.u.l = zstrtol(ptr, &ptr, 0);
/* Should we set lastbase here? */
lastbase = 16;
return NUM;
}
else if (isset(OCTALZEROES) &&
(memchr(nptr, '.', strlen(nptr)) == NULL) &&
idigit(*nptr)) {
yyval.u.l = zstrtol(ptr, &ptr, 0);
lastbase = 8;
return NUM;
}
}
while (idigit(*nptr))
nptr++;
if (*nptr == '.' || *nptr == 'e' || *nptr == 'E') {
/* it's a float */
yyval.type = MN_FLOAT;
#ifdef USE_LOCALE
prev_locale = dupstring(setlocale(LC_NUMERIC, NULL));
setlocale(LC_NUMERIC, "POSIX");
#endif
yyval.u.d = strtod(ptr, &nptr);
#ifdef USE_LOCALE
if (prev_locale) setlocale(LC_NUMERIC, prev_locale);
#endif
if (ptr == nptr || *nptr == '.') {
zerr("bad floating point constant", NULL, 0);
return EOI;
}
ptr = nptr;
} else {
/* it's an integer */
yyval.u.l = zstrtol(ptr, &ptr, 10);
if (*ptr == '#') {
ptr++;
yyval.u.l = zstrtol(ptr, &ptr, lastbase = yyval.u.l);
}
}
return NUM;
}
/**/
int outputradix;
/**/ /**/
static int static int
zzlex(void) zzlex(void)
{ {
#ifdef USE_LOCALE
char *prev_locale;
#endif
int cct = 0; int cct = 0;
yyval.type = MN_INTEGER; yyval.type = MN_INTEGER;
@ -218,7 +279,14 @@ zzlex(void)
ptr++; ptr++;
return MINUSEQ; return MINUSEQ;
} }
return (unary) ? UMINUS : MINUS; if (unary) {
if (idigit(*ptr) || *ptr == '.') {
ptr--;
return lexconstant();
} else
return UMINUS;
} else
return MINUS;
case '(': case '(':
return M_INPAR; return M_INPAR;
case ')': case ')':
@ -340,61 +408,53 @@ zzlex(void)
return EOI; return EOI;
case '[': case '[':
{ {
int base = zstrtol(ptr, &ptr, 10); int n;
if (*ptr == ']') if (idigit(*ptr)) {
ptr++; n = zstrtol(ptr, &ptr, 10);
yyval.u.l = zstrtol(ptr, &ptr, lastbase = base); if (*ptr != ']' || !idigit(*++ptr)) {
return NUM; zerr("bad base syntax", NULL, 0);
return EOI;
}
yyval.u.l = zstrtol(ptr, &ptr, lastbase = n);
return NUM;
}
if (*ptr == '#') {
n = 1;
if (*++ptr == '#') {
n = -1;
ptr++;
}
if (!idigit(*ptr))
goto bofs;
outputradix = n * zstrtol(ptr, &ptr, 10);
} else {
bofs:
zerr("bad output format specification", NULL, 0);
return EOI;
}
if(*ptr != ']')
goto bofs;
ptr++;
break;
} }
case ' ': case ' ':
case '\t': case '\t':
case '\n': case '\n':
break; break;
case '0':
if (*ptr == 'x' || *ptr == 'X') {
/* Should we set lastbase here? */
yyval.u.l = zstrtol(++ptr, &ptr, lastbase = 16);
return NUM;
}
/* Fall through! */ /* Fall through! */
default: default:
if (idigit(*--ptr) || *ptr == '.') { if (idigit(*--ptr) || *ptr == '.')
char *nptr; return lexconstant();
for (nptr = ptr; idigit(*nptr); nptr++);
if (*nptr == '.' || *nptr == 'e' || *nptr == 'E') {
/* it's a float */
yyval.type = MN_FLOAT;
#ifdef USE_LOCALE
prev_locale = setlocale(LC_NUMERIC, NULL);
setlocale(LC_NUMERIC, "POSIX");
#endif
yyval.u.d = strtod(ptr, &nptr);
#ifdef USE_LOCALE
setlocale(LC_NUMERIC, prev_locale);
#endif
if (ptr == nptr || *nptr == '.') {
zerr("bad floating point constant", NULL, 0);
return EOI;
}
ptr = nptr;
} else {
/* it's an integer */
yyval.u.l = zstrtol(ptr, &ptr, 10);
if (*ptr == '#') {
ptr++;
yyval.u.l = zstrtol(ptr, &ptr, lastbase = yyval.u.l);
}
}
return NUM;
}
if (*ptr == '#') { if (*ptr == '#') {
if (*++ptr == '\\' || *ptr == '#') { if (*++ptr == '\\' || *ptr == '#') {
int v; int v;
ptr++; ptr++;
if (!*ptr) {
zerr("character missing after ##", NULL, 0);
return EOI;
}
ptr = getkeystring(ptr, NULL, 6, &v); ptr = getkeystring(ptr, NULL, 6, &v);
yyval.u.l = v; yyval.u.l = v;
return NUM; return NUM;
@ -446,7 +506,7 @@ static struct mathvalue *stack;
/**/ /**/
static void static void
push(mnumber val, char *lval) push(mnumber val, char *lval, int getme)
{ {
if (sp == STACKSZ - 1) if (sp == STACKSZ - 1)
zerr("stack overflow", NULL, 0); zerr("stack overflow", NULL, 0);
@ -454,8 +514,21 @@ push(mnumber val, char *lval)
sp++; sp++;
stack[sp].val = val; stack[sp].val = val;
stack[sp].lval = lval; stack[sp].lval = lval;
if (getme)
stack[sp].val.type = MN_UNSET;
} }
/**/
static mnumber
pop(int noget)
{
struct mathvalue *mv = stack+sp;
if (mv->val.type == MN_UNSET && !noget)
mv->val = getnparam(mv->lval);
sp--;
return errflag ? zero_mnumber : mv->val;
}
/**/ /**/
static mnumber static mnumber
@ -465,10 +538,12 @@ getcvar(char *s)
mnumber mn; mnumber mn;
mn.type = MN_INTEGER; mn.type = MN_INTEGER;
queue_signals();
if (!(t = getsparam(s))) if (!(t = getsparam(s)))
mn.u.l = 0; mn.u.l = 0;
else else
mn.u.l = STOUC(*t == Meta ? t[1] ^ 32 : *t); mn.u.l = STOUC(*t == Meta ? t[1] ^ 32 : *t);
unqueue_signals();
return mn; return mn;
} }
@ -485,6 +560,7 @@ setvar(char *s, mnumber v)
} }
if (noeval) if (noeval)
return v; return v;
untokenize(s);
setnparam(s, v); setnparam(s, v);
return v; return v;
} }
@ -582,8 +658,10 @@ op(int what)
if (tp & (OP_A2|OP_A2IR|OP_A2IO|OP_E2|OP_E2IO)) { if (tp & (OP_A2|OP_A2IR|OP_A2IO|OP_E2|OP_E2IO)) {
/* Make sure anyone seeing this message reports it. */ /* Make sure anyone seeing this message reports it. */
DPUTS(sp < 1, "BUG: math: not enough wallabies in outback."); DPUTS(sp < 1, "BUG: math: not enough wallabies in outback.");
b = stack[sp--].val; b = pop(0);
a = stack[sp--].val; a = pop(what == EQ);
if (errflag)
return;
if (tp & (OP_A2IO|OP_E2IO)) { if (tp & (OP_A2IO|OP_E2IO)) {
/* coerce to integers */ /* coerce to integers */
@ -595,10 +673,11 @@ op(int what)
b.type = MN_INTEGER; b.type = MN_INTEGER;
b.u.l = (zlong)b.u.d; b.u.l = (zlong)b.u.d;
} }
} else if (a.type != b.type && what != COMMA) { } else if (a.type != b.type && what != COMMA &&
(a.type != MN_UNSET || what != EQ)) {
/* /*
* Different types, so coerce to float. * Different types, so coerce to float.
* It may happen during an assigment that the LHS * It may happen during an assignment that the LHS
* variable is actually an integer, but there's still * variable is actually an integer, but there's still
* no harm in doing the arithmetic in floating point; * no harm in doing the arithmetic in floating point;
* the assignment will do the correct conversion. * the assignment will do the correct conversion.
@ -752,13 +831,15 @@ op(int what)
} }
if (tp & (OP_E2|OP_E2IO)) { if (tp & (OP_E2|OP_E2IO)) {
lv = stack[sp+1].lval; lv = stack[sp+1].lval;
push(setvar(lv,c), lv); push(setvar(lv,c), lv, 0);
} else } else
push(c,NULL); push(c,NULL, 0);
return; return;
} }
spval = &stack[sp].val; spval = &stack[sp].val;
if (stack[sp].val.type == MN_UNSET)
*spval = getnparam(stack[sp].lval);
switch (what) { switch (what) {
case NOT: case NOT:
if (spval->type & MN_FLOAT) { if (spval->type & MN_FLOAT) {
@ -804,11 +885,13 @@ op(int what)
break; break;
case QUEST: case QUEST:
DPUTS(sp < 2, "BUG: math: three shall be the number of the counting."); DPUTS(sp < 2, "BUG: math: three shall be the number of the counting.");
c = stack[sp--].val; c = pop(0);
b = stack[sp--].val; b = pop(0);
a = stack[sp--].val; a = pop(0);
if (errflag)
return;
/* b and c can stay different types in this case. */ /* b and c can stay different types in this case. */
push(((a.type & MN_FLOAT) ? a.u.d : a.u.l) ? b : c, NULL); push(((a.type & MN_FLOAT) ? a.u.d : a.u.l) ? b : c, NULL, 0);
break; break;
case COLON: case COLON:
zerr("':' without '?'", NULL, 0); zerr("':' without '?'", NULL, 0);
@ -839,7 +922,11 @@ static void
bop(int tk) bop(int tk)
{ {
mnumber *spval = &stack[sp].val; mnumber *spval = &stack[sp].val;
int tst = (spval->type & MN_FLOAT) ? (zlong)spval->u.d : spval->u.l; int tst;
if (stack[sp].val.type == MN_UNSET)
*spval = getnparam(stack[sp].lval);
tst = (spval->type & MN_FLOAT) ? (zlong)spval->u.d : spval->u.l;
switch (tk) { switch (tk) {
case DAND: case DAND:
@ -902,10 +989,29 @@ mathevall(char *s, int prek, char **ep)
stack[0].val.u.l = 0; stack[0].val.u.l = 0;
mathparse(prek); mathparse(prek);
*ep = ptr; *ep = ptr;
DPUTS(!errflag && sp, DPUTS(!errflag && sp > 0,
"BUG: math: wallabies roaming too freely in outback"); "BUG: math: wallabies roaming too freely in outback");
ret = stack[0].val; 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) { if (--mlevel) {
lastbase = xlastbase; lastbase = xlastbase;
@ -929,6 +1035,9 @@ matheval(char *s)
char *junk; char *junk;
mnumber x; mnumber x;
int xmtok = mtok; int xmtok = mtok;
/* maintain outputradix across levels of evaluation */
if (!mlevel)
outputradix = 0;
if (!*s) { if (!*s) {
x.type = MN_INTEGER; x.type = MN_INTEGER;
@ -943,7 +1052,7 @@ matheval(char *s)
} }
/**/ /**/
zlong mod_export zlong
mathevali(char *s) mathevali(char *s)
{ {
mnumber x = matheval(s); mnumber x = matheval(s);
@ -1014,22 +1123,25 @@ mathparse(int pc)
if (errflag) if (errflag)
return; return;
mtok = zzlex(); mtok = zzlex();
/* Handle empty input */
if (pc == TOPPREC && mtok == EOI)
return;
checkunary(mtok, optr); checkunary(mtok, optr);
while (prec[mtok] <= pc) { while (prec[mtok] <= pc) {
if (errflag) if (errflag)
return; return;
switch (mtok) { switch (mtok) {
case NUM: case NUM:
push(yyval, NULL); push(yyval, NULL, 0);
break; break;
case ID: case ID:
push((noeval ? zero_mnumber : getnparam(yylval)), yylval); push(zero_mnumber, yylval, !noeval);
break; break;
case CID: case CID:
push((noeval ? zero_mnumber : getcvar(yylval)), yylval); push((noeval ? zero_mnumber : getcvar(yylval)), yylval, 0);
break; break;
case FUNC: case FUNC:
push((noeval ? zero_mnumber : callmathfunc(yylval)), yylval); push((noeval ? zero_mnumber : callmathfunc(yylval)), yylval, 0);
break; break;
case M_INPAR: case M_INPAR:
mathparse(TOPPREC); mathparse(TOPPREC);
@ -1040,6 +1152,8 @@ mathparse(int pc)
} }
break; break;
case QUEST: case QUEST:
if (stack[sp].val.type == MN_UNSET)
stack[sp].val = getnparam(stack[sp].lval);
q = (stack[sp].val.type == MN_FLOAT) ? (zlong)stack[sp].val.u.d : q = (stack[sp].val.type == MN_FLOAT) ? (zlong)stack[sp].val.u.d :
stack[sp].val.u.l; stack[sp].val.u.l;