Skip to content

Commit 49985d4

Browse files
committed
Address feedback
1 parent 84f5a95 commit 49985d4

7 files changed

Lines changed: 429 additions & 404 deletions

File tree

‎InternalDocs/parser.md‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -528,9 +528,11 @@ separately. The keyword tables and the `@trailer` code are only in the main C
528528
file, while the `@header` and `@subheader` code is copied into every C file,
529529
so it must not define non-`static` functions or variables (they would be defined
530530
more than once). Rules that are called from another C file, including rules of
531-
included files that the trailer calls, can't be `static`; the generated code
532-
gives them a `_PyPegen_` prefix with a `#define`, since CPython requires that
533-
prefix on its non-static symbols. Helper rules (for groups, repetitions and so on)
531+
included files that the trailer or a C action calls, can't be `static`; the
532+
generated code gives them a `_PyPegen_` prefix with a `#define`, since CPython
533+
requires that prefix on its non-static symbols, and marks them
534+
`Py_LOCAL_SYMBOL` so that they aren't exported from the shared library. Helper
535+
rules (for groups, repetitions and so on)
534536
are never shared between files: each C file gets its own `static` copy.
535537

536538
Tokenization

‎Lib/test/test_peg_generator/test_c_generator.py‎

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -342,8 +342,8 @@ def test_included_rules_go_to_their_own_file(self):
342342
self.assertNotIn("_PyPegen_start_rule", output)
343343
self.assertIn("static asdl_seq *_loop1_", root)
344344
self.assertIn("static void *\nstart_rule(Parser *p)", root)
345-
self.assertIn("\nvoid *\nitem_rule(Parser *p)", root)
346-
self.assertIn("\nvoid *\ninvalid_item_rule(Parser *p)", extra)
345+
self.assertIn("\nPy_LOCAL_SYMBOL void *\nitem_rule(Parser *p)", root)
346+
self.assertIn("\nPy_LOCAL_SYMBOL void *\ninvalid_item_rule(Parser *p)", extra)
347347
# A helper for an identical subexpression is not shared between
348348
# files: each one has its own static copy.
349349
self.assertIn("static void *_tmp_", root)
@@ -361,8 +361,21 @@ def test_trailer_can_call_rules_of_included_files(self):
361361
self.assertIn("return start_rule(p);", root)
362362
for output in root, rules:
363363
self.assertIn("#define start_rule _PyPegen_start_rule\n", output)
364-
self.assertIn("\nvoid *start_rule(Parser *p);\n", output)
365-
self.assertIn("\nvoid *\nstart_rule(Parser *p)\n{", rules)
364+
self.assertIn("\nPy_LOCAL_SYMBOL void *start_rule(Parser *p);\n", output)
365+
self.assertIn("\nPy_LOCAL_SYMBOL void *\nstart_rule(Parser *p)\n{", rules)
366+
367+
def test_actions_can_call_rules_of_included_files(self):
368+
main = self.write("main.gram", """\
369+
@include "extra.gram"
370+
start: NAME NEWLINE { extra_rule(p) }
371+
""")
372+
self.write("extra.gram", "extra: NAME\n")
373+
root, included = self.generate(main)
374+
extra = included["extra.gram"]
375+
for output in root, extra:
376+
self.assertIn("#define extra_rule _PyPegen_extra_rule\n", output)
377+
self.assertIn("\nPy_LOCAL_SYMBOL void *extra_rule(Parser *p);\n", output)
378+
self.assertIn("\nPy_LOCAL_SYMBOL void *\nextra_rule(Parser *p)\n{", extra)
366379

367380
def test_without_includes_all_rules_are_static(self):
368381
main = self.write("main.gram", """\

‎Parser/parser.c‎

Lines changed: 199 additions & 199 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎Parser/parser_invalid.c‎

Lines changed: 194 additions & 194 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎Tools/peg_generator/pegen/c_generator.py‎

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,8 @@ def collect_rules(self) -> None:
123123
self._collected = True
124124

125125
def _shared_rules(self) -> set[str]:
126-
# Rules called from a rule whose code goes in another C file.
126+
# Rules called from a rule (or its actions) whose code goes in
127+
# another C file.
127128
shared = set()
128129
for rule in self.all_rules.values():
129130
collector = _NameCollector()
@@ -138,7 +139,7 @@ def _shared_rules(self) -> set[str]:
138139
# The trailer is C code in the root file that calls rules by their
139140
# function name (the default one calls start_rule()).
140141
trailer = self.grammar.metas.get("trailer", EXTENSION_SUFFIX) or ""
141-
for name in re.findall(r"\b(\w+)_rule\b", trailer):
142+
for name in _RULE_CALL.findall(trailer):
142143
callee = self.rules.get(name)
143144
if callee is not None and callee.source is not None:
144145
shared.add(name)
@@ -170,9 +171,18 @@ def _prepare_trailer(self, rules: tuple[CRule, ...]) -> str | None:
170171
return None
171172

172173

174+
# A call to a rule's C function, as written in actions and the trailer.
175+
_RULE_CALL = re.compile(r"\b(\w+)_rule\b")
176+
177+
173178
class _NameCollector(grammar.GrammarVisitor):
174179
def __init__(self) -> None:
175180
self.names: set[str] = set()
176181

177182
def visit_NameLeaf(self, node: grammar.NameLeaf) -> None:
178183
self.names.add(node.value)
184+
185+
def visit_Alt(self, node: grammar.Alt) -> None:
186+
if node.action:
187+
self.names.update(_RULE_CALL.findall(node.action))
188+
self.generic_visit(node)

‎Tools/peg_generator/pegen/c_generator_file.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ def _emit_declarations(self) -> None:
7777
for rule in shared_rules:
7878
name = f"{rule.signature.name}_rule"
7979
self.print(f"#define {name} {SHARED_RULE_PREFIX}{name}")
80-
self.print(rule.signature.declaration(static=False))
80+
self.print(f"Py_LOCAL_SYMBOL {rule.signature.declaration(static=False)}")
8181
self.print()
8282
for _, rule in self._own_rules():
8383
if not rule.shared:

‎Tools/peg_generator/pegen/c_generator_rules.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ def emit(self) -> None:
9797
self._print(f"// {line}")
9898
if rule.left_recursive and rule.leader:
9999
self._print(f"static {result_type} {signature.name}_raw(Parser *);")
100-
self._print(result_type if rule.shared else f"static {result_type}")
100+
self._print(f"Py_LOCAL_SYMBOL {result_type}" if rule.shared else f"static {result_type}")
101101
self._print(f"{signature.name}_rule(Parser *p)")
102102
if rule.left_recursive and rule.leader:
103103
self._emit_left_recursive_wrapper()

0 commit comments

Comments
 (0)