diff options
Diffstat (limited to 'Tools/peg_generator/pegen/grammar_visualizer.py')
-rw-r--r-- | Tools/peg_generator/pegen/grammar_visualizer.py | 65 |
1 files changed, 65 insertions, 0 deletions
diff --git a/Tools/peg_generator/pegen/grammar_visualizer.py b/Tools/peg_generator/pegen/grammar_visualizer.py new file mode 100644 index 0000000..b1d51d2 --- /dev/null +++ b/Tools/peg_generator/pegen/grammar_visualizer.py @@ -0,0 +1,65 @@ +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() |