1
0
Fork 0
mirror of git://git.code.sf.net/p/zsh/code synced 2025-09-02 22:11:54 +02:00

manual/7915

This commit is contained in:
Tanaka Akira 1999-09-17 15:12:01 +00:00
parent 77c1b9a3d4
commit d0f024f152
12 changed files with 812 additions and 354 deletions

View file

@ -6,12 +6,13 @@ sect(Arithmetic Evaluation)
cindex(arithmetic evaluation)
cindex(evaluation, arithmetic)
findex(let, use of)
The shell can perform integer arithmetic, either using the builtin tt(let),
or via a substitution of the form tt($((...))). The shell is usually
compiled to use 8-byte precision where this is available, otherwise
precision is 4 bytes. This can be tested, for example, by giving the
command `tt(print - $(( 12345678901 )))'; if the number appears unchanged,
the precision is at least 8 bytes.
The shell can perform integer and floating point arithmetic, either using
the builtin tt(let), or via a substitution of the form tt($((...))). For
integers, the shell is usually compiled to use 8-byte precision where this
is available, otherwise precision is 4 bytes. This can be tested, for
example, by giving the command `tt(print - $(( 12345678901 )))'; if the
number appears unchanged, the precision is at least 8 bytes. Floating
point arithmetic is always double precision.
The tt(let) builtin command takes arithmetic expressions as arguments; each
is evaluated separately. Since many of the arithmetic operators, as well
@ -32,9 +33,9 @@ both assigning the value 3 to the shell variable tt(foo) and returning a
zero status.
cindex(bases, in arithmetic)
Numbers can be in bases other than 10.
Integers can be in bases other than 10.
A leading `tt(0x)' or `tt(0X)' denotes hexadecimal.
Numbers may also be of the form `var(base)tt(#)var(n)',
Integers may also be of the form `var(base)tt(#)var(n)',
where var(base) is a decimal number between two and thirty-six
representing the arithmetic base and var(n)
is a number in that base (for example, `tt(16#ff)' is 255 in hexadecimal).
@ -42,6 +43,11 @@ The var(base)tt(#) may also be omitted, in which case
base 10 is used. For backwards compatibility the form
`tt([)var(base)tt(])var(n)' is also accepted.
Floating point constants are recognized by the presence of a decimal point
or an exponent. The decimal point may be the first character of the
constant, but the exponent character tt(e) or tt(E) may not, as it will be
taken for a parameter name.
cindex(arithmetic operators)
cindex(operators, arithmetic)
An arithmetic expression uses nearly the same syntax, precedence, and
@ -67,9 +73,9 @@ sitem(tt(= PLUS()= -= *= /= %= &= ^= |= <<= >>= &&= ||= ^^= **=))(assignment)
sitem(tt(,))(comma operator)
endsitem()
The operators `tt(&&)', `tt(||)', `tt(&&=)', and `tt(||=)' are short-circuiting,
and only one of the latter two expressions in a ternary operator
is evaluated. Note the precedence of the bitwise AND, OR,
The operators `tt(&&)', `tt(||)', `tt(&&=)', and `tt(||=)' are
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.
An expression of the form `tt(#\)var(x)' where var(x) is any character
@ -95,4 +101,41 @@ cindex(integer parameters)
findex(integer, use of)
Arithmetic evaluation is performed on the value of each
assignment to a named parameter declared integer
in this manner.
in this manner. Assigning a floating point number to an integer results in
rounding down to the next integer.
cindex(parameters, floating point)
cindex(floating point parameters)
findex(float, use of)
Likewise, floating point numbers can be declared with the tt(float)
builtin; there are two types, differing only in their output format, as
described for the tt(typeset) builtin. The output format can be bypassed
by using arithmetic substitution instead of the parameter substitution,
i.e. `tt(${)var(float)tt(})' uses the defined format, but
`tt($LPAR()LPAR())var(float)tt(RPAR()RPAR())' uses a generic floating point
format.
Promotion of integer to floating point values is performed where
necessary. In addition, if any operator which requires an integer
(`tt(~)', `tt(&)', `tt(|)', `tt(^)', `tt(%)', `tt(<<)', `tt(>>)' and their
equivalents with assignment) is given a floating point argument, it will be
silently rounded down to the next integer.
Scalar variables can hold integer or floating point values at different
times; there is no memory of the numeric type in this case.
If a variable is first assigned in a numeric context without previously
being declared, it will be implicitly typed as tt(integer) or tt(float) and
retain that type either until the type is explicitly changed or until the
end of the scope. This can have unforeseen consequences. For example, in
the loop
example(for (( f = 0; f < 1; f += 0.1 )); do;
# use $f
done)
if tt(f) has not already been declared, the first assignment will cause it
to be created as an integer, and consequently the operation `tt(f += 0.1)'
will always cause the result to be truncated to zero, so that the loop will
fail. A simple fix would be to turn the initialization into `tt(f = 0.0)'.
It is therefore best to declare numeric variables with explicit types.

View file

@ -347,6 +347,11 @@ item(var(job) ...)(
Bring each specified var(job) in turn to the foreground.
If no var(job) is specified, resume the current job.
)
findex(float)
item(tt(float) [ {tt(PLUE())|tt(-)}tt(EFghlrtux) ] [ var(name)[tt(=)var(value)] ... ])(
Equivalent to tt(typeset -E), except that options irrelevant to floating
point numbers are not permitted.
)
findex(functions)
item(tt(functions) [ {tt(PLUS())|tt(-)}tt(tum) ] [ var(name) ... ])(
Equivalent to tt(typeset -f).
@ -533,7 +538,7 @@ sitem([var(mm)tt(:)]var(ss))(minutes and seconds)
endsitem()
)
findex(local)
item(tt(local) [ {tt(PLUS())|tt(-)}tt(ALRUZahilrtu) [var(n)]] [ var(name)[tt(=)var(value)] ] ...)(
item(tt(local) [ {tt(PLUS())|tt(-)}tt(AEFLRUZahilrtu) [var(n)]] [ var(name)[tt(=)var(value)] ] ...)(
Same as tt(typeset), except that the options tt(-g), tt(-x) and
tt(-f) are not permitted.
)
@ -922,7 +927,7 @@ Equivalent to tt(whence -v).
findex(typeset)
cindex(parameters, setting)
cindex(parameters, declaring)
xitem(tt(typeset) [ {tt(PLUS())|tt(-)}tt(ALRUZafghilrtuxm) [var(n)]] [ \
xitem(tt(typeset) [ {tt(PLUS())|tt(-)}tt(AEFLRUZafghilrtuxm) [var(n)]] [ \
var(name)[tt(=)var(value)] ... ])
item(tt(typeset) -T [ {tt(PLUS()|tt(-))}tt(LRUZrux) ] \
var(SCALAR)[tt(=)var(value)] var(array))(
@ -1061,6 +1066,18 @@ Use an internal integer representation. If var(n) is nonzero it
defines the output arithmetic base, otherwise it is determined by the
first assignment.
)
item(tt(-E))(
Use an internal double-precision floating point representation. On output
the variable will be converted to scientific notation. If var(n) is
nonzero it defines the number of significant figures to display; the
default is ten.
)
item(tt(-F))(
Use an internal double-precision floating point representation. On output
the variable will be converted to fixed-point decimal notation. If var(n)
is nonzero it defines the number of digits to display after the decimal
point; the default is ten.
)
item(tt(-l))(
Convert the result to lower case whenever the parameter is expanded.
The value is em(not) converted when assigned.

View file

@ -90,6 +90,8 @@ paramtypestr(Param pm)
case PM_SCALAR: val = "scalar"; break;
case PM_ARRAY: val = "array"; break;
case PM_INTEGER: val = "integer"; break;
case PM_EFLOAT:
case PM_FFLOAT: val = "float"; break;
case PM_HASHED: val = "association"; break;
}
DPUTS(!val, "BUG: type not handled in parameter");

View file

@ -50,7 +50,7 @@ static struct builtin builtins[] =
BUILTIN("cd", 0, bin_cd, 0, 2, BIN_CD, NULL, NULL),
BUILTIN("chdir", 0, bin_cd, 0, 2, BIN_CD, NULL, NULL),
BUILTIN("continue", BINF_PSPECIAL, bin_break, 0, 1, BIN_CONTINUE, NULL, NULL),
BUILTIN("declare", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "ALRTUZafghilrtux", NULL),
BUILTIN("declare", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "AEFLRTUZafghilrtux", NULL),
BUILTIN("dirs", 0, bin_dirs, 0, -1, 0, "v", NULL),
BUILTIN("disable", 0, bin_enable, 0, -1, BIN_DISABLE, "afmr", NULL),
BUILTIN("disown", 0, bin_fg, 0, -1, BIN_DISOWN, NULL, NULL),
@ -60,10 +60,11 @@ static struct builtin builtins[] =
BUILTIN("enable", 0, bin_enable, 0, -1, BIN_ENABLE, "afmr", NULL),
BUILTIN("eval", BINF_PSPECIAL, bin_eval, 0, -1, BIN_EVAL, NULL, NULL),
BUILTIN("exit", BINF_PSPECIAL, bin_break, 0, 1, BIN_EXIT, NULL, NULL),
BUILTIN("export", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, BIN_EXPORT, "LRTUZafhilrtu", "xg"),
BUILTIN("export", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, BIN_EXPORT, "EFLRTUZafhilrtu", "xg"),
BUILTIN("false", 0, bin_false, 0, -1, 0, NULL, NULL),
BUILTIN("fc", BINF_FCOPTS, bin_fc, 0, -1, BIN_FC, "nlreIRWAdDfEim", NULL),
BUILTIN("fg", 0, bin_fg, 0, -1, BIN_FG, NULL, NULL),
BUILTIN("float", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "EFghlrtux", "E"),
BUILTIN("functions", BINF_TYPEOPTS, bin_functions, 0, -1, 0, "mtuU", NULL),
BUILTIN("getln", 0, bin_read, 0, -1, 0, "ecnAlE", "zr"),
BUILTIN("getopts", 0, bin_getopts, 2, -1, 0, NULL, NULL),
@ -78,7 +79,7 @@ static struct builtin builtins[] =
BUILTIN("jobs", 0, bin_fg, 0, -1, BIN_JOBS, "dlpZrs", NULL),
BUILTIN("kill", 0, bin_kill, 0, -1, 0, NULL, NULL),
BUILTIN("let", 0, bin_let, 1, -1, 0, NULL, NULL),
BUILTIN("local", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "ALRTUZahilrtu", NULL),
BUILTIN("local", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "AEFLRTUZahilrtu", NULL),
BUILTIN("log", 0, bin_log, 0, 0, 0, NULL, NULL),
BUILTIN("logout", 0, bin_break, 0, 1, BIN_LOGOUT, NULL, NULL),
@ -97,7 +98,7 @@ static struct builtin builtins[] =
BUILTIN("pwd", 0, bin_pwd, 0, 0, 0, "rLP", NULL),
BUILTIN("r", BINF_R, bin_fc, 0, -1, BIN_FC, "nrl", NULL),
BUILTIN("read", 0, bin_read, 0, -1, 0, "rzu0123456789pkqecnAlE", NULL),
BUILTIN("readonly", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "ALRTUZafghiltux", "r"),
BUILTIN("readonly", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "AEFLRTUZafghiltux", "r"),
BUILTIN("rehash", 0, bin_hash, 0, 0, 0, "dfv", "r"),
BUILTIN("return", BINF_PSPECIAL, bin_break, 0, 1, BIN_RETURN, NULL, NULL),
BUILTIN("set", BINF_PSPECIAL, bin_set, 0, -1, 0, NULL, NULL),
@ -111,7 +112,7 @@ static struct builtin builtins[] =
BUILTIN("trap", BINF_PSPECIAL, bin_trap, 0, -1, 0, NULL, NULL),
BUILTIN("true", 0, bin_true, 0, -1, 0, NULL, NULL),
BUILTIN("type", 0, bin_whence, 0, -1, 0, "ampfsw", "v"),
BUILTIN("typeset", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "ALRTUZafghilrtuxm", NULL),
BUILTIN("typeset", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "AEFLRTUZafghilrtuxm", NULL),
BUILTIN("umask", 0, bin_umask, 0, 1, 0, "S", NULL),
BUILTIN("unalias", 0, bin_unhash, 1, -1, 0, "m", "a"),
BUILTIN("unfunction", 0, bin_unhash, 1, -1, 0, "m", "f"),
@ -215,6 +216,7 @@ execbuiltin(LinkList args, Builtin bn)
LinkNode n;
char ops[MAX_OPS], *arg, *pp, *name, **argv, **oargv, *optstr;
char *oxarg, *xarg = NULL;
char typenumstr[] = TYPESET_OPTNUM;
int flags, sense, argc = 0, execop, xtr = isset(XTRACE), lxarg = 0;
/* initialise some static variables */
@ -289,8 +291,7 @@ execbuiltin(LinkList args, Builtin bn)
/* "typeset" may take a numeric argument *
* at the tail of the options */
if (idigit(*arg) && (flags & BINF_TYPEOPT) &&
(arg[-1] == 'L' || arg[-1] == 'R' ||
arg[-1] == 'Z' || arg[-1] == 'i'))
strchr(typenumstr, arg[-1]))
auxlen = (int)zstrtol(arg, &arg, 10);
/* The above loop may have exited on an invalid option. (We *
* assume that any option requiring metafication is invalid.) */
@ -315,9 +316,9 @@ execbuiltin(LinkList args, Builtin bn)
auxdata = arg;
arg = (char *) ugetnode(args);
}
/* for "typeset", -L, -R, -Z and -i take a numeric extra argument */
if ((flags & BINF_TYPEOPT) && (execop == 'L' || execop == 'R' ||
execop == 'Z' || execop == 'i') && arg && idigit(*arg)) {
/* some "typeset" options take a numeric extra argument */
if ((flags & BINF_TYPEOPT) && strchr(typenumstr, execop) &&
arg && idigit(*arg)) {
auxlen = atoi(arg);
arg = (char *) ugetnode(args);
}
@ -1571,10 +1572,15 @@ typeset_single(char *cname, char *pname, Param pm, int func,
}
/* attempting a type conversion, or making a tied colonarray? */
if ((tc = (usepm || newspecial)
&& (((off & pm->flags) | (on & ~pm->flags)) &
(PM_INTEGER|PM_HASHED|PM_ARRAY|PM_TIED|PM_AUTOLOAD))))
usepm = 0;
tc = 0;
if (usepm || newspecial) {
int chflags = ((off & pm->flags) | (on & ~pm->flags)) &
(PM_INTEGER|PM_EFLOAT|PM_FFLOAT|PM_HASHED|
PM_ARRAY|PM_TIED|PM_AUTOLOAD);
/* keep the parameter if just switching between floating types */
if ((tc = chflags && chflags != (PM_EFLOAT|PM_FFLOAT)))
usepm = 0;
}
if (tc && (pm->flags & PM_SPECIAL)) {
zerrnam(cname, "%s: can't change type of a special parameter",
pname, 0);
@ -1615,7 +1621,8 @@ typeset_single(char *cname, char *pname, Param pm, int func,
}
pm->flags = (pm->flags | on) & ~off;
/* This auxlen/pm->ct stuff is a nasty hack. */
if ((on & (PM_LEFT | PM_RIGHT_B | PM_RIGHT_Z | PM_INTEGER)) &&
if ((on & (PM_LEFT | PM_RIGHT_B | PM_RIGHT_Z | PM_INTEGER |
PM_EFLOAT | PM_FFLOAT)) &&
auxlen)
pm->ct = auxlen;
if (!(pm->flags & (PM_ARRAY|PM_HASHED))) {
@ -1711,7 +1718,8 @@ typeset_single(char *cname, char *pname, Param pm, int func,
* Final tweak: if we've turned on one of the flags with
* numbers, we should use the appropriate integer.
*/
if (on & (PM_LEFT|PM_RIGHT_B|PM_RIGHT_Z|PM_INTEGER))
if (on & (PM_LEFT|PM_RIGHT_B|PM_RIGHT_Z|PM_INTEGER|
PM_EFLOAT|PM_FFLOAT))
pm->ct = auxlen;
else
pm->ct = 0;
@ -1756,6 +1764,10 @@ typeset_single(char *cname, char *pname, Param pm, int func,
case PM_INTEGER:
pm->sets.ifn(pm, 0);
break;
case PM_EFLOAT:
case PM_FFLOAT:
pm->sets.ffn(pm, 0.0);
break;
case PM_ARRAY:
pm->sets.afn(pm, mkarray(NULL));
break;
@ -1785,7 +1797,7 @@ bin_typeset(char *name, char **argv, char *ops, int func)
Param pm;
Asgment asg;
Patprog pprog;
char *optstr = "aiALRZlurtxUhT";
char *optstr = TYPESET_OPTSTR;
int on = 0, off = 0, roff, bit = PM_ARRAY;
int i;
int returnval = 0, printflags = 0;
@ -1805,14 +1817,24 @@ bin_typeset(char *name, char **argv, char *ops, int func)
roff = off;
/* Sanity checks on the options. Remove conficting options. */
if (on & PM_FFLOAT) {
off |= PM_RIGHT_B | PM_LEFT | PM_RIGHT_Z | PM_UPPER | PM_ARRAY
| PM_INTEGER | PM_EFLOAT;
/* Allow `float -F' to work even though float sets -E by default */
on &= ~PM_EFLOAT;
}
if (on & PM_EFLOAT)
off |= PM_RIGHT_B | PM_LEFT | PM_RIGHT_Z | PM_UPPER | PM_ARRAY
| PM_INTEGER | PM_FFLOAT;
if (on & PM_INTEGER)
off |= PM_RIGHT_B | PM_LEFT | PM_RIGHT_Z | PM_UPPER | PM_ARRAY;
off |= PM_RIGHT_B | PM_LEFT | PM_RIGHT_Z | PM_UPPER | PM_ARRAY |
PM_EFLOAT | PM_FFLOAT;
if (on & PM_LEFT)
off |= PM_RIGHT_B | PM_INTEGER;
off |= PM_RIGHT_B | PM_INTEGER | PM_EFLOAT | PM_FFLOAT;
if (on & PM_RIGHT_B)
off |= PM_LEFT | PM_INTEGER;
off |= PM_LEFT | PM_INTEGER | PM_EFLOAT | PM_FFLOAT;
if (on & PM_RIGHT_Z)
off |= PM_INTEGER;
off |= PM_INTEGER | PM_EFLOAT | PM_FFLOAT;
if (on & PM_UPPER)
off |= PM_LOWER;
if (on & PM_LOWER)
@ -1820,7 +1842,7 @@ bin_typeset(char *name, char **argv, char *ops, int func)
if (on & PM_HASHED)
off |= PM_ARRAY;
if (on & PM_TIED)
off |= PM_INTEGER | PM_ARRAY | PM_HASHED;
off |= PM_INTEGER | PM_EFLOAT | PM_FFLOAT | PM_ARRAY | PM_HASHED;
on &= ~off;
@ -2900,7 +2922,7 @@ bin_shift(char *name, char **argv, char *ops, int func)
/* optional argument can be either numeric or an array */
if (*argv && !getaparam(*argv))
num = matheval(*argv++);
num = mathevali(*argv++);
if (num < 0) {
zwarnnam(name, "argument to shift must be non-negative", NULL, 0);
@ -3042,7 +3064,7 @@ bin_break(char *name, char **argv, char *ops, int func)
/* handle one optional numeric argument */
if (*argv) {
num = matheval(*argv++);
num = mathevali(*argv++);
nump = 1;
}
@ -3857,7 +3879,7 @@ bin_let(char *name, char **argv, char *ops, int func)
zlong val = 0;
while (*argv)
val = matheval(*argv++);
val = mathevali(*argv++);
/* Errors in math evaluation in let are non-fatal. */
errflag = 0;
return !val;

View file

@ -127,6 +127,45 @@ evalcond(Cond c)
fprintf(stderr, " -%c %s", c->type, (char *)left);
}
if (c->type >= COND_EQ && c->type <= COND_GE) {
mnumber mn1, mn2;
mn1 = matheval(left);
mn2 = matheval(right);
if (((mn1.type|mn2.type) & (MN_INTEGER|MN_FLOAT)) ==
(MN_INTEGER|MN_FLOAT)) {
/* promote to float */
if (mn1.type & MN_INTEGER) {
mn1.type = MN_FLOAT;
mn1.u.d = (double)mn1.u.l;
}
if (mn2.type & MN_INTEGER) {
mn2.type = MN_FLOAT;
mn2.u.d = (double)mn2.u.l;
}
}
switch(c->type) {
case COND_EQ:
return (mn1.type & MN_FLOAT) ? (mn1.u.d == mn2.u.d) :
(mn1.u.l == mn2.u.l);
case COND_NE:
return (mn1.type & MN_FLOAT) ? (mn1.u.d != mn2.u.d) :
(mn1.u.l != mn2.u.l);
case COND_LT:
return (mn1.type & MN_FLOAT) ? (mn1.u.d < mn2.u.d) :
(mn1.u.l < mn2.u.l);
case COND_GT:
return (mn1.type & MN_FLOAT) ? (mn1.u.d > mn2.u.d) :
(mn1.u.l > mn2.u.l);
case COND_LE:
return (mn1.type & MN_FLOAT) ? (mn1.u.d <= mn2.u.d) :
(mn1.u.l <= mn2.u.l);
case COND_GE:
return (mn1.type & MN_FLOAT) ? (mn1.u.d >= mn2.u.d) :
(mn1.u.l >= mn2.u.l);
}
}
switch (c->type) {
case COND_STREQ:
return matchpat(left, right);
@ -185,19 +224,7 @@ evalcond(Cond c)
case 'N':
return ((st = getstat(left)) && st->st_atime <= st->st_mtime);
case 't':
return isatty(matheval(left));
case COND_EQ:
return matheval(left) == matheval(right);
case COND_NE:
return matheval(left) != matheval(right);
case COND_LT:
return matheval(left) < matheval(right);
case COND_GT:
return matheval(left) > matheval(right);
case COND_LE:
return matheval(left) <= matheval(right);
case COND_GE:
return matheval(left) >= matheval(right);
return isatty(mathevali(left));
case COND_NT:
case COND_OT:
{
@ -323,7 +350,7 @@ cond_val(char **args, int num)
singsub(&s);
untokenize(s);
return matheval(s);
return mathevali(s);
}
/**/

View file

@ -2182,6 +2182,10 @@ restore_params(LinkList restorelist, LinkList removelist)
case PM_INTEGER:
tpm->sets.ifn(tpm, pm->u.val);
break;
case PM_EFLOAT:
case PM_FFLOAT:
tpm->sets.ffn(tpm, pm->u.dval);
break;
case PM_ARRAY:
tpm->sets.afn(tpm, pm->u.arr);
break;
@ -2757,7 +2761,7 @@ execarith(Cmd cmd, LinkList args, int flags)
while ((e = (char *) ugetnode(args))) {
if (isset(XTRACE))
fprintf(stderr, " %s", e);
val = matheval(e);
val = mathevali(e);
}
if (isset(XTRACE)) {
fprintf(stderr, " ))\n");

View file

@ -30,6 +30,8 @@
#include "zsh.mdh"
#include "math.pro"
#include <math.h>
/* nonzero means we are not evaluating, just parsing */
/**/
@ -42,7 +44,7 @@ int lastbase;
static char *ptr;
static zlong yyval;
static mnumber yyval;
static LV yylval;
static int mlevel = 0;
@ -53,12 +55,27 @@ static int unary = 1;
/* LR = left-to-right associativity *
* RL = right-to-left associativity *
* BOO = short-circuiting boolean */
* BOOL = short-circuiting boolean */
#define LR 0
#define RL 1
#define BOOL 2
#define MTYPE(x) ((x) & 3)
/*
* OP_A2 2 argument
* OP_A2IR 2 argument with return type integer
* OP_A2IO 2 arguments, must be integer, returning integer
* OP_E2 2 argument with assignment
* OP_E2IO 2 arguments with assignment, must be integer, return integer
*/
#define OP_A2 4
#define OP_A2IR 8
#define OP_A2IO 16
#define OP_E2 32
#define OP_E2IO 64
#define M_INPAR 0
#define M_OUTPAR 1
#define NOT 2
@ -135,17 +152,17 @@ static int prec[TOKCOUNT] =
static int type[TOKCOUNT] =
{
LR, LR, RL, RL, RL,
RL, RL, RL, LR, LR,
LR, LR, LR, LR, LR,
LR, LR, LR, LR, LR,
LR, LR, LR, LR, BOOL,
BOOL, LR, RL, RL, RL,
RL, RL, RL, RL, RL,
RL, RL, RL, RL, RL,
BOOL, BOOL, RL, RL, RL,
RL, RL, LR, LR, RL,
LR, RL
/* 0 */ LR, LR, RL, RL, RL,
/* 5 */ RL, RL, RL, LR|OP_A2IO, LR|OP_A2IO,
/* 10 */ LR|OP_A2IO, LR|OP_A2, LR|OP_A2, LR|OP_A2IO, LR|OP_A2,
/* 15 */ LR|OP_A2, LR|OP_A2IO, LR|OP_A2IO, LR|OP_A2IR, LR|OP_A2IR,
/* 20 */ LR|OP_A2IR, LR|OP_A2IR, LR|OP_A2IR, LR|OP_A2IR, BOOL|OP_A2IO,
/* 25 */ BOOL|OP_A2IO, LR|OP_A2IO, RL, RL, RL|OP_E2,
/* 30 */ RL|OP_E2, RL|OP_E2, RL|OP_E2, RL|OP_E2, RL|OP_E2IO,
/* 35 */ RL|OP_E2IO, RL|OP_E2IO, RL|OP_E2IO, RL|OP_E2IO, RL|OP_E2IO,
/* 40 */ BOOL|OP_E2IO, BOOL|OP_E2IO, RL|OP_A2IO, RL|OP_A2, RL,
/* 45 */ RL, RL, LR, LR, RL|OP_A2,
/* 50 */ LR, RL|OP_E2
};
#define LVCOUNT 32
@ -162,6 +179,7 @@ zzlex(void)
{
int cct = 0;
yyval.type = MN_INTEGER;
for (;; cct = 0)
switch (*ptr++) {
case '+':
@ -302,11 +320,11 @@ zzlex(void)
return EQ;
case '$':
unary = 0;
yyval = mypid;
yyval.u.l = mypid;
return NUM;
case '?':
if (unary) {
yyval = lastval;
yyval.u.l = lastval;
unary = 0;
return NUM;
}
@ -329,7 +347,7 @@ zzlex(void)
if (*ptr == ']')
ptr++;
yyval = zstrtol(ptr, &ptr, lastbase = base);
yyval.u.l = zstrtol(ptr, &ptr, lastbase = base);
return NUM;
}
case ' ':
@ -340,18 +358,33 @@ zzlex(void)
if (*ptr == 'x' || *ptr == 'X') {
unary = 0;
/* Should we set lastbase here? */
yyval = zstrtol(++ptr, &ptr, lastbase = 16);
yyval.u.l = zstrtol(++ptr, &ptr, lastbase = 16);
return NUM;
}
/* Fall through! */
default:
if (idigit(*--ptr)) {
if (idigit(*--ptr) || *ptr == '.') {
char *nptr;
unary = 0;
yyval = zstrtol(ptr, &ptr, 10);
for (nptr = ptr; idigit(*nptr); nptr++);
if (*ptr == '#') {
ptr++;
yyval = zstrtol(ptr, &ptr, lastbase = yyval);
if (*nptr == '.' || *nptr == 'e' || *nptr == 'E') {
/* it's a float */
yyval.type = MN_FLOAT;
yyval.u.d = strtod(ptr, &nptr);
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;
}
@ -361,7 +394,7 @@ zzlex(void)
ptr++;
ptr = getkeystring(ptr, NULL, 6, &v);
yyval = v;
yyval.u.l = v;
unary = 0;
return NUM;
}
@ -395,7 +428,7 @@ zzlex(void)
return cct ? CID : ID;
}
else if (cct) {
yyval = poundgetfn(NULL);
yyval.u.l = poundgetfn(NULL);
unary = 0;
return NUM;
}
@ -411,14 +444,14 @@ static int sp = -1; /* stack pointer */
struct mathvalue {
LV lval;
zlong val;
mnumber val;
};
static struct mathvalue *stack;
/**/
static void
push(zlong val, LV lval)
push(mnumber val, LV lval)
{
if (sp == STACKSZ - 1)
zerr("stack overflow", NULL, 0);
@ -430,275 +463,308 @@ push(zlong val, LV lval)
/**/
static zlong
static mnumber
getcvar(LV s)
{
char *t;
mnumber mn;
mn.type = MN_INTEGER;
if (!(t = getsparam(lvals[s])))
return 0;
return STOUC(*t == Meta ? t[1] ^ 32 : *t);
mn.u.l = 0;
else
mn.u.l = STOUC(*t == Meta ? t[1] ^ 32 : *t);
return mn;
}
/**/
static zlong
setvar(LV s, zlong v)
static mnumber
setvar(LV s, mnumber v)
{
if (s == -1 || s >= lvc) {
zerr("lvalue required", NULL, 0);
return 0;
v.type = MN_INTEGER;
v.u.l = 0;
}
if (noeval)
return v;
setiparam(lvals[s], v);
setnparam(lvals[s], v);
return v;
}
/**/
static int
notzero(zlong a)
notzero(mnumber a)
{
if (a == 0) {
if ((a.type & MN_INTEGER) ? a.u.l == 0 : a.u.d == 0.0) {
zerr("division by zero", NULL, 0);
return 0;
}
return 1;
}
/* macro to pop two values off the value stack */
#define pop2() { \
if (sp < 1) { \
zerr("bad math expression: unbalanced stack", NULL, 0); \
return; \
} \
b = stack[sp--].val; \
a = stack[sp--].val; \
}
/* macro to pop three values off the value stack */
#define pop3() { \
if (sp < 2) { \
zerr("bad math expression: unbalanced stack", NULL, 0); \
return; \
} \
c = stack[sp--].val; \
b = stack[sp--].val; \
a = stack[sp--].val; \
}
#define nolval() {stack[sp].lval= -1;}
#define pushv(X) { push(X,-1); }
#define pop2lv() { pop2() lv = stack[sp+1].lval; }
#define set(X) { push(setvar(lv,X),lv); }
/**/
void
op(int what)
{
zlong a, b, c;
mnumber a, b, c, *spval;
LV lv;
int tp = type[what];
if (sp < 0) {
zerr("bad math expression: stack empty", NULL, 0);
return;
}
if (tp & (OP_A2|OP_A2IR|OP_A2IO|OP_E2|OP_E2IO)) {
if (sp < 1) {
zerr("bad math expression: unbalanced stack", NULL, 0); \
return;
}
b = stack[sp--].val;
a = stack[sp--].val;
if (tp & (OP_A2IO|OP_E2IO)) {
/* coerce to integers */
if (a.type & MN_FLOAT) {
a.type = MN_INTEGER;
a.u.l = (zlong)a.u.d;
}
if (b.type & MN_FLOAT) {
b.type = MN_INTEGER;
b.u.l = (zlong)b.u.d;
}
} else if (a.type != b.type && what != COMMA) {
/*
* Different types, so coerce to float.
* It may happen during an assigment that the LHS
* variable is actually an integer, but there's still
* no harm in doing the arithmetic in floating point;
* the assignment will do the correct conversion.
* This way, if the parameter is actually a scalar, but
* used to contain an integer, we can write a float into it.
*/
if (a.type & MN_INTEGER) {
a.type = MN_FLOAT;
a.u.d = (double)a.u.l;
}
if (b.type & MN_INTEGER) {
b.type = MN_FLOAT;
b.u.d = (double)b.u.l;
}
}
/*
* type for operation: usually same as operands, but e.g.
* (a == b) returns int.
*/
c.type = (tp & OP_A2IR) ? MN_INTEGER : a.type;
switch(what) {
case AND:
case ANDEQ:
c.u.l = a.u.l & b.u.l;
break;
case XOR:
case XOREQ:
c.u.l = a.u.l ^ b.u.l;
break;
case OR:
case OREQ:
c.u.l = a.u.l | b.u.l;
break;
case MUL:
case MULEQ:
if (c.type == MN_FLOAT)
c.u.d = a.u.d * b.u.d;
else
c.u.l = a.u.l * b.u.l;
break;
case DIV:
case DIVEQ:
if (!notzero(b))
return;
if (c.type == MN_FLOAT)
c.u.d = a.u.d / b.u.d;
else
c.u.l = a.u.l / b.u.l;
break;
case MOD:
case MODEQ:
if (!notzero(b))
return;
c.u.l = a.u.l % b.u.l;
break;
case PLUS:
case PLUSEQ:
if (c.type == MN_FLOAT)
c.u.d = a.u.d + b.u.d;
else
c.u.l = a.u.l + b.u.l;
break;
case MINUS:
case MINUSEQ:
if (c.type == MN_FLOAT)
c.u.d = a.u.d - b.u.d;
else
c.u.l = a.u.l - b.u.l;
break;
case SHLEFT:
case SHLEFTEQ:
c.u.l = a.u.l << b.u.l;
break;
case SHRIGHT:
case SHRIGHTEQ:
c.u.l = a.u.l >> b.u.l;
break;
case LES:
c.u.l = (zlong)
(a.type == MN_FLOAT ? (a.u.d < b.u.d) : (a.u.l < b.u.l));
break;
case LEQ:
c.u.l = (zlong)
(a.type == MN_FLOAT ? (a.u.d <= b.u.d) : (a.u.l <= b.u.l));
break;
case GRE:
c.u.l = (zlong)
(a.type == MN_FLOAT ? (a.u.d > b.u.d) : (a.u.l > b.u.l));
break;
case GEQ:
c.u.l = (zlong)
(a.type == MN_FLOAT ? (a.u.d >= b.u.d) : (a.u.l >= b.u.l));
break;
case DEQ:
c.u.l = (zlong)
(a.type == MN_FLOAT ? (a.u.d == b.u.d) : (a.u.l == b.u.l));
break;
case NEQ:
c.u.l = (zlong)
(a.type == MN_FLOAT ? (a.u.d != b.u.d) : (a.u.l != b.u.l));
break;
case DAND:
case DANDEQ:
c.u.l = (zlong)(a.u.l && b.u.l);
break;
case DOR:
case DOREQ:
c.u.l = (zlong)(a.u.l || b.u.l);
break;
case DXOR:
case DXOREQ:
c.u.l = (zlong)((a.u.l && !b.u.l) || (!a.u.l && b.u.l));
break;
case COMMA:
c = b;
break;
case POWER:
case POWEREQ:
if (c.type == MN_INTEGER && b.u.l < 0) {
/* produces a real result, so cast to real. */
a.type = b.type = c.type = MN_FLOAT;
a.u.d = (double) a.u.l;
b.u.d = (double) b.u.l;
}
if (c.type == MN_INTEGER) {
for (c.u.l = 1; b.u.l--; c.u.l *= a.u.l);
} else {
if (b.u.d <= 0 && !notzero(a))
return;
if (a.u.d < 0) {
/* Error if (-num ** b) and b is not an integer */
double tst = (double)(zlong)b.u.d;
if (tst != b.u.d) {
zerr("imaginary power", NULL, 0);
return;
}
}
c.u.d = pow(a.u.d, b.u.d);
}
break;
case EQ:
c = b;
break;
}
if (tp & (OP_E2|OP_E2IO)) {
lv = stack[sp+1].lval;
push(setvar(lv,c), lv);
} else
push(c,-1);
return;
}
spval = &stack[sp].val;
switch (what) {
case NOT:
stack[sp].val = !stack[sp].val;
nolval();
if (spval->type & MN_FLOAT) {
spval->u.l = !spval->u.d;
spval->type = MN_INTEGER;
} else
spval->u.l = !spval->u.l;
stack[sp].lval = -1;
break;
case COMP:
stack[sp].val = ~stack[sp].val;
nolval();
if (spval->type & MN_FLOAT) {
spval->u.l = ~((zlong)spval->u.d);
spval->type = MN_INTEGER;
} else
spval->u.l = ~spval->u.l;
stack[sp].lval = -1;
break;
case POSTPLUS:
(void)setvar(stack[sp].lval, stack[sp].val + 1);
a = *spval;
if (spval->type & MN_FLOAT)
a.u.d++;
else
a.u.l++;
(void)setvar(stack[sp].lval, a);
break;
case POSTMINUS:
(void)setvar(stack[sp].lval, stack[sp].val - 1);
a = *spval;
if (spval->type & MN_FLOAT)
a.u.d--;
else
a.u.l--;
(void)setvar(stack[sp].lval, a);
break;
case UPLUS:
nolval();
stack[sp].lval = -1;
break;
case UMINUS:
stack[sp].val = -stack[sp].val;
nolval();
break;
case AND:
pop2();
pushv(a & b);
break;
case XOR:
pop2();
pushv(a ^ b);
break;
case OR:
pop2();
pushv(a | b);
break;
case MUL:
pop2();
pushv(a * b);
break;
case DIV:
pop2();
if (notzero(b))
pushv(a / b);
break;
case MOD:
pop2();
if (notzero(b))
pushv(a % b);
break;
case PLUS:
pop2();
pushv(a + b);
break;
case MINUS:
pop2();
pushv(a - b);
break;
case SHLEFT:
pop2();
pushv(a << b);
break;
case SHRIGHT:
pop2();
pushv(a >> b);
break;
case LES:
pop2();
pushv((zlong)(a < b));
break;
case LEQ:
pop2();
pushv((zlong)(a <= b));
break;
case GRE:
pop2();
pushv((zlong)(a > b));
break;
case GEQ:
pop2();
pushv((zlong)(a >= b));
break;
case DEQ:
pop2();
pushv((zlong)(a == b));
break;
case NEQ:
pop2();
pushv((zlong)(a != b));
break;
case DAND:
pop2();
pushv((zlong)(a && b));
break;
case DOR:
pop2();
pushv((zlong)(a || b));
break;
case DXOR:
pop2();
pushv((zlong)((a && !b) || (!a && b)));
if (spval->type & MN_FLOAT)
spval->u.d = -spval->u.d;
else
spval->u.l = -spval->u.l;
stack[sp].lval = -1;
break;
case QUEST:
pop3();
pushv((a) ? b : c);
if (sp < 2) {
zerr("bad math expression: unbalanced stack", NULL, 0);
return;
}
c = stack[sp--].val;
b = stack[sp--].val;
a = stack[sp--].val;
/* b and c can stay different types in this case. */
push(((a.type & MN_FLOAT) ? a.u.d : a.u.l) ? b : c, -1);
break;
case COLON:
break;
case EQ:
pop2();
lv = stack[sp + 1].lval;
set(b);
break;
case PLUSEQ:
pop2lv();
set(a + b);
break;
case MINUSEQ:
pop2lv();
set(a - b);
break;
case MULEQ:
pop2lv();
set(a * b);
break;
case DIVEQ:
pop2lv();
if (notzero(b))
set(a / b);
break;
case MODEQ:
pop2lv();
if (notzero(b))
set(a % b);
break;
case ANDEQ:
pop2lv();
set(a & b);
break;
case XOREQ:
pop2lv();
set(a ^ b);
break;
case OREQ:
pop2lv();
set(a | b);
break;
case SHLEFTEQ:
pop2lv();
set(a << b);
break;
case SHRIGHTEQ:
pop2lv();
set(a >> b);
break;
case DANDEQ:
pop2lv();
set((zlong)(a && b));
break;
case DOREQ:
pop2lv();
set((zlong)(a || b));
break;
case DXOREQ:
pop2lv();
set((zlong)((a && !b) || (!a && b)));
break;
case COMMA:
pop2();
pushv(b);
break;
case PREPLUS:
stack[sp].val = setvar(stack[sp].lval,
stack[sp].val + 1);
if (spval->type & MN_FLOAT)
spval->u.d++;
else
spval->u.l++;
setvar(stack[sp].lval, *spval);
break;
case PREMINUS:
stack[sp].val = setvar(stack[sp].lval,
stack[sp].val - 1);
break;
case POWER:
pop2();
if (b < 0) {
zerr("can't handle negative exponents", NULL, 0);
return;
}
for (c = 1; b--; c *= a);
pushv(c);
break;
case POWEREQ:
pop2lv();
if (b < 0) {
zerr("can't handle negative exponents", NULL, 0);
return;
}
for (c = 1; b--; c *= a);
set(c);
if (spval->type & MN_FLOAT)
spval->u.d--;
else
spval->u.l--;
setvar(stack[sp].lval, *spval);
break;
default:
zerr("out of integers", NULL, 0);
@ -711,15 +777,18 @@ op(int what)
static void
bop(int tk)
{
mnumber *spval = &stack[sp].val;
int tst = (spval->type & MN_FLOAT) ? (zlong)spval->u.d : spval->u.l;
switch (tk) {
case DAND:
case DANDEQ:
if (!stack[sp].val)
if (!tst)
noeval++;
break;
case DOR:
case DOREQ:
if (stack[sp].val)
if (tst)
noeval++;
break;
};
@ -727,21 +796,19 @@ bop(int tk)
/**/
static zlong
static mnumber
mathevall(char *s, int prek, char **ep)
{
int t0;
int xlastbase, xnoeval, xunary, xlvc;
char *xptr;
zlong xyyval;
mnumber xyyval;
LV xyylval;
char **xlvals = 0, *nlvals[LVCOUNT];
int xsp;
struct mathvalue *xstack = 0, nstack[STACKSZ];
zlong ret;
mnumber ret;
xlastbase = xnoeval = xunary = xlvc = xyyval = xyylval = xsp = 0;
xptr = NULL;
if (mlevel++) {
xlastbase = lastbase;
xnoeval = noeval;
@ -754,6 +821,11 @@ mathevall(char *s, int prek, char **ep)
xsp = sp;
xstack = stack;
} else {
xlastbase = xnoeval = xunary = xlvc = xyylval = xsp = 0;
xyyval.type = MN_INTEGER;
xyyval.u.l = 0;
xptr = NULL;
}
stack = nstack;
lastbase = -1;
@ -790,15 +862,18 @@ mathevall(char *s, int prek, char **ep)
/**/
zlong
mnumber
matheval(char *s)
{
char *junk;
zlong x;
mnumber x;
int xmtok = mtok;
if (!*s)
return 0;
if (!*s) {
x.type = MN_INTEGER;
x.u.l = 0;
return x;
}
x = mathevall(s, TOPPREC, &junk);
mtok = xmtok;
if (*junk)
@ -806,19 +881,27 @@ matheval(char *s)
return x;
}
/**/
zlong
mathevali(char *s)
{
mnumber x = matheval(s);
return (x.type & MN_FLOAT) ? (zlong)x.u.d : x.u.l;
}
/**/
zlong
mathevalarg(char *s, char **ss)
{
zlong x;
mnumber x;
int xmtok = mtok;
x = mathevall(s, ARGPREC, ss);
if (mtok == COMMA)
(*ss)--;
mtok = xmtok;
return x;
return (x.type & MN_FLOAT) ? (zlong)x.u.d : x.u.l;
}
@ -842,7 +925,7 @@ mathparse(int pc)
push(yyval, -1);
break;
case ID:
push(getiparam(lvals[yylval]), yylval);
push(getnparam(lvals[yylval]), yylval);
break;
case CID:
push(getcvar(yylval), yylval);
@ -856,7 +939,8 @@ mathparse(int pc)
}
break;
case QUEST:
q = stack[sp].val;
q = (stack[sp].val.type == MN_FLOAT) ? (zlong)stack[sp].val.u.d :
stack[sp].val.u.l;
if (!q)
noeval++;
@ -873,9 +957,9 @@ mathparse(int pc)
default:
otok = mtok;
onoeval = noeval;
if (type[otok] == BOOL)
if (MTYPE(type[otok]) == BOOL)
bop(otok);
mathparse(prec[otok] - (type[otok] != RL));
mathparse(prec[otok] - (MTYPE(type[otok]) != RL));
noeval = onoeval;
op(otok);
continue;

View file

@ -96,7 +96,13 @@ static int h_m[1025], h_push, h_pop, h_free;
#endif
#define H_ISIZE sizeof(zlong)
/* Make sure we align to the longest fundamental type. */
union mem_align {
zlong l;
double d;
};
#define H_ISIZE sizeof(union mem_align)
#define HEAPSIZE (16384 - H_ISIZE)
#define HEAP_ARENA_SIZE (HEAPSIZE - sizeof(struct heap))
#define HEAPFREE (16384 - H_ISIZE)
@ -514,13 +520,12 @@ ztrdup(const char *s)
/*
Below is a simple segment oriented memory allocator for systems on
which it is better than the system's one. Memory is given in blocks
aligned to an integer multiple of sizeof(zlong) (4 bytes on most machines,
but 8 bytes on e.g. a dec alpha; it will be 8 bytes if we are using
long long's or equivalent). Each block is preceded by a header
which contains the length of the data part (in bytes). In allocated
blocks only this field of the structure m_hdr is senseful. In free
blocks the second field (next) is a pointer to the next free segment
on the free list.
aligned to an integer multiple of sizeof(union mem_align), which will
probably be 64-bit as it is the longer of zlong or double. Each block is
preceded by a header which contains the length of the data part (in
bytes). In allocated blocks only this field of the structure m_hdr is
senseful. In free blocks the second field (next) is a pointer to the next
free segment on the free list.
On top of this simple allocator there is a second allocator for small
chunks of data. It should be both faster and less space-consuming than
@ -576,7 +581,7 @@ ztrdup(const char *s)
struct m_shdr {
struct m_shdr *next; /* next one on free list */
#ifdef ZSH_64_BIT_TYPE
#ifdef PAD_64_BIT
/* dummy to make this 64-bit aligned */
struct m_shdr *dummy;
#endif
@ -584,18 +589,25 @@ struct m_shdr {
struct m_hdr {
zlong len; /* length of memory block */
#if defined(PAD_64_BIT) && !defined(ZSH_64_BIT_TYPE)
/* either 1 or 2 zlong's, whichever makes up 64 bits. */
zlong dummy1;
#endif
struct m_hdr *next; /* if free: next on free list
if block of small blocks: next one with
small blocks of same size*/
struct m_shdr *free; /* if block of small blocks: free list */
zlong used; /* if block of small blocks: number of used
blocks */
#if defined(PAD_64_BIT) && !defined(ZSH_64_BIT_TYPE)
zlong dummy2;
#endif
};
/* alignment for memory blocks */
#define M_ALIGN (sizeof(zlong))
#define M_ALIGN (sizeof(union mem_align))
/* length of memory header, length of first field of memory header and
minimal size of a block left free (if we allocate memory and take a
@ -604,7 +616,11 @@ struct m_hdr {
the free list) */
#define M_HSIZE (sizeof(struct m_hdr))
#define M_ISIZE (sizeof(zlong))
#if defined(PAD_64_BIT) && !defined(ZSH_64_BIT_TYPE)
# define M_ISIZE (2*sizeof(zlong))
#else
# define M_ISIZE (sizeof(zlong))
#endif
#define M_MIN (2 * M_ISIZE)
/* M_FREE is the number of bytes that have to be free before memory is
@ -649,10 +665,18 @@ static char *m_high, *m_low;
#define M_SIDX(S) ((S) / M_ISIZE)
#define M_SNUM 128
#define M_SLEN(M) ((M)->len / M_SNUM)
#if defined(PAD_64_BIT) && !defined(ZSH_64_BIT_TYPE)
/* Include the dummy in the alignment */
#define M_SBLEN(S) ((S) * M_SNUM + sizeof(struct m_shdr *) + \
2*sizeof(zlong) + sizeof(struct m_hdr *))
#define M_BSLEN(S) (((S) - sizeof(struct m_shdr *) - \
2*sizeof(zlong) - sizeof(struct m_hdr *)) / M_SNUM)
#else
#define M_SBLEN(S) ((S) * M_SNUM + sizeof(struct m_shdr *) + \
sizeof(zlong) + sizeof(struct m_hdr *))
#define M_BSLEN(S) (((S) - sizeof(struct m_shdr *) - \
sizeof(zlong) - sizeof(struct m_hdr *)) / M_SNUM)
#endif
#define M_NSMALL 13
static struct m_hdr *m_small[M_NSMALL];

View file

@ -557,6 +557,11 @@ assigngetset(Param pm)
pm->sets.ifn = intsetfn;
pm->gets.ifn = intgetfn;
break;
case PM_EFLOAT:
case PM_FFLOAT:
pm->sets.ffn = floatsetfn;
pm->gets.ffn = floatgetfn;
break;
case PM_ARRAY:
pm->sets.afn = arrsetfn;
pm->gets.afn = arrgetfn;
@ -651,6 +656,10 @@ copyparam(Param tpm, Param pm, int toplevel)
case PM_INTEGER:
tpm->u.val = pm->gets.ifn(pm);
break;
case PM_EFLOAT:
case PM_FFLOAT:
tpm->u.dval = pm->gets.ffn(pm);
break;
case PM_ARRAY:
tpm->u.arr = arrdup(pm->gets.afn(pm));
break;
@ -1242,7 +1251,7 @@ char *
getstrvalue(Value v)
{
char *s, **ss;
static char buf[(sizeof(zlong) * 8) + 4];
char buf[(sizeof(zlong) * 8) + 4];
if (!v)
return hcalloc(1);
@ -1276,6 +1285,11 @@ getstrvalue(Value v)
convbase(buf, v->pm->gets.ifn(v->pm), v->pm->ct);
s = dupstring(buf);
break;
case PM_EFLOAT:
case PM_FFLOAT:
s = convfloat(v->pm->gets.ffn(v->pm), v->pm->ct,
v->pm->flags, NULL);
break;
case PM_SCALAR:
s = v->pm->gets.cfn(v->pm);
break;
@ -1349,7 +1363,30 @@ getintvalue(Value v)
return v->a;
if (PM_TYPE(v->pm->flags) == PM_INTEGER)
return v->pm->gets.ifn(v->pm);
return matheval(getstrvalue(v));
if (v->pm->flags & (PM_EFLOAT|PM_FFLOAT))
return (zlong)v->pm->gets.ffn(v->pm);
return mathevali(getstrvalue(v));
}
/**/
mnumber
getnumvalue(Value v)
{
mnumber mn;
mn.type = MN_INTEGER;
if (!v || v->isarr) {
mn.u.l = 0;
} else if (v->inv) {
mn.u.l = v->a;
} else if (PM_TYPE(v->pm->flags) == PM_INTEGER) {
mn.u.l = v->pm->gets.ifn(v->pm);
} else if (v->pm->flags & (PM_EFLOAT|PM_FFLOAT)) {
mn.type = MN_FLOAT;
mn.u.d = v->pm->gets.ffn(v->pm);
} else
return matheval(getstrvalue(v));
return mn;
}
/**/
@ -1405,12 +1442,21 @@ setstrvalue(Value v, char *val)
break;
case PM_INTEGER:
if (val) {
(v->pm->sets.ifn) (v->pm, matheval(val));
(v->pm->sets.ifn) (v->pm, mathevali(val));
zsfree(val);
}
if (!v->pm->ct && lastbase != -1)
v->pm->ct = lastbase;
break;
case PM_EFLOAT:
case PM_FFLOAT:
if (val) {
mnumber mn = matheval(val);
(v->pm->sets.ffn) (v->pm, (mn.type & MN_FLOAT) ? mn.u.d :
(double)mn.u.l);
zsfree(val);
}
break;
case PM_ARRAY:
MUSTUSEHEAP("setstrvalue");
{
@ -1428,6 +1474,9 @@ setstrvalue(Value v, char *val)
return;
if (PM_TYPE(v->pm->flags) == PM_INTEGER)
convbase(val = buf, v->pm->gets.ifn(v->pm), v->pm->ct);
else if (v->pm->flags & (PM_EFLOAT|PM_FFLOAT))
val = convfloat(v->pm->gets.ffn(v->pm), v->pm->ct,
v->pm->flags, NULL);
else
val = v->pm->gets.cfn(v->pm);
if (v->pm->env)
@ -1440,9 +1489,9 @@ setstrvalue(Value v, char *val)
/**/
static void
setintvalue(Value v, zlong val)
setnumvalue(Value v, mnumber val)
{
char buf[DIGBUFSIZE];
char buf[DIGBUFSIZE], *p;
if (v->pm->flags & PM_READONLY) {
zerr("read-only variable: %s", v->pm->nam, 0);
@ -1455,11 +1504,21 @@ setintvalue(Value v, zlong val)
switch (PM_TYPE(v->pm->flags)) {
case PM_SCALAR:
case PM_ARRAY:
convbase(buf, val, 0);
setstrvalue(v, ztrdup(buf));
if (val.type & MN_INTEGER)
convbase(p = buf, val.u.l, 0);
else
p = convfloat(val.u.d, 0, 0, NULL);
setstrvalue(v, ztrdup(p));
break;
case PM_INTEGER:
(v->pm->sets.ifn) (v->pm, val);
(v->pm->sets.ifn) (v->pm, (val.type & MN_INTEGER) ? val.u.l :
(zlong) val.u.d);
setstrvalue(v, NULL);
break;
case PM_EFLOAT:
case PM_FFLOAT:
(v->pm->sets.ffn) (v->pm, (val.type & MN_INTEGER) ?
(double)val.u.l : val.u.d);
setstrvalue(v, NULL);
break;
}
@ -1544,6 +1603,22 @@ getiparam(char *s)
return getintvalue(v);
}
/* Retrieve a numerical parameter, either integer or floating */
/**/
mnumber
getnparam(char *s)
{
Value v;
if (!(v = getvalue(&s, 1))) {
mnumber mn;
mn.type = MN_INTEGER;
mn.u.l = 0;
return mn;
}
return getnumvalue(v);
}
/* Retrieve a scalar (string) parameter */
/**/
@ -1709,6 +1784,7 @@ setiparam(char *s, zlong val)
Value v;
char *t = s;
Param pm;
mnumber mnval;
if (!isident(s)) {
zerr("not an identifier: %s", s, 0);
@ -1721,7 +1797,41 @@ setiparam(char *s, zlong val)
pm->u.val = val;
return pm;
}
setintvalue(v, val);
mnval.type = MN_INTEGER;
mnval.u.l = val;
setnumvalue(v, mnval);
return v->pm;
}
/*
* Like setiparam(), but can take an mnumber which can be integer or
* floating.
*/
/**/
Param
setnparam(char *s, mnumber val)
{
Value v;
char *t = s;
Param pm;
if (!isident(s)) {
zerr("not an identifier: %s", s, 0);
errflag = 1;
return NULL;
}
if (!(v = getvalue(&s, 1))) {
pm = createparam(t, (val.type & MN_INTEGER) ? PM_INTEGER
: PM_FFLOAT);
DPUTS(!pm, "BUG: parameter not created");
if (val.type & MN_INTEGER)
pm->u.val = val.u.l;
else
pm->u.dval = val.u.d;
return pm;
}
setnumvalue(v, val);
return v->pm;
}
@ -1834,6 +1944,24 @@ intsetfn(Param pm, zlong x)
pm->u.val = x;
}
/* Function to get value of a floating point parameter */
/**/
static double
floatgetfn(Param pm)
{
return pm->u.dval;
}
/* Function to set value of an integer parameter */
/**/
static void
floatsetfn(Param pm, double x)
{
pm->u.dval = x;
}
/* Function to get value of a scalar (string) parameter */
/**/
@ -2680,6 +2808,62 @@ convbase(char *s, zlong v, int base)
}
}
/*
* Convert a floating point value for output.
* Unlike convbase(), this has its own internal storage and returns
* a value from the heap;
*/
/**/
char *
convfloat(double dval, int digits, int flags, FILE *fout)
{
char fmt[] = "%.*e";
MUSTUSEHEAP("convfloat");
/*
* The difficulty with the buffer size is that a %f conversion
* prints all digits before the decimal point: with 64 bit doubles,
* that's around 310. We can't check without doing some quite
* serious floating point operations we'd like to avoid.
* Then we are liable to get all the digits
* we asked for after the decimal point, or we should at least
* bargain for it. So we just allocate 512 + digits. This
* should work until somebody decides on 128-bit doubles.
*/
if (!(flags & (PM_EFLOAT|PM_FFLOAT))) {
/*
* Conversion from a floating point expression without using
* a variable. The best bet in this case just seems to be
* to use the general %g format with something like the maximum
* double precision.
*/
fmt[3] = 'g';
if (!digits)
digits = 17;
} else {
if (flags & PM_FFLOAT)
fmt[3] = 'f';
if (digits <= 0)
digits = 10;
if (flags & PM_EFLOAT) {
/*
* Here, we are given the number of significant figures, but
* %e wants the number of decimal places (unlike %g)
*/
digits--;
}
}
if (fout) {
fprintf(fout, fmt, digits, dval);
return NULL;
} else {
VARARR(char, buf, 512 + digits);
sprintf(buf, fmt, digits, dval);
return dupstring(buf);
}
}
/* Start a parameter scope */
/**/
@ -2733,6 +2917,10 @@ scanendscope(HashNode hn, int flags)
case PM_INTEGER:
pm->sets.ifn(pm, tpm->u.val);
break;
case PM_EFLOAT:
case PM_FFLOAT:
pm->sets.ffn(pm, tpm->u.dval);
break;
case PM_ARRAY:
pm->sets.afn(pm, tpm->u.arr);
break;
@ -2786,6 +2974,8 @@ printparamnode(HashNode hn, int printflags)
printf("undefined ");
if (p->flags & PM_INTEGER)
printf("integer ");
if (p->flags & (PM_EFLOAT|PM_FFLOAT))
printf("float ");
else if (p->flags & PM_ARRAY)
printf("array ");
else if (p->flags & PM_HASHED)
@ -2843,6 +3033,11 @@ printparamnode(HashNode hn, int printflags)
printf("%ld", p->gets.ifn(p));
#endif
break;
case PM_EFLOAT:
case PM_FFLOAT:
/* float */
convfloat(p->gets.ffn(p), p->ct, p->flags, stdout);
break;
case PM_ARRAY:
/* array */
if (!(printflags & PRINT_KV_PAIR))

View file

@ -685,7 +685,7 @@ get_intarg(char **s)
singsub(&p);
if (errflag)
return -1;
ret = matheval(p);
ret = mathevali(p);
if (errflag)
return -1;
if (ret < 0)
@ -1045,6 +1045,8 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
case PM_SCALAR: val = "scalar"; break;
case PM_ARRAY: val = "array"; break;
case PM_INTEGER: val = "integer"; break;
case PM_EFLOAT:
case PM_FFLOAT: val = "float"; break;
case PM_HASHED: val = "association"; break;
}
val = dupstring(val);
@ -1823,12 +1825,16 @@ arithsubst(char *a, char **bptr, char *rest)
{
char *s = *bptr, *t;
char buf[DIGBUFSIZE], *b = buf;
zlong v;
mnumber v;
singsub(&a);
v = matheval(a);
convbase(buf, v, 0);
t = *bptr = (char *)ncalloc(strlen(*bptr) + strlen(buf) + strlen(rest) + 1);
if (v.type & MN_FLOAT)
b = convfloat(v.u.d, 0, 0, NULL);
else
convbase(buf, v.u.l, 0);
t = *bptr = (char *)ncalloc(strlen(*bptr) + strlen(b) +
strlen(rest) + 1);
t--;
while ((*++t = *s++));
t--;

View file

@ -57,7 +57,26 @@ typedef long zlong;
typedef unsigned long zulong;
#endif
/*
* Double float support requires 64-bit alignment, so if longs and
* pointers are less we need to pad out.
*/
#ifndef LONG_IS_64_BIT
# define PAD_64_BIT 1
#endif
/* math.c */
typedef struct {
union {
zlong l;
double d;
} u;
int type;
} mnumber;
#define MN_INTEGER 1 /* mnumber is integer */
#define MN_FLOAT 2 /* mnumber is floating point */
typedef int LV;
/* Character tokens are sometimes casted to (unsigned char)'s. *
@ -953,6 +972,8 @@ struct param {
char **arr; /* value if declared array (PM_ARRAY) */
char *str; /* value if declared string (PM_SCALAR) */
zlong val; /* value if declared integer (PM_INTEGER) */
double dval; /* value if declared float
(PM_EFLOAT|PM_FFLOAT) */
HashTable hash; /* value if declared assoc (PM_HASHED) */
} u;
@ -960,6 +981,7 @@ struct param {
union {
void (*cfn) _((Param, char *));
void (*ifn) _((Param, zlong));
void (*ffn) _((Param, double));
void (*afn) _((Param, char **));
void (*hfn) _((Param, HashTable));
} sets;
@ -968,6 +990,7 @@ struct param {
union {
char *(*cfn) _((Param));
zlong (*ifn) _((Param));
double (*ffn) _((Param));
char **(*afn) _((Param));
HashTable (*hfn) _((Param));
} gets;
@ -988,41 +1011,50 @@ struct param {
#define PM_SCALAR 0 /* scalar */
#define PM_ARRAY (1<<0) /* array */
#define PM_INTEGER (1<<1) /* integer */
#define PM_HASHED (1<<2) /* association */
#define PM_EFLOAT (1<<2) /* double with %e output */
#define PM_FFLOAT (1<<3) /* double with %f output */
#define PM_HASHED (1<<4) /* association */
#define PM_TYPE(X) (X & (PM_SCALAR|PM_INTEGER|PM_ARRAY|PM_HASHED))
#define PM_TYPE(X) \
(X & (PM_SCALAR|PM_INTEGER|PM_EFLOAT|PM_FFLOAT|PM_ARRAY|PM_HASHED))
#define PM_LEFT (1<<3) /* left justify, remove leading blanks */
#define PM_RIGHT_B (1<<4) /* right justify, fill with leading blanks */
#define PM_RIGHT_Z (1<<5) /* right justify, fill with leading zeros */
#define PM_LOWER (1<<6) /* all lower case */
#define PM_LEFT (1<<5) /* left justify, remove leading blanks */
#define PM_RIGHT_B (1<<6) /* right justify, fill with leading blanks */
#define PM_RIGHT_Z (1<<7) /* right justify, fill with leading zeros */
#define PM_LOWER (1<<8) /* all lower case */
/* The following are the same since they *
* both represent -u option to typeset */
#define PM_UPPER (1<<7) /* all upper case */
#define PM_UNDEFINED (1<<7) /* undefined (autoloaded) shell function */
#define PM_UPPER (1<<9) /* all upper case */
#define PM_UNDEFINED (1<<9) /* undefined (autoloaded) shell function */
#define PM_READONLY (1<<8) /* readonly */
#define PM_TAGGED (1<<9) /* tagged */
#define PM_EXPORTED (1<<10) /* exported */
#define PM_READONLY (1<<10) /* readonly */
#define PM_TAGGED (1<<11) /* tagged */
#define PM_EXPORTED (1<<12) /* exported */
/* The following are the same since they *
* both represent -U option to typeset */
#define PM_UNIQUE (1<<11) /* remove duplicates */
#define PM_UNALIASED (1<<11) /* do not expand aliases when autoloading */
#define PM_UNIQUE (1<<13) /* remove duplicates */
#define PM_UNALIASED (1<<13) /* do not expand aliases when autoloading */
#define PM_HIDE (1<<12) /* Special behaviour hidden by local */
#define PM_TIED (1<<13) /* array tied to colon-path or v.v. */
#define PM_HIDE (1<<14) /* Special behaviour hidden by local */
#define PM_TIED (1<<15) /* array tied to colon-path or v.v. */
/* Remaining flags do not correspond directly to command line arguments */
#define PM_LOCAL (1<<14) /* this parameter will be made local */
#define PM_SPECIAL (1<<15) /* special builtin parameter */
#define PM_DONTIMPORT (1<<16) /* do not import this variable */
#define PM_RESTRICTED (1<<17) /* cannot be changed in restricted mode */
#define PM_UNSET (1<<18) /* has null value */
#define PM_REMOVABLE (1<<19) /* special can be removed from paramtab */
#define PM_AUTOLOAD (1<<20) /* autoloaded from module */
#define PM_NORESTORE (1<<21) /* do not restore value of local special */
#define PM_LOCAL (1<<16) /* this parameter will be made local */
#define PM_SPECIAL (1<<17) /* special builtin parameter */
#define PM_DONTIMPORT (1<<18) /* do not import this variable */
#define PM_RESTRICTED (1<<19) /* cannot be changed in restricted mode */
#define PM_UNSET (1<<20) /* has null value */
#define PM_REMOVABLE (1<<21) /* special can be removed from paramtab */
#define PM_AUTOLOAD (1<<22) /* autoloaded from module */
#define PM_NORESTORE (1<<23) /* do not restore value of local special */
/* The option string corresponds to the first of the variables above */
#define TYPESET_OPTSTR "aiEFALRZlurtxUhT"
/* These typeset options take an optional numeric argument */
#define TYPESET_OPTNUM "LRZiEF"
/* Flags for extracting elements of arrays and associative arrays */
#define SCANPM_WANTVALS (1<<0)
@ -1481,7 +1513,7 @@ struct heap {
struct heap *next; /* next one */
size_t used; /* bytes used from the heap */
struct heapstack *sp; /* used by pushheap() to save the value used */
#ifdef ZSH_64_BIT_TYPE
#ifdef PAD_64_BIT
size_t dummy; /* Make sure sizeof(heap) is a multiple of 8 */
#endif
#define arena(X) ((char *) (X) + sizeof(struct heap))

View file

@ -458,6 +458,8 @@ dnl in case they require objects that exist only in the static version
dnl and might not be compiled into the zsh executable.
AC_CHECK_LIB(c, printf)
AC_CHECK_LIB(m, pow)
dnl Prefer BSD termcap library to SysV curses library, except on certain
dnl versions of AIX and HP-UX.
case "$host_os" in