n_actions.py 39 KB

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