32299: add use of underscores on arithmetic output for spacing

This commit is contained in:
Peter Stephenson 2014-01-23 10:32:59 +00:00
parent 6c603a4127
commit 22b8fd6da9
7 changed files with 221 additions and 19 deletions

View File

@ -1,3 +1,10 @@
2014-01-23 Peter Stephenson <p.stephenson@samsung.com>
* 32299: Doc/Zsh/arith.yo, Functions/Misc/zcalc, Src/math.c,
Src/params.c, Src/subst.c, Test/C01arith.ztst: add
ability to use "_" at the end of a [#] arithmetic expression
to get underscores to space numeric output.
2014-01-22 Barton E. Schaefer <schaefer@zsh.org>
* unposted: Src/mem.c: reformulate 32285 to lift the fheap->sp

View File

@ -76,6 +76,27 @@ have output base 16, while tt(x) (assuming it does not already exist) is
implicitly typed by the arithmetic evaluation, where it acquires the output
base 8.
The var(base) may be replaced or followed by an underscore, which may
itself be followed by a positive integer (if it is missing the value 3
is used). This indicates that underscores should be inserted into the
output string, grouping the number for visual clarity. The following
integer specifies the number of digits to group together. For example:
example(setopt cbases
print $(( [#16_4] 65536 ** 2 )))
outputs `tt(0x1_0000_0000)'.
The feature can be used with floating
point numbers, in which case the base must be omitted; grouping
is away from the decimal point. For example,
example(zmodload zsh/mathfunc
print $(( [#_] sqrt(1e7) )))
outputs `tt(3_162.277_660_168_379_5)' (the number of decimal places
shown may vary).
pindex(C_BASES, use of)
pindex(OCTAL_ZEROES, use of)
If the tt(C_BASES) option is set, hexadecimal numbers in the standard C

View File

@ -196,7 +196,7 @@ while (( expression_mode )) ||
# special cases
# Set default base if `[#16]' or `[##16]' etc. on its own.
# Unset it if `[#]' or `[##]'.
if [[ $line = (#b)[[:blank:]]#('[#'(\#|)(<->|)']')[[:blank:]]#(*) ]]; then
if [[ $line = (#b)[[:blank:]]#('[#'(\#|)((<->|)(|_|_<->))']')[[:blank:]]#(*) ]]; then
if [[ -z $match[4] ]]; then
if [[ -z $match[3] ]]; then
defbase=

View File

@ -555,6 +555,9 @@ lexconstant(void)
/**/
int outputradix;
/**/
int outputunderscore;
/**/
static int
zzlex(void)
@ -713,7 +716,7 @@ zzlex(void)
return EOI;
case '[':
{
int n;
int n, checkradix = 0;
if (idigit(*ptr)) {
n = zstrtol(ptr, &ptr, 10);
@ -730,9 +733,19 @@ zzlex(void)
n = -1;
ptr++;
}
if (!idigit(*ptr))
if (!idigit(*ptr) && *ptr != '_')
goto bofs;
outputradix = n * zstrtol(ptr, &ptr, 10);
if (idigit(*ptr)) {
outputradix = n * zstrtol(ptr, &ptr, 10);
checkradix = 1;
}
if (*ptr == '_') {
ptr++;
if (idigit(*ptr))
outputunderscore = zstrtol(ptr, &ptr, 10);
else
outputunderscore = 3;
}
} else {
bofs:
zerr("bad output format specification");
@ -740,11 +753,13 @@ zzlex(void)
}
if(*ptr != ']')
goto bofs;
n = (outputradix < 0) ? -outputradix : outputradix;
if (n < 2 || n > 36) {
zerr("invalid base (must be 2 to 36 inclusive): %d",
outputradix);
return EOI;
if (checkradix) {
n = (outputradix < 0) ? -outputradix : outputradix;
if (n < 2 || n > 36) {
zerr("invalid base (must be 2 to 36 inclusive): %d",
outputradix);
return EOI;
}
}
ptr++;
break;
@ -1337,9 +1352,9 @@ matheval(char *s)
char *junk;
mnumber x;
int xmtok = mtok;
/* maintain outputradix across levels of evaluation */
/* maintain outputradix and outputunderscore across levels of evaluation */
if (!mlevel)
outputradix = 0;
outputradix = outputunderscore = 0;
if (!*s) {
x.type = MN_INTEGER;

View File

@ -2416,9 +2416,10 @@ setnumvalue(Value v, mnumber val)
if ((val.type & MN_INTEGER) || outputradix) {
if (!(val.type & MN_INTEGER))
val.u.l = (zlong) val.u.d;
convbase(p = buf, val.u.l, outputradix);
p = convbase_underscore(buf, val.u.l, outputradix,
outputunderscore);
} else
p = convfloat(val.u.d, 0, 0, NULL);
p = convfloat_underscore(val.u.d, outputunderscore);
setstrvalue(v, ztrdup(p));
break;
case PM_INTEGER:
@ -4555,9 +4556,14 @@ delenv(Param pm)
*/
}
/*
* Guts of convbase: this version can return the number of digits
* sans any base discriminator.
*/
/**/
mod_export void
convbase(char *s, zlong v, int base)
void
convbase_ptr(char *s, zlong v, int base, int *ndigits)
{
int digs = 0;
zulong x;
@ -4583,6 +4589,8 @@ convbase(char *s, zlong v, int base)
x /= base;
if (!digs)
digs = 1;
if (ndigits)
*ndigits = digs;
s[digs--] = '\0';
x = v;
while (digs >= 0) {
@ -4593,6 +4601,64 @@ convbase(char *s, zlong v, int base)
}
}
/*
* Basic conversion of integer to a string given a base.
* If 0 base is 10.
* If negative no base discriminator is output.
*/
/**/
mod_export void
convbase(char *s, zlong v, int base)
{
convbase_ptr(s, v, base, NULL);
}
/*
* Add underscores to converted integer for readability with given spacing.
* s is as for convbase: at least BDIGBUFSIZE.
* If underscores were added, returned value with underscores comes from
* heap, else the returned value is s.
*/
/**/
char *
convbase_underscore(char *s, zlong v, int base, int underscore)
{
char *retptr, *sptr, *dptr;
int ndigits, nunderscore, mod, len;
convbase_ptr(s, v, base, &ndigits);
if (underscore <= 0)
return s;
nunderscore = (ndigits - 1) / underscore;
if (!nunderscore)
return s;
len = strlen(s);
retptr = zhalloc(len + nunderscore + 1);
mod = 0;
memcpy(retptr, s, len - ndigits);
sptr = s + len;
dptr = retptr + len + nunderscore;
/* copy the null */
*dptr-- = *sptr--;
for (;;) {
*dptr = *sptr;
if (!--ndigits)
break;
dptr--;
sptr--;
if (++mod == underscore) {
mod = 0;
*dptr-- = '_';
}
}
return retptr;
}
/*
* Convert a floating point value for output.
* Unlike convbase(), this has its own internal storage and returns
@ -4659,6 +4725,83 @@ convfloat(double dval, int digits, int flags, FILE *fout)
return ret;
}
/*
* convert float to string with basic options but inserting underscores
* for readability.
*/
/**/
char *convfloat_underscore(double dval, int underscore)
{
int ndigits_int = 0, ndigits_frac = 0, nunderscore, len;
char *s, *retptr, *sptr, *dptr;
s = convfloat(dval, 0, 0, NULL);
if (underscore <= 0)
return s;
/*
* Count the number of digits before and after the decimal point, if any.
*/
sptr = s;
if (*sptr == '-')
sptr++;
while (idigit(*sptr)) {
ndigits_int++;
sptr++;
}
if (*sptr == '.') {
sptr++;
while (idigit(*sptr)) {
ndigits_frac++;
sptr++;
}
}
/*
* Work out how many underscores to insert --- remember we
* put them in integer and fractional parts separately.
*/
nunderscore = (ndigits_int-1) / underscore + (ndigits_frac-1) / underscore;
if (!nunderscore)
return s;
len = strlen(s);
dptr = retptr = zhalloc(len + nunderscore + 1);
/*
* Insert underscores in integer part.
* Grouping starts from the point in both directions.
*/
sptr = s;
if (*sptr == '-')
*dptr++ = *sptr++;
while (ndigits_int) {
*dptr++ = *sptr++;
if (--ndigits_int && !(ndigits_int % underscore))
*dptr++ = '_';
}
if (ndigits_frac) {
/*
* Insert underscores in the fractional part.
*/
int mod = 0;
/* decimal point, we already checked */
*dptr++ = *sptr++;
while (ndigits_frac) {
*dptr++ = *sptr++;
mod++;
if (--ndigits_frac && mod == underscore) {
*dptr++ = '_';
mod = 0;
}
}
}
/* Copy exponent and anything else up to null */
while ((*dptr++ = *sptr++))
;
return retptr;
}
/* Start a parameter scope */
/**/

View File

@ -3754,19 +3754,19 @@ static char *
arithsubst(char *a, char **bptr, char *rest)
{
char *s = *bptr, *t;
char buf[BDIGBUFSIZE], *b = buf;
char buf[BDIGBUFSIZE], *b;
mnumber v;
singsub(&a);
v = matheval(a);
if ((v.type & MN_FLOAT) && !outputradix)
b = convfloat(v.u.d, 0, 0, NULL);
b = convfloat_underscore(v.u.d, outputunderscore);
else {
if (v.type & MN_FLOAT)
v.u.l = (zlong) v.u.d;
convbase(buf, v.u.l, outputradix);
b = convbase_underscore(buf, v.u.l, outputradix, outputunderscore);
}
t = *bptr = (char *) hcalloc(strlen(*bptr) + strlen(b) +
t = *bptr = (char *) hcalloc(strlen(*bptr) + strlen(b) +
strlen(rest) + 1);
t--;
while ((*++t = *s++));

View File

@ -266,3 +266,19 @@
>48.5
>77.5
>63.5
underscore_integer() {
setopt cbases localoptions
print $(( [#_] 1000000 ))
print $(( [#16_] 65536 ))
print $(( [#16_4] 65536 * 32768 ))
}
underscore_integer
0:Grouping output with underscores: integers
>1_000_000
>0x10_000
>0x8000_0000
print $(( [#_] (5. ** 10) / 16. ))
0:Grouping output with underscores: floating point
>610_351.562_5