parse27.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418
  1. # Copyright (c) 2016-2020, 2023-2024 Rocky Bernstein
  2. # Copyright (c) 2005 by Dan Pascu <dan@windowmaker.org>
  3. # Copyright (c) 2000-2002 by hartmut Goebel <hartmut@goebel.noris.de>
  4. from spark_parser import DEFAULT_DEBUG as PARSER_DEFAULT_DEBUG
  5. from xdis import next_offset
  6. from uncompyle6.parser import PythonParserSingle, nop_func
  7. from uncompyle6.parsers.parse2 import Python2Parser
  8. from uncompyle6.parsers.reducecheck import (
  9. aug_assign1_check,
  10. except_handler,
  11. for_block_check,
  12. ifelsestmt,
  13. or_check,
  14. tryelsestmt,
  15. )
  16. class Python27Parser(Python2Parser):
  17. def __init__(self, debug_parser=PARSER_DEFAULT_DEBUG):
  18. super(Python27Parser, self).__init__(debug_parser)
  19. self.customized = {}
  20. def p_comprehension27(self, args):
  21. """
  22. list_for ::= expr for_iter store list_iter JUMP_BACK
  23. list_comp ::= BUILD_LIST_0 list_iter
  24. lc_body ::= expr LIST_APPEND
  25. for_iter ::= GET_ITER COME_FROM FOR_ITER
  26. stmt ::= set_comp_func
  27. # Dictionary and set comprehensions were added in Python 2.7
  28. expr ::= dict_comp
  29. dict_comp ::= LOAD_DICTCOMP MAKE_FUNCTION_0 expr GET_ITER CALL_FUNCTION_1
  30. stmt ::= dict_comp_func
  31. dict_comp_func ::= BUILD_MAP_0 LOAD_FAST FOR_ITER store
  32. comp_iter JUMP_BACK ending_return
  33. set_comp_func ::= BUILD_SET_0 LOAD_FAST FOR_ITER store comp_iter
  34. JUMP_BACK ending_return
  35. comp_iter ::= comp_if_not
  36. comp_if_not ::= expr jmp_true comp_iter
  37. comp_body ::= dict_comp_body
  38. comp_body ::= set_comp_body
  39. comp_for ::= expr for_iter store comp_iter JUMP_BACK
  40. dict_comp_body ::= expr expr MAP_ADD
  41. set_comp_body ::= expr SET_ADD
  42. # See also common Python p_list_comprehension
  43. """
  44. def p_try27(self, args):
  45. """
  46. # If the last except is a "raise" we might not have a final COME_FROM
  47. # FIXME: need a check on this rule since this accepts try_except when
  48. # we shouldn't
  49. try_except ::= SETUP_EXCEPT suite_stmts_opt POP_BLOCK
  50. except_handler
  51. tryfinallystmt ::= SETUP_FINALLY suite_stmts_opt
  52. POP_BLOCK LOAD_CONST
  53. COME_FROM_FINALLY suite_stmts_opt END_FINALLY
  54. tryelsestmt ::= SETUP_EXCEPT suite_stmts_opt POP_BLOCK
  55. except_handler_else else_suite COME_FROM
  56. tryelsestmtl ::= SETUP_EXCEPT suite_stmts_opt POP_BLOCK
  57. except_handler_else else_suitel JUMP_BACK COME_FROM
  58. tryelsestmtl ::= SETUP_EXCEPT suite_stmts_opt POP_BLOCK
  59. except_handler_else else_suitel
  60. tryelsestmtc ::= SETUP_EXCEPT suite_stmts_opt POP_BLOCK
  61. except_handler_else else_suitec COME_FROM
  62. except_stmt ::= except_cond2 except_suite
  63. except_cond1 ::= DUP_TOP expr COMPARE_OP
  64. jmp_false POP_TOP POP_TOP POP_TOP
  65. except_cond2 ::= DUP_TOP expr COMPARE_OP
  66. jmp_false POP_TOP store POP_TOP
  67. for_block ::= l_stmts_opt JUMP_BACK
  68. # In 2.7 there is occasionally a for_block has an unusual
  69. # form: there is a JUMP_ABSOLUTE which jumps to the second JUMP_BACK
  70. # listed below. Both JUMP_BACKS go to the same position so the
  71. # the JUMP_ABSOLUTE and JUMP_BACK not necessary
  72. for_block ::= l_stmts_opt JUMP_ABSOLUTE JUMP_BACK JUMP_BACK
  73. """
  74. def p_jump27(self, args):
  75. """
  76. iflaststmtl ::= testexpr c_stmts
  77. _ifstmts_jump ::= c_stmts_opt JUMP_FORWARD come_froms
  78. pb_come_from ::= POP_BLOCK COME_FROM
  79. # FIXME: Common with 3.0+
  80. jmp_false ::= POP_JUMP_IF_FALSE
  81. jmp_true ::= POP_JUMP_IF_TRUE
  82. ret_and ::= expr JUMP_IF_FALSE_OR_POP return_expr_or_cond COME_FROM
  83. ret_or ::= expr JUMP_IF_TRUE_OR_POP return_expr_or_cond COME_FROM
  84. if_exp_ret ::= expr POP_JUMP_IF_FALSE expr RETURN_END_IF COME_FROM return_expr_or_cond
  85. expr_jitop ::= expr JUMP_IF_TRUE_OR_POP
  86. or ::= expr_jitop expr COME_FROM
  87. and ::= expr JUMP_IF_FALSE_OR_POP expr COME_FROM
  88. # compare_chained{middle,2} is used exclusively in chained_compare
  89. compared_chained_middle ::= expr DUP_TOP ROT_THREE COMPARE_OP
  90. JUMP_IF_FALSE_OR_POP compared_chained_middle
  91. COME_FROM
  92. compared_chained_middle ::= expr DUP_TOP ROT_THREE COMPARE_OP
  93. JUMP_IF_FALSE_OR_POP compare_chained_right COME_FROM
  94. return_lambda ::= RETURN_VALUE
  95. return_lambda ::= RETURN_VALUE_LAMBDA
  96. compare_chained_right ::= expr COMPARE_OP return_lambda
  97. compare_chained_right ::= expr COMPARE_OP return_lambda
  98. # if_exp_true are for conditions which always evaluate true
  99. # There is dead or non-optional remnants of the condition code though,
  100. # and we use that to match on to reconstruct the source more accurately.
  101. # FIXME: we should do analysis and reduce *only* if there is dead code?
  102. # right now we check that expr is "or". Any other nodes types?
  103. expr ::= if_exp_true
  104. if_exp_true ::= expr JUMP_FORWARD expr COME_FROM
  105. if_exp ::= expr jmp_false expr JUMP_FORWARD expr COME_FROM
  106. if_exp ::= expr jmp_false expr JUMP_ABSOLUTE expr
  107. """
  108. def p_stmt27(self, args):
  109. """
  110. stmt ::= ifelsestmtr
  111. stmt ::= ifelsestmtc
  112. # assert condition
  113. assert ::= assert_expr jmp_true LOAD_ASSERT RAISE_VARARGS_1
  114. # assert condition, expr
  115. assert2 ::= assert_expr jmp_true LOAD_ASSERT expr CALL_FUNCTION_1 RAISE_VARARGS_1
  116. continue ::= JUMP_BACK JUMP_ABSOLUTE
  117. for_block ::= returns _come_froms
  118. with ::= expr SETUP_WITH POP_TOP suite_stmts_opt
  119. POP_BLOCK LOAD_CONST COME_FROM_WITH
  120. WITH_CLEANUP END_FINALLY
  121. with_as ::= expr SETUP_WITH store suite_stmts_opt
  122. POP_BLOCK LOAD_CONST COME_FROM_WITH
  123. WITH_CLEANUP END_FINALLY
  124. whilestmt ::= SETUP_LOOP testexpr returns
  125. _come_froms POP_BLOCK COME_FROM
  126. # 2.7.5 (and before to 2.7.0?)
  127. while1stmt ::= SETUP_LOOP l_stmts_opt JUMP_BACK COME_FROM
  128. while1stmt ::= SETUP_LOOP l_stmts_opt CONTINUE COME_FROM
  129. while1stmt ::= SETUP_LOOP returns COME_FROM
  130. while1elsestmt ::= SETUP_LOOP l_stmts JUMP_BACK
  131. else_suitel COME_FROM
  132. while1stmt ::= SETUP_LOOP returns pb_come_from
  133. while1stmt ::= SETUP_LOOP l_stmts_opt JUMP_BACK POP_BLOCK COME_FROM
  134. whilestmt ::= SETUP_LOOP testexpr l_stmts_opt JUMP_BACK POP_BLOCK
  135. _come_froms
  136. # Should this be JUMP_BACK+ ?
  137. # JUMP_BACK should all be to the same location
  138. whilestmt ::= SETUP_LOOP testexpr l_stmts_opt JUMP_BACK
  139. JUMP_BACK POP_BLOCK _come_froms
  140. while1elsestmt ::= SETUP_LOOP l_stmts JUMP_BACK POP_BLOCK
  141. else_suitel COME_FROM
  142. whileelsestmt ::= SETUP_LOOP testexpr l_stmts_opt JUMP_BACK POP_BLOCK
  143. else_suitel COME_FROM
  144. return_stmts ::= _stmts return_stmt
  145. return_stmts ::= return_stmt
  146. return_stmt ::= return
  147. ifstmt ::= testexpr return_stmts COME_FROM
  148. ifstmt ::= testexpr return_if_stmts COME_FROM
  149. ifelsestmt ::= testexpr c_stmts_opt JUMP_FORWARD else_suite come_froms
  150. ifelsestmtc ::= testexpr c_stmts_opt JUMP_ABSOLUTE else_suitec
  151. ifelsestmtc ::= testexpr c_stmts_opt JUMP_FORWARD else_suite come_froms
  152. ifelsestmtl ::= testexpr c_stmts_opt JUMP_BACK else_suitel
  153. ifelsestmtl ::= testexpr c_stmts_opt CONTINUE else_suitel
  154. # In the future when we have ifelsestmtl checking we should add something like:
  155. # ifelsestmtl ::= testexpr c_stmts_opt JUMP_FORWARD else_suite come_froms
  156. # c_stmts ::= ifelsestmtl
  157. # "if"/"else" statement that ends in a RETURN
  158. ifelsestmtr ::= testexpr return_if_stmts COME_FROM returns
  159. # Common with 2.6
  160. return_if_lambda ::= RETURN_END_IF_LAMBDA COME_FROM
  161. stmt ::= if_exp_lambda
  162. stmt ::= if_exp_not_lambda
  163. if_exp_lambda ::= expr jmp_false expr return_if_lambda
  164. return_stmt_lambda LAMBDA_MARKER
  165. if_exp_not_lambda ::= expr jmp_true expr return_if_lambda
  166. return_stmt_lambda LAMBDA_MARKER
  167. expr ::= if_exp_not
  168. if_exp_not ::= expr jmp_true expr _jump expr COME_FROM
  169. kv3 ::= expr expr STORE_MAP
  170. """
  171. def customize_grammar_rules(self, tokens, customize):
  172. # 2.7 changes COME_FROM to COME_FROM_FINALLY
  173. self.remove_rules(
  174. """
  175. while1elsestmt ::= SETUP_LOOP l_stmts JUMP_BACK else_suite COME_FROM
  176. tryfinallystmt ::= SETUP_FINALLY suite_stmts_opt
  177. POP_BLOCK LOAD_CONST COME_FROM suite_stmts_opt
  178. END_FINALLY
  179. """
  180. )
  181. if "PyPy" in customize:
  182. # PyPy-specific customizations
  183. self.addRule(
  184. """
  185. return_if_stmt ::= return_expr RETURN_END_IF come_froms
  186. """,
  187. nop_func,
  188. )
  189. super(Python27Parser, self).customize_grammar_rules(tokens, customize)
  190. # FIXME: Put more in this table
  191. self.reduce_check_table = {
  192. "aug_assign1": aug_assign1_check,
  193. "except_handler": except_handler,
  194. "for_block": for_block_check.for_block_invalid,
  195. "ifelsestmt": ifelsestmt,
  196. "ifelsestmtc": ifelsestmt,
  197. "or": or_check,
  198. "tryelsestmt": tryelsestmt,
  199. "tryelsestmtl": tryelsestmt,
  200. }
  201. self.check_reduce["and"] = "AST"
  202. self.check_reduce["aug_assign1"] = "AST"
  203. self.check_reduce["if_exp"] = "AST"
  204. self.check_reduce["except_handler"] = "tokens"
  205. self.check_reduce["except_handler_else"] = "tokens"
  206. self.check_reduce["for_block"] = "tokens"
  207. self.check_reduce["or"] = "AST"
  208. self.check_reduce["raise_stmt1"] = "AST"
  209. self.check_reduce["ifelsestmt"] = "AST"
  210. self.check_reduce["ifelsestmtc"] = "AST"
  211. self.check_reduce["iflaststmtl"] = "AST"
  212. self.check_reduce["list_if_not"] = "AST"
  213. self.check_reduce["list_if"] = "AST"
  214. self.check_reduce["comp_if"] = "AST"
  215. self.check_reduce["if_exp_true"] = "tokens"
  216. self.check_reduce["whilestmt"] = "tokens"
  217. return
  218. def reduce_is_invalid(self, rule, ast, tokens, first, last):
  219. invalid = super(Python27Parser, self).reduce_is_invalid(
  220. rule, ast, tokens, first, last
  221. )
  222. lhs = rule[0]
  223. n = len(tokens)
  224. fn = self.reduce_check_table.get(lhs, None)
  225. if fn:
  226. invalid = fn(self, lhs, n, rule, ast, tokens, first, last)
  227. last = min(last, n - 1)
  228. if invalid:
  229. return invalid
  230. if rule == ("comp_if", ("expr", "jmp_false", "comp_iter")):
  231. jmp_false = ast[1]
  232. if jmp_false[0] == "POP_JUMP_IF_FALSE":
  233. return tokens[first].offset < jmp_false[0].attr < tokens[last].offset
  234. pass
  235. elif (rule[0], rule[1][0:5]) == (
  236. "if_exp",
  237. ("expr", "jmp_false", "expr", "JUMP_ABSOLUTE", "expr"),
  238. ):
  239. jmp_false = ast[1]
  240. if jmp_false[0] == "POP_JUMP_IF_FALSE":
  241. else_instr = ast[4].first_child()
  242. if jmp_false[0].attr != else_instr.offset:
  243. return True
  244. end_offset = ast[3].attr
  245. return end_offset < tokens[last].offset
  246. pass
  247. elif rule[0] == "raise_stmt1":
  248. return ast[0] == "expr" and ast[0][0] == "or"
  249. elif rule[0] in ("assert", "assert2"):
  250. jump_inst = ast[1][0]
  251. jump_target = jump_inst.attr
  252. return not (
  253. last >= len(tokens)
  254. or jump_target == tokens[last].offset
  255. or jump_target == next_offset(ast[-1].op, ast[-1].opc, ast[-1].offset)
  256. )
  257. elif rule == ("ifstmt", ("testexpr", "_ifstmts_jump")):
  258. for i in range(last - 1, last - 4, -1):
  259. t = tokens[i]
  260. if t == "JUMP_FORWARD":
  261. return t.attr > tokens[min(last, len(tokens) - 1)].off2int()
  262. elif t not in ("POP_TOP", "COME_FROM"):
  263. break
  264. pass
  265. pass
  266. elif rule == ("iflaststmtl", ("testexpr", "c_stmts")):
  267. testexpr = ast[0]
  268. if testexpr[0] in ("testfalse", "testtrue"):
  269. test = testexpr[0]
  270. if len(test) > 1 and test[1].kind.startswith("jmp_"):
  271. jmp_target = test[1][0].attr
  272. if last == len(tokens):
  273. last -= 1
  274. while isinstance(tokens[first].offset, str) and first < last:
  275. first += 1
  276. if first == last:
  277. return True
  278. while first < last and isinstance(tokens[last].offset, str):
  279. last -= 1
  280. return tokens[first].off2int() < jmp_target < tokens[last].off2int()
  281. pass
  282. pass
  283. pass
  284. elif rule == ("list_if_not", ("expr", "jmp_true", "list_iter")):
  285. jump_inst = ast[1][0]
  286. jump_offset = jump_inst.attr
  287. return jump_inst.offset < jump_offset < tokens[last].offset
  288. elif rule == ("list_if", ("expr", "jmp_false", "list_iter")):
  289. jump_inst = ast[1][0]
  290. jump_offset = jump_inst.attr
  291. return jump_inst.offset < jump_offset < tokens[last].offset
  292. elif rule == ("or", ("expr", "jmp_true", "expr", "\\e_come_from_opt")):
  293. # Test that jmp_true doesn't jump inside the middle the "or"
  294. # or that it jumps to the same place as the end of "and"
  295. jmp_true = ast[1][0]
  296. jmp_target = jmp_true.offset + jmp_true.attr + 3
  297. return not (
  298. jmp_target == tokens[last].offset
  299. or tokens[last].pattr == jmp_true.pattr
  300. )
  301. elif rule[0] == "whilestmt" and rule[1][0:-2] == (
  302. "SETUP_LOOP",
  303. "testexpr",
  304. "l_stmts_opt",
  305. "JUMP_BACK",
  306. "JUMP_BACK",
  307. ):
  308. # Make sure that the jump backs all go to the same place
  309. i = last - 1
  310. while tokens[i] != "JUMP_BACK":
  311. i -= 1
  312. return tokens[i].attr != tokens[i - 1].attr
  313. elif rule[0] == "if_exp_true":
  314. return (first) > 0 and tokens[first - 1] == "POP_JUMP_IF_FALSE"
  315. return False
  316. class Python27ParserSingle(Python27Parser, PythonParserSingle):
  317. pass
  318. if __name__ == "__main__":
  319. # Check grammar
  320. p = Python27Parser()
  321. p.check_grammar()
  322. from xdis.version_info import IS_PYPY, PYTHON_VERSION_TRIPLE
  323. if PYTHON_VERSION_TRIPLE[:2] == (2, 7):
  324. lhs, rhs, tokens, right_recursive, dup_rhs = p.check_sets()
  325. from uncompyle6.scanner import get_scanner
  326. s = get_scanner(PYTHON_VERSION_TRIPLE, IS_PYPY)
  327. opcode_set = set(s.opc.opname).union(
  328. set(
  329. """JUMP_BACK CONTINUE RETURN_END_IF COME_FROM
  330. LOAD_GENEXPR LOAD_ASSERT LOAD_SETCOMP LOAD_DICTCOMP
  331. LAMBDA_MARKER RETURN_LAST
  332. """.split()
  333. )
  334. )
  335. remain_tokens = set(tokens) - opcode_set
  336. import re
  337. remain_tokens = set([re.sub(r"_\d+$", "", t) for t in remain_tokens])
  338. remain_tokens = set([re.sub("_CONT$", "", t) for t in remain_tokens])
  339. remain_tokens = set(remain_tokens) - opcode_set
  340. print(remain_tokens)
  341. # p.dump_grammar()