cpython/Tools/peg_generator/pegen/grammar_visualizer.py

66 lines
1.8 KiB
Python

import argparse
import sys
from typing import Any, Iterator, Callable
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()