customize35.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347
  1. # Copyright (c) 2019-2020, 2022, 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.5 version-specific semantic actions here.
  16. """
  17. from xdis import co_flags_is_async, iscode
  18. from uncompyle6.semantics.consts import INDENT_PER_LEVEL, PRECEDENCE, TABLE_DIRECT
  19. from uncompyle6.semantics.helper import flatten_list, gen_function_parens_adjust
  20. #######################
  21. # Python 3.5+ Changes #
  22. #######################
  23. def customize_for_version35(self, version: tuple):
  24. # fmt: off
  25. self.TABLE_DIRECT.update(
  26. {
  27. # nested await expressions like:
  28. # return await (await bar())
  29. # need parenthesis.
  30. "await_expr": ("await %p", (0, PRECEDENCE["await_expr"] - 1)),
  31. "await_stmt": ("%|%c\n", 0),
  32. "async_for_stmt": (
  33. "%|async for %c in %c:\n%+%|%c%-\n\n",
  34. (9, "store"),
  35. (1, "expr"),
  36. (25, ("for_block", "pass")),
  37. ),
  38. "async_forelse_stmt": (
  39. "%|async for %c in %c:\n%+%c%-%|else:\n%+%c%-\n\n",
  40. (9, "store"),
  41. (1, "expr"),
  42. (25, "for_block"),
  43. (-2, "else_suite"),
  44. ),
  45. "async_with_stmt": (
  46. "%|async with %c:\n%+%c%-",
  47. (0, "expr"),
  48. 3
  49. ),
  50. "async_with_as_stmt": (
  51. "%|async with %c as %c:\n%+%c%-",
  52. (0, "expr"),
  53. (2, "store"),
  54. 3,
  55. ),
  56. "dict_unpack": ("{**%C}", (0, -1, ", **")),
  57. # "unmapexpr": ( "{**%c}", 0), # done by n_unmapexpr
  58. }
  59. )
  60. # fmt: on
  61. def async_call(node):
  62. self.f.write("async ")
  63. node.kind == "call"
  64. p = self.prec
  65. self.prec = 80
  66. self.template_engine(("%c(%P)", 0, (1, -4, ", ", 100)), node)
  67. self.prec = p
  68. node.kind == "async_call"
  69. self.prune()
  70. self.n_async_call = async_call
  71. def n_build_list_unpack(node):
  72. """
  73. prettyprint a list or tuple
  74. """
  75. p = self.prec
  76. self.prec = 100
  77. lastnode = node.pop()
  78. lastnodetype = lastnode.kind
  79. # If this build list is inside a CALL_FUNCTION_VAR,
  80. # then the first * has already been printed.
  81. # Until I have a better way to check for CALL_FUNCTION_VAR,
  82. # will assume that if the text ends in *.
  83. last_was_star = self.f.getvalue().endswith("*")
  84. if lastnodetype.startswith("BUILD_LIST"):
  85. self.write("[")
  86. endchar = "]"
  87. else:
  88. endchar = ""
  89. flat_elems = flatten_list(node)
  90. self.indent_more(INDENT_PER_LEVEL)
  91. sep = ""
  92. for elem in flat_elems:
  93. if elem in ("ROT_THREE", "EXTENDED_ARG"):
  94. continue
  95. assert elem == "expr"
  96. line_number = self.line_number
  97. use_star = True
  98. value = self.traverse(elem)
  99. if value.startswith("("):
  100. assert value.endswith(")")
  101. use_star = False
  102. value = value[1:-1].rstrip(
  103. " "
  104. ) # Remove starting "(" and trailing ")" and additional spaces
  105. if value == "":
  106. pass
  107. else:
  108. if value.endswith(","): # if args has only one item
  109. value = value[:-1]
  110. if line_number != self.line_number:
  111. sep += "\n" + self.indent + INDENT_PER_LEVEL[:-1]
  112. else:
  113. if sep != "":
  114. sep += " "
  115. if not last_was_star and use_star:
  116. sep += "*"
  117. pass
  118. else:
  119. last_was_star = False
  120. self.write(sep, value)
  121. sep = ","
  122. self.write(endchar)
  123. self.indent_less(INDENT_PER_LEVEL)
  124. self.prec = p
  125. self.prune()
  126. return
  127. self.n_build_list_unpack = n_build_list_unpack
  128. def n_call(node):
  129. p = self.prec
  130. self.prec = 100
  131. mapping = self._get_mapping(node)
  132. table = mapping[0]
  133. key = node
  134. for i in mapping[1:]:
  135. key = key[i]
  136. pass
  137. if key.kind.startswith("CALL_FUNCTION_VAR_KW"):
  138. # Python 3.5 changes the stack position of
  139. # *args: kwargs come after *args whereas
  140. # in earlier Pythons, *args is at the end
  141. # which simplifies things from our
  142. # perspective. Python 3.6+ replaces
  143. # CALL_FUNCTION_VAR_KW with
  144. # CALL_FUNCTION_EX We will just swap the
  145. # order to make it look like earlier
  146. # Python 3.
  147. entry = table[key.kind]
  148. kwarg_pos = entry[2][1]
  149. args_pos = kwarg_pos - 1
  150. # Put last node[args_pos] after subsequent kwargs
  151. while node[kwarg_pos] == "kwarg" and kwarg_pos < len(node):
  152. # swap node[args_pos] with node[kwargs_pos]
  153. node[kwarg_pos], node[args_pos] = node[args_pos], node[kwarg_pos]
  154. args_pos = kwarg_pos
  155. kwarg_pos += 1
  156. elif key.kind.startswith("CALL_FUNCTION_VAR"):
  157. # CALL_FUNCTION_VAR's top element of the stack contains
  158. # the variable argument list, then comes
  159. # annotation args, then keyword args.
  160. # In the most least-top-most stack entry, but position 1
  161. # in node order, the positional args.
  162. argc = node[-1].attr
  163. nargs = argc & 0xFF
  164. kwargs = (argc >> 8) & 0xFF
  165. # FIXME: handle annotation args
  166. if nargs > 0:
  167. template = ("%c(%P, ", 0, (1, nargs + 1, ", ", 100))
  168. else:
  169. template = ("%c(", 0)
  170. self.template_engine(template, node)
  171. args_node = node[-2]
  172. if args_node in ("pos_arg", "expr"):
  173. args_node = args_node[0]
  174. if args_node == "build_list_unpack":
  175. template = ("*%P)", (0, len(args_node) - 1, ", *", 100))
  176. self.template_engine(template, args_node)
  177. else:
  178. if len(node) - nargs > 3:
  179. template = (
  180. "*%c, %P)",
  181. nargs + 1,
  182. (nargs + kwargs + 1, -1, ", ", 100),
  183. )
  184. else:
  185. template = ("*%c)", nargs + 1)
  186. self.template_engine(template, node)
  187. self.prec = p
  188. self.prune()
  189. else:
  190. gen_function_parens_adjust(key, node)
  191. self.prec = 100
  192. self.default(node)
  193. self.n_call = n_call
  194. def is_async_fn(node):
  195. code_node = node[0][0]
  196. for n in node[0]:
  197. if hasattr(n, "attr") and iscode(n.attr):
  198. code_node = n
  199. break
  200. pass
  201. pass
  202. is_code = hasattr(code_node, "attr") and iscode(code_node.attr)
  203. return is_code and co_flags_is_async(code_node.attr.co_flags)
  204. def n_function_def(node):
  205. if is_async_fn(node):
  206. self.template_engine(("\n\n%|async def %c\n", -2), node)
  207. else:
  208. self.default(node)
  209. self.prune()
  210. self.n_function_def = n_function_def
  211. def n_mkfuncdeco0(node):
  212. if is_async_fn(node):
  213. self.template_engine(("%|async def %c\n", 0), node)
  214. else:
  215. self.default(node)
  216. self.prune()
  217. self.n_mkfuncdeco0 = n_mkfuncdeco0
  218. def unmapexpr(node):
  219. last_n = node[0][-1]
  220. for n in node[0]:
  221. self.preorder(n)
  222. if n != last_n:
  223. self.f.write(", **")
  224. pass
  225. pass
  226. self.prune()
  227. pass
  228. self.n_unmapexpr = unmapexpr
  229. # FIXME: start here
  230. def n_list_unpack(node):
  231. """
  232. prettyprint an unpacked list or tuple
  233. """
  234. p = self.prec
  235. self.prec = 100
  236. lastnode = node.pop()
  237. lastnodetype = lastnode.kind
  238. # If this build list is inside a CALL_FUNCTION_VAR,
  239. # then the first * has already been printed.
  240. # Until I have a better way to check for CALL_FUNCTION_VAR,
  241. # will assume that if the text ends in *.
  242. last_was_star = self.f.getvalue().endswith("*")
  243. if lastnodetype.startswith("BUILD_LIST"):
  244. self.write("[")
  245. endchar = "]"
  246. elif lastnodetype.startswith("BUILD_TUPLE"):
  247. # Tuples can appear places that can NOT
  248. # have parenthesis around them, like array
  249. # subscripts. We check for that by seeing
  250. # if a tuple item is some sort of slice.
  251. no_parens = False
  252. for n in node:
  253. if n == "expr" and n[0].kind.startswith("build_slice"):
  254. no_parens = True
  255. break
  256. pass
  257. if no_parens:
  258. endchar = ""
  259. else:
  260. self.write("(")
  261. endchar = ")"
  262. pass
  263. elif lastnodetype.startswith("BUILD_SET"):
  264. self.write("{")
  265. endchar = "}"
  266. elif lastnodetype.startswith("BUILD_MAP_UNPACK"):
  267. self.write("{*")
  268. endchar = "}"
  269. elif lastnodetype.startswith("ROT_TWO"):
  270. self.write("(")
  271. endchar = ")"
  272. else:
  273. raise TypeError(
  274. "Internal Error: n_build_list expects list, tuple, set, or unpack"
  275. )
  276. flat_elems = flatten_list(node)
  277. self.indent_more(INDENT_PER_LEVEL)
  278. sep = ""
  279. for elem in flat_elems:
  280. if elem in ("ROT_THREE", "EXTENDED_ARG"):
  281. continue
  282. assert elem == "expr"
  283. line_number = self.line_number
  284. value = self.traverse(elem)
  285. if elem[0] == "tuple":
  286. assert value[0] == "("
  287. assert value[-1] == ")"
  288. value = value[1:-1]
  289. if value[-1] == ",":
  290. # singleton tuple
  291. value = value[:-1]
  292. else:
  293. value = "*" + value
  294. if line_number != self.line_number:
  295. sep += "\n" + self.indent + INDENT_PER_LEVEL[:-1]
  296. else:
  297. if sep != "":
  298. sep += " "
  299. if not last_was_star:
  300. pass
  301. else:
  302. last_was_star = False
  303. self.write(sep, value)
  304. sep = ","
  305. if lastnode.attr == 1 and lastnodetype.startswith("BUILD_TUPLE"):
  306. self.write(",")
  307. self.write(endchar)
  308. self.indent_less(INDENT_PER_LEVEL)
  309. self.prec = p
  310. self.prune()
  311. return
  312. self.n_tuple_unpack = n_list_unpack