mirror of
				https://github.com/python/cpython.git
				synced 2025-11-04 11:49:12 +00:00 
			
		
		
		
	Simplify the peg generator logic by extracting as much visitors as possible to disentangle the flow and separate concerns.
		
			
				
	
	
		
			64 lines
		
	
	
	
		
			1.8 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			64 lines
		
	
	
	
		
			1.8 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
import argparse
 | 
						|
import sys
 | 
						|
from typing import Any, Callable, Iterator
 | 
						|
 | 
						|
from pegen.build import build_parser
 | 
						|
from pegen.grammar import Grammar, Rule
 | 
						|
 | 
						|
argparser = argparse.ArgumentParser(
 | 
						|
    prog="pegen", description="Pretty print the AST for a given PEG grammar"
 | 
						|
)
 | 
						|
argparser.add_argument("filename", help="Grammar description")
 | 
						|
 | 
						|
 | 
						|
class ASTGrammarPrinter:
 | 
						|
    def children(self, node: Rule) -> Iterator[Any]:
 | 
						|
        for value in node:
 | 
						|
            if isinstance(value, list):
 | 
						|
                yield from value
 | 
						|
            else:
 | 
						|
                yield value
 | 
						|
 | 
						|
    def name(self, node: Rule) -> str:
 | 
						|
        if not list(self.children(node)):
 | 
						|
            return repr(node)
 | 
						|
        return node.__class__.__name__
 | 
						|
 | 
						|
    def print_grammar_ast(self, grammar: Grammar, printer: Callable[..., None] = print) -> None:
 | 
						|
        for rule in grammar.rules.values():
 | 
						|
            printer(self.print_nodes_recursively(rule))
 | 
						|
 | 
						|
    def print_nodes_recursively(self, node: Rule, prefix: str = "", istail: bool = True) -> str:
 | 
						|
 | 
						|
        children = list(self.children(node))
 | 
						|
        value = self.name(node)
 | 
						|
 | 
						|
        line = prefix + ("└──" if istail else "├──") + value + "\n"
 | 
						|
        sufix = "   " if istail else "│  "
 | 
						|
 | 
						|
        if not children:
 | 
						|
            return line
 | 
						|
 | 
						|
        *children, last = children
 | 
						|
        for child in children:
 | 
						|
            line += self.print_nodes_recursively(child, prefix + sufix, False)
 | 
						|
        line += self.print_nodes_recursively(last, prefix + sufix, True)
 | 
						|
 | 
						|
        return line
 | 
						|
 | 
						|
 | 
						|
def main() -> None:
 | 
						|
    args = argparser.parse_args()
 | 
						|
 | 
						|
    try:
 | 
						|
        grammar, parser, tokenizer = build_parser(args.filename)
 | 
						|
    except Exception as err:
 | 
						|
        print("ERROR: Failed to parse grammar file", file=sys.stderr)
 | 
						|
        sys.exit(1)
 | 
						|
 | 
						|
    visitor = ASTGrammarPrinter()
 | 
						|
    visitor.print_grammar_ast(grammar)
 | 
						|
 | 
						|
 | 
						|
if __name__ == "__main__":
 | 
						|
    main()
 |