Skip to content

Commit 949dcef

Browse files
committed
lib2to3: Support named assignment expressions
There are two copies of the grammar -- the one used by Python itself as Grammar/Grammar, and the one used by lib2to3 which has necessarily diverged at Lib/lib2to3/Grammar.txt because it needs to support older syntax an we want it to be reasonable stable to avoid requiring fixer rewrites. This brings suport for syntax like `if x:= foo():` to match what the live Python grammar does.
1 parent 8af4712 commit 949dcef

File tree

6 files changed

+27
-6
lines changed

6 files changed

+27
-6
lines changed

Lib/lib2to3/Grammar.txt

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,8 @@ assert_stmt: 'assert' test [',' test]
6767

6868
compound_stmt: if_stmt | while_stmt | for_stmt | try_stmt | with_stmt | funcdef | classdef | decorated | async_stmt
6969
async_stmt: ASYNC (funcdef | with_stmt | for_stmt)
70-
if_stmt: 'if' test ':' suite ('elif' test ':' suite)* ['else' ':' suite]
71-
while_stmt: 'while' test ':' suite ['else' ':' suite]
70+
if_stmt: 'if' namedexpr_test ':' suite ('elif' namedexpr_test ':' suite)* ['else' ':' suite]
71+
while_stmt: 'while' namedexpr_test ':' suite ['else' ':' suite]
7272
for_stmt: 'for' exprlist 'in' testlist ':' suite ['else' ':' suite]
7373
try_stmt: ('try' ':' suite
7474
((except_clause ':' suite)+
@@ -91,6 +91,7 @@ testlist_safe: old_test [(',' old_test)+ [',']]
9191
old_test: or_test | old_lambdef
9292
old_lambdef: 'lambda' [varargslist] ':' old_test
9393

94+
namedexpr_test: test [':=' test]
9495
test: or_test ['if' or_test 'else' test] | lambdef
9596
or_test: and_test ('or' and_test)*
9697
and_test: not_test ('and' not_test)*
@@ -111,8 +112,8 @@ atom: ('(' [yield_expr|testlist_gexp] ')' |
111112
'{' [dictsetmaker] '}' |
112113
'`' testlist1 '`' |
113114
NAME | NUMBER | STRING+ | '.' '.' '.')
114-
listmaker: (test|star_expr) ( comp_for | (',' (test|star_expr))* [','] )
115-
testlist_gexp: (test|star_expr) ( comp_for | (',' (test|star_expr))* [','] )
115+
listmaker: (namedexpr_test|star_expr) ( comp_for | (',' (namedexpr_test|star_expr))* [','] )
116+
testlist_gexp: (namedexpr_test|star_expr) ( comp_for | (',' (namedexpr_test|star_expr))* [','] )
116117
lambdef: 'lambda' [varargslist] ':' test
117118
trailer: '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME
118119
subscriptlist: subscript (',' subscript)* [',']
@@ -137,6 +138,7 @@ arglist: argument (',' argument)* [',']
137138
# multiple (test comp_for) arguments are blocked; keyword unpackings
138139
# that precede iterable unpackings are blocked; etc.
139140
argument: ( test [comp_for] |
141+
test ':=' test |
140142
test '=' test |
141143
'**' test |
142144
'*' test )

Lib/lib2to3/pgen2/grammar.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,7 @@ def report(self):
178178
// DOUBLESLASH
179179
//= DOUBLESLASHEQUAL
180180
-> RARROW
181+
:= COLONEQUAL
181182
"""
182183

183184
opmap = {}

Lib/lib2to3/pgen2/token.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,8 @@
6565
AWAIT = 56
6666
ASYNC = 57
6767
ERRORTOKEN = 58
68-
N_TOKENS = 59
68+
COLONEQUAL = 59
69+
N_TOKENS = 60
6970
NT_OFFSET = 256
7071
#--end constants--
7172

Lib/lib2to3/pgen2/tokenize.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ def _combinations(*l):
9393
r"~")
9494

9595
Bracket = '[][(){}]'
96-
Special = group(r'\r?\n', r'[:;.,`@]')
96+
Special = group(r'\r?\n', r':=', r'[:;.,`@]')
9797
Funny = group(Operator, Bracket, Special)
9898

9999
PlainToken = group(Number, Funny, String, Name)

Lib/lib2to3/tests/test_parser.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -629,6 +629,21 @@ def test_multiline_str_literals(self):
629629
self.validate(s)
630630

631631

632+
class TestNamedAssignments(GrammarTest):
633+
634+
def test_named_assignment_if(self):
635+
driver.parse_string("if f := x(): pass\n")
636+
637+
def test_named_assignment_while(self):
638+
driver.parse_string("while f := x(): pass\n")
639+
640+
def test_named_assignment_generator(self):
641+
driver.parse_string("any((lastNum := num) == 1 for num in [1, 2, 3])\n")
642+
643+
def test_named_assignment_listcomp(self):
644+
driver.parse_string("[(lastNum := num) == 1 for num in [1, 2, 3]]\n")
645+
646+
632647
class TestPickleableException(unittest.TestCase):
633648
def test_ParseError(self):
634649
err = ParseError('msg', 2, None, (1, 'context'))
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
lib2to3 now recognizes named assignment expressions (the walrus operator,
2+
``:=``)

0 commit comments

Comments
 (0)