mirror of
https://github.com/python/cpython.git
synced 2025-07-18 00:35:17 +00:00

Simplify the peg generator logic by extracting as much visitors as possible to disentangle the flow and separate concerns.
41 lines
1.3 KiB
Python
41 lines
1.3 KiB
Python
from typing import Optional
|
|
|
|
from pegen import grammar
|
|
from pegen.grammar import Alt, GrammarVisitor, Rhs, Rule
|
|
|
|
|
|
class ValidationError(Exception):
|
|
pass
|
|
|
|
|
|
class GrammarValidator(GrammarVisitor):
|
|
def __init__(self, grammar: grammar.Grammar) -> None:
|
|
self.grammar = grammar
|
|
self.rulename: Optional[str] = None
|
|
|
|
def validate_rule(self, rulename: str, node: Rule) -> None:
|
|
self.rulename = rulename
|
|
self.visit(node)
|
|
self.rulename = None
|
|
|
|
|
|
class SubRuleValidator(GrammarValidator):
|
|
def visit_Rhs(self, node: Rhs) -> None:
|
|
for index, alt in enumerate(node.alts):
|
|
alts_to_consider = node.alts[index + 1 :]
|
|
for other_alt in alts_to_consider:
|
|
self.check_intersection(alt, other_alt)
|
|
|
|
def check_intersection(self, first_alt: Alt, second_alt: Alt) -> None:
|
|
if str(second_alt).startswith(str(first_alt)):
|
|
raise ValidationError(
|
|
f"In {self.rulename} there is an alternative that will "
|
|
f"never be visited:\n{second_alt}"
|
|
)
|
|
|
|
|
|
def validate_grammar(the_grammar: grammar.Grammar) -> None:
|
|
for validator_cls in GrammarValidator.__subclasses__():
|
|
validator = validator_cls(the_grammar)
|
|
for rule_name, rule in the_grammar.rules.items():
|
|
validator.validate_rule(rule_name, rule)
|