parse35.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. # Copyright (c) 2016-2017, 2019, 2021, 2023-2024
  2. # Rocky Bernstein
  3. """
  4. spark grammar differences over Python 3.4 for Python 3.5.
  5. """
  6. from __future__ import print_function
  7. from spark_parser import DEFAULT_DEBUG as PARSER_DEFAULT_DEBUG
  8. from uncompyle6.parser import PythonParserSingle, nop_func
  9. from uncompyle6.parsers.parse34 import Python34Parser
  10. class Python35Parser(Python34Parser):
  11. def __init__(self, debug_parser=PARSER_DEFAULT_DEBUG):
  12. super(Python35Parser, self).__init__(debug_parser)
  13. self.customized = {}
  14. def p_35on(self, args):
  15. """
  16. # FIXME! isolate this to only loops!
  17. _ifstmts_jump ::= c_stmts_opt come_froms
  18. ifelsestmt ::= testexpr c_stmts_opt jump_forward_else else_suite _come_froms
  19. pb_ja ::= POP_BLOCK JUMP_ABSOLUTE
  20. # The number of canned instructions in new statements is mind boggling.
  21. # I'm sure by the time Python 4 comes around these will be turned
  22. # into special opcodes
  23. while1stmt ::= SETUP_LOOP l_stmts COME_FROM JUMP_BACK
  24. POP_BLOCK COME_FROM_LOOP
  25. while1stmt ::= SETUP_LOOP l_stmts POP_BLOCK COME_FROM_LOOP
  26. while1elsestmt ::= SETUP_LOOP l_stmts JUMP_BACK
  27. POP_BLOCK else_suite COME_FROM_LOOP
  28. # The following rule is for Python 3.5+ where we can have stuff like
  29. # while ..
  30. # if
  31. # ...
  32. # the end of the if will jump back to the loop and there will be a COME_FROM
  33. # after the jump
  34. l_stmts ::= lastl_stmt come_froms l_stmts
  35. # Python 3.5+ Await statement
  36. expr ::= await_expr
  37. await_expr ::= expr GET_AWAITABLE LOAD_CONST YIELD_FROM
  38. stmt ::= await_stmt
  39. await_stmt ::= await_expr POP_TOP
  40. # Python 3.5+ has WITH_CLEANUP_START/FINISH
  41. with ::= expr
  42. SETUP_WITH POP_TOP suite_stmts_opt
  43. POP_BLOCK LOAD_CONST COME_FROM_WITH
  44. WITH_CLEANUP_START WITH_CLEANUP_FINISH END_FINALLY
  45. with_as ::= expr
  46. SETUP_WITH store suite_stmts_opt
  47. POP_BLOCK LOAD_CONST COME_FROM_WITH
  48. WITH_CLEANUP_START WITH_CLEANUP_FINISH END_FINALLY
  49. # Python 3.5+ async additions
  50. stmt ::= async_for_stmt
  51. async_for_stmt ::= SETUP_LOOP expr
  52. GET_AITER
  53. LOAD_CONST YIELD_FROM SETUP_EXCEPT GET_ANEXT LOAD_CONST
  54. YIELD_FROM
  55. store
  56. POP_BLOCK jump_except COME_FROM_EXCEPT DUP_TOP
  57. LOAD_GLOBAL COMPARE_OP POP_JUMP_IF_FALSE
  58. POP_TOP POP_TOP POP_TOP POP_EXCEPT POP_BLOCK
  59. JUMP_ABSOLUTE END_FINALLY COME_FROM
  60. for_block POP_BLOCK JUMP_ABSOLUTE
  61. COME_FROM_LOOP
  62. async_for_stmt ::= SETUP_LOOP expr
  63. GET_AITER
  64. LOAD_CONST YIELD_FROM SETUP_EXCEPT GET_ANEXT LOAD_CONST
  65. YIELD_FROM
  66. store
  67. POP_BLOCK jump_except COME_FROM_EXCEPT DUP_TOP
  68. LOAD_GLOBAL COMPARE_OP POP_JUMP_IF_FALSE
  69. POP_TOP POP_TOP POP_TOP POP_EXCEPT POP_BLOCK
  70. JUMP_ABSOLUTE END_FINALLY JUMP_BACK
  71. pass POP_BLOCK JUMP_ABSOLUTE
  72. COME_FROM_LOOP
  73. stmt ::= async_forelse_stmt
  74. async_forelse_stmt ::= SETUP_LOOP expr
  75. GET_AITER
  76. LOAD_CONST YIELD_FROM SETUP_EXCEPT GET_ANEXT LOAD_CONST
  77. YIELD_FROM
  78. store
  79. POP_BLOCK JUMP_FORWARD COME_FROM_EXCEPT DUP_TOP
  80. LOAD_GLOBAL COMPARE_OP POP_JUMP_IF_FALSE
  81. POP_TOP POP_TOP POP_TOP POP_EXCEPT POP_BLOCK
  82. JUMP_ABSOLUTE END_FINALLY COME_FROM
  83. for_block pb_ja
  84. else_suite COME_FROM_LOOP
  85. inplace_op ::= INPLACE_MATRIX_MULTIPLY
  86. binary_operator ::= BINARY_MATRIX_MULTIPLY
  87. # Python 3.5+ does jump optimization
  88. # In <.3.5 the below is a JUMP_FORWARD to a JUMP_ABSOLUTE.
  89. return_if_lambda ::= RETURN_END_IF_LAMBDA COME_FROM
  90. return ::= return_expr RETURN_END_IF
  91. jb_else ::= JUMP_BACK ELSE
  92. ifelsestmtc ::= testexpr c_stmts_opt JUMP_FORWARD else_suitec
  93. ifelsestmtl ::= testexpr c_stmts_opt jb_else else_suitel
  94. # 3.5 Has jump optimization which can route the end of an
  95. # "if/then" back to a loop just before an else.
  96. jump_absolute_else ::= jb_else
  97. jump_absolute_else ::= CONTINUE ELSE
  98. # Our hacky "ELSE" determination doesn't do a good job and really
  99. # determine the start of an "else". It could also be the end of an
  100. # "if-then" which ends in a "continue". Perhaps with real control-flow
  101. # analysis we'll sort this out. Or call "ELSE" something more appropriate.
  102. _ifstmts_jump ::= c_stmts_opt ELSE
  103. # ifstmt ::= testexpr c_stmts_opt
  104. iflaststmt ::= testexpr c_stmts_opt JUMP_FORWARD
  105. # Python 3.3+ also has yield from. 3.5 does it
  106. # differently than 3.3, 3.4
  107. yield_from ::= expr GET_YIELD_FROM_ITER LOAD_CONST YIELD_FROM
  108. """
  109. def customize_grammar_rules(self, tokens, customize):
  110. self.remove_rules(
  111. """
  112. yield_from ::= expr GET_ITER LOAD_CONST YIELD_FROM
  113. yield_from ::= expr expr YIELD_FROM
  114. with ::= expr SETUP_WITH POP_TOP suite_stmts_opt
  115. POP_BLOCK LOAD_CONST COME_FROM_WITH
  116. WITH_CLEANUP END_FINALLY
  117. with_as ::= expr SETUP_WITH store suite_stmts_opt
  118. POP_BLOCK LOAD_CONST COME_FROM_WITH
  119. WITH_CLEANUP END_FINALLY
  120. """
  121. )
  122. super(Python35Parser, self).customize_grammar_rules(tokens, customize)
  123. for i, token in enumerate(tokens):
  124. opname = token.kind
  125. if opname == "LOAD_ASSERT":
  126. if "PyPy" in customize:
  127. rules_str = """
  128. stmt ::= JUMP_IF_NOT_DEBUG stmts COME_FROM
  129. """
  130. self.add_unique_doc_rules(rules_str, customize)
  131. # FIXME: I suspect this is wrong for 3.6 and 3.5, but
  132. # I haven't verified what the 3.7ish fix is
  133. elif opname == "BUILD_MAP_UNPACK_WITH_CALL":
  134. if self.version < (3, 7):
  135. self.addRule("expr ::= unmapexpr", nop_func)
  136. nargs = token.attr % 256
  137. map_unpack_n = "map_unpack_%s" % nargs
  138. rule = map_unpack_n + " ::= " + "expr " * (nargs)
  139. self.addRule(rule, nop_func)
  140. rule = "unmapexpr ::= %s %s" % (map_unpack_n, opname)
  141. self.addRule(rule, nop_func)
  142. call_token = tokens[i + 1]
  143. rule = "call ::= expr unmapexpr " + call_token.kind
  144. self.addRule(rule, nop_func)
  145. elif opname == "BEFORE_ASYNC_WITH" and self.version < (3, 8):
  146. # Some Python 3.5+ async additions
  147. rules_str = """
  148. stmt ::= async_with_stmt
  149. async_with_pre ::= BEFORE_ASYNC_WITH GET_AWAITABLE LOAD_CONST YIELD_FROM SETUP_ASYNC_WITH
  150. async_with_post ::= COME_FROM_ASYNC_WITH
  151. WITH_CLEANUP_START GET_AWAITABLE LOAD_CONST YIELD_FROM
  152. WITH_CLEANUP_FINISH END_FINALLY
  153. async_with_stmt ::= expr
  154. async_with_pre
  155. POP_TOP
  156. suite_stmts_opt
  157. POP_BLOCK LOAD_CONST
  158. async_with_post
  159. async_with_stmt ::= expr
  160. async_with_pre
  161. POP_TOP
  162. suite_stmts_opt
  163. async_with_post
  164. stmt ::= async_with_as_stmt
  165. async_with_as_stmt ::= expr
  166. async_with_pre
  167. store
  168. suite_stmts_opt
  169. POP_BLOCK LOAD_CONST
  170. async_with_post
  171. """
  172. self.addRule(rules_str, nop_func)
  173. elif opname == "BUILD_MAP_UNPACK":
  174. self.addRule(
  175. """
  176. expr ::= dict_unpack
  177. dict_unpack ::= dict_comp BUILD_MAP_UNPACK
  178. """,
  179. nop_func,
  180. )
  181. elif opname == "SETUP_WITH":
  182. # Python 3.5+ has WITH_CLEANUP_START/FINISH
  183. rules_str = """
  184. with ::= expr
  185. SETUP_WITH POP_TOP suite_stmts_opt
  186. POP_BLOCK LOAD_CONST COME_FROM_WITH
  187. WITH_CLEANUP_START WITH_CLEANUP_FINISH END_FINALLY
  188. with_as ::= expr
  189. SETUP_WITH store suite_stmts_opt
  190. POP_BLOCK LOAD_CONST COME_FROM_WITH
  191. WITH_CLEANUP_START WITH_CLEANUP_FINISH END_FINALLY
  192. """
  193. self.addRule(rules_str, nop_func)
  194. pass
  195. return
  196. def custom_classfunc_rule(self, opname, token, customize, *args):
  197. args_pos, args_kw = self.get_pos_kw(token)
  198. # Additional exprs for * and ** args:
  199. # 0 if neither
  200. # 1 for CALL_FUNCTION_VAR or CALL_FUNCTION_KW
  201. # 2 for * and ** args (CALL_FUNCTION_VAR_KW).
  202. # Yes, this computation based on instruction name is a little bit hoaky.
  203. nak = (len(opname) - len("CALL_FUNCTION")) // 3
  204. uniq_param = args_kw + args_pos
  205. if frozenset(("GET_AWAITABLE", "YIELD_FROM")).issubset(self.seen_ops):
  206. rule = (
  207. "async_call ::= expr "
  208. + ("pos_arg " * args_pos)
  209. + ("kwarg " * args_kw)
  210. + "expr " * nak
  211. + token.kind
  212. + " GET_AWAITABLE LOAD_CONST YIELD_FROM"
  213. )
  214. self.add_unique_rule(rule, token.kind, uniq_param, customize)
  215. self.add_unique_rule(
  216. "expr ::= async_call", token.kind, uniq_param, customize
  217. )
  218. if opname.startswith("CALL_FUNCTION_VAR"):
  219. # Python 3.5 changes the stack position of *args. KW args come
  220. # after *args.
  221. # Note: Python 3.6+ replaces CALL_FUNCTION_VAR and
  222. # CALL_FUNCTION_VAR_KW with CALL_FUNCTION_EX
  223. token.kind = self.call_fn_name(token)
  224. if opname.endswith("KW"):
  225. kw = "expr "
  226. else:
  227. kw = ""
  228. rule = (
  229. "call ::= expr expr "
  230. + ("pos_arg " * args_pos)
  231. + ("kwarg " * args_kw)
  232. + kw
  233. + token.kind
  234. )
  235. # Note: semantic actions make use of the fact of whether "args_pos"
  236. # zero or not in creating a template rule.
  237. self.add_unique_rule(rule, token.kind, args_pos, customize)
  238. else:
  239. super(Python35Parser, self).custom_classfunc_rule(
  240. opname, token, customize, *args
  241. )
  242. class Python35ParserSingle(Python35Parser, PythonParserSingle):
  243. pass
  244. if __name__ == "__main__":
  245. # Check grammar
  246. p = Python35Parser()
  247. p.check_grammar()
  248. from xdis.version_info import IS_PYPY, PYTHON_VERSION_TRIPLE
  249. if PYTHON_VERSION_TRIPLE[:2] == (3, 5):
  250. lhs, rhs, tokens, right_recursive, dup_rhs = p.check_sets()
  251. from uncompyle6.scanner import get_scanner
  252. s = get_scanner(PYTHON_VERSION_TRIPLE, IS_PYPY)
  253. opcode_set = set(s.opc.opname).union(
  254. set(
  255. """JUMP_BACK CONTINUE RETURN_END_IF COME_FROM
  256. LOAD_GENEXPR LOAD_ASSERT LOAD_SETCOMP LOAD_DICTCOMP LOAD_CLASSNAME
  257. LAMBDA_MARKER RETURN_LAST
  258. """.split()
  259. )
  260. )
  261. remain_tokens = set(tokens) - opcode_set
  262. import re
  263. remain_tokens = set([re.sub(r"_\d+$", "", t) for t in remain_tokens])
  264. remain_tokens = set([re.sub("_CONT$", "", t) for t in remain_tokens])
  265. remain_tokens = set(remain_tokens) - opcode_set
  266. print(remain_tokens)
  267. # print(sorted(p.rule2name.items()))