Skip to content

Commit 35ad425

Browse files
authored
bpo-40528: Implement a metadata system for ASDL Generator (GH-20193)
ASDL Generator was lack of proper annotation related to generated module. This patch implements a MetadataVisitor that produces a metadata object to pass to other visitors that are visiting that same module. For the inital patch, it dynamically retrieves int sequences (like cmpop), that was previously hardcoded. It offers an interface that is easy to extend.
1 parent d1ae570 commit 35ad425

File tree

1 file changed

+75
-24
lines changed

1 file changed

+75
-24
lines changed

‎Parser/asdl_c.py‎

Lines changed: 75 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import os
55
import sys
66
import textwrap
7+
import types
78

89
from argparse import ArgumentParser
910
from contextlib import contextmanager
@@ -100,11 +101,12 @@ def asdl_of(name, obj):
100101
class EmitVisitor(asdl.VisitorBase):
101102
"""Visit that emits lines"""
102103

103-
def __init__(self, file):
104+
def __init__(self, file, metadata = None):
104105
self.file = file
105106
self.identifiers = set()
106107
self.singletons = set()
107108
self.types = set()
109+
self._metadata = metadata
108110
super(EmitVisitor, self).__init__()
109111

110112
def emit_identifier(self, name):
@@ -127,6 +129,42 @@ def emit(self, s, depth, reflow=True):
127129
line = (" " * TABSIZE * depth) + line
128130
self.file.write(line + "\n")
129131

132+
@property
133+
def metadata(self):
134+
if self._metadata is None:
135+
raise ValueError(
136+
"%s was expecting to be annnotated with metadata"
137+
% type(self).__name__
138+
)
139+
return self._metadata
140+
141+
@metadata.setter
142+
def metadata(self, value):
143+
self._metadata = value
144+
145+
class MetadataVisitor(asdl.VisitorBase):
146+
def __init__(self, *args, **kwargs):
147+
super().__init__(*args, **kwargs)
148+
149+
# Metadata:
150+
# - simple_sums: Tracks the list of compound type
151+
# names where all the constructors
152+
# belonging to that type lack of any
153+
# fields.
154+
self.metadata = types.SimpleNamespace(
155+
simple_sums=set()
156+
)
157+
158+
def visitModule(self, mod):
159+
for dfn in mod.dfns:
160+
self.visit(dfn)
161+
162+
def visitType(self, type):
163+
self.visit(type.value, type.name)
164+
165+
def visitSum(self, sum, name):
166+
if is_simple(sum):
167+
self.metadata.simple_sums.add(name)
130168

131169
class TypeDefVisitor(EmitVisitor):
132170
def visitModule(self, mod):
@@ -244,7 +282,7 @@ def visitField(self, field, depth):
244282
ctype = get_c_type(field.type)
245283
name = field.name
246284
if field.seq:
247-
if field.type == 'cmpop':
285+
if field.type in self.metadata.simple_sums:
248286
self.emit("asdl_int_seq *%(name)s;" % locals(), depth)
249287
else:
250288
_type = field.type
@@ -304,7 +342,7 @@ def get_args(self, fields):
304342
name = f.name
305343
# XXX should extend get_c_type() to handle this
306344
if f.seq:
307-
if f.type == 'cmpop':
345+
if f.type in self.metadata.simple_sums:
308346
ctype = "asdl_int_seq *"
309347
else:
310348
ctype = f"asdl_{f.type}_seq *"
@@ -549,16 +587,11 @@ def visitFieldDeclaration(self, field, name, sum=None, prod=None, depth=0):
549587
ctype = get_c_type(field.type)
550588
self.emit("%s %s;" % (ctype, field.name), depth)
551589

552-
def isSimpleSum(self, field):
553-
# XXX can the members of this list be determined automatically?
554-
return field.type in ('expr_context', 'boolop', 'operator',
555-
'unaryop', 'cmpop')
556-
557590
def isNumeric(self, field):
558591
return get_c_type(field.type) in ("int", "bool")
559592

560593
def isSimpleType(self, field):
561-
return self.isSimpleSum(field) or self.isNumeric(field)
594+
return field.type in self.metadata.simple_sums or self.isNumeric(field)
562595

563596
def visitField(self, field, name, sum=None, prod=None, depth=0):
564597
ctype = get_c_type(field.type)
@@ -1282,18 +1315,23 @@ def emit(s, d):
12821315

12831316
def set(self, field, value, depth):
12841317
if field.seq:
1285-
# XXX should really check for is_simple, but that requires a symbol table
1286-
if field.type == "cmpop":
1318+
if field.type in self.metadata.simple_sums:
12871319
# While the sequence elements are stored as void*,
1288-
# ast2obj_cmpop expects an enum
1320+
# simple sums expects an enum
12891321
self.emit("{", depth)
12901322
self.emit("Py_ssize_t i, n = asdl_seq_LEN(%s);" % value, depth+1)
12911323
self.emit("value = PyList_New(n);", depth+1)
12921324
self.emit("if (!value) goto failed;", depth+1)
12931325
self.emit("for(i = 0; i < n; i++)", depth+1)
12941326
# This cannot fail, so no need for error handling
1295-
self.emit("PyList_SET_ITEM(value, i, ast2obj_cmpop(state, (cmpop_ty)asdl_seq_GET(%s, i)));" % value,
1296-
depth+2, reflow=False)
1327+
self.emit(
1328+
"PyList_SET_ITEM(value, i, ast2obj_{0}(state, ({0}_ty)asdl_seq_GET({1}, i)));".format(
1329+
field.type,
1330+
value
1331+
),
1332+
depth + 2,
1333+
reflow=False,
1334+
)
12971335
self.emit("}", depth)
12981336
else:
12991337
self.emit("value = ast2obj_list(state, (asdl_seq*)%s, ast2obj_%s);" % (value, field.type), depth)
@@ -1362,11 +1400,13 @@ class PartingShots(StaticVisitor):
13621400
"""
13631401

13641402
class ChainOfVisitors:
1365-
def __init__(self, *visitors):
1403+
def __init__(self, *visitors, metadata = None):
13661404
self.visitors = visitors
1405+
self.metadata = metadata
13671406

13681407
def visit(self, object):
13691408
for v in self.visitors:
1409+
v.metadata = self.metadata
13701410
v.visit(object)
13711411
v.emit("", 0)
13721412

@@ -1468,7 +1508,7 @@ def generate_module_def(mod, f, internal_h):
14681508
f.write(' return 1;\n')
14691509
f.write('};\n\n')
14701510

1471-
def write_header(mod, f):
1511+
def write_header(mod, metadata, f):
14721512
f.write(textwrap.dedent("""
14731513
#ifndef Py_INTERNAL_AST_H
14741514
#define Py_INTERNAL_AST_H
@@ -1483,12 +1523,19 @@ def write_header(mod, f):
14831523
#include "pycore_asdl.h"
14841524
14851525
""").lstrip())
1486-
c = ChainOfVisitors(TypeDefVisitor(f),
1487-
SequenceDefVisitor(f),
1488-
StructVisitor(f))
1526+
1527+
c = ChainOfVisitors(
1528+
TypeDefVisitor(f),
1529+
SequenceDefVisitor(f),
1530+
StructVisitor(f),
1531+
metadata=metadata
1532+
)
14891533
c.visit(mod)
1534+
14901535
f.write("// Note: these macros affect function definitions, not only call sites.\n")
1491-
PrototypeVisitor(f).visit(mod)
1536+
prototype_visitor = PrototypeVisitor(f, metadata=metadata)
1537+
prototype_visitor.visit(mod)
1538+
14921539
f.write(textwrap.dedent("""
14931540
14941541
PyObject* PyAST_mod2obj(mod_ty t);
@@ -1535,8 +1582,7 @@ def write_internal_h_footer(mod, f):
15351582
#endif /* !Py_INTERNAL_AST_STATE_H */
15361583
"""), file=f)
15371584

1538-
1539-
def write_source(mod, f, internal_h_file):
1585+
def write_source(mod, metadata, f, internal_h_file):
15401586
generate_module_def(mod, f, internal_h_file)
15411587

15421588
v = ChainOfVisitors(
@@ -1549,6 +1595,7 @@ def write_source(mod, f, internal_h_file):
15491595
Obj2ModVisitor(f),
15501596
ASTModuleVisitor(f),
15511597
PartingShots(f),
1598+
metadata=metadata
15521599
)
15531600
v.visit(mod)
15541601

@@ -1561,6 +1608,10 @@ def main(input_filename, c_filename, h_filename, internal_h_filename, dump_modul
15611608
if not asdl.check(mod):
15621609
sys.exit(1)
15631610

1611+
metadata_visitor = MetadataVisitor()
1612+
metadata_visitor.visit(mod)
1613+
metadata = metadata_visitor.metadata
1614+
15641615
with c_filename.open("w") as c_file, \
15651616
h_filename.open("w") as h_file, \
15661617
internal_h_filename.open("w") as internal_h_file:
@@ -1569,8 +1620,8 @@ def main(input_filename, c_filename, h_filename, internal_h_filename, dump_modul
15691620
internal_h_file.write(auto_gen_msg)
15701621

15711622
write_internal_h_header(mod, internal_h_file)
1572-
write_source(mod, c_file, internal_h_file)
1573-
write_header(mod, h_file)
1623+
write_source(mod, metadata, c_file, internal_h_file)
1624+
write_header(mod, metadata, h_file)
15741625
write_internal_h_footer(mod, internal_h_file)
15751626

15761627
print(f"{c_filename}, {h_filename}, {internal_h_filename} regenerated.")

0 commit comments

Comments
 (0)