Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
cython
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Gwenaël Samain
cython
Commits
50e67150
Commit
50e67150
authored
Mar 24, 2016
by
Robert Bradshaw
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #499 from cython/pep515_underscores_in_numbers
Implement PEP 515: allow underscores in number literals
parents
fe3a65f7
0c03ca0d
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
170 additions
and
9 deletions
+170
-9
Cython/Compiler/Lexicon.py
Cython/Compiler/Lexicon.py
+10
-7
Cython/Compiler/Scanning.pxd
Cython/Compiler/Scanning.pxd
+1
-0
Cython/Compiler/Scanning.py
Cython/Compiler/Scanning.py
+8
-2
Cython/Compiler/Tests/TestGrammar.py
Cython/Compiler/Tests/TestGrammar.py
+129
-0
runtests.py
runtests.py
+1
-0
tests/run/int_literals.pyx
tests/run/int_literals.pyx
+21
-0
No files found.
Cython/Compiler/Lexicon.py
View file @
50e67150
...
...
@@ -26,15 +26,18 @@ def make_lexicon():
hexdigit
=
Any
(
"0123456789ABCDEFabcdef"
)
indentation
=
Bol
+
Rep
(
Any
(
"
\
t
"
))
decimal
=
Rep1
(
digit
)
def
underscore_digits
(
d
):
return
Rep1
(
d
)
+
Rep
(
Str
(
"_"
)
+
Rep1
(
d
))
decimal
=
underscore_digits
(
digit
)
dot
=
Str
(
"."
)
exponent
=
Any
(
"Ee"
)
+
Opt
(
Any
(
"+-"
))
+
decimal
decimal_fract
=
(
decimal
+
dot
+
Opt
(
decimal
))
|
(
dot
+
decimal
)
name
=
letter
+
Rep
(
letter
|
digit
)
intconst
=
decimal
|
(
Str
(
"0"
)
+
((
Any
(
"Xx"
)
+
Rep1
(
hexdigit
))
|
(
Any
(
"Oo"
)
+
Rep1
(
octdigit
))
|
(
Any
(
"Bb"
)
+
Rep1
(
bindigit
))
))
intconst
=
decimal
|
(
Str
(
"0"
)
+
((
Any
(
"Xx"
)
+
underscore_digits
(
hexdigit
))
|
(
Any
(
"Oo"
)
+
underscore_digits
(
octdigit
))
|
(
Any
(
"Bb"
)
+
underscore_digits
(
bindigit
))
))
intsuffix
=
(
Opt
(
Any
(
"Uu"
))
+
Opt
(
Any
(
"Ll"
))
+
Opt
(
Any
(
"Ll"
)))
|
(
Opt
(
Any
(
"Ll"
))
+
Opt
(
Any
(
"Ll"
))
+
Opt
(
Any
(
"Uu"
)))
intliteral
=
intconst
+
intsuffix
fltconst
=
(
decimal_fract
+
Opt
(
exponent
))
|
(
decimal
+
exponent
)
...
...
@@ -67,9 +70,9 @@ def make_lexicon():
return
Lexicon
([
(
name
,
IDENT
),
(
intliteral
,
'INT'
),
(
fltconst
,
'FLOAT'
),
(
imagconst
,
'IMAG'
),
(
intliteral
,
Method
(
'strip_underscores'
,
symbol
=
'INT'
)
),
(
fltconst
,
Method
(
'strip_underscores'
,
symbol
=
'FLOAT'
)
),
(
imagconst
,
Method
(
'strip_underscores'
,
symbol
=
'IMAG'
)
),
(
punct
|
diphthong
,
TEXT
),
(
bra
,
Method
(
'open_bracket_action'
)),
...
...
Cython/Compiler/Scanning.pxd
View file @
50e67150
...
...
@@ -11,6 +11,7 @@ cdef initial_compile_time_env()
cdef
class
Method
:
cdef
object
name
cdef
dict
kwargs
cdef
readonly
object
__name__
# for tracing the scanner
@
cython
.
final
...
...
Cython/Compiler/Scanning.py
View file @
50e67150
...
...
@@ -53,12 +53,15 @@ pyx_reserved_words = py_reserved_words + [
class
Method
(
object
):
def
__init__
(
self
,
name
):
def
__init__
(
self
,
name
,
**
kwargs
):
self
.
name
=
name
self
.
kwargs
=
kwargs
or
None
self
.
__name__
=
name
# for Plex tracing
def
__call__
(
self
,
stream
,
text
):
return
getattr
(
stream
,
self
.
name
)(
text
)
method
=
getattr
(
stream
,
self
.
name
)
# self.kwargs is almost always unused => avoid call overhead
return
method
(
text
,
**
self
.
kwargs
)
if
self
.
kwargs
is
not
None
else
method
(
text
)
#------------------------------------------------------------------
...
...
@@ -340,6 +343,9 @@ class PyrexScanner(Scanner):
if
self
.
parse_comments
:
self
.
produce
(
'commentline'
,
text
)
def
strip_underscores
(
self
,
text
,
symbol
):
self
.
produce
(
symbol
,
text
.
replace
(
'_'
,
''
))
def
current_level
(
self
):
return
self
.
indentation_stack
[
-
1
]
...
...
Cython/Compiler/Tests/TestGrammar.py
0 → 100644
View file @
50e67150
# mode: run
# tag: syntax
"""
Uses TreeFragment to test invalid syntax.
"""
from
__future__
import
absolute_import
from
...TestUtils
import
CythonTest
from
..Errors
import
CompileError
from
..
import
ExprNodes
# Copied from CPython's test_grammar.py
VALID_UNDERSCORE_LITERALS
=
[
'0_0_0'
,
'4_2'
,
'1_0000_0000'
,
'0b1001_0100'
,
'0xffff_ffff'
,
'0o5_7_7'
,
'1_00_00.5'
,
'1_00_00.5j'
,
'1_00_00.5e5'
,
'1_00_00j'
,
'1_00_00e5_1'
,
'1e1_0'
,
'.1_4'
,
'.1_4e1'
,
'.1_4j'
,
]
# Copied from CPython's test_grammar.py
INVALID_UNDERSCORE_LITERALS
=
[
# Trailing underscores:
'0_'
,
'42_'
,
'1.4j_'
,
'0b1_'
,
'0xf_'
,
'0o5_'
,
# Underscores in the base selector:
'0_b0'
,
'0_xf'
,
'0_o5'
,
# Underscore right after the base selector:
'0b_0'
,
'0x_f'
,
'0o_5'
,
# Old-style octal, still disallowed:
#'0_7',
#'09_99',
# Special case with exponent:
'0 if 1_Else 1'
,
# Underscore right before a dot:
'1_.4'
,
'1_.4j'
,
# Underscore right after a dot:
'1._4'
,
'1._4j'
,
'._5'
,
# Underscore right after a sign:
'1.0e+_1'
,
# Multiple consecutive underscores:
'4_______2'
,
'0.1__4'
,
'0b1001__0100'
,
'0xffff__ffff'
,
'0o5__77'
,
'1e1__0'
,
# Underscore right before j:
'1.4_j'
,
'1.4e5_j'
,
# Underscore right before e:
'1_e1'
,
'1.4_e1'
,
# Underscore right after e:
'1e_1'
,
'1.4e_1'
,
# Whitespace in literals
'1_ 2'
,
'1 _2'
,
'1_2.2_ 1'
,
'1_2.2 _1'
,
'1_2e _1'
,
'1_2e2 _1'
,
'1_2e 2_1'
,
]
class
TestGrammar
(
CythonTest
):
def
test_invalid_number_literals
(
self
):
for
literal
in
INVALID_UNDERSCORE_LITERALS
:
for
expression
in
[
'%s'
,
'1 + %s'
,
'%s + 1'
,
'2 * %s'
,
'%s * 2'
]:
code
=
'x = '
+
expression
%
literal
try
:
self
.
fragment
(
u'''
\
# cython: language_level=3
'''
+
code
)
except
CompileError
as
exc
:
assert
code
in
[
s
.
strip
()
for
s
in
str
(
exc
).
splitlines
()],
str
(
exc
)
else
:
assert
False
,
"Invalid Cython code '%s' failed to raise an exception"
%
code
def
test_valid_number_literals
(
self
):
for
literal
in
VALID_UNDERSCORE_LITERALS
:
for
i
,
expression
in
enumerate
([
'%s'
,
'1 + %s'
,
'%s + 1'
,
'2 * %s'
,
'%s * 2'
]):
code
=
'x = '
+
expression
%
literal
node
=
self
.
fragment
(
u'''
\
# cython: language_level=3
'''
+
code
).
root
assert
node
is
not
None
literal_node
=
node
.
stats
[
0
].
rhs
# StatListNode([SingleAssignmentNode('x', expr)])
if
i
>
0
:
# Add/MulNode() -> literal is first or second operand
literal_node
=
literal_node
.
operand2
if
i
%
2
else
literal_node
.
operand1
if
'j'
in
literal
or
'J'
in
literal
:
assert
isinstance
(
literal_node
,
ExprNodes
.
ImagNode
)
elif
'.'
in
literal
or
'e'
in
literal
or
'E'
in
literal
and
not
(
'0x'
in
literal
or
'0X'
in
literal
):
assert
isinstance
(
literal_node
,
ExprNodes
.
FloatNode
)
else
:
assert
isinstance
(
literal_node
,
ExprNodes
.
IntNode
)
if
__name__
==
"__main__"
:
import
unittest
unittest
.
main
()
runtests.py
View file @
50e67150
...
...
@@ -2100,3 +2100,4 @@ if __name__ == '__main__':
except PendingThreadsError:
# normal program exit won't kill the threads, do it the hard way here
flush_and_terminate(1)
sys.exit(1)
tests/run/int_literals.pyx
View file @
50e67150
# mode: run
# tag: syntax
from
__future__
import
absolute_import
cimport
cython
from
cython
cimport
typeof
import
sys
def
valid_underscore_literals
():
"""
>>> valid_underscore_literals()
"""
# Copied from CPython's test_grammar.py
assert
0_0_0
==
0
assert
4_2
==
42
assert
1_0000_0000
==
100000000
assert
0b1001_0100
==
0b10010100
assert
0xffff_ffff
==
0xffffffff
assert
0o5_7_7
==
0o577
assert
1_00_00.5
==
10000.5
assert
1e1_0
==
1e10
assert
.
1_4
==
.
14
@
cython
.
test_assert_path_exists
(
'//IntNode[@longness = "LL"]'
,
'//IntNode[@longness = "L"]'
,
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment