1 import argparse
2 import sys
3 from typing import Any, Callable, Iterator
4
5 from pegen.build import build_parser
6 from pegen.grammar import Grammar, Rule
7
8 argparser = argparse.ArgumentParser(
9 prog="pegen", description="Pretty print the AST for a given PEG grammar"
10 )
11 argparser.add_argument("filename", help="Grammar description")
12
13
14 class ESC[4;38;5;81mASTGrammarPrinter:
15 def children(self, node: Rule) -> Iterator[Any]:
16 for value in node:
17 if isinstance(value, list):
18 yield from value
19 else:
20 yield value
21
22 def name(self, node: Rule) -> str:
23 if not list(self.children(node)):
24 return repr(node)
25 return node.__class__.__name__
26
27 def print_grammar_ast(self, grammar: Grammar, printer: Callable[..., None] = print) -> None:
28 for rule in grammar.rules.values():
29 printer(self.print_nodes_recursively(rule))
30
31 def print_nodes_recursively(self, node: Rule, prefix: str = "", istail: bool = True) -> str:
32
33 children = list(self.children(node))
34 value = self.name(node)
35
36 line = prefix + ("└──" if istail else "├──") + value + "\n"
37 sufix = " " if istail else "│ "
38
39 if not children:
40 return line
41
42 *children, last = children
43 for child in children:
44 line += self.print_nodes_recursively(child, prefix + sufix, False)
45 line += self.print_nodes_recursively(last, prefix + sufix, True)
46
47 return line
48
49
50 def main() -> None:
51 args = argparser.parse_args()
52
53 try:
54 grammar, parser, tokenizer = build_parser(args.filename)
55 except Exception as err:
56 print("ERROR: Failed to parse grammar file", file=sys.stderr)
57 sys.exit(1)
58
59 visitor = ASTGrammarPrinter()
60 visitor.print_grammar_ast(grammar)
61
62
63 if __name__ == "__main__":
64 main()