mirror of
git://git.code.sf.net/p/zsh/code
synced 2025-01-01 05:16:05 +01:00
32299: add use of underscores on arithmetic output for spacing
This commit is contained in:
parent
6c603a4127
commit
22b8fd6da9
7 changed files with 221 additions and 19 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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=
|
||||
|
|
35
Src/math.c
35
Src/math.c
|
@ -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;
|
||||
|
|
151
Src/params.c
151
Src/params.c
|
@ -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 */
|
||||
|
||||
/**/
|
||||
|
|
|
@ -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++));
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue