gh-143054: Disallow non-top-level Cut for now (GH-143622) · python/cpython@f0a0467 · GitHub
Skip to content

Commit f0a0467

Browse files
authored
gh-143054: Disallow non-top-level Cut for now (GH-143622)
The behaviour of Cut in nested parentheses, Repeat, Opt, and similar is somewhat chaotic. Apparently even the academic papers on PEG aren't as clear as they could be. And it doesn't really matter. Python only uses top-level cuts. When that changes, we can clarify as much as necessary (and even change the implementation to make sense for what we'll need). Document that this is deliberately unspecified, and add a test to make sure any decision is deliberate, tested and documented.
1 parent a7ba3b1 commit f0a0467

4 files changed

Lines changed: 85 additions & 3 deletions

File tree

Doc/reference/grammar.rst

Lines changed: 11 additions & 2 deletions

Lib/test/test_peg_generator/test_grammar_validator.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
test_tools.skip_if_missing("peg_generator")
55
with test_tools.imports_under_tool("peg_generator"):
66
from pegen.grammar_parser import GeneratedParser as GrammarParser
7-
from pegen.validator import SubRuleValidator, ValidationError, RaiseRuleValidator
7+
from pegen.validator import SubRuleValidator, ValidationError
8+
from pegen.validator import RaiseRuleValidator, CutValidator
89
from pegen.testutil import parse_string
910
from pegen.grammar import Grammar
1011

@@ -59,3 +60,18 @@ def test_raising_valid_rule(self) -> None:
5960
with self.assertRaises(ValidationError):
6061
for rule_name, rule in grammar.rules.items():
6162
validator.validate_rule(rule_name, rule)
63+
64+
def test_cut_validator(self) -> None:
65+
grammar_source = """
66+
star: (OP ~ OP)*
67+
plus: (OP ~ OP)+
68+
bracket: [OP ~ OP]
69+
gather: OP.(OP ~ OP)+
70+
nested: [OP | NAME ~ OP]
71+
"""
72+
grammar: Grammar = parse_string(grammar_source, GrammarParser)
73+
validator = CutValidator(grammar)
74+
for rule_name, rule in grammar.rules.items():
75+
with self.subTest(rule_name):
76+
with self.assertRaises(ValidationError):
77+
validator.validate_rule(rule_name, rule)

Lib/test/test_peg_generator/test_pegen.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -755,6 +755,30 @@ def test_cut(self) -> None:
755755
],
756756
)
757757

758+
def test_cut_is_local_in_rule(self) -> None:
759+
grammar = """
760+
start:
761+
| inner
762+
| 'x' { "ok" }
763+
inner:
764+
| 'x' ~ 'y'
765+
| 'x'
766+
"""
767+
parser_class = make_parser(grammar)
768+
node = parse_string("x", parser_class)
769+
self.assertEqual(node, 'ok')
770+
771+
def test_cut_is_local_in_parens(self) -> None:
772+
# we currently don't guarantee this behavior, see gh-143054
773+
grammar = """
774+
start:
775+
| ('x' ~ 'y' | 'x')
776+
| 'x' { "ok" }
777+
"""
778+
parser_class = make_parser(grammar)
779+
node = parse_string("x", parser_class)
780+
self.assertEqual(node, 'ok')
781+
758782
def test_dangling_reference(self) -> None:
759783
grammar = """
760784
start: foo ENDMARKER

Tools/peg_generator/pegen/validator.py

Lines changed: 33 additions & 0 deletions

0 commit comments

Comments
 (0)