1 #!/usr/bin/env python3.8
2 """Find the maximum amount of nesting for an expression that can be parsed
3 without causing a parse error.
4
5 Starting at the INITIAL_NESTING_DEPTH, an expression containing n parenthesis
6 around a 0 is generated then tested with both the C and Python parsers. We
7 continue incrementing the number of parenthesis by 10 until both parsers have
8 failed. As soon as a single parser fails, we stop testing that parser.
9
10 The grammar file, initial nesting size, and amount by which the nested size is
11 incremented on each success can be controlled by changing the GRAMMAR_FILE,
12 INITIAL_NESTING_DEPTH, or NESTED_INCR_AMT variables.
13
14 Usage: python -m scripts.find_max_nesting
15 """
16 import sys
17 import ast
18
19 GRAMMAR_FILE = "data/python.gram"
20 INITIAL_NESTING_DEPTH = 10
21 NESTED_INCR_AMT = 10
22
23
24 FAIL = "\033[91m"
25 ENDC = "\033[0m"
26
27
28 def check_nested_expr(nesting_depth: int) -> bool:
29 expr = f"{'(' * nesting_depth}0{')' * nesting_depth}"
30 try:
31 ast.parse(expr)
32 print(f"Nesting depth of {nesting_depth} is successful")
33 return True
34 except Exception as err:
35 print(f"{FAIL}(Failed with nesting depth of {nesting_depth}{ENDC}")
36 print(f"{FAIL}\t{err}{ENDC}")
37 return False
38
39
40 def main() -> None:
41 print(f"Testing {GRAMMAR_FILE} starting at nesting depth of {INITIAL_NESTING_DEPTH}...")
42
43 nesting_depth = INITIAL_NESTING_DEPTH
44 succeeded = True
45 while succeeded:
46 expr = f"{'(' * nesting_depth}0{')' * nesting_depth}"
47 if succeeded:
48 succeeded = check_nested_expr(nesting_depth)
49 nesting_depth += NESTED_INCR_AMT
50
51 sys.exit(1)
52
53
54 if __name__ == "__main__":
55 main()