mirror of
				https://github.com/python/cpython.git
				synced 2025-11-03 23:21:29 +00:00 
			
		
		
		
	The Full Grammar specification in the docs omits rule actions, so grammar rules that raise a syntax error looked like valid syntax.
This was solved in ef940de by hiding those rules in the custom syntax highlighter.
This moves all syntax-error alternatives to invalid rules, adds a validator that ensures that actions containing RAISE_SYNTAX_ERROR are in invalid rules, and reverts the syntax highlighter hack.
		
	
			
		
			
				
	
	
		
			53 lines
		
	
	
	
		
			1.8 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			53 lines
		
	
	
	
		
			1.8 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}"
 | 
						|
            )
 | 
						|
 | 
						|
 | 
						|
class RaiseRuleValidator(GrammarValidator):
 | 
						|
    def visit_Alt(self, node: Alt) -> None:
 | 
						|
        if self.rulename and self.rulename.startswith('invalid'):
 | 
						|
            # raising is allowed in invalid rules
 | 
						|
            return
 | 
						|
        if node.action and 'RAISE_SYNTAX_ERROR' in node.action:
 | 
						|
            raise ValidationError(
 | 
						|
                f"In {self.rulename!r} there is an alternative that contains "
 | 
						|
                f"RAISE_SYNTAX_ERROR; this is only allowed in invalid_ rules"
 | 
						|
            )
 | 
						|
 | 
						|
 | 
						|
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)
 |