summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog8
-rw-r--r--Doc/Zsh/arith.yo10
-rw-r--r--NEWS23
-rw-r--r--Src/math.c39
-rw-r--r--Src/utils.c28
-rw-r--r--Test/C01arith.ztst23
6 files changed, 112 insertions, 19 deletions
diff --git a/ChangeLog b/ChangeLog
index cf53ffbe6..c9339beb8 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2012-09-11  Peter Stephenson  <pws@csr.com>
+
+	* 30647, 30649: Doc/Zsh/arith.yo, Src/math.c, Src/utils.c,
+	Test/C01arith.ztst: allow underscores in numeric constants
+	within math evaluation.
+
 2012-09-09  Peter Stephenson  <p.w.stephenson@ntlworld.com>
 
 	* Jun T.: 30664: configure.ac: fix some Yodl version issues.
@@ -151,5 +157,5 @@
 
 *****************************************************
 * This is used by the shell to define $ZSH_PATCHLEVEL
-* $Revision: 1.5718 $
+* $Revision: 1.5719 $
 *****************************************************
diff --git a/Doc/Zsh/arith.yo b/Doc/Zsh/arith.yo
index dd18ee720..2674c7817 100644
--- a/Doc/Zsh/arith.yo
+++ b/Doc/Zsh/arith.yo
@@ -48,6 +48,12 @@ 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.
 
+An integer expression or a base given in the form
+`var(base)tt(#)var(n)' may contain underscores (`tt(_)') after the
+leading digit for visual guidance; these are ignored in computation.
+Examples are tt(1_000_000) or tt(0xffff_ffff) which are equivalent to
+tt(1000000) and tt(0xffffffff) respectively.
+
 It is also possible to specify a base to be used for output in the form
 `tt([#)var(base)tt(])', for example `tt([#16])'.  This is used when
 outputting arithmetical substitutions or when assigning to scalar
@@ -87,7 +93,9 @@ output is valid syntax for input.  If the tt(#) is doubled, for example
 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.
+taken for a parameter name.  All numeric parts (before and after the
+decimal point and in the exponent) may contain underscores after the
+leading digit for visual guidance; these are ignored in computation.
 
 cindex(arithmetic operators)
 cindex(operators, arithmetic)
diff --git a/NEWS b/NEWS
index 23097b6aa..68d4ad657 100644
--- a/NEWS
+++ b/NEWS
@@ -4,8 +4,27 @@ CHANGES FROM PREVIOUS VERSIONS OF ZSH
 
 Note also the list of incompatibilities in the README file.
 
-Changes between 4.2 and 5.0
----------------------------
+Changes since 5.0.0
+-------------------
+
+"functions -T" turns on tracing for the specified function(s) only,
+similar to "functions -t" except that tracing is turned off for any
+functions called from the specified one(s) that don't also have the -t
+or -T flag.
+
+In file completion, the recursive-files style can be set to an array of
+patterns to matche against "$PWD/".  In any matched location, it is
+possibly to complete files in arbitrarily deep subdirectories without
+needing to type the directory prefix.  See example in the zshcompsys
+manual.
+
+The _user_expand completer now allows expansion functions in the
+user-expand files to return a string in REPLY that will be used to name
+the set of expansions returned.
+
+
+Changes between 4.2 and 5.0.0
+-----------------------------
 
 The following changes first appeared in the 4.3 series of releases;
 see also the file Etc/NEWS-4.3.
diff --git a/Src/math.c b/Src/math.c
index c7d384019..abc5f994e 100644
--- a/Src/math.c
+++ b/Src/math.c
@@ -452,7 +452,7 @@ lexconstant(void)
 	nptr++;
 	if (*nptr == 'x' || *nptr == 'X') {
 	    /* Let zstrtol parse number with base */
-	    yyval.u.l = zstrtol(ptr, &ptr, 0);
+	    yyval.u.l = zstrtol_underscore(ptr, &ptr, 0, 1);
 	    /* Should we set lastbase here? */
 	    lastbase = 16;
 	    return NUM;
@@ -466,13 +466,13 @@ lexconstant(void)
 	     * it can't be a base indication (always decimal)
 	     * or a floating point number.
 	     */
-	    for (ptr2 = nptr; idigit(*ptr2); ptr2++)
+	    for (ptr2 = nptr; idigit(*ptr2) || *ptr2 == '_'; ptr2++)
 		;
 
 	    if (ptr2 > nptr && *ptr2 != '.' && *ptr2 != 'e' &&
 		*ptr2 != 'E' && *ptr2 != '#')
 	    {
-		yyval.u.l = zstrtol(ptr, &ptr, 0);
+		yyval.u.l = zstrtol_underscore(ptr, &ptr, 0, 1);
 		lastbase = 8;
 		return NUM;
 	    }
@@ -481,17 +481,43 @@ lexconstant(void)
     }
     else
     {
-	while (idigit(*nptr))
+	while (idigit(*nptr) || *nptr == '_')
 	    nptr++;
     }
 
     if (*nptr == '.' || *nptr == 'e' || *nptr == 'E') {
+	char *ptr2;
 	/* it's a float */
 	yyval.type = MN_FLOAT;
 #ifdef USE_LOCALE
 	prev_locale = dupstring(setlocale(LC_NUMERIC, NULL));
 	setlocale(LC_NUMERIC, "POSIX");
 #endif
+	if (*nptr == '.') {
+	    nptr++;
+	    while (idigit(*nptr) || *nptr == '_')
+		nptr++;
+	}
+	if (*nptr == 'e' || *nptr == 'E') {
+	    nptr++;
+	    if (*nptr == '+' || *nptr == '-')
+		nptr++;
+	    while (idigit(*nptr) || *nptr == '_')
+		nptr++;
+	}
+	for (ptr2 = ptr; ptr2 < nptr; ptr2++) {
+	    if (*ptr2 == '_') {
+		int len = nptr - ptr;
+		ptr = strdup(ptr);
+		for (ptr2 = ptr; len; len--) {
+		    if (*ptr2 == '_')
+			chuck(ptr2);
+		    else
+			ptr2++;
+		}
+		break;
+	    }
+	}
 	yyval.u.d = strtod(ptr, &nptr);
 #ifdef USE_LOCALE
 	if (prev_locale) setlocale(LC_NUMERIC, prev_locale);
@@ -503,11 +529,12 @@ lexconstant(void)
 	ptr = nptr;
     } else {
 	/* it's an integer */
-	yyval.u.l = zstrtol(ptr, &ptr, 10);
+	yyval.u.l = zstrtol_underscore(ptr, &ptr, 10, 1);
 
 	if (*ptr == '#') {
 	    ptr++;
-	    yyval.u.l = zstrtol(ptr, &ptr, lastbase = yyval.u.l);
+	    lastbase = yyval.u.l;
+	    yyval.u.l = zstrtol_underscore(ptr, &ptr, lastbase, 1);
 	}
     }
     return NUM;
diff --git a/Src/utils.c b/Src/utils.c
index d35ca1dfd..cadb06f61 100644
--- a/Src/utils.c
+++ b/Src/utils.c
@@ -2030,13 +2030,20 @@ skipparens(char inpar, char outpar, char **s)
    return level;
 }
 
+/**/
+mod_export zlong
+zstrtol(const char *s, char **t, int base)
+{
+    return zstrtol_underscore(s, t, base, 0);
+}
+
 /* Convert string to zlong (see zsh.h).  This function (without the z) *
  * is contained in the ANSI standard C library, but a lot of them seem *
  * to be broken.                                                       */
 
 /**/
 mod_export zlong
-zstrtol(const char *s, char **t, int base)
+zstrtol_underscore(const char *s, char **t, int base, int underscore)
 {
     const char *inp, *trunc = NULL;
     zulong calc = 0, newcalc = 0;
@@ -2062,22 +2069,24 @@ zstrtol(const char *s, char **t, int base)
     if (base < 2 || base > 36) {
 	zerr("invalid base (must be 2 to 36 inclusive): %d", base);
 	return (zlong)0;
-    } else if (base <= 10)
-	for (; *s >= '0' && *s < ('0' + base); s++) {
-	    if (trunc)
+    } else if (base <= 10) {
+	for (; (*s >= '0' && *s < ('0' + base)) ||
+		 (underscore && *s == '_'); s++) {
+	    if (trunc || *s == '_')
 		continue;
 	    newcalc = calc * base + *s - '0';
 	    if (newcalc < calc)
 	    {
-	      trunc = s;
-	      continue;
+		trunc = s;
+		continue;
 	    }
 	    calc = newcalc;
 	}
-    else
+    } else {
 	for (; idigit(*s) || (*s >= 'a' && *s < ('a' + base - 10))
-	     || (*s >= 'A' && *s < ('A' + base - 10)); s++) {
-	    if (trunc)
+	     || (*s >= 'A' && *s < ('A' + base - 10))
+	     || (underscore && *s == '_'); s++) {
+	    if (trunc || *s == '_')
 		continue;
 	    newcalc = calc*base + (idigit(*s) ? (*s - '0') : (*s & 0x1f) + 9);
 	    if (newcalc < calc)
@@ -2087,6 +2096,7 @@ zstrtol(const char *s, char **t, int base)
 	    }
 	    calc = newcalc;
 	}
+    }
 
     /*
      * Special case: check for a number that was just too long for
diff --git a/Test/C01arith.ztst b/Test/C01arith.ztst
index 1f0d2d0f3..e5845d37f 100644
--- a/Test/C01arith.ztst
+++ b/Test/C01arith.ztst
@@ -210,3 +210,26 @@
   print $x
 0:double increment for repeated expression
 >2
+
+  # Floating point.  Default precision should take care of rounding errors.
+  print $(( 1_0.000_000e0_1 ))
+  # Integer.
+  print $(( 0x_ff_ff_ ))
+  # _ are parts of variable names that don't start with a digit
+  __myvar__=42
+  print $(( __myvar__ + $__myvar__ ))
+  # _ is not part of variable name that does start with a digit
+  # (which are substituted before math eval)
+  set -- 6
+  print $(( $1_000_000 ))
+  # Underscores in expressions with no whitespace
+  print $(( 3_000_+4_000_/2 ))
+  # Underscores may appear in the base descriptor, for what it's worth...
+  print $(( 1_6_#f_f_ ))
+0:underscores in math constants
+>100.
+>65535
+>84
+>6000000
+>5000
+>255