n_actions.py 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239
  1. # Copyright (c) 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. """
  16. Custom Nonterminal action functions. See NonterminalActions docstring.
  17. """
  18. from spark_parser.ast import GenericASTTraversalPruningException
  19. from xdis import iscode
  20. from decompyle3.parsers.treenode import SyntaxTree
  21. from decompyle3.scanners.tok import Token
  22. from decompyle3.semantics.consts import (
  23. INDENT_PER_LEVEL,
  24. NO_PARENTHESIS_EVER,
  25. NONE,
  26. PARENTHESIS_ALWAYS,
  27. PRECEDENCE,
  28. minint,
  29. )
  30. from decompyle3.semantics.helper import flatten_list
  31. from decompyle3.semantics.make_function36 import make_function36
  32. from decompyle3.util import better_repr
  33. class NonterminalActions:
  34. """
  35. Methods here all start with n_ and the remaining portion should be a nonterminal
  36. name defined by some rule.
  37. These methods take priority over names defined in table constants.
  38. All of the methods should have the same signature: (self, node) and return None.
  39. node is the subtree of the parse tree the that nonterminal name as the root.
  40. """
  41. def n_alias(self, node: SyntaxTree):
  42. if self.version <= (2, 1):
  43. if len(node) == 2:
  44. store = node[1]
  45. assert store == "store"
  46. if store[0].pattr == node[0].pattr:
  47. self.write("import %s\n" % node[0].pattr)
  48. else:
  49. self.write("import %s as %s\n" % (node[0].pattr, store[0].pattr))
  50. pass
  51. pass
  52. self.prune() # stop recursing
  53. store_node = node[-1][-1]
  54. assert store_node.kind.startswith("STORE_")
  55. iname = node[0].pattr # import name
  56. sname = store_node.pattr # store_name
  57. if iname and iname == sname or iname.startswith(sname + "."):
  58. self.write(iname)
  59. else:
  60. self.write(iname, " as ", sname)
  61. self.prune() # stop recursing
  62. n_alias37 = n_alias
  63. def n_assign(self, node: SyntaxTree):
  64. # A horrible hack for Python 3.0 .. 3.2
  65. if (3, 0) <= self.version <= (3, 2) and len(node) == 2:
  66. if (
  67. node[0][0] == "LOAD_FAST"
  68. and node[0][0].pattr == "__locals__"
  69. and node[1][0].kind == "STORE_LOCALS"
  70. ):
  71. self.prune()
  72. self.default(node)
  73. def n_assign2(self, node: SyntaxTree):
  74. for n in node[-2:]:
  75. if n[0] == "unpack":
  76. n[0].kind = "unpack_w_parens"
  77. self.default(node)
  78. def n_assign3(self, node: SyntaxTree):
  79. for n in node[-3:]:
  80. if n[0] == "unpack":
  81. n[0].kind = "unpack_w_parens"
  82. self.default(node)
  83. def n_attribute(self, node: SyntaxTree):
  84. if node[0] == "LOAD_CONST" or node[0] == "expr" and node[0][0] == "LOAD_CONST":
  85. # FIXME: I didn't record which constants parenthesis is
  86. # necessary. However, I suspect that we could further
  87. # refine this by looking at operator precedence and
  88. # eval'ing the constant value (pattr) and comparing with
  89. # the type of the constant.
  90. node.kind = "attribute_w_parens"
  91. self.default(node)
  92. def n_bin_op(self, node: SyntaxTree):
  93. """bin_op (formerly "binary_expr") is the Python AST BinOp"""
  94. self.preorder(node[0])
  95. self.write(" ")
  96. self.preorder(node[-1])
  97. self.write(" ")
  98. # Try to avoid a trailing parentheses by lowering the priority a little
  99. self.prec -= 1
  100. self.preorder(node[1])
  101. self.prec += 1
  102. self.prune()
  103. def n_build_slice2(self, node: SyntaxTree):
  104. p = self.prec
  105. self.prec = NO_PARENTHESIS_EVER
  106. if not node[0].isNone():
  107. self.preorder(node[0])
  108. self.write(":")
  109. if not node[1].isNone():
  110. self.preorder(node[1])
  111. self.prec = p
  112. self.prune() # stop recursing
  113. def n_build_slice3(self, node: SyntaxTree):
  114. p = self.prec
  115. self.prec = NO_PARENTHESIS_EVER
  116. if not node[0].isNone():
  117. self.preorder(node[0])
  118. self.write(":")
  119. if not node[1].isNone():
  120. self.preorder(node[1])
  121. self.write(":")
  122. if not node[2].isNone():
  123. self.preorder(node[2])
  124. self.prec = p
  125. self.prune() # stop recursing
  126. def n_classdef(self, node: SyntaxTree):
  127. self.n_classdef36(node)
  128. # class definition ('class X(A,B,C):')
  129. cclass = self.currentclass
  130. # Pick out various needed bits of information
  131. # * class_name - the name of the class
  132. # * subclass_info - the parameters to the class e.g.
  133. # class Foo(bar, baz)
  134. # -----------
  135. # * subclass_code - the code for the subclass body
  136. if node == "classdefdeco2":
  137. build_class = node
  138. else:
  139. build_class = node[0]
  140. build_list = build_class[1][0]
  141. if hasattr(build_class[-3][0], "attr"):
  142. subclass_code = build_class[-3][0].attr
  143. class_name = build_class[0].pattr
  144. elif (
  145. build_class[-3] == "mkfunc"
  146. and node == "classdefdeco2"
  147. and build_class[-3][0] == "load_closure"
  148. ):
  149. subclass_code = build_class[-3][1].attr
  150. class_name = build_class[-3][0][0].pattr
  151. elif hasattr(node[0][0], "pattr"):
  152. subclass_code = build_class[-3][1].attr
  153. class_name = node[0][0].pattr
  154. else:
  155. raise RuntimeError("Internal Error n_classdef: cannot find class name")
  156. if node == "classdefdeco2":
  157. self.write("\n")
  158. else:
  159. self.write("\n\n")
  160. self.currentclass = str(class_name)
  161. self.write(self.indent, "class ", self.currentclass)
  162. self.print_super_classes(build_list)
  163. self.println(":")
  164. # class body
  165. self.indent_more()
  166. self.build_class(subclass_code)
  167. self.indent_less()
  168. self.currentclass = cclass
  169. if len(self.param_stack) > 1:
  170. self.write("\n\n")
  171. else:
  172. self.write("\n\n\n")
  173. self.prune()
  174. n_classdefdeco2 = n_classdef
  175. def n_const_list(self, node: SyntaxTree):
  176. """
  177. prettyprint a constant dict, list, set or tuple.
  178. """
  179. p = self.prec
  180. lastnodetype = node[2].kind
  181. flat_elems = node[1]
  182. is_dict = lastnodetype.endswith("DICT")
  183. if lastnodetype.endswith("LIST"):
  184. self.write("[")
  185. endchar = "]"
  186. elif lastnodetype.endswith("SET") or is_dict:
  187. self.write("{")
  188. endchar = "}"
  189. else:
  190. # from trepan.api import debug; debug()
  191. raise TypeError(
  192. f"Internal Error: n_const_list expects dict, list set, or set; got {lastnodetype}"
  193. )
  194. self.indent_more(INDENT_PER_LEVEL)
  195. sep = ""
  196. if is_dict:
  197. keys = flat_elems[-1].attr
  198. assert isinstance(keys, tuple)
  199. assert len(keys) == len(flat_elems) - 1
  200. for i, elem in enumerate(flat_elems[:-1]):
  201. assert elem.kind == "ADD_VALUE"
  202. if elem.optype in ("local", "name"):
  203. value = elem.attr
  204. elif elem.optype == "const" and not isinstance(elem.attr, str):
  205. value = elem.attr
  206. else:
  207. value = elem.pattr
  208. if elem.linestart is not None:
  209. if elem.linestart != self.line_number:
  210. sep += "\n" + self.indent + INDENT_PER_LEVEL[:-1]
  211. self.line_number = elem.linestart
  212. else:
  213. if sep != "":
  214. sep += " "
  215. self.write(f"{sep} {repr(keys[i])}: {value}")
  216. sep = ","
  217. else:
  218. for elem in flat_elems:
  219. assert elem.kind == "ADD_VALUE"
  220. if elem.optype in ("local", "name"):
  221. value = elem.attr
  222. elif elem.optype == "const" and not isinstance(elem.attr, str):
  223. value = elem.attr
  224. else:
  225. value = elem.pattr
  226. if elem.linestart is not None:
  227. if elem.linestart != self.line_number:
  228. sep += "\n" + self.indent + INDENT_PER_LEVEL[:-1]
  229. self.line_number = elem.linestart
  230. else:
  231. if sep != "":
  232. sep += " "
  233. self.write(sep, value)
  234. sep = ","
  235. self.write(endchar)
  236. self.indent_less(INDENT_PER_LEVEL)
  237. self.prec = p
  238. self.prune()
  239. return
  240. def n_delete_subscript(self, node: SyntaxTree):
  241. if node[-2][0] == "build_list" and node[-2][0][-1].kind.startswith(
  242. "BUILD_TUPLE"
  243. ):
  244. if node[-2][0][-1] != "BUILD_TUPLE_0":
  245. node[-2][0].kind = "build_tuple2"
  246. self.default(node)
  247. n_store_subscript = n_subscript = n_delete_subscript
  248. def n_dict(self, node: SyntaxTree):
  249. """
  250. Prettyprint a dict.
  251. 'dict' is something like k = {'a': 1, 'b': 42}"
  252. We will use source-code line breaks to guide us when to break.
  253. """
  254. if len(node) == 1 and node[0] == "const_list":
  255. self.preorder(node[0])
  256. self.prune()
  257. return
  258. p = self.prec
  259. self.prec = PRECEDENCE["dict"]
  260. self.indent_more(INDENT_PER_LEVEL)
  261. sep = INDENT_PER_LEVEL[:-1]
  262. if node[0] != "dict_entry":
  263. self.write("{")
  264. line_number = self.line_number
  265. if self.version >= (3, 0) and not self.is_pypy:
  266. if node[0].kind.startswith("kvlist"):
  267. # Python 3.5+ style key/value list in dict
  268. kv_node = node[0]
  269. ll = list(kv_node)
  270. length = len(ll)
  271. if kv_node[-1].kind.startswith("BUILD_MAP"):
  272. length -= 1
  273. i = 0
  274. # Respect line breaks from source
  275. while i < length:
  276. self.write(sep)
  277. name = self.traverse(ll[i], indent="")
  278. if i > 0:
  279. line_number = self.indent_if_source_nl(
  280. line_number, self.indent + INDENT_PER_LEVEL[:-1]
  281. )
  282. line_number = self.line_number
  283. self.write(name, ": ")
  284. value = self.traverse(
  285. ll[i + 1], indent=self.indent + (len(name) + 2) * " "
  286. )
  287. self.write(value)
  288. sep = ", "
  289. if line_number != self.line_number:
  290. sep += "\n" + self.indent + INDENT_PER_LEVEL[:-1]
  291. line_number = self.line_number
  292. i += 2
  293. pass
  294. pass
  295. elif len(node) > 1 and node[1].kind.startswith("kvlist"):
  296. # Python 3.0..3.4 style key/value list in dict
  297. kv_node = node[1]
  298. ll = list(kv_node)
  299. if len(ll) > 0 and ll[0].kind == "kv3":
  300. # Python 3.2 does this
  301. kv_node = node[1][0]
  302. ll = list(kv_node)
  303. i = 0
  304. while i < len(ll):
  305. self.write(sep)
  306. name = self.traverse(ll[i + 1], indent="")
  307. if i > 0:
  308. line_number = self.indent_if_source_nl(
  309. line_number, self.indent + INDENT_PER_LEVEL[:-1]
  310. )
  311. pass
  312. line_number = self.line_number
  313. self.write(name, ": ")
  314. value = self.traverse(
  315. ll[i], indent=self.indent + (len(name) + 2) * " "
  316. )
  317. self.write(value)
  318. sep = ", "
  319. if line_number != self.line_number:
  320. sep += "\n" + self.indent + INDENT_PER_LEVEL[:-1]
  321. line_number = self.line_number
  322. else:
  323. sep += " "
  324. i += 3
  325. pass
  326. pass
  327. elif node[-1].kind.startswith("BUILD_CONST_KEY_MAP"):
  328. # Python 3.6+ style const map
  329. keys = node[-2].pattr
  330. values = node[:-2]
  331. # FIXME: Line numbers?
  332. for key, value in zip(keys, values):
  333. self.write(sep)
  334. self.write(repr(key))
  335. line_number = self.line_number
  336. self.write(":")
  337. self.write(self.traverse(value[0]))
  338. sep = ", "
  339. if line_number != self.line_number:
  340. sep += "\n" + self.indent + INDENT_PER_LEVEL[:-1]
  341. line_number = self.line_number
  342. else:
  343. sep += " "
  344. pass
  345. pass
  346. if sep.startswith(",\n"):
  347. self.write(sep[1:])
  348. pass
  349. elif node[0].kind.startswith("dict_entry"):
  350. assert self.version >= (3, 5)
  351. template = ("%C", (0, len(node[0]), ", **"))
  352. self.template_engine(template, node[0])
  353. sep = ""
  354. elif node[-1].kind.startswith("BUILD_MAP_UNPACK") or node[
  355. -1
  356. ].kind.startswith("dict_entry"):
  357. assert self.version >= (3, 5)
  358. # FIXME: I think we can intermingle dict_comp's with other
  359. # dictionary kinds of things. The most common though is
  360. # a sequence of dict_comp's
  361. kwargs = node[-1].attr
  362. template = ("**%C", (0, kwargs, ", **"))
  363. self.template_engine(template, node)
  364. sep = ""
  365. pass
  366. elif self.version >= (3, 6) and self.is_pypy:
  367. # FIXME: DRY with above
  368. if node[-1].kind.startswith("BUILD_CONST_KEY_MAP"):
  369. # Python 3.6+ style const map
  370. keys = node[-2].pattr
  371. values = node[:-2]
  372. # FIXME: Line numbers?
  373. for key, value in zip(keys, values):
  374. self.write(sep)
  375. self.write(repr(key))
  376. line_number = self.line_number
  377. self.write(":")
  378. self.write(self.traverse(value[0]))
  379. sep = ", "
  380. if line_number != self.line_number:
  381. sep += "\n" + self.indent + INDENT_PER_LEVEL[:-1]
  382. line_number = self.line_number
  383. else:
  384. sep += " "
  385. pass
  386. pass
  387. if sep.startswith(",\n"):
  388. self.write(sep[1:])
  389. pass
  390. else:
  391. # Python 2 style kvlist. Find beginning of kvlist.
  392. if node[0].kind.startswith("BUILD_MAP"):
  393. if len(node) > 1 and node[1].kind in ("kvlist", "kvlist_n"):
  394. kv_node = node[1]
  395. else:
  396. kv_node = node[1:]
  397. else:
  398. assert node[-1].kind.startswith("kvlist")
  399. kv_node = node[-1]
  400. first_time = True
  401. for kv in kv_node:
  402. assert kv in ("kv", "kv2", "kv3")
  403. # kv ::= DUP_TOP expr ROT_TWO expr STORE_SUBSCR
  404. # kv2 ::= DUP_TOP expr expr ROT_THREE STORE_SUBSCR
  405. # kv3 ::= expr expr STORE_MAP
  406. # FIXME: DRY this and the above
  407. indent = self.indent + " "
  408. if kv == "kv":
  409. self.write(sep)
  410. name = self.traverse(kv[-2], indent="")
  411. if first_time:
  412. line_number = self.indent_if_source_nl(line_number, indent)
  413. first_time = False
  414. pass
  415. line_number = self.line_number
  416. self.write(name, ": ")
  417. value = self.traverse(
  418. kv[1], indent=self.indent + (len(name) + 2) * " "
  419. )
  420. elif kv == "kv2":
  421. self.write(sep)
  422. name = self.traverse(kv[1], indent="")
  423. if first_time:
  424. line_number = self.indent_if_source_nl(line_number, indent)
  425. first_time = False
  426. pass
  427. line_number = self.line_number
  428. self.write(name, ": ")
  429. value = self.traverse(
  430. kv[-3], indent=self.indent + (len(name) + 2) * " "
  431. )
  432. elif kv == "kv3":
  433. self.write(sep)
  434. name = self.traverse(kv[-2], indent="")
  435. if first_time:
  436. line_number = self.indent_if_source_nl(line_number, indent)
  437. first_time = False
  438. pass
  439. line_number = self.line_number
  440. self.write(name, ": ")
  441. line_number = self.line_number
  442. value = self.traverse(
  443. kv[0], indent=self.indent + (len(name) + 2) * " "
  444. )
  445. pass
  446. self.write(value)
  447. sep = ", "
  448. if line_number != self.line_number:
  449. sep += "\n" + self.indent + INDENT_PER_LEVEL[:-1]
  450. line_number = self.line_number
  451. else:
  452. sep += " "
  453. pass
  454. pass
  455. pass
  456. if sep.startswith(",\n"):
  457. self.write(sep[1:])
  458. if node[0] != "dict_entry":
  459. self.write("}")
  460. self.indent_less(INDENT_PER_LEVEL)
  461. self.prec = p
  462. self.prune()
  463. # In the old days this node would never get called because
  464. # it was embedded inside some sort of comprehension
  465. # Nowadays, we allow starting any code object, not just
  466. # a top-level module. In doing so we can
  467. # now encounter this outside of the embedding of
  468. # a comprehension.
  469. def n_dict_comp_func(self, node: SyntaxTree):
  470. self.write("{")
  471. self.comprehension_walk_newer(node, 4, 0, collection_node=node[1])
  472. self.write("}")
  473. self.prune()
  474. n_set_comp_func = n_dict_comp_func
  475. def n_docstring(self, node: SyntaxTree):
  476. indent = self.indent
  477. doc_node = node[0]
  478. if doc_node.attr:
  479. docstring = doc_node.attr
  480. if not isinstance(docstring, str):
  481. # FIXME: we have mistakenly tagged something as a doc
  482. # string in transform when it isn't one.
  483. # The rule in n_mkfunc is pretty flaky.
  484. self.prune()
  485. return
  486. else:
  487. docstring = node[0].pattr
  488. quote = '"""'
  489. if docstring.find(quote) >= 0:
  490. if docstring.find("'''") == -1:
  491. quote = "'''"
  492. self.write(indent)
  493. docstring = repr(docstring.expandtabs())[1:-1]
  494. for orig, replace in (
  495. ("\\\\", "\t"),
  496. ("\\r\\n", "\n"),
  497. ("\\n", "\n"),
  498. ("\\r", "\n"),
  499. ('\\"', '"'),
  500. ("\\'", "'"),
  501. ):
  502. docstring = docstring.replace(orig, replace)
  503. # Do a raw string if there are backslashes but no other escaped characters:
  504. # also check some edge cases
  505. if (
  506. "\t" in docstring
  507. and "\\" not in docstring
  508. and len(docstring) >= 2
  509. and docstring[-1] != "\t"
  510. and (docstring[-1] != '"' or docstring[-2] == "\t")
  511. ):
  512. self.write("r") # raw string
  513. # Restore backslashes unescaped since raw
  514. docstring = docstring.replace("\t", "\\")
  515. else:
  516. # Escape the last character if it is the same as the
  517. # triple quote character.
  518. quote1 = quote[-1]
  519. if len(docstring) and docstring[-1] == quote1:
  520. docstring = docstring[:-1] + "\\" + quote1
  521. # Escape triple quote when needed
  522. if quote == '"""':
  523. replace_str = '\\"""'
  524. else:
  525. assert quote == "'''"
  526. replace_str = "\\'''"
  527. docstring = docstring.replace(quote, replace_str)
  528. docstring = docstring.replace("\t", "\\\\")
  529. lines = docstring.split("\n")
  530. self.write(quote)
  531. if len(lines) == 0:
  532. self.println(quote)
  533. elif len(lines) == 1:
  534. self.println(lines[0], quote)
  535. else:
  536. self.println(lines[0])
  537. for line in lines[1:-1]:
  538. if line:
  539. self.println(line)
  540. else:
  541. self.println("\n\n")
  542. pass
  543. pass
  544. self.println(lines[-1], quote)
  545. self.prune()
  546. def n_elifelsestmtr(self, node: SyntaxTree):
  547. if node[2] == "COME_FROM":
  548. return_stmts_node = node[3]
  549. node.kind = "elifelsestmtr2"
  550. else:
  551. return_stmts_node = node[2]
  552. if len(return_stmts_node) != 2:
  553. self.default(node)
  554. for n in return_stmts_node[0]:
  555. if not (n[0] == "ifstmt" and n[0][1][0] == "return_if_stmts"):
  556. self.default(node)
  557. return
  558. self.write(self.indent, "elif ")
  559. self.preorder(node[0])
  560. self.println(":")
  561. self.indent_more()
  562. self.preorder(node[1])
  563. self.indent_less()
  564. for n in return_stmts_node[0]:
  565. n[0].kind = "elifstmt"
  566. self.preorder(n)
  567. self.println(self.indent, "else:")
  568. self.indent_more()
  569. self.preorder(return_stmts_node[1])
  570. self.indent_less()
  571. self.prune()
  572. def n_except_cond2(self, node: SyntaxTree):
  573. unpack_node = -3 if node[-1] == "come_from_opt" else -2
  574. if node[unpack_node][0] == "unpack":
  575. node[unpack_node][0].kind = "unpack_w_parens"
  576. self.default(node)
  577. def n_expr(self, node: SyntaxTree):
  578. first_child = node[0]
  579. p = self.prec
  580. if first_child.kind.startswith("bin_op"):
  581. n = node[0][-1][0]
  582. else:
  583. n = node[0]
  584. # if (hasattr(n, 'linestart') and n.linestart and
  585. # hasattr(self, 'current_line_number')):
  586. # self.source_linemap[self.current_line_number] = n.linestart
  587. if n.kind != "expr":
  588. self.prec = PRECEDENCE.get(n.kind, PARENTHESIS_ALWAYS)
  589. if n == "LOAD_CONST" and repr(n.pattr)[0] == "-":
  590. self.prec = 6
  591. # print("XXX", n.kind, p, "<", self.prec)
  592. # print(self.f.getvalue())
  593. if p < self.prec:
  594. # print(f"PREC {p}, {node[0].kind}")
  595. self.write("(")
  596. self.preorder(node[0])
  597. self.write(")")
  598. else:
  599. self.preorder(node[0])
  600. self.prec = p
  601. self.prune()
  602. # In the old days this node would never get called because
  603. # it was embedded inside some sort of comprehension
  604. # Nowadays, we allow starting any code object, not just
  605. # a top-level module. In doing so we can
  606. # now encounter this outside of the embedding of
  607. # a comprehension.
  608. def n_generator_exp(self, node: SyntaxTree):
  609. self.write("(")
  610. if node[0].kind in ("load_closure", "load_genexpr") and self.version >= (3, 8):
  611. is_lambda = self.is_lambda
  612. self.closure_walk(
  613. node, collection_index=4 if isinstance(node[4], SyntaxTree) else 3
  614. )
  615. self.is_lambda = is_lambda
  616. else:
  617. code_index = -6
  618. if self.version < (3, 8) and node != "genexpr_func":
  619. iter_index = 4 if node[4] == "expr" else 3
  620. self.comprehension_walk(node, iter_index=iter_index)
  621. else:
  622. self.comprehension_walk_newer(node, iter_index=4, code_index=code_index)
  623. self.write(")")
  624. self.prune()
  625. n_genexpr_func = n_generator_exp_async = n_generator_exp
  626. def n_ifelsestmtr(self, node):
  627. if node[2] == "COME_FROM":
  628. return_stmts_node = node[3]
  629. node.kind = "ifelsestmtr2"
  630. else:
  631. return_stmts_node = node[2]
  632. if len(return_stmts_node) != 2:
  633. self.default(node)
  634. if not (
  635. return_stmts_node[0][0][0] == "ifstmt"
  636. and return_stmts_node[0][0][0][1][0] == "return_if_stmts"
  637. ) and not (
  638. return_stmts_node[0][-1][0] == "ifstmt"
  639. and return_stmts_node[0][-1][0][1][0] == "return_if_stmts"
  640. ):
  641. self.default(node)
  642. return
  643. self.write(self.indent, "if ")
  644. self.preorder(node[0])
  645. self.println(":")
  646. self.indent_more()
  647. self.preorder(node[1])
  648. self.indent_less()
  649. if_ret_at_end = False
  650. if len(return_stmts_node[0]) >= 3:
  651. if (
  652. return_stmts_node[0][-1][0] == "ifstmt"
  653. and return_stmts_node[0][-1][0][1][0] == "return_if_stmts"
  654. ):
  655. if_ret_at_end = True
  656. past_else = False
  657. prev_stmt_is_if_ret = True
  658. for n in return_stmts_node[0]:
  659. if n[0] == "ifstmt" and n[0][1][0] == "return_if_stmts":
  660. if prev_stmt_is_if_ret:
  661. n[0].kind = "elifstmt"
  662. prev_stmt_is_if_ret = True
  663. else:
  664. prev_stmt_is_if_ret = False
  665. if not past_else and not if_ret_at_end:
  666. self.println(self.indent, "else:")
  667. self.indent_more()
  668. past_else = True
  669. self.preorder(n)
  670. if not past_else or if_ret_at_end:
  671. self.println(self.indent, "else:")
  672. self.indent_more()
  673. self.preorder(return_stmts_node[1])
  674. self.indent_less()
  675. self.prune()
  676. n_ifelsestmtr2 = n_ifelsestmtr
  677. def n_import_as37(self, node):
  678. if node[1].attr is not None:
  679. self.template_engine(
  680. (
  681. "%|from %c import %[1]{attr[0]} as %c\n",
  682. (2, ("importlist37", "IMPORT_NAME_ATTR")),
  683. (-2, "store"),
  684. ),
  685. node,
  686. )
  687. else:
  688. self.template_engine(
  689. (
  690. "%|import %c as %c\n",
  691. (2, ("importlist37", "IMPORT_NAME_ATTR")),
  692. (-2, "store"),
  693. ),
  694. node,
  695. )
  696. self.prune()
  697. def n_lambda_body(self, node: SyntaxTree):
  698. make_function36(self, node, is_lambda=True, code_node=node[-2])
  699. self.prune() # stop recursing
  700. def n_list(self, node: SyntaxTree):
  701. """
  702. prettyprint a dict, list, set or tuple.
  703. """
  704. p = self.prec
  705. if len(node) == 1:
  706. lastnode = node[0]
  707. flat_elems = []
  708. else:
  709. self.prec = PRECEDENCE["yield"] - 1
  710. lastnode = node.pop()
  711. flat_elems = flatten_list(node)
  712. lastnodetype = lastnode.kind
  713. if lastnodetype.startswith("BUILD_LIST") or lastnodetype == "expr":
  714. self.write("[")
  715. endchar = "]"
  716. elif lastnodetype.startswith("BUILD_MAP_UNPACK"):
  717. self.write("{*")
  718. endchar = "}"
  719. elif lastnodetype.startswith("BUILD_SET"):
  720. self.write("{")
  721. endchar = "}"
  722. elif lastnodetype.startswith("BUILD_TUPLE") or node == "tuple":
  723. # Tuples can appear places that can NOT
  724. # have parenthesis around them, like array
  725. # subscripts. We check for that by seeing
  726. # if a tuple item is some sort of slice.
  727. no_parens = False
  728. for n in node:
  729. if n == "arg":
  730. n = n[0]
  731. if n == "expr" and n[0].kind.startswith("slice"):
  732. no_parens = True
  733. break
  734. pass
  735. if no_parens:
  736. endchar = ""
  737. else:
  738. self.write("(")
  739. endchar = ")"
  740. pass
  741. elif lastnodetype.startswith("ROT_TWO"):
  742. self.write("(")
  743. endchar = ")"
  744. else:
  745. raise TypeError(
  746. "Internal Error: n_build_list expects list, tuple, set, or unpack"
  747. )
  748. self.indent_more(INDENT_PER_LEVEL)
  749. sep = ""
  750. for elem in flat_elems:
  751. if elem in ("ROT_THREE", "EXTENDED_ARG"):
  752. continue
  753. assert elem in ("expr", "list", "lists")
  754. line_number = self.line_number
  755. value = self.traverse(elem)
  756. if line_number != self.line_number:
  757. sep += "\n" + self.indent + INDENT_PER_LEVEL[:-1]
  758. else:
  759. if sep != "":
  760. sep += " "
  761. self.write(sep, value)
  762. sep = ","
  763. if lastnodetype.startswith("BUILD_TUPLE") and lastnode.attr == 1:
  764. self.write(",")
  765. self.write(endchar)
  766. self.indent_less(INDENT_PER_LEVEL)
  767. self.prec = p
  768. self.prune()
  769. return
  770. n_set = n_build_set = n_tuple = n_list
  771. def n_list(self, node):
  772. """
  773. prettyprint a dict, list, set or tuple.
  774. """
  775. p = self.prec
  776. if len(node) == 1:
  777. lastnode = node[0]
  778. flat_elems = []
  779. else:
  780. self.prec = PRECEDENCE["yield"] - 1
  781. lastnode = node.pop()
  782. flat_elems = flatten_list(node)
  783. lastnodetype = lastnode.kind
  784. if lastnodetype.startswith("BUILD_LIST") or lastnodetype == "expr":
  785. self.write("[")
  786. endchar = "]"
  787. elif lastnodetype.startswith("BUILD_MAP_UNPACK"):
  788. self.write("{*")
  789. endchar = "}"
  790. elif lastnodetype.startswith("BUILD_SET"):
  791. self.write("{")
  792. endchar = "}"
  793. elif lastnodetype.startswith("BUILD_TUPLE") or node == "tuple":
  794. # Tuples can appear places that can NOT
  795. # have parenthesis around them, like array
  796. # subscripts. We check for that by seeing
  797. # if a tuple item is some sort of slice.
  798. no_parens = False
  799. for n in node:
  800. if n == "arg":
  801. n = n[0]
  802. if n == "expr" and n[0].kind.startswith("slice"):
  803. no_parens = True
  804. break
  805. pass
  806. if no_parens:
  807. endchar = ""
  808. else:
  809. self.write("(")
  810. endchar = ")"
  811. pass
  812. elif lastnodetype.startswith("ROT_TWO"):
  813. self.write("(")
  814. endchar = ")"
  815. else:
  816. raise TypeError(
  817. "Internal Error: n_build_list expects list, tuple, set, or unpack"
  818. )
  819. self.indent_more(INDENT_PER_LEVEL)
  820. sep = ""
  821. for elem in flat_elems:
  822. if elem in ("ROT_THREE", "EXTENDED_ARG"):
  823. continue
  824. assert elem in ("expr", "list", "lists")
  825. line_number = self.line_number
  826. value = self.traverse(elem)
  827. if line_number != self.line_number:
  828. sep += "\n" + self.indent + INDENT_PER_LEVEL[:-1]
  829. else:
  830. if sep != "":
  831. sep += " "
  832. self.write(sep, value)
  833. sep = ","
  834. if (
  835. isinstance(lastnode, Token)
  836. and lastnode.attr == 1
  837. and lastnodetype.startswith("BUILD_TUPLE")
  838. ):
  839. self.write(",")
  840. self.write(endchar)
  841. self.indent_less(INDENT_PER_LEVEL)
  842. self.prec = p
  843. self.prune()
  844. return
  845. n_set = n_build_set = n_tuple = n_list
  846. def n_list_comp(self, node):
  847. self.write("[")
  848. if node[0].kind == "load_closure":
  849. self.listcomp_closure3(node)
  850. else:
  851. if node == "list_comp_async":
  852. # comprehension_walk_newer needs to pick out from node since
  853. # there isn't an iter_index at the top level
  854. list_iter_index = None
  855. else:
  856. list_iter_index = 5 if self.is_pypy else 1
  857. self.comprehension_walk_newer(node, list_iter_index, 0)
  858. self.write("]")
  859. self.prune()
  860. n_list_comp_async = n_list_comp
  861. def n_mkfunc(self, node):
  862. # MAKE_FUNCTION ..
  863. code_node = node[-3]
  864. if not iscode(code_node.attr):
  865. # docstring exists
  866. code_node = node[-4]
  867. code = code_node.attr
  868. assert iscode(code)
  869. func_name = code.co_name
  870. self.write(func_name)
  871. self.indent_more()
  872. make_function36(self, node, is_lambda=False, code_node=code_node)
  873. if len(self.param_stack) > 1:
  874. self.write("\n\n")
  875. else:
  876. self.write("\n\n\n")
  877. self.indent_less()
  878. self.prune() # stop recursing
  879. def n_return(self, node):
  880. if self.params["is_lambda"] or node[0] in (
  881. "pop_return",
  882. "popb_return",
  883. "pop_ex_return",
  884. ):
  885. self.preorder(node[0])
  886. self.prune()
  887. else:
  888. self.write(self.indent, "return")
  889. # One reason we worry over whether we use "return None" or "return"
  890. # is that inside a generator, "return None" is illegal.
  891. # Thank you, Python!
  892. if self.return_none or not self.is_return_none(node):
  893. self.write(" ")
  894. self.preorder(node[0])
  895. self.println()
  896. self.prune() # stop recursing
  897. def n_return_call_lambda(self, node):
  898. # Understand where the non-psuedo instructions lie.
  899. opt_start = 1 if node[0].kind in ("come_from_", "COME_FROM") else 0
  900. call_index = -3 if node[-1].kind == "COME_FROM" else -2
  901. call_fn = node[call_index]
  902. assert call_fn.kind.startswith("CALL_FUNCTION")
  903. # Just print the args
  904. self.template_engine(
  905. ("%P", (opt_start, call_fn.attr + opt_start, ", ", 100)), node
  906. )
  907. self.prune()
  908. def n_return_expr(self, node):
  909. if len(node) == 1 and node[0] == "expr":
  910. # If expr is yield we want parens.
  911. self.prec = PRECEDENCE["yield"] - 1
  912. self.n_expr(node[0])
  913. else:
  914. self.n_expr(node)
  915. n_return_expr_or_cond = n_expr
  916. # Python 3.x can have be dead code as a result of its optimization?
  917. # So we'll add a # at the end of the return lambda so the rest is ignored
  918. def n_return_expr_lambda(self, node):
  919. if 1 <= len(node) <= 2:
  920. self.preorder(node[0])
  921. self.prune()
  922. else:
  923. # We can't comment out dead code like we do above because
  924. # there may be a trailing ')' that needs to be written.
  925. assert len(node) == 3 and node[2] in (
  926. "RETURN_VALUE_LAMBDA",
  927. "LAMBDA_MARKER",
  928. )
  929. self.preorder(node[0])
  930. self.prune()
  931. def n_return_if_stmt(self, node):
  932. if self.params["is_lambda"]:
  933. self.write(" return ")
  934. self.preorder(node[0])
  935. self.prune()
  936. else:
  937. self.write(self.indent, "return")
  938. if self.return_none or not self.is_return_none(node):
  939. self.write(" ")
  940. self.preorder(node[0])
  941. self.println()
  942. self.prune() # stop recursing
  943. def n_set_comp(self, node):
  944. self.write("{")
  945. if node[0] in ["LOAD_SETCOMP", "LOAD_DICTCOMP"]:
  946. self.comprehension_walk_newer(node, 4, 0)
  947. elif node[0].kind == "load_closure":
  948. # Token GET_ITER forms or nonterminal "get_iter" forms
  949. assert node[-2].kind.lower() in ("get_iter", "get_aiter")
  950. collection_index = -2
  951. if node[collection_index] == "GET_ITER":
  952. collection_index -= 1
  953. self.closure_walk(node, collection_index=collection_index)
  954. else:
  955. # Token GET_ITER forms or nonterminal "get_iter" forms
  956. if node[1] == "set_iter":
  957. self.comprehension_walk_newer(node[1], iter_index=3)
  958. else:
  959. assert node[-2].kind.lower() in ("get_iter", "get_aiter")
  960. self.comprehension_walk(node, iter_index=-2)
  961. self.write("}")
  962. self.prune()
  963. n_dict_comp = n_set_comp
  964. # In the old days this node would never get called because
  965. # it was embedded inside some sort of comprehension
  966. # Nowadays, we allow starting any code object, not just
  967. # a top-level module. In doing so we can
  968. # now encounter this outside of the embedding of
  969. # a comprehension.
  970. def n_set_comp_async(self, node):
  971. self.write("{")
  972. if node[0] in ["BUILD_SET_0", "BUILD_MAP_0"]:
  973. self.comprehension_walk_newer(node[1], 3, 0, collection_node=node[1])
  974. if node[0] in ["LOAD_SETCOMP", "LOAD_DICTCOMP"]:
  975. get_aiter = node[3]
  976. assert get_aiter == "get_aiter", node.kind
  977. self.comprehension_walk_newer(node, 1, 0, collection_node=get_aiter[0])
  978. self.write("}")
  979. self.prune()
  980. n_dict_comp_async = n_set_comp_async
  981. # This could be a rule but we have handling to remove None
  982. # e.g. a[:5] rather than a[None:5]
  983. def n_slice2(self, node):
  984. p = self.prec
  985. self.prec = NO_PARENTHESIS_EVER
  986. if not node[0].isNone():
  987. self.preorder(node[0])
  988. self.write(":")
  989. if not node[1].isNone():
  990. self.preorder(node[1])
  991. self.prec = p
  992. self.prune() # stop recursing
  993. # This could be a rule but we have handling to remove None's
  994. # e.g. a[:] rather than a[None:None]
  995. def n_slice3(self, node):
  996. p = self.prec
  997. self.prec = NO_PARENTHESIS_EVER
  998. if not node[0].isNone():
  999. self.preorder(node[0])
  1000. self.write(":")
  1001. if not node[1].isNone():
  1002. self.preorder(node[1])
  1003. self.write(":")
  1004. if not node[2].isNone():
  1005. self.preorder(node[2])
  1006. self.prec = p
  1007. self.prune() # stop recursing
  1008. def n_str(self, node):
  1009. self.write(node[0].pattr)
  1010. self.prune()
  1011. def n_store(self, node):
  1012. expr = node[0]
  1013. if expr == "expr" and expr[0] == "LOAD_CONST" and node[1] == "STORE_ATTR":
  1014. # FIXME: I didn't record which constants parenthesis is
  1015. # necessary. However, I suspect that we could further
  1016. # refine this by looking at operator precedence and
  1017. # eval'ing the constant value (pattr) and comparing with
  1018. # the type of the constant.
  1019. node.kind = "store_w_parens"
  1020. self.default(node)
  1021. def n_unpack(self, node):
  1022. if node[0].kind.startswith("UNPACK_EX"):
  1023. # Python 3+
  1024. before_count, after_count = node[0].attr
  1025. for i in range(before_count + 1):
  1026. self.preorder(node[i])
  1027. if i != 0:
  1028. self.write(", ")
  1029. self.write("*")
  1030. for i in range(1, after_count + 2):
  1031. self.preorder(node[before_count + i])
  1032. if i != after_count + 1:
  1033. self.write(", ")
  1034. self.prune()
  1035. return
  1036. if node[0] == "UNPACK_SEQUENCE_0":
  1037. self.write("[]")
  1038. self.prune()
  1039. return
  1040. for n in node[1:]:
  1041. if n[0].kind == "unpack":
  1042. n[0].kind = "unpack_w_parens"
  1043. unpack_prec = PRECEDENCE["unpack"]
  1044. old_prec = self.prec
  1045. need_parens = old_prec < unpack_prec
  1046. self.prec = unpack_prec
  1047. if need_parens:
  1048. self.write("(")
  1049. try:
  1050. self.default(node)
  1051. except GenericASTTraversalPruningException:
  1052. self.prec = old_prec
  1053. if need_parens:
  1054. self.write(")")
  1055. raise
  1056. n_unpack_w_parens = n_unpack
  1057. def n_yield(self, node):
  1058. if node != SyntaxTree("yield", [NONE, Token("YIELD_VALUE")]):
  1059. self.template_engine(("yield %c", 0), node)
  1060. elif self.version <= (2, 4):
  1061. # Early versions of Python don't allow a plain "yield"
  1062. self.write("yield None")
  1063. else:
  1064. self.write("yield")
  1065. self.prune() # stop recursing
  1066. def n_LOAD_CONST(self, node):
  1067. attr = node.attr
  1068. data = node.pattr
  1069. datatype = type(data)
  1070. if isinstance(data, float):
  1071. self.write(better_repr(data))
  1072. elif isinstance(data, complex):
  1073. self.write(better_repr(data))
  1074. elif isinstance(datatype, int) and data == minint:
  1075. # convert to hex, since decimal representation
  1076. # would result in 'LOAD_CONST; UNARY_NEGATIVE'
  1077. # change:hG/2002-02-07: this was done for all negative integers
  1078. # todo: check whether this is necessary in Python 2.1
  1079. self.write(hex(data))
  1080. elif datatype is type(Ellipsis):
  1081. self.write("...")
  1082. elif attr is None:
  1083. # LOAD_CONST 'None' only occurs, when None is
  1084. # implicit eg. in 'return' w/o params
  1085. # pass
  1086. self.write("None")
  1087. elif isinstance(data, tuple):
  1088. self.pp_tuple(data)
  1089. elif isinstance(attr, bool):
  1090. self.write(repr(attr))
  1091. elif self.FUTURE_UNICODE_LITERALS:
  1092. # The FUTURE_UNICODE_LITERALS compiler flag
  1093. # in 2.6 on change the way
  1094. # strings are interpreted:
  1095. # u'xxx' -> 'xxx'
  1096. # xxx' -> b'xxx'
  1097. if isinstance(data, str):
  1098. self.write("b" + repr(data))
  1099. else:
  1100. self.write(repr(data))
  1101. else:
  1102. self.write(repr(data))
  1103. # LOAD_CONST is a terminal, so stop processing/recursing early
  1104. self.prune()