mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 13:41:24 +00:00 
			
		
		
		
	 18f1226884
			
		
	
	
		18f1226884
		
			
		
	
	
	
	
		
			
			(cherry picked from commit ba6fd87e41)
Co-authored-by: Lysandros Nikolaou <lisandrosnik@gmail.com>
		
	
			
		
			
				
	
	
		
			121 lines
		
	
	
	
		
			3.1 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable file
		
	
	
	
	
			
		
		
	
	
			121 lines
		
	
	
	
		
			3.1 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable file
		
	
	
	
	
| #!/usr/bin/env python3.8
 | |
| 
 | |
| """Show the parse tree for a given program, nicely formatted.
 | |
| 
 | |
| Example:
 | |
| 
 | |
| $ scripts/show_parse.py a+b
 | |
| Module(
 | |
|     body=[
 | |
|         Expr(
 | |
|             value=BinOp(
 | |
|                 left=Name(id="a", ctx=Load()), op=Add(), right=Name(id="b", ctx=Load())
 | |
|             )
 | |
|         )
 | |
|     ],
 | |
|     type_ignores=[],
 | |
| )
 | |
| $
 | |
| 
 | |
| Use -v to show line numbers and column offsets.
 | |
| 
 | |
| The formatting is done using black.  You can also import this module
 | |
| and call one of its functions.
 | |
| """
 | |
| 
 | |
| import argparse
 | |
| import ast
 | |
| import difflib
 | |
| import os
 | |
| import sys
 | |
| import tempfile
 | |
| 
 | |
| import _peg_parser
 | |
| 
 | |
| from typing import List
 | |
| 
 | |
| sys.path.insert(0, os.getcwd())
 | |
| from pegen.ast_dump import ast_dump
 | |
| 
 | |
| parser = argparse.ArgumentParser()
 | |
| parser.add_argument(
 | |
|     "-d", "--diff", action="store_true", help="show diff between grammar and ast (requires -g)"
 | |
| )
 | |
| parser.add_argument(
 | |
|     "-p",
 | |
|     "--parser",
 | |
|     choices=["new", "old"],
 | |
|     default="new",
 | |
|     help="choose the parser to use"
 | |
| )
 | |
| parser.add_argument(
 | |
|     "-m",
 | |
|     "--multiline",
 | |
|     action="store_true",
 | |
|     help="concatenate program arguments using newline instead of space",
 | |
| )
 | |
| parser.add_argument("-v", "--verbose", action="store_true", help="show line/column numbers")
 | |
| parser.add_argument("program", nargs="+", help="program to parse (will be concatenated)")
 | |
| 
 | |
| 
 | |
| def format_tree(tree: ast.AST, verbose: bool = False) -> str:
 | |
|     with tempfile.NamedTemporaryFile("w+") as tf:
 | |
|         tf.write(ast_dump(tree, include_attributes=verbose))
 | |
|         tf.write("\n")
 | |
|         tf.flush()
 | |
|         cmd = f"black -q {tf.name}"
 | |
|         sts = os.system(cmd)
 | |
|         if sts:
 | |
|             raise RuntimeError(f"Command {cmd!r} failed with status 0x{sts:x}")
 | |
|         tf.seek(0)
 | |
|         return tf.read()
 | |
| 
 | |
| 
 | |
| def diff_trees(a: ast.AST, b: ast.AST, verbose: bool = False) -> List[str]:
 | |
|     sa = format_tree(a, verbose)
 | |
|     sb = format_tree(b, verbose)
 | |
|     la = sa.splitlines()
 | |
|     lb = sb.splitlines()
 | |
|     return list(difflib.unified_diff(la, lb, "a", "b", lineterm=""))
 | |
| 
 | |
| 
 | |
| def show_parse(source: str, verbose: bool = False) -> str:
 | |
|     tree = _peg_parser.parse_string(source, oldparser=True)
 | |
|     return format_tree(tree, verbose).rstrip("\n")
 | |
| 
 | |
| 
 | |
| def print_parse(source: str, verbose: bool = False) -> None:
 | |
|     print(show_parse(source, verbose))
 | |
| 
 | |
| 
 | |
| def main() -> None:
 | |
|     args = parser.parse_args()
 | |
|     new_parser = args.parser == "new"
 | |
|     if args.multiline:
 | |
|         sep = "\n"
 | |
|     else:
 | |
|         sep = " "
 | |
|     program = sep.join(args.program)
 | |
|     if new_parser:
 | |
|         tree = _peg_parser.parse_string(program)
 | |
| 
 | |
|         if args.diff:
 | |
|             a = _peg_parser.parse_string(program, oldparser=True)
 | |
|             b = tree
 | |
|             diff = diff_trees(a, b, args.verbose)
 | |
|             if diff:
 | |
|                 for line in diff:
 | |
|                     print(line)
 | |
|             else:
 | |
|                 print("# Trees are the same")
 | |
|         else:
 | |
|             print("# Parsed using the new parser")
 | |
|             print(format_tree(tree, args.verbose))
 | |
|     else:
 | |
|         tree = _peg_parser.parse_string(program, oldparser=True)
 | |
|         print("# Parsed using the old parser")
 | |
|         print(format_tree(tree, args.verbose))
 | |
| 
 | |
| 
 | |
| if __name__ == "__main__":
 | |
|     main()
 |