From d0f024f1527bdae5ad40e0b1076723e94e752459 Mon Sep 17 00:00:00 2001 From: Tanaka Akira Date: Fri, 17 Sep 1999 15:12:01 +0000 Subject: manual/7915 --- Doc/Zsh/arith.yo | 67 +++++- Doc/Zsh/builtins.yo | 21 +- Src/Modules/parameter.c | 2 + Src/builtin.c | 72 ++++-- Src/cond.c | 55 +++-- Src/exec.c | 6 +- Src/math.c | 590 +++++++++++++++++++++++++++--------------------- Src/mem.c | 46 +++- Src/params.c | 213 ++++++++++++++++- Src/subst.c | 14 +- Src/zsh.h | 80 +++++-- configure.in | 2 + 12 files changed, 813 insertions(+), 355 deletions(-) diff --git a/Doc/Zsh/arith.yo b/Doc/Zsh/arith.yo index bb91b6fe9..f40a84732 100644 --- a/Doc/Zsh/arith.yo +++ b/Doc/Zsh/arith.yo @@ -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. diff --git a/Doc/Zsh/builtins.yo b/Doc/Zsh/builtins.yo index 0a1f917d4..5a559eaee 100644 --- a/Doc/Zsh/builtins.yo +++ b/Doc/Zsh/builtins.yo @@ -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. diff --git a/Src/Modules/parameter.c b/Src/Modules/parameter.c index 57a49320b..dc21298cb 100644 --- a/Src/Modules/parameter.c +++ b/Src/Modules/parameter.c @@ -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"); diff --git a/Src/builtin.c b/Src/builtin.c index 7af09737d..ca765ccfc 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -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; diff --git a/Src/cond.c b/Src/cond.c index a4b652fee..c0a3235be 100644 --- a/Src/cond.c +++ b/Src/cond.c @@ -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); } /**/ diff --git a/Src/exec.c b/Src/exec.c index 3e44adfc2..61b9fe60f 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -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"); diff --git a/Src/math.c b/Src/math.c index 10821b284..f91a58ea8 100644 --- a/Src/math.c +++ b/Src/math.c @@ -30,6 +30,8 @@ #include "zsh.mdh" #include "math.pro" +#include + /* 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); - - if (*ptr == '#') { - ptr++; - yyval = zstrtol(ptr, &ptr, lastbase = yyval); + for (nptr = ptr; idigit(*nptr); nptr++); + + 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; diff --git a/Src/mem.c b/Src/mem.c index 7f3b4688f..9d0073cbc 100644 --- a/Src/mem.c +++ b/Src/mem.c @@ -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]; diff --git a/Src/params.c b/Src/params.c index 75c5d915d..ca1c610c6 100644 --- a/Src/params.c +++ b/Src/params.c @@ -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)) diff --git a/Src/subst.c b/Src/subst.c index 486ac60c9..f5d13b500 100644 --- a/Src/subst.c +++ b/Src/subst.c @@ -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--; diff --git a/Src/zsh.h b/Src/zsh.h index a974b830e..51df16313 100644 --- a/Src/zsh.h +++ b/Src/zsh.h @@ -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)) diff --git a/configure.in b/configure.in index 5177222a4..6d8df8151 100644 --- a/configure.in +++ b/configure.in @@ -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 -- cgit 1.4.1