customize3.py 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. # Copyright (c) 2018-2022 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 version-specific semantic actions here.
  16. """
  17. from xdis import co_flags_is_async, iscode
  18. from decompyle3.scanner import Code
  19. from decompyle3.semantics.consts import TABLE_DIRECT
  20. from decompyle3.semantics.customize37 import customize_for_version37
  21. from decompyle3.semantics.customize38 import customize_for_version38
  22. from decompyle3.semantics.helper import is_lambda_mode
  23. def customize_for_version3(self, version):
  24. TABLE_DIRECT.update(
  25. {
  26. "comp_for": (" for %c in %c", (2, "store"), (0, "expr")),
  27. "if_exp_not": (
  28. "%c if not %c else %c",
  29. (2, "expr"),
  30. (0, "expr"),
  31. (4, "expr"),
  32. ),
  33. "except_cond2": ("%|except %c as %c:\n", (1, "expr"), (5, "store")),
  34. "function_def_annotate": ("\n\n%|def %c%c\n", -1, 0),
  35. # When a generator is a single parameter of a function,
  36. # it doesn't need the surrounding parenethesis.
  37. "call_generator": ("%c%P", 0, (1, -1, ", ", 100)),
  38. "importmultiple": ("%|import %c%c\n", 2, 3),
  39. "import_cont": (", %c", 2),
  40. "raise_stmt2": ("%|raise %c from %c\n", 0, 1),
  41. "tf_tryelsestmtc3": ("%c%-%c%|else:\n%+%c", 1, 3, 5),
  42. "store_locals": ("%|# inspect.currentframe().f_locals = __locals__\n",),
  43. "with": ("%|with %c:\n%+%c%-", 0, 3),
  44. }
  45. )
  46. assert version >= (3, 7)
  47. # In 2.5+ and 3.0+ "except" handlers and the "finally" can appear in one
  48. # "try" statement. So the below has the effect of combining the
  49. # "tryfinally" with statement with the "try_except" statement.
  50. # FIXME: something doesn't smell right, since the semantics
  51. # are different. See test_fileio.py for an example that shows this.
  52. def n_tryfinallystmt(node):
  53. suite_stmts = node[1][0]
  54. if len(suite_stmts) == 1 and suite_stmts[0] == "stmt":
  55. stmt = suite_stmts[0]
  56. try_something = stmt[0]
  57. if try_something == "try_except":
  58. try_something.kind = "tf_try_except"
  59. if try_something.kind.startswith("tryelsestmt"):
  60. if try_something == "c_tryelsestmt":
  61. try_something.kind = "tf_tryelsestmtc3"
  62. else:
  63. try_something.kind = "tf_tryelsestmt"
  64. self.default(node)
  65. self.n_tryfinallystmt = n_tryfinallystmt
  66. def listcomp_closure3(node):
  67. """List comprehensions in Python 3 when handled as a closure.
  68. See if we can combine code.
  69. """
  70. # FIXME: DRY with comprehension_walk_newer
  71. p = self.prec
  72. self.prec = 27
  73. code_obj = node[1].attr
  74. assert iscode(code_obj), node[1]
  75. code = Code(code_obj, self.scanner, self.currentclass, self.debug_opts["asm"])
  76. tree = self.build_ast(
  77. code._tokens,
  78. code._customize,
  79. code,
  80. is_lambda=is_lambda_mode(self.compile_mode),
  81. )
  82. self.customize(code._customize)
  83. # skip over: sstmt, stmt, return, return_expr
  84. # and other singleton derivations
  85. while len(tree) == 1 or (
  86. tree in ("sstmt", "return", "return_expr_lambda", "lambda_start")
  87. and tree[-1]
  88. in ("LAMBDA_MARKER", "RETURN_VALUE_LAMBDA", "RETURN_LAST", "RETURN_VALUE")
  89. ):
  90. self.prec = 100
  91. tree = tree[0]
  92. n = tree[1]
  93. # Pick out important parts of the comprehension:
  94. # * the variables we iterate over: "stores"
  95. # * the results we accumulate: "n"
  96. # collections is the name of the expression(s) we are iterating over
  97. collections = [node[-3]]
  98. list_ifs = []
  99. assert n == "list_iter"
  100. stores = []
  101. # Find the list comprehension body. It is the inner-most
  102. # node that is not list_.. .
  103. while n == "list_iter":
  104. # recurse one step
  105. n = n[0]
  106. if n == "list_for":
  107. stores.append(n[2])
  108. n = n[3]
  109. if n[0] == "list_for":
  110. # Dog-paddle down largely singleton reductions
  111. # to find the collection (expr)
  112. c = n[0][0]
  113. if c == "expr":
  114. c = c[0]
  115. # FIXME: grammar is wonky here? Is this really an attribute?
  116. if c == "attribute":
  117. c = c[0]
  118. collections.append(c)
  119. pass
  120. elif n in ("list_if", "list_if_not", "list_if_or_not"):
  121. if n[0].kind == "expr":
  122. list_ifs.append(n)
  123. else:
  124. list_ifs.append([1])
  125. n = n[-2] if n[-1] == "come_from_opt" else n[-1]
  126. pass
  127. elif n == "list_if37":
  128. list_ifs.append(n)
  129. n = n[-1]
  130. pass
  131. elif n == "list_afor":
  132. collections.append(n[0][0])
  133. n = n[1]
  134. stores.append(n[1][0])
  135. n = n[2] if n[2].kind == "list_iter" else n[3]
  136. pass
  137. assert n == "lc_body", tree
  138. self.preorder(n[0])
  139. # FIXME: add indentation around "for"'s and "in"'s
  140. n_colls = len(collections)
  141. for i, store in enumerate(stores):
  142. if i >= n_colls:
  143. break
  144. if collections[i] == "LOAD_DEREF" and co_flags_is_async(code_obj.co_flags):
  145. self.write(" async")
  146. pass
  147. self.write(" for ")
  148. self.preorder(store)
  149. self.write(" in ")
  150. self.preorder(collections[i])
  151. if i < len(list_ifs):
  152. self.preorder(list_ifs[i])
  153. pass
  154. pass
  155. self.prec = p
  156. self.listcomp_closure3 = listcomp_closure3
  157. TABLE_DIRECT.update(
  158. {
  159. "c_tryelsestmt": (
  160. "%|try:\n%+%c%-%c%|else:\n%+%c%-",
  161. (1, "c_suite_stmts"),
  162. (3, "c_except_handler"),
  163. (5, "else_suitec"),
  164. ),
  165. "LOAD_CLASSDEREF": ("%{pattr}",),
  166. }
  167. )
  168. TABLE_DIRECT.update({"LOAD_CLASSDEREF": ("%{pattr}",)})
  169. if version >= (3, 7):
  170. customize_for_version37(self, version)
  171. if version >= (3, 8):
  172. customize_for_version38(self, version)
  173. pass # version >= 3.8
  174. pass # 3.7
  175. return