Skip to content

Commit 3084070

Browse files
DemonstrandumpicnixzJelleZijlstra
authored
gh-131421: fix ASDL grammar for Dict to have an expr?* keys field (#131419)
In the `ast` documentation for Python: * https://docs.python.org/3/library/ast.html#ast.Dict it is made clear that: > When doing dictionary unpacking using dictionary literals the expression to be expanded goes in the values list, with a `None` at the corresponding position in `keys`. Hence, `keys` is really a `expr?*` and *not* a `expr*`. Co-authored-by: Bénédikt Tran <[email protected]> Co-authored-by: Jelle Zijlstra <[email protected]>
1 parent af5799f commit 3084070

File tree

3 files changed

+40
-30
lines changed

3 files changed

+40
-30
lines changed

Parser/Python.asdl

+1-1
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ module Python
6363
| UnaryOp(unaryop op, expr operand)
6464
| Lambda(arguments args, expr body)
6565
| IfExp(expr test, expr body, expr orelse)
66-
| Dict(expr* keys, expr* values)
66+
| Dict(expr?* keys, expr* values)
6767
| Set(expr* elts)
6868
| ListComp(expr elt, comprehension* generators)
6969
| SetComp(expr elt, comprehension* generators)

Parser/asdl.py

+37-27
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,15 @@
1212
# type ::= product | sum
1313
# product ::= fields ["attributes" fields]
1414
# fields ::= "(" { field, "," } field ")"
15-
# field ::= TypeId ["?" | "*"] [Id]
15+
# field ::= TypeId { "?" | "*" } [Id]
1616
# sum ::= constructor { "|" constructor } ["attributes" fields]
1717
# constructor ::= ConstructorId [fields]
1818
#
1919
# [1] "The Zephyr Abstract Syntax Description Language" by Wang, et. al. See
2020
# http://asdl.sourceforge.net/
2121
#-------------------------------------------------------------------------------
2222
from collections import namedtuple
23+
import enum
2324
import re
2425

2526
__all__ = [
@@ -64,34 +65,43 @@ def __init__(self, name, fields=None):
6465
def __repr__(self):
6566
return 'Constructor({0.name}, {0.fields})'.format(self)
6667

68+
class Quantifier(enum.Enum):
69+
OPTIONAL = enum.auto()
70+
SEQUENCE = enum.auto()
71+
6772
class Field(AST):
68-
def __init__(self, type, name=None, seq=False, opt=False):
73+
def __init__(self, type, name=None, quantifiers=None):
6974
self.type = type
7075
self.name = name
71-
self.seq = seq
72-
self.opt = opt
76+
self.seq = False
77+
self.opt = False
78+
self.quantifiers = quantifiers or []
79+
if len(self.quantifiers) > 0:
80+
self.seq = self.quantifiers[-1] is Quantifier.SEQUENCE
81+
self.opt = self.quantifiers[-1] is Quantifier.OPTIONAL
7382

7483
def __str__(self):
75-
if self.seq:
76-
extra = "*"
77-
elif self.opt:
78-
extra = "?"
79-
else:
80-
extra = ""
84+
extra = ""
85+
for mod in self.quantifiers:
86+
if mod is Quantifier.SEQUENCE:
87+
extra += "*"
88+
elif mod is Quantifier.OPTIONAL:
89+
extra += "?"
8190

8291
return "{}{} {}".format(self.type, extra, self.name)
8392

8493
def __repr__(self):
85-
if self.seq:
86-
extra = ", seq=True"
87-
elif self.opt:
88-
extra = ", opt=True"
89-
else:
90-
extra = ""
94+
extra = ""
95+
for mod in self.quantifiers:
96+
if mod is Quantifier.SEQUENCE:
97+
extra += ", SEQUENCE"
98+
elif mod is Quantifier.OPTIONAL:
99+
extra += ", OPTIONAL"
100+
91101
if self.name is None:
92-
return 'Field({0.type}{1})'.format(self, extra)
102+
return 'Field({0.type}, quantifiers=[{1}])'.format(self, extra)
93103
else:
94-
return 'Field({0.type}, {0.name}{1})'.format(self, extra)
104+
return 'Field({0.type}, {0.name}, quantifiers=[{1}])'.format(self, extra)
95105

96106
class Sum(AST):
97107
def __init__(self, types, attributes=None):
@@ -314,10 +324,10 @@ def _parse_fields(self):
314324
self._match(TokenKind.LParen)
315325
while self.cur_token.kind == TokenKind.TypeId:
316326
typename = self._advance()
317-
is_seq, is_opt = self._parse_optional_field_quantifier()
327+
quantifiers = self._parse_optional_field_quantifier()
318328
id = (self._advance() if self.cur_token.kind in self._id_kinds
319329
else None)
320-
fields.append(Field(typename, id, seq=is_seq, opt=is_opt))
330+
fields.append(Field(typename, id, quantifiers=quantifiers))
321331
if self.cur_token.kind == TokenKind.RParen:
322332
break
323333
elif self.cur_token.kind == TokenKind.Comma:
@@ -339,14 +349,14 @@ def _parse_optional_attributes(self):
339349
return None
340350

341351
def _parse_optional_field_quantifier(self):
342-
is_seq, is_opt = False, False
343-
if self.cur_token.kind == TokenKind.Asterisk:
344-
is_seq = True
345-
self._advance()
346-
elif self.cur_token.kind == TokenKind.Question:
347-
is_opt = True
352+
quantifiers = []
353+
while self.cur_token.kind in (TokenKind.Asterisk, TokenKind.Question):
354+
if self.cur_token.kind == TokenKind.Asterisk:
355+
quantifiers.append(Quantifier.SEQUENCE)
356+
elif self.cur_token.kind == TokenKind.Question:
357+
quantifiers.append(Quantifier.OPTIONAL)
348358
self._advance()
349-
return is_seq, is_opt
359+
return quantifiers
350360

351361
def _advance(self):
352362
""" Return the value of the current token and read the next one into

Python/Python-ast.c

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)