[pypy-commit] pypy py3.6: Add an option to allow underscores in integer literals. RPython part.
amauryfa
pypy.commits at gmail.com
Wed Jan 3 17:54:55 EST 2018
Author: Amaury Forgeot d'Arc <amauryfa at gmail.com>
Branch: py3.6
Changeset: r93621:23240e4a895f
Date: 2018-01-03 22:51 +0100
http://bitbucket.org/pypy/pypy/changeset/23240e4a895f/
Log: Add an option to allow underscores in integer literals. RPython
part.
diff --git a/rpython/rlib/rarithmetic.py b/rpython/rlib/rarithmetic.py
--- a/rpython/rlib/rarithmetic.py
+++ b/rpython/rlib/rarithmetic.py
@@ -845,7 +845,7 @@
# String parsing support
# ---------------------------
-def string_to_int(s, base=10):
+def string_to_int(s, base=10, allow_underscores=False):
"""Utility to converts a string to an integer.
If base is 0, the proper base is guessed based on the leading
characters of 's'. Raises ParseStringError in case of error.
@@ -854,7 +854,8 @@
from rpython.rlib.rstring import (
NumberStringParser, ParseStringOverflowError, strip_spaces)
s = literal = strip_spaces(s)
- p = NumberStringParser(s, literal, base, 'int')
+ p = NumberStringParser(s, literal, base, 'int',
+ allow_underscores=allow_underscores)
base = p.base
result = 0
while True:
diff --git a/rpython/rlib/rbigint.py b/rpython/rlib/rbigint.py
--- a/rpython/rlib/rbigint.py
+++ b/rpython/rlib/rbigint.py
@@ -272,7 +272,7 @@
@staticmethod
@jit.elidable
- def fromstr(s, base=0):
+ def fromstr(s, base=0, allow_underscores=False):
"""As string_to_int(), but ignores an optional 'l' or 'L' suffix
and returns an rbigint."""
from rpython.rlib.rstring import NumberStringParser, \
@@ -281,7 +281,8 @@
if (s.endswith('l') or s.endswith('L')) and base < 22:
# in base 22 and above, 'L' is a valid digit! try: long('L',22)
s = s[:-1]
- parser = NumberStringParser(s, literal, base, 'long')
+ parser = NumberStringParser(s, literal, base, 'long',
+ allow_underscores=allow_underscores)
return rbigint._from_numberstring_parser(parser)
@staticmethod
diff --git a/rpython/rlib/rstring.py b/rpython/rlib/rstring.py
--- a/rpython/rlib/rstring.py
+++ b/rpython/rlib/rstring.py
@@ -417,6 +417,9 @@
def __init__(self, msg):
self.msg = msg
+ def __str__(self):
+ return self.msg
+
class InvalidBaseError(ParseStringError):
"""Signals an invalid base argument"""
@@ -431,7 +434,7 @@
raise ParseStringError("invalid literal for %s() with base %d" %
(self.fname, self.original_base))
- def __init__(self, s, literal, base, fname):
+ def __init__(self, s, literal, base, fname, allow_underscores=False):
self.fname = fname
sign = 1
if s.startswith('-'):
@@ -441,6 +444,8 @@
s = strip_spaces(s[1:])
self.sign = sign
self.original_base = base
+ self.allow_underscores = allow_underscores
+ self.last_is_underscore = False
if base == 0:
if s.startswith('0x') or s.startswith('0X'):
@@ -480,13 +485,22 @@
digit = (digit - ord('A')) + 10
elif 'a' <= c <= 'z':
digit = (digit - ord('a')) + 10
+ elif c == '_' and self.allow_underscores:
+ if self.last_is_underscore:
+ self.error()
+ self.last_is_underscore = True
+ self.i += 1
+ return self.next_digit()
else:
self.error()
if digit >= self.base:
self.error()
self.i += 1
+ self.last_is_underscore = False
return digit
else:
+ if self.last_is_underscore:
+ self.error()
return -1
def prev_digit(self):
diff --git a/rpython/rlib/test/test_rarithmetic.py b/rpython/rlib/test/test_rarithmetic.py
--- a/rpython/rlib/test/test_rarithmetic.py
+++ b/rpython/rlib/test/test_rarithmetic.py
@@ -553,6 +553,51 @@
py.test.raises(ParseStringError, string_to_int, '+'+s, base)
py.test.raises(ParseStringError, string_to_int, '-'+s, base)
+ def test_number_underscores(self):
+ VALID_UNDERSCORE_LITERALS = [
+ '0_0_0',
+ '4_2',
+ '1_0000_0000',
+ '0b1001_0100',
+ '0xffff_ffff',
+ '0o5_7_7',
+ '0b_0',
+ '0x_f',
+ '0o_5',
+ ]
+ INVALID_UNDERSCORE_LITERALS = [
+ # Trailing underscores:
+ '0_',
+ '42_',
+ '1.4j_',
+ '0x_',
+ '0b1_',
+ '0xf_',
+ '0o5_',
+ # Underscores in the base selector:
+ '0_b0',
+ '0_xf',
+ '0_o5',
+ # Old-style octal, still disallowed:
+ '09_99',
+ # Multiple consecutive underscores:
+ '4_______2',
+ '0b1001__0100',
+ '0xffff__ffff',
+ '0x___',
+ '0o5__77',
+ '1e1__0',
+ ]
+ for x in VALID_UNDERSCORE_LITERALS:
+ print x
+ y = string_to_int(x, base=0, allow_underscores=True)
+ assert y == int(x.replace('_', ''), base=0)
+ for x in INVALID_UNDERSCORE_LITERALS:
+ print x
+ py.test.raises(ParseStringError, string_to_int, x, base=0,
+ allow_underscores=True)
+
+
class TestExplicitIntsizes:
_32_max = 2147483647
More information about the pypy-commit
mailing list