customize37.py 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478
  1. # Copyright (c) 2019-2024 by 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. """Isolate Python 3.7 version-specific semantic actions here.
  16. """
  17. import re
  18. from uncompyle6.semantics.consts import INDENT_PER_LEVEL, PRECEDENCE
  19. from uncompyle6.semantics.helper import flatten_list
  20. # FIXME get from a newer xdis
  21. FSTRING_CONVERSION_MAP = {1: "!s", 2: "!r", 3: "!a", "X": ":X"}
  22. #######################
  23. def customize_for_version37(self, version: tuple):
  24. ########################
  25. # Python 3.7+ changes
  26. #######################
  27. # fmt: off
  28. PRECEDENCE["attribute37"] = 2
  29. PRECEDENCE["call_ex"] = 1
  30. PRECEDENCE["call_ex_kw"] = 1
  31. PRECEDENCE["call_ex_kw2"] = 1
  32. PRECEDENCE["call_ex_kw3"] = 1
  33. PRECEDENCE["call_ex_kw4"] = 1
  34. PRECEDENCE["call_kw"] = 0
  35. PRECEDENCE["call_kw36"] = 1
  36. PRECEDENCE["formatted_value1"] = 38 # f"...". This has to be below "named_expr" to make
  37. # f'{(x := 10)}' preserve parenthesis
  38. PRECEDENCE["formatted_value2"] = 38 # See above
  39. PRECEDENCE["if_exp_37a"] = 28
  40. PRECEDENCE["if_exp_37b"] = 28
  41. PRECEDENCE["dict_unpack"] = 0 # **{...}
  42. # fmt: on
  43. self.TABLE_DIRECT.update(
  44. {
  45. "and_not": ("%c and not %c", (0, "expr"), (2, "expr")),
  46. "ann_assign": (
  47. "%|%[2]{attr}: %c\n",
  48. 0,
  49. ),
  50. "ann_assign_init": (
  51. "%|%[2]{attr}: %c = %c\n",
  52. 0,
  53. 1,
  54. ),
  55. "async_for_stmt": (
  56. "%|async for %c in %c:\n%+%c%-\n\n",
  57. (7, "store"),
  58. (1, "expr"),
  59. (17, "for_block"),
  60. ),
  61. "async_for_stmt37": (
  62. "%|async for %c in %c:\n%+%c%-\n\n",
  63. (8, "store"),
  64. (1, "expr"),
  65. (17, ("for_block", "pass")),
  66. ),
  67. "async_with_stmt": ("%|async with %c:\n%+%c%-", (0, "expr"), 3),
  68. "async_with_as_stmt": (
  69. "%|async with %c as %c:\n%+%c%-",
  70. (0, "expr"),
  71. (2, "store"),
  72. 3,
  73. ),
  74. "async_forelse_stmt": (
  75. "%|async for %c in %c:\n%+%c%-%|else:\n%+%c%-\n\n",
  76. (8, "store"),
  77. (1, "expr"),
  78. (-10, "for_block"),
  79. (-2, "else_suite"),
  80. ),
  81. "attribute37": ("%c.%[1]{pattr}", (0, "expr")),
  82. "attributes37": (
  83. "%[0]{pattr} import %c",
  84. (0, "IMPORT_NAME_ATTR"),
  85. (1, "IMPORT_FROM"),
  86. ),
  87. # nested await expressions like:
  88. # return await (await bar())
  89. # need parenthesis.
  90. # Note there are async dictionary expressions are like await expr's
  91. # the below is just the default fersion
  92. "await_expr": ("await %p", (0, PRECEDENCE["await_expr"] - 1)),
  93. "await_stmt": ("%|%c\n", 0),
  94. "c_async_with_stmt": ("%|async with %c:\n%+%c%-", (0, "expr"), 3),
  95. "call_ex": ("%c(%p)", (0, "expr"), (1, 100)),
  96. "compared_chained_middlea_37": (
  97. ' %[3]{pattr.replace("-", " ")} %p %p',
  98. (0, PRECEDENCE["compare"] - 1),
  99. (-4, PRECEDENCE["compare"] - 1),
  100. ),
  101. "compared_chained_middle_false_37": (
  102. ' %[3]{pattr.replace("-", " ")} %p %p',
  103. (0, PRECEDENCE["compare"] - 1),
  104. (-4, PRECEDENCE["compare"] - 1),
  105. ),
  106. "compare_chained_right_false_37": (
  107. ' %[3]{pattr.replace("-", " ")} %p %p',
  108. (0, PRECEDENCE["compare"] - 1),
  109. (-5, PRECEDENCE["compare"] - 1),
  110. ),
  111. "compared_chained_middleb_false_37": (
  112. ' %[3]{pattr.replace("-", " ")} %p %p',
  113. (0, PRECEDENCE["compare"] - 1),
  114. (-4, PRECEDENCE["compare"] - 1),
  115. ),
  116. "compared_chained_middlec_37": (
  117. ' %[3]{pattr.replace("-", " ")} %p %p',
  118. (0, PRECEDENCE["compare"] - 1),
  119. (-2, PRECEDENCE["compare"] - 1),
  120. ),
  121. "compare_chained_righta_37": (
  122. '%[1]{pattr.replace("-", " ")} %p',
  123. (0, PRECEDENCE["compare"] - 1),
  124. ),
  125. "compare_chained_rightb_false_37": (
  126. '%[1]{pattr.replace("-", " ")} %p',
  127. (0, PRECEDENCE["compare"] - 1),
  128. ),
  129. "compare_chained_righta_false_37": (
  130. '%[1]{pattr.replace("-", " ")} %p',
  131. (0, PRECEDENCE["compare"] - 1),
  132. ),
  133. "compare_chained_rightc_37": (
  134. '%[3]{pattr.replace("-", " ")} %p %p',
  135. (0, PRECEDENCE["compare"] - 1),
  136. (6, PRECEDENCE["compare"] - 1),
  137. ),
  138. "if_exp37": ("%p if %c else %c", (1, "expr", 27), 0, 3),
  139. "except_return": ("%|except:\n%+%c%-", 3),
  140. "if_exp_37a": (
  141. "%p if %p else %p",
  142. (1, "expr", 27),
  143. (0, 27),
  144. (4, "expr", 27),
  145. ),
  146. "if_exp_37b": (
  147. "%p if %p else %p",
  148. (2, "expr", 27),
  149. (0, "expr", 27),
  150. (5, "expr", 27),
  151. ),
  152. "ifstmtl": ("%|if %c:\n%+%c%-", (0, "testexpr"), (1, "_ifstmts_jumpl")),
  153. "import_as37": ("%|import %c as %c\n", 2, -2),
  154. "import_from37": ("%|from %[2]{pattr} import %c\n", (3, "importlist37")),
  155. "import_from_as37": (
  156. "%|from %c as %c\n",
  157. (2, "import_from_attr37"),
  158. (3, "store"),
  159. ),
  160. "import_one": (
  161. "%c",
  162. (0, "importlists"),
  163. ),
  164. "importattr37": ("%c", (0, "IMPORT_NAME_ATTR")),
  165. "import_from_attr37": (
  166. "%c import %c",
  167. (0, "IMPORT_NAME_ATTR"),
  168. (1, "IMPORT_FROM"),
  169. ),
  170. "list_afor": (
  171. " async for %[1]{%c} in %c%[1]{%c}",
  172. (1, "store"),
  173. (0, "get_aiter"),
  174. (3, "list_iter"),
  175. ),
  176. "list_if37": (" if %p%c", (0, 27), 1),
  177. "list_if37_not": (" if not %p%c", (0, 27), 1),
  178. "testfalse_not_or": ("not %c or %c", (0, "expr"), (2, "expr")),
  179. "testfalse_not_and": ("not (%c)", 0),
  180. "testfalsel": ("not %c", (0, "expr")),
  181. "try_except36": ("%|try:\n%+%c%-%c\n\n", 1, -2),
  182. "tryfinally36": ("%|try:\n%+%c%-%|finally:\n%+%c%-\n\n", (1, "returns"), 3),
  183. "dict_unpack": ("{**%C}", (0, -1, ", **")),
  184. "unpack_list": ("*%c", (0, "list")),
  185. "yield_from": ("yield from %c", (0, "expr")),
  186. }
  187. )
  188. # fmt: on
  189. def gen_function_parens_adjust(mapping_key, node):
  190. """If we can avoid the outer parenthesis
  191. of a generator function, set the node key to
  192. 'call_generator' and the caller will do the default
  193. action on that. Otherwise we do nothing.
  194. """
  195. if mapping_key.kind != "CALL_FUNCTION_1":
  196. return
  197. args_node = node[-2]
  198. if args_node == "pos_arg":
  199. assert args_node[0] == "expr"
  200. n = args_node[0][0]
  201. if n == "generator_exp":
  202. node.kind = "call_generator"
  203. pass
  204. return
  205. def n_assert_invert(node):
  206. testtrue = node[0]
  207. assert testtrue == "testtrue"
  208. testtrue.kind = "assert"
  209. self.default(testtrue)
  210. self.n_assert_invert = n_assert_invert
  211. def n_async_call(node):
  212. self.f.write("async ")
  213. node.kind = "call"
  214. p = self.prec
  215. self.prec = 80
  216. self.template_engine(("%c(%P)", 0, (1, -4, ", ", 100)), node)
  217. self.prec = p
  218. node.kind = "async_call"
  219. self.prune()
  220. self.n_async_call = n_async_call
  221. def n_attribute37(node):
  222. expr = node[0]
  223. assert expr == "expr"
  224. if expr[0] == "LOAD_CONST":
  225. # FIXME: I didn't record which constants parenthesis is
  226. # necessary. However, I suspect that we could further
  227. # refine this by looking at operator precedence and
  228. # eval'ing the constant value (pattr) and comparing with
  229. # the type of the constant.
  230. node.kind = "attribute_w_parens"
  231. self.default(node)
  232. self.n_attribute37 = n_attribute37
  233. def n_build_list_unpack(node):
  234. """
  235. prettyprint a list or tuple
  236. """
  237. p = self.prec
  238. self.prec = 100
  239. lastnode = node.pop()
  240. lastnodetype = lastnode.kind
  241. # If this build list is inside a CALL_FUNCTION_VAR,
  242. # then the first * has already been printed.
  243. # Until I have a better way to check for CALL_FUNCTION_VAR,
  244. # will assume that if the text ends in *.
  245. last_was_star = self.f.getvalue().endswith("*")
  246. if lastnodetype.startswith("BUILD_LIST"):
  247. self.write("[")
  248. endchar = "]"
  249. else:
  250. endchar = ""
  251. flat_elems = flatten_list(node)
  252. self.indent_more(INDENT_PER_LEVEL)
  253. sep = ""
  254. for elem in flat_elems:
  255. if elem in ("ROT_THREE", "EXTENDED_ARG"):
  256. continue
  257. assert elem == "expr"
  258. line_number = self.line_number
  259. use_star = True
  260. value = self.traverse(elem)
  261. if value.startswith("("):
  262. assert value.endswith(")")
  263. use_star = False
  264. value = value[1:-1].rstrip(
  265. " "
  266. ) # Remove starting '(' and trailing ')' and additional spaces
  267. if value == "":
  268. pass
  269. else:
  270. if value.endswith(","): # if args has only one item
  271. value = value[:-1]
  272. if line_number != self.line_number:
  273. sep += "\n" + self.indent + INDENT_PER_LEVEL[:-1]
  274. else:
  275. if sep != "":
  276. sep += " "
  277. if not last_was_star and use_star:
  278. sep += "*"
  279. pass
  280. else:
  281. last_was_star = False
  282. self.write(sep, value)
  283. sep = ","
  284. self.write(endchar)
  285. self.indent_less(INDENT_PER_LEVEL)
  286. self.prec = p
  287. self.prune()
  288. return
  289. self.n_build_list_unpack = n_build_list_unpack
  290. def n_c_with(node):
  291. if len(node) == 1 and node[0] == "with":
  292. node = node[0]
  293. else:
  294. node.kind = "with"
  295. self.default(node)
  296. self.n_c_with = n_c_with
  297. def n_c_except_suite(node):
  298. node_len = len(node)
  299. if node_len == 1 and node[0] in ("except_suite", "c_returns"):
  300. node = node[0]
  301. self.default(node)
  302. elif node[1] in ("c_suite_stmts", "c_except_suite"):
  303. node = node[1][0]
  304. template = ("%+%c%-", 0)
  305. self.template_engine(template, node)
  306. self.prune()
  307. self.n_c_except_suite = n_c_except_suite
  308. self.n_c_with = n_c_with
  309. def n_call(node):
  310. p = self.prec
  311. self.prec = 100
  312. mapping = self._get_mapping(node)
  313. table = mapping[0]
  314. key = node
  315. for i in mapping[1:]:
  316. key = key[i]
  317. pass
  318. opname = key.kind
  319. if opname.startswith("CALL_FUNCTION_VAR_KW"):
  320. # Python 3.5 changes the stack position of
  321. # *args: kwargs come after *args whereas
  322. # in earlier Pythons, *args is at the end
  323. # which simplifies things from our
  324. # perspective. Python 3.6+ replaces
  325. # CALL_FUNCTION_VAR_KW with
  326. # CALL_FUNCTION_EX We will just swap the
  327. # order to make it look like earlier
  328. # Python 3.
  329. entry = table[key.kind]
  330. kwarg_pos = entry[2][1]
  331. args_pos = kwarg_pos - 1
  332. # Put last node[args_pos] after subsequent kwargs
  333. while node[kwarg_pos] == "kwarg" and kwarg_pos < len(node):
  334. # swap node[args_pos] with node[kwargs_pos]
  335. node[kwarg_pos], node[args_pos] = node[args_pos], node[kwarg_pos]
  336. args_pos = kwarg_pos
  337. kwarg_pos += 1
  338. elif opname.startswith("CALL_FUNCTION_VAR"):
  339. # CALL_FUNCTION_VAR's top element of the stack contains
  340. # the variable argument list, then comes
  341. # annotation args, then keyword args.
  342. # In the most least-top-most stack entry, but position 1
  343. # in node order, the positional args.
  344. argc = node[-1].attr
  345. nargs = argc & 0xFF
  346. kwargs = (argc >> 8) & 0xFF
  347. # FIXME: handle annotation args
  348. if nargs > 0:
  349. template = ("%c(%P, ", 0, (1, nargs + 1, ", ", 100))
  350. else:
  351. template = ("%c(", 0)
  352. self.template_engine(template, node)
  353. args_node = node[-2]
  354. if args_node in ("pos_arg", "expr"):
  355. args_node = args_node[0]
  356. if args_node == "build_list_unpack":
  357. template = ("*%P)", (0, len(args_node) - 1, ", *", 100))
  358. self.template_engine(template, args_node)
  359. else:
  360. if len(node) - nargs > 3:
  361. template = (
  362. "*%c, %P)",
  363. nargs + 1,
  364. (nargs + kwargs + 1, -1, ", ", 100),
  365. )
  366. else:
  367. template = ("*%c)", nargs + 1)
  368. self.template_engine(template, node)
  369. self.prec = p
  370. self.prune()
  371. elif (
  372. opname.startswith("CALL_FUNCTION_1")
  373. and opname == "CALL_FUNCTION_1"
  374. or not re.match(r"\d", opname[-1])
  375. ):
  376. template = "(%c)(%p)" if node[0][0] == "lambda_body" else "%c(%p)"
  377. self.template_engine(
  378. (template, (0, "expr"), (1, PRECEDENCE["yield"] - 1)), node
  379. )
  380. self.prec = p
  381. self.prune()
  382. else:
  383. gen_function_parens_adjust(key, node)
  384. self.prec = p
  385. self.default(node)
  386. self.n_call = n_call
  387. def n_compare_chained(node):
  388. if node[0] in (
  389. "c_compare_chained37",
  390. "c_compare_chained37_false",
  391. "compare_chained37",
  392. "compare_chained37_false",
  393. ):
  394. self.default(node[0])
  395. else:
  396. self.default(node)
  397. self.n_compare_chained = self.n_c_compare_chained = n_compare_chained
  398. def n_importlist37(node):
  399. if len(node) == 1:
  400. self.default(node)
  401. return
  402. n = len(node) - 1
  403. for i in range(n, -1, -1):
  404. if node[i] != "ROT_TWO":
  405. break
  406. self.template_engine(("%C", (0, i + 1, ", ")), node)
  407. self.prune()
  408. return
  409. self.n_importlist37 = n_importlist37
  410. def n_list_comp_async(node):
  411. self.write("[")
  412. if node[0].kind == "load_closure":
  413. self.listcomp_closure3(node)
  414. else:
  415. self.comprehension_walk_newer(node, iter_index=3, code_index=0)
  416. self.write("]")
  417. self.prune()
  418. self.n_list_comp_async = n_list_comp_async
  419. # FIXME: The following adjusts I guess a bug in the parser.
  420. # It might be as simple as renaming grammar symbol "testtrue" to "testtrue_or_false"
  421. # and then keeping this as is with the name change.
  422. # Fixing in the parsing by inspection is harder than doing it here.
  423. def n_testtrue(node):
  424. compare_chained37 = node[0]
  425. if (
  426. compare_chained37 == "compare_chained37"
  427. and compare_chained37[1] == "compared_chained_middleb_37"
  428. ):
  429. compared_chained_middleb_37 = compare_chained37[1]
  430. if (
  431. len(compared_chained_middleb_37) > 2
  432. and compared_chained_middleb_37[-2] == "JUMP_FORWARD"
  433. ):
  434. node.kind = "testfalse"
  435. pass
  436. pass
  437. self.default(node)
  438. return
  439. self.n_testtrue = n_testtrue