1 from typing import Optional
2
3 from pegen import grammar
4 from pegen.grammar import Alt, GrammarVisitor, Rhs, Rule
5
6
7 class ESC[4;38;5;81mValidationError(ESC[4;38;5;149mException):
8 pass
9
10
11 class ESC[4;38;5;81mGrammarValidator(ESC[4;38;5;149mGrammarVisitor):
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
22 class ESC[4;38;5;81mSubRuleValidator(ESC[4;38;5;149mGrammarValidator):
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
37 def 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)