1from typing import Optional 2 3from pegen import grammar 4from pegen.grammar import Alt, GrammarVisitor, Rhs, Rule 5 6 7class ValidationError(Exception): 8 pass 9 10 11class GrammarValidator(GrammarVisitor): 12 def __init__(self, grammar: grammar.Grammar) -> None: 13 self.grammar = grammar 14 self.rulename: Optional[str] = None 15 16 def validate_rule(self, rulename: str, node: Rule) -> None: 17 self.rulename = rulename 18 self.visit(node) 19 self.rulename = None 20 21 22class SubRuleValidator(GrammarValidator): 23 def visit_Rhs(self, node: Rhs) -> None: 24 for index, alt in enumerate(node.alts): 25 alts_to_consider = node.alts[index + 1 :] 26 for other_alt in alts_to_consider: 27 self.check_intersection(alt, other_alt) 28 29 def check_intersection(self, first_alt: Alt, second_alt: Alt) -> None: 30 if str(second_alt).startswith(str(first_alt)): 31 raise ValidationError( 32 f"In {self.rulename} there is an alternative that will " 33 f"never be visited:\n{second_alt}" 34 ) 35 36 37def validate_grammar(the_grammar: grammar.Grammar) -> None: 38 for validator_cls in GrammarValidator.__subclasses__(): 39 validator = validator_cls(the_grammar) 40 for rule_name, rule in the_grammar.rules.items(): 41 validator.validate_rule(rule_name, rule) 42