parse36.py 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744
  1. # Copyright (c) 2016-2020, 2022-2024 Rocky Bernstein
  2. #
  3. # This program is free software: you can redistribute it and/or modify
  4. # it under the terms of the GNU General Public License as published by
  5. # the Free Software Foundation, either version 3 of the License, or
  6. # (at your option) any later version.
  7. #
  8. # This program is distributed in the hope that it will be useful,
  9. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. # GNU General Public License for more details.
  12. #
  13. # You should have received a copy of the GNU General Public License
  14. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  15. """
  16. spark grammar differences over Python 3.5 for Python 3.6.
  17. """
  18. from __future__ import print_function
  19. from spark_parser import DEFAULT_DEBUG as PARSER_DEFAULT_DEBUG
  20. from uncompyle6.parser import PythonParserSingle, nop_func
  21. from uncompyle6.parsers.parse35 import Python35Parser
  22. from uncompyle6.scanners.tok import Token
  23. class Python36Parser(Python35Parser):
  24. def __init__(self, debug_parser=PARSER_DEFAULT_DEBUG):
  25. super(Python36Parser, self).__init__(debug_parser)
  26. self.customized = {}
  27. def p_36_jump(self, args):
  28. """
  29. # Zero or one COME_FROM
  30. # And/or expressions have this
  31. come_from_opt ::= COME_FROM?
  32. """
  33. def p_36_misc(self, args):
  34. """sstmt ::= sstmt RETURN_LAST
  35. # long except clauses in a loop can sometimes cause a JUMP_BACK to turn into a
  36. # JUMP_FORWARD to a JUMP_BACK. And when this happens there is an additional
  37. # ELSE added to the except_suite. With better flow control perhaps we can
  38. # sort this out better.
  39. except_suite ::= c_stmts_opt POP_EXCEPT jump_except ELSE
  40. except_suite_finalize ::= SETUP_FINALLY c_stmts_opt except_var_finalize END_FINALLY
  41. _jump ELSE
  42. # 3.6 redoes how return_closure works. FIXME: Isolate to LOAD_CLOSURE
  43. return_closure ::= LOAD_CLOSURE DUP_TOP STORE_NAME RETURN_VALUE RETURN_LAST
  44. for_block ::= l_stmts_opt come_from_loops JUMP_BACK
  45. come_from_loops ::= COME_FROM_LOOP*
  46. whilestmt ::= SETUP_LOOP testexpr l_stmts_opt
  47. JUMP_BACK come_froms POP_BLOCK
  48. whilestmt ::= SETUP_LOOP testexpr l_stmts_opt
  49. JUMP_BACK come_froms POP_BLOCK COME_FROM_LOOP
  50. whilestmt ::= SETUP_LOOP testexpr l_stmts_opt
  51. come_froms JUMP_BACK come_froms POP_BLOCK COME_FROM_LOOP
  52. # 3.6 due to jump optimization, we sometimes add RETURN_END_IF where
  53. # RETURN_VALUE is meant. Specifically, this can happen in
  54. # ifelsestmt -> ...else_suite _. suite_stmts... (last) stmt
  55. return ::= return_expr RETURN_END_IF
  56. return ::= return_expr RETURN_VALUE COME_FROM
  57. return_stmt_lambda ::= return_expr RETURN_VALUE_LAMBDA COME_FROM
  58. # A COME_FROM is dropped off because of JUMP-to-JUMP optimization
  59. and ::= expr jmp_false expr
  60. and ::= expr jmp_false expr jmp_false
  61. jf_cf ::= JUMP_FORWARD COME_FROM
  62. cf_jf_else ::= come_froms JUMP_FORWARD ELSE
  63. if_exp ::= expr jmp_false expr jf_cf expr COME_FROM
  64. async_for_stmt36 ::= SETUP_LOOP expr
  65. GET_AITER
  66. LOAD_CONST YIELD_FROM
  67. SETUP_EXCEPT GET_ANEXT LOAD_CONST
  68. YIELD_FROM
  69. store
  70. POP_BLOCK JUMP_BACK COME_FROM_EXCEPT DUP_TOP
  71. LOAD_GLOBAL COMPARE_OP POP_JUMP_IF_TRUE
  72. END_FINALLY for_block
  73. COME_FROM
  74. POP_TOP POP_TOP POP_TOP POP_EXCEPT POP_TOP POP_BLOCK
  75. COME_FROM_LOOP
  76. async_for_stmt36 ::= SETUP_LOOP expr
  77. GET_AITER
  78. LOAD_CONST YIELD_FROM SETUP_EXCEPT GET_ANEXT LOAD_CONST
  79. YIELD_FROM
  80. store
  81. POP_BLOCK JUMP_FORWARD COME_FROM_EXCEPT DUP_TOP
  82. LOAD_GLOBAL COMPARE_OP POP_JUMP_IF_TRUE
  83. END_FINALLY
  84. COME_FROM
  85. for_block
  86. COME_FROM
  87. POP_TOP POP_TOP POP_TOP POP_EXCEPT POP_TOP POP_BLOCK
  88. COME_FROM_LOOP
  89. async_for_stmt ::= SETUP_LOOP expr
  90. GET_AITER
  91. LOAD_CONST YIELD_FROM SETUP_EXCEPT GET_ANEXT LOAD_CONST
  92. YIELD_FROM
  93. store
  94. POP_BLOCK JUMP_FORWARD COME_FROM_EXCEPT DUP_TOP
  95. LOAD_GLOBAL COMPARE_OP POP_JUMP_IF_FALSE
  96. POP_TOP POP_TOP POP_TOP POP_EXCEPT POP_BLOCK
  97. JUMP_ABSOLUTE END_FINALLY COME_FROM
  98. for_block POP_BLOCK
  99. COME_FROM_LOOP
  100. stmt ::= async_for_stmt36
  101. stmt ::= async_forelse_stmt36
  102. async_forelse_stmt ::= SETUP_LOOP expr
  103. GET_AITER
  104. LOAD_CONST YIELD_FROM SETUP_EXCEPT GET_ANEXT LOAD_CONST
  105. YIELD_FROM
  106. store
  107. POP_BLOCK JUMP_FORWARD COME_FROM_EXCEPT DUP_TOP
  108. LOAD_GLOBAL COMPARE_OP POP_JUMP_IF_FALSE
  109. POP_TOP POP_TOP POP_TOP POP_EXCEPT POP_BLOCK
  110. JUMP_ABSOLUTE END_FINALLY COME_FROM
  111. for_block POP_BLOCK
  112. else_suite COME_FROM_LOOP
  113. async_forelse_stmt36 ::= SETUP_LOOP expr
  114. GET_AITER
  115. LOAD_CONST YIELD_FROM SETUP_EXCEPT GET_ANEXT LOAD_CONST
  116. YIELD_FROM
  117. store
  118. POP_BLOCK JUMP_FORWARD COME_FROM_EXCEPT DUP_TOP
  119. LOAD_GLOBAL COMPARE_OP POP_JUMP_IF_TRUE
  120. END_FINALLY COME_FROM
  121. for_block _come_froms
  122. POP_TOP POP_TOP POP_TOP POP_EXCEPT POP_TOP
  123. POP_BLOCK
  124. else_suite COME_FROM_LOOP
  125. # Adds a COME_FROM_ASYNC_WITH over 3.5
  126. # FIXME: remove corresponding rule for 3.5?
  127. except_suite ::= c_stmts_opt COME_FROM POP_EXCEPT jump_except COME_FROM
  128. jb_cfs ::= JUMP_BACK come_froms
  129. # If statement inside a loop.
  130. stmt ::= ifstmtl
  131. ifstmtl ::= testexpr _ifstmts_jumpl
  132. _ifstmts_jumpl ::= c_stmts JUMP_BACK
  133. ifelsestmtl ::= testexpr c_stmts_opt jb_cfs else_suitel
  134. ifelsestmtl ::= testexpr c_stmts_opt cf_jf_else else_suitel
  135. ifelsestmt ::= testexpr c_stmts_opt cf_jf_else else_suite _come_froms
  136. ifelsestmt ::= testexpr c_stmts come_froms else_suite come_froms
  137. # In 3.6+, A sequence of statements ending in a RETURN can cause
  138. # JUMP_FORWARD END_FINALLY to be omitted from try middle
  139. except_return ::= POP_TOP POP_TOP POP_TOP returns
  140. except_handler ::= JUMP_FORWARD COME_FROM_EXCEPT except_return
  141. # Try middle following a returns
  142. except_handler36 ::= COME_FROM_EXCEPT except_stmts END_FINALLY
  143. stmt ::= try_except36
  144. try_except36 ::= SETUP_EXCEPT returns except_handler36
  145. opt_come_from_except
  146. try_except36 ::= SETUP_EXCEPT suite_stmts
  147. try_except36 ::= SETUP_EXCEPT suite_stmts_opt POP_BLOCK
  148. except_handler36 opt_come_from_except
  149. # 3.6 omits END_FINALLY sometimes
  150. except_handler36 ::= COME_FROM_EXCEPT except_stmts
  151. except_handler36 ::= JUMP_FORWARD COME_FROM_EXCEPT except_stmts
  152. except_handler ::= jmp_abs COME_FROM_EXCEPT except_stmts
  153. stmt ::= tryfinally36
  154. tryfinally36 ::= SETUP_FINALLY returns
  155. COME_FROM_FINALLY suite_stmts
  156. tryfinally36 ::= SETUP_FINALLY returns
  157. COME_FROM_FINALLY suite_stmts_opt END_FINALLY
  158. except_suite_finalize ::= SETUP_FINALLY returns
  159. COME_FROM_FINALLY suite_stmts_opt END_FINALLY _jump
  160. stmt ::= tryfinally_return_stmt
  161. tryfinally_return_stmt ::= SETUP_FINALLY suite_stmts_opt POP_BLOCK LOAD_CONST
  162. COME_FROM_FINALLY
  163. compare_chained_right ::= expr COMPARE_OP come_froms JUMP_FORWARD
  164. """
  165. # Some of this is duplicated from parse37. Eventually we'll probably rebase from
  166. # that and then we can remove this.
  167. def p_36_conditionals(self, args):
  168. """
  169. expr ::= if_exp37
  170. if_exp37 ::= expr expr jf_cfs expr COME_FROM
  171. jf_cfs ::= JUMP_FORWARD _come_froms
  172. ifelsestmt ::= testexpr c_stmts_opt jf_cfs else_suite opt_come_from_except
  173. """
  174. def customize_grammar_rules(self, tokens, customize):
  175. # self.remove_rules("""
  176. # """)
  177. super(Python36Parser, self).customize_grammar_rules(tokens, customize)
  178. self.remove_rules(
  179. """
  180. _ifstmts_jumpl ::= c_stmts_opt
  181. _ifstmts_jumpl ::= _ifstmts_jump
  182. except_handler ::= JUMP_FORWARD COME_FROM_EXCEPT except_stmts END_FINALLY COME_FROM
  183. async_for_stmt ::= SETUP_LOOP expr
  184. GET_AITER
  185. LOAD_CONST YIELD_FROM SETUP_EXCEPT GET_ANEXT LOAD_CONST
  186. YIELD_FROM
  187. store
  188. POP_BLOCK jump_except COME_FROM_EXCEPT DUP_TOP
  189. LOAD_GLOBAL COMPARE_OP POP_JUMP_IF_FALSE
  190. POP_TOP POP_TOP POP_TOP POP_EXCEPT POP_BLOCK
  191. JUMP_ABSOLUTE END_FINALLY COME_FROM
  192. for_block POP_BLOCK JUMP_ABSOLUTE
  193. COME_FROM_LOOP
  194. async_forelse_stmt ::= SETUP_LOOP expr
  195. GET_AITER
  196. LOAD_CONST YIELD_FROM SETUP_EXCEPT GET_ANEXT LOAD_CONST
  197. YIELD_FROM
  198. store
  199. POP_BLOCK JUMP_FORWARD COME_FROM_EXCEPT DUP_TOP
  200. LOAD_GLOBAL COMPARE_OP POP_JUMP_IF_FALSE
  201. POP_TOP POP_TOP POP_TOP POP_EXCEPT POP_BLOCK
  202. JUMP_ABSOLUTE END_FINALLY COME_FROM
  203. for_block pb_ja
  204. else_suite COME_FROM_LOOP
  205. """
  206. )
  207. self.check_reduce["call_kw"] = "AST"
  208. # Opcode names in the custom_ops_processed set have rules that get added
  209. # unconditionally and the rules are constant. So they need to be done
  210. # only once and if we see the opcode a second we don't have to consider
  211. # adding more rules.
  212. #
  213. # Note: BUILD_TUPLE_UNPACK_WITH_CALL gets considered by
  214. # default because it starts with BUILD. So we'll set to ignore it from
  215. # the start.
  216. custom_ops_processed = set()
  217. for i, token in enumerate(tokens):
  218. opname = token.kind
  219. if opname == "FORMAT_VALUE":
  220. rules_str = """
  221. expr ::= formatted_value1
  222. formatted_value1 ::= expr FORMAT_VALUE
  223. """
  224. self.add_unique_doc_rules(rules_str, customize)
  225. elif opname == "FORMAT_VALUE_ATTR":
  226. rules_str = """
  227. expr ::= formatted_value2
  228. formatted_value2 ::= expr expr FORMAT_VALUE_ATTR
  229. """
  230. self.add_unique_doc_rules(rules_str, customize)
  231. elif opname == "MAKE_FUNCTION_CLOSURE":
  232. if "LOAD_DICTCOMP" in self.seen_ops:
  233. # Is there something general going on here?
  234. rule = """
  235. dict_comp ::= load_closure LOAD_DICTCOMP LOAD_STR
  236. MAKE_FUNCTION_CLOSURE expr
  237. GET_ITER CALL_FUNCTION_1
  238. """
  239. self.addRule(rule, nop_func)
  240. elif "LOAD_SETCOMP" in self.seen_ops:
  241. rule = """
  242. set_comp ::= load_closure LOAD_SETCOMP LOAD_STR
  243. MAKE_FUNCTION_CLOSURE expr
  244. GET_ITER CALL_FUNCTION_1
  245. """
  246. self.addRule(rule, nop_func)
  247. elif opname == "BEFORE_ASYNC_WITH":
  248. rules_str = """
  249. stmt ::= async_with_stmt
  250. async_with_pre ::= BEFORE_ASYNC_WITH GET_AWAITABLE LOAD_CONST YIELD_FROM SETUP_ASYNC_WITH
  251. async_with_post ::= COME_FROM_ASYNC_WITH
  252. WITH_CLEANUP_START GET_AWAITABLE LOAD_CONST YIELD_FROM
  253. WITH_CLEANUP_FINISH END_FINALLY
  254. async_with_as_stmt ::= expr
  255. async_with_pre
  256. store
  257. suite_stmts_opt
  258. POP_BLOCK LOAD_CONST
  259. async_with_post
  260. stmt ::= async_with_as_stmt
  261. async_with_stmt ::= expr
  262. POP_TOP
  263. suite_stmts_opt
  264. POP_BLOCK LOAD_CONST
  265. async_with_post
  266. async_with_stmt ::= expr
  267. POP_TOP
  268. suite_stmts_opt
  269. async_with_post
  270. """
  271. self.addRule(rules_str, nop_func)
  272. elif opname.startswith("BUILD_STRING"):
  273. v = token.attr
  274. rules_str = """
  275. expr ::= joined_str
  276. joined_str ::= %sBUILD_STRING_%d
  277. """ % (
  278. "expr " * v,
  279. v,
  280. )
  281. self.add_unique_doc_rules(rules_str, customize)
  282. if "FORMAT_VALUE_ATTR" in self.seen_ops:
  283. rules_str = """
  284. formatted_value_attr ::= expr expr FORMAT_VALUE_ATTR expr BUILD_STRING
  285. expr ::= formatted_value_attr
  286. """
  287. self.add_unique_doc_rules(rules_str, customize)
  288. elif opname.startswith("BUILD_MAP_UNPACK_WITH_CALL"):
  289. v = token.attr
  290. rule = "build_map_unpack_with_call ::= %s%s" % ("expr " * v, opname)
  291. self.addRule(rule, nop_func)
  292. elif opname.startswith("BUILD_TUPLE_UNPACK_WITH_CALL"):
  293. v = token.attr
  294. rule = (
  295. "build_tuple_unpack_with_call ::= "
  296. + "expr1024 " * int(v // 1024)
  297. + "expr32 " * int((v // 32) % 32)
  298. + "expr " * (v % 32)
  299. + opname
  300. )
  301. self.addRule(rule, nop_func)
  302. rule = "starred ::= %s %s" % ("expr " * v, opname)
  303. self.addRule(rule, nop_func)
  304. elif opname == "GET_AITER":
  305. self.addRule(
  306. """
  307. expr ::= generator_exp_async
  308. generator_exp_async ::= load_genexpr LOAD_STR MAKE_FUNCTION_0 expr
  309. GET_AITER LOAD_CONST YIELD_FROM CALL_FUNCTION_1
  310. stmt ::= genexpr_func_async
  311. func_async_prefix ::= _come_froms
  312. LOAD_CONST YIELD_FROM
  313. SETUP_EXCEPT GET_ANEXT LOAD_CONST YIELD_FROM
  314. func_async_middle ::= POP_BLOCK JUMP_FORWARD COME_FROM_EXCEPT
  315. DUP_TOP LOAD_GLOBAL COMPARE_OP POP_JUMP_IF_TRUE
  316. END_FINALLY COME_FROM
  317. genexpr_func_async ::= LOAD_ARG func_async_prefix
  318. store func_async_middle comp_iter
  319. JUMP_BACK
  320. POP_TOP POP_TOP POP_TOP POP_EXCEPT POP_TOP
  321. expr ::= list_comp_async
  322. list_comp_async ::= LOAD_LISTCOMP LOAD_STR MAKE_FUNCTION_0
  323. expr GET_AITER
  324. LOAD_CONST YIELD_FROM CALL_FUNCTION_1
  325. GET_AWAITABLE LOAD_CONST
  326. YIELD_FROM
  327. expr ::= list_comp_async
  328. list_afor2 ::= func_async_prefix
  329. store func_async_middle list_iter
  330. JUMP_BACK
  331. POP_TOP POP_TOP POP_TOP POP_EXCEPT POP_TOP
  332. list_comp_async ::= BUILD_LIST_0 LOAD_ARG list_afor2
  333. get_aiter ::= expr GET_AITER
  334. list_afor ::= get_aiter list_afor2
  335. list_iter ::= list_afor
  336. """,
  337. nop_func,
  338. )
  339. elif opname == "GET_AITER":
  340. self.add_unique_doc_rules("get_aiter ::= expr GET_AITER", customize)
  341. if not {"MAKE_FUNCTION_0", "MAKE_FUNCTION_CLOSURE"} in self.seen_ops:
  342. self.addRule(
  343. """
  344. expr ::= dict_comp_async
  345. expr ::= generator_exp_async
  346. expr ::= list_comp_async
  347. dict_comp_async ::= LOAD_DICTCOMP
  348. LOAD_STR
  349. MAKE_FUNCTION_0
  350. get_aiter
  351. CALL_FUNCTION_1
  352. dict_comp_async ::= BUILD_MAP_0 LOAD_ARG
  353. dict_comp_async
  354. func_async_middle ::= POP_BLOCK JUMP_FORWARD COME_FROM_EXCEPT
  355. DUP_TOP LOAD_GLOBAL COMPARE_OP POP_JUMP_IF_TRUE
  356. END_FINALLY COME_FROM
  357. func_async_prefix ::= _come_froms SETUP_EXCEPT GET_ANEXT LOAD_CONST YIELD_FROM
  358. generator_exp_async ::= load_genexpr LOAD_STR MAKE_FUNCTION_0
  359. get_aiter CALL_FUNCTION_1
  360. genexpr_func_async ::= LOAD_ARG func_async_prefix
  361. store func_async_middle comp_iter
  362. JUMP_LOOP COME_FROM
  363. POP_TOP POP_TOP POP_TOP POP_EXCEPT POP_TOP
  364. # FIXME this is a workaround for probably some bug in the Earley parser
  365. # if we use get_aiter, then list_comp_async doesn't match, and I don't
  366. # understand why.
  367. expr_get_aiter ::= expr GET_AITER
  368. list_afor ::= get_aiter list_afor2
  369. list_afor2 ::= func_async_prefix
  370. store func_async_middle list_iter
  371. JUMP_LOOP COME_FROM
  372. POP_TOP POP_TOP POP_TOP POP_EXCEPT POP_TOP
  373. list_comp_async ::= BUILD_LIST_0 LOAD_ARG list_afor2
  374. list_comp_async ::= LOAD_LISTCOMP LOAD_STR MAKE_FUNCTION_0
  375. expr_get_aiter CALL_FUNCTION_1
  376. GET_AWAITABLE LOAD_CONST
  377. YIELD_FROM
  378. list_iter ::= list_afor
  379. set_comp_async ::= LOAD_SETCOMP
  380. LOAD_STR
  381. MAKE_FUNCTION_0
  382. get_aiter
  383. CALL_FUNCTION_1
  384. set_comp_async ::= LOAD_CLOSURE
  385. BUILD_TUPLE_1
  386. LOAD_SETCOMP
  387. LOAD_STR MAKE_FUNCTION_CLOSURE
  388. get_aiter CALL_FUNCTION_1
  389. await
  390. """,
  391. nop_func,
  392. )
  393. custom_ops_processed.add(opname)
  394. self.addRule(
  395. """
  396. dict_comp_async ::= BUILD_MAP_0 LOAD_ARG
  397. dict_comp_async
  398. expr ::= dict_comp_async
  399. expr ::= generator_exp_async
  400. expr ::= list_comp_async
  401. expr ::= set_comp_async
  402. func_async_middle ::= POP_BLOCK JUMP_FORWARD COME_FROM_EXCEPT
  403. DUP_TOP LOAD_GLOBAL COMPARE_OP POP_JUMP_IF_TRUE
  404. END_FINALLY _come_froms
  405. get_aiter ::= expr GET_AITER
  406. list_afor ::= get_aiter list_afor2
  407. list_comp_async ::= BUILD_LIST_0 LOAD_ARG list_afor2
  408. list_iter ::= list_afor
  409. set_afor ::= get_aiter set_afor2
  410. set_iter ::= set_afor
  411. set_iter ::= set_for
  412. set_comp_async ::= BUILD_SET_0 LOAD_ARG
  413. set_comp_async
  414. """,
  415. nop_func,
  416. )
  417. custom_ops_processed.add(opname)
  418. elif opname == "GET_ANEXT":
  419. self.addRule(
  420. """
  421. func_async_prefix ::= _come_froms SETUP_EXCEPT GET_ANEXT LOAD_CONST YIELD_FROM
  422. func_async_prefix ::= _come_froms SETUP_FINALLY GET_ANEXT LOAD_CONST YIELD_FROM POP_BLOCK
  423. func_async_prefix ::= _come_froms
  424. LOAD_CONST YIELD_FROM
  425. SETUP_EXCEPT GET_ANEXT LOAD_CONST YIELD_FROM
  426. func_async_middle ::= JUMP_FORWARD COME_FROM_EXCEPT
  427. DUP_TOP LOAD_GLOBAL COMPARE_OP POP_JUMP_IF_TRUE
  428. list_comp_async ::= BUILD_LIST_0 LOAD_ARG list_afor2
  429. list_afor2 ::= func_async_prefix
  430. store list_iter
  431. JUMP_BACK COME_FROM_FINALLY
  432. END_ASYNC_FOR
  433. list_afor2 ::= func_async_prefix
  434. store func_async_middle list_iter
  435. JUMP_LOOP COME_FROM
  436. POP_TOP POP_TOP POP_TOP POP_EXCEPT POP_TOP
  437. """,
  438. nop_func,
  439. )
  440. custom_ops_processed.add(opname)
  441. elif opname == "SETUP_ANNOTATIONS":
  442. # 3.6 Variable Annotations PEP 526
  443. # This seems to come before STORE_ANNOTATION, and doesn't
  444. # correspond to direct Python source code.
  445. rule = """
  446. stmt ::= SETUP_ANNOTATIONS
  447. stmt ::= ann_assign_init_value
  448. stmt ::= ann_assign_no_init
  449. ann_assign_init_value ::= expr store store_annotation
  450. ann_assign_no_init ::= store_annotation
  451. store_annotation ::= LOAD_NAME STORE_ANNOTATION
  452. store_annotation ::= subscript STORE_ANNOTATION
  453. """
  454. self.addRule(rule, nop_func)
  455. # Check to combine assignment + annotation into one statement
  456. self.check_reduce["assign"] = "token"
  457. elif opname == "WITH_CLEANUP_START":
  458. rules_str = """
  459. stmt ::= with_null
  460. with_null ::= with_suffix
  461. with_suffix ::= WITH_CLEANUP_START WITH_CLEANUP_FINISH END_FINALLY
  462. """
  463. self.addRule(rules_str, nop_func)
  464. elif opname == "SETUP_WITH":
  465. rules_str = """
  466. with ::= expr SETUP_WITH POP_TOP suite_stmts_opt COME_FROM_WITH
  467. with_suffix
  468. # Removes POP_BLOCK LOAD_CONST from 3.6-
  469. with_as ::= expr SETUP_WITH store suite_stmts_opt COME_FROM_WITH
  470. with_suffix
  471. with ::= expr SETUP_WITH POP_TOP suite_stmts_opt POP_BLOCK
  472. BEGIN_FINALLY COME_FROM_WITH
  473. with_suffix
  474. """
  475. self.addRule(rules_str, nop_func)
  476. pass
  477. pass
  478. return
  479. def custom_classfunc_rule(self, opname, token, customize, next_token, is_pypy):
  480. args_pos, args_kw = self.get_pos_kw(token)
  481. # Additional exprs for * and ** args:
  482. # 0 if neither
  483. # 1 for CALL_FUNCTION_VAR or CALL_FUNCTION_KW
  484. # 2 for * and ** args (CALL_FUNCTION_VAR_KW).
  485. # Yes, this computation based on instruction name is a little bit hoaky.
  486. nak = (len(opname) - len("CALL_FUNCTION")) // 3
  487. uniq_param = args_kw + args_pos
  488. if frozenset(("GET_AWAITABLE", "YIELD_FROM")).issubset(self.seen_ops):
  489. rule = (
  490. "async_call ::= expr "
  491. + ("pos_arg " * args_pos)
  492. + ("kwarg " * args_kw)
  493. + "expr " * nak
  494. + token.kind
  495. + " GET_AWAITABLE LOAD_CONST YIELD_FROM"
  496. )
  497. self.add_unique_rule(rule, token.kind, uniq_param, customize)
  498. self.add_unique_rule(
  499. "expr ::= async_call", token.kind, uniq_param, customize
  500. )
  501. if opname.startswith("CALL_FUNCTION_KW"):
  502. if is_pypy:
  503. # PYPY doesn't follow CPython 3.6 CALL_FUNCTION_KW conventions
  504. super(Python36Parser, self).custom_classfunc_rule(
  505. opname, token, customize, next_token, is_pypy
  506. )
  507. else:
  508. self.addRule("expr ::= call_kw36", nop_func)
  509. values = "expr " * token.attr
  510. rule = "call_kw36 ::= expr {values} LOAD_CONST {opname}".format(
  511. **locals()
  512. )
  513. self.add_unique_rule(rule, token.kind, token.attr, customize)
  514. elif opname == "CALL_FUNCTION_EX_KW":
  515. # Note: this doesn't exist in 3.7 and later
  516. self.addRule(
  517. """expr ::= call_ex_kw4
  518. call_ex_kw4 ::= expr
  519. expr
  520. expr
  521. CALL_FUNCTION_EX_KW
  522. """,
  523. nop_func,
  524. )
  525. if "BUILD_MAP_UNPACK_WITH_CALL" in self.seen_op_basenames:
  526. self.addRule(
  527. """expr ::= call_ex_kw
  528. call_ex_kw ::= expr expr build_map_unpack_with_call
  529. CALL_FUNCTION_EX_KW
  530. """,
  531. nop_func,
  532. )
  533. if "BUILD_TUPLE_UNPACK_WITH_CALL" in self.seen_op_basenames:
  534. # FIXME: should this be parameterized by EX value?
  535. self.addRule(
  536. """expr ::= call_ex_kw3
  537. call_ex_kw3 ::= expr
  538. build_tuple_unpack_with_call
  539. expr
  540. CALL_FUNCTION_EX_KW
  541. """,
  542. nop_func,
  543. )
  544. if "BUILD_MAP_UNPACK_WITH_CALL" in self.seen_op_basenames:
  545. # FIXME: should this be parameterized by EX value?
  546. self.addRule(
  547. """expr ::= call_ex_kw2
  548. call_ex_kw2 ::= expr
  549. build_tuple_unpack_with_call
  550. build_map_unpack_with_call
  551. CALL_FUNCTION_EX_KW
  552. """,
  553. nop_func,
  554. )
  555. elif opname == "CALL_FUNCTION_EX":
  556. self.addRule(
  557. """
  558. expr ::= call_ex
  559. starred ::= expr
  560. call_ex ::= expr starred CALL_FUNCTION_EX
  561. """,
  562. nop_func,
  563. )
  564. if self.version >= (3, 6):
  565. if "BUILD_MAP_UNPACK_WITH_CALL" in self.seen_ops:
  566. self.addRule(
  567. """
  568. expr ::= call_ex_kw
  569. call_ex_kw ::= expr expr
  570. build_map_unpack_with_call CALL_FUNCTION_EX
  571. """,
  572. nop_func,
  573. )
  574. if "BUILD_TUPLE_UNPACK_WITH_CALL" in self.seen_ops:
  575. self.addRule(
  576. """
  577. expr ::= call_ex_kw3
  578. call_ex_kw3 ::= expr
  579. build_tuple_unpack_with_call
  580. %s
  581. CALL_FUNCTION_EX
  582. """
  583. % "expr "
  584. * token.attr,
  585. nop_func,
  586. )
  587. pass
  588. # FIXME: Is this right?
  589. self.addRule(
  590. """
  591. expr ::= call_ex_kw4
  592. call_ex_kw4 ::= expr
  593. expr
  594. expr
  595. CALL_FUNCTION_EX
  596. """,
  597. nop_func,
  598. )
  599. pass
  600. else:
  601. super(Python36Parser, self).custom_classfunc_rule(
  602. opname, token, customize, next_token, is_pypy
  603. )
  604. def reduce_is_invalid(self, rule, ast, tokens, first, last):
  605. invalid = super(Python36Parser, self).reduce_is_invalid(
  606. rule, ast, tokens, first, last
  607. )
  608. if invalid:
  609. return invalid
  610. if rule[0] == "assign":
  611. # Try to combine assignment + annotation into one statement
  612. if (
  613. len(tokens) >= last + 1
  614. and tokens[last] == "LOAD_NAME"
  615. and tokens[last + 1] == "STORE_ANNOTATION"
  616. and tokens[last - 1].pattr == tokens[last + 1].pattr
  617. ):
  618. # Will handle as ann_assign_init_value
  619. return True
  620. pass
  621. if rule[0] == "call_kw":
  622. # Make sure we don't derive call_kw
  623. nt = ast[0]
  624. while not isinstance(nt, Token):
  625. if nt[0] == "call_kw":
  626. return True
  627. nt = nt[0]
  628. pass
  629. pass
  630. return False
  631. class Python36ParserSingle(Python36Parser, PythonParserSingle):
  632. pass
  633. if __name__ == "__main__":
  634. # Check grammar
  635. p = Python36Parser()
  636. p.check_grammar()
  637. from xdis.version_info import IS_PYPY, PYTHON_VERSION_TRIPLE
  638. if PYTHON_VERSION_TRIPLE[:2] == (3, 6):
  639. lhs, rhs, tokens, right_recursive, dup_rhs = p.check_sets()
  640. from uncompyle6.scanner import get_scanner
  641. s = get_scanner(PYTHON_VERSION_TRIPLE, IS_PYPY)
  642. opcode_set = set(s.opc.opname).union(
  643. set(
  644. """JUMP_BACK CONTINUE RETURN_END_IF COME_FROM
  645. LOAD_GENEXPR LOAD_ASSERT LOAD_SETCOMP LOAD_DICTCOMP LOAD_CLASSNAME
  646. LAMBDA_MARKER RETURN_LAST
  647. """.split()
  648. )
  649. )
  650. remain_tokens = set(tokens) - opcode_set
  651. import re
  652. remain_tokens = set([re.sub(r"_\d+$", "", t) for t in remain_tokens])
  653. remain_tokens = set([re.sub("_CONT$", "", t) for t in remain_tokens])
  654. remain_tokens = set(remain_tokens) - opcode_set
  655. print(remain_tokens)
  656. # print(sorted(p.rule2name.items()))