mirror of https://github.com/python/cpython
66 lines
1.8 KiB
Python
66 lines
1.8 KiB
Python
import argparse
|
|
import sys
|
|
|
|
from typing import Any, Iterator, Iterable, 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()
|