fragments.py 79 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370
  1. # Copyright (c) 2015-2019, 2021-2025 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. Creates Python source code from an uncompyle6 parse tree,
  17. and indexes fragments which can be accessed by instruction offset
  18. address.
  19. See https://github.com/rocky/python-uncompyle6/wiki/Table-driven-semantic-actions.
  20. for a more complete explanation, nicely marked up and with examples.
  21. We add some format specifiers here not used in pysource
  22. 1. %x
  23. -----
  24. %x takes an argument (src, (dest...)) and copies all of the range attributes
  25. from src to all nodes under dest.
  26. For example in:
  27. 'import': ( '%|import %c%x\n', 2, (2,(0,1)), ),
  28. node 2 range information, it in %c, is copied to nodes 0 and 1. If
  29. 1. is a nonterminal, all the nodes under it get node2 range information.
  30. 2. %r
  31. -----
  32. %r associates recursively location information for the string that follows
  33. For example in:
  34. 'break': ( '%|%rbreak\n', ),
  35. The node will be associated with the text break, excluding the trailing newline.
  36. Note we associate the accumulated text with the node normally, but we just don't
  37. do it recursively which is where offsets are probably located.
  38. 2. %b
  39. -----
  40. %b associates the text from the specified index to what we have now.
  41. it takes an integer argument.
  42. For example in:
  43. 'importmultiple': ( '%|import%b %c%c\n', 0, 2, 3 ),
  44. The node position 0 will be associated with "import".
  45. """
  46. # FIXME: DRY code with pysource
  47. import re
  48. from bisect import bisect_right
  49. from collections import namedtuple
  50. from typing import Optional
  51. from spark_parser import DEFAULT_DEBUG as PARSER_DEFAULT_DEBUG
  52. from spark_parser.ast import GenericASTTraversalPruningException
  53. from xdis import iscode
  54. from xdis.version_info import IS_PYPY, PYTHON_VERSION_TRIPLE
  55. from uncompyle6.parser import ParserError as ParserError, parse
  56. from uncompyle6.parsers.treenode import SyntaxTree
  57. from uncompyle6.scanner import Code, Token, get_scanner
  58. from uncompyle6.semantics.check_ast import checker
  59. from uncompyle6.semantics.consts import (
  60. INDENT_PER_LEVEL,
  61. NONE,
  62. PASS,
  63. PRECEDENCE,
  64. TABLE_DIRECT,
  65. escape,
  66. )
  67. from uncompyle6.semantics.helper import find_code_node
  68. from uncompyle6.semantics.pysource import (
  69. DEFAULT_DEBUG_OPTS,
  70. TREE_DEFAULT_DEBUG,
  71. SourceWalker,
  72. StringIO,
  73. find_globals_and_nonlocals,
  74. )
  75. from uncompyle6.show import maybe_show_asm, maybe_show_tree
  76. NodeInfo = namedtuple("NodeInfo", "node start finish")
  77. ExtractInfo = namedtuple(
  78. "ExtractInfo",
  79. "lineNo lineStartOffset markerLine selectedLine selectedText nonterminal",
  80. )
  81. TABLE_DIRECT_FRAGMENT = {
  82. "break": ("%|%rbreak\n",),
  83. "continue ": ("%|%rcontinue\n",),
  84. "pass": ("%|%rpass\n",),
  85. "raise_stmt0": ("%|%rraise\n",),
  86. "import": ("%|import %c%x\n", 2, (2, (0, 1))),
  87. "import_cont": (", %c%x", (2, "alias"), (2, (0, 1))),
  88. "import_from": ("%|from %[2]{pattr}%x import %c\n", (2, (0, 1)), (3, "importlist")),
  89. "importfrom": ("%|from %[2]{pattr}%x import %c\n", (2, (0, 1)), 3),
  90. # FIXME only in <= 2.4
  91. "importmultiple": ("%|import%b %c%c\n", 0, 2, 3),
  92. "list_for": (" for %c%x in %c%c", 2, (2, (1,)), 0, 3),
  93. "for": ("%|for%b %c%x in %c:\n%+%c%-\n\n", 0, (3, "store"), (3, (2,)), 1, 4),
  94. "forelsestmt": (
  95. "%|for%b %c%x in %c:\n%+%c%-%|else:\n%+%c%-\n\n",
  96. 0,
  97. (3, "store"),
  98. (3, (2,)),
  99. 1,
  100. 4,
  101. -2,
  102. ),
  103. "forelselaststmt": (
  104. "%|for%b %c%x in %c:\n%+%c%-%|else:\n%+%c%-",
  105. 0,
  106. (3, "store"),
  107. (3, (2,)),
  108. 1,
  109. 4,
  110. -2,
  111. ),
  112. "forelselaststmtl": (
  113. "%|for%b %c%x in %c:\n%+%c%-%|else:\n%+%c%-\n\n",
  114. 0,
  115. (3, "store"),
  116. (3, (2,)),
  117. 1,
  118. 4,
  119. -2,
  120. ),
  121. "whilestmt": ("%|while%b %c:\n%+%c%-\n\n", 0, 1, 2),
  122. "whileelsestmt": ("%|while%b %c:\n%+%c%-%|else:\n%+%c%-\n\n", 0, 1, 2, -2),
  123. "whileelselaststmt": ("%|while%b %c:\n%+%c%-%|else:\n%+%c%-", 0, 1, 2, -2),
  124. }
  125. class FragmentsWalker(SourceWalker, object):
  126. MAP_DIRECT_FRAGMENT = ()
  127. stacked_params = ("f", "indent", "is_lambda", "_globals")
  128. def __init__(
  129. self,
  130. version: tuple,
  131. scanner,
  132. showast=TREE_DEFAULT_DEBUG,
  133. debug_parser=PARSER_DEFAULT_DEBUG,
  134. compile_mode="exec",
  135. is_pypy=IS_PYPY,
  136. linestarts={},
  137. tolerate_errors=True,
  138. ):
  139. SourceWalker.__init__(
  140. self,
  141. version=version,
  142. out=StringIO(),
  143. scanner=scanner,
  144. showast=showast,
  145. debug_parser=debug_parser,
  146. compile_mode=compile_mode,
  147. is_pypy=is_pypy,
  148. linestarts=linestarts,
  149. tolerate_errors=tolerate_errors,
  150. )
  151. # hide_internal suppresses displaying the additional instructions that sometimes
  152. # exist in code but but were not written in the source code.
  153. # An example is:
  154. # __module__ = __name__
  155. # If showing source code we generally don't want to show this. However in fragment
  156. # deparsing we generally do need to see these instructions since we may be stopped
  157. # at one. So here we do not want to suppress showing such instructions.
  158. self.hide_internal = False
  159. self.offsets = {}
  160. self.last_finish = -1
  161. self.is_pypy = is_pypy
  162. # FIXME: is there a better way?
  163. self.MAP_DIRECT_FRAGMENT = (dict(TABLE_DIRECT, **TABLE_DIRECT_FRAGMENT),)
  164. return
  165. f = property(
  166. lambda s: s.params["f"],
  167. lambda s, x: s.params.__setitem__("f", x),
  168. lambda s: s.params.__delitem__("f"),
  169. None,
  170. )
  171. indent = property(
  172. lambda s: s.params["indent"],
  173. lambda s, x: s.params.__setitem__("indent", x),
  174. lambda s: s.params.__delitem__("indent"),
  175. None,
  176. )
  177. is_lambda = property(
  178. lambda s: s.params["is_lambda"],
  179. lambda s, x: s.params.__setitem__("is_lambda", x),
  180. lambda s: s.params.__delitem__("is_lambda"),
  181. None,
  182. )
  183. _globals = property(
  184. lambda s: s.params["_globals"],
  185. lambda s, x: s.params.__setitem__("_globals", x),
  186. lambda s: s.params.__delitem__("_globals"),
  187. None,
  188. )
  189. def set_pos_info(self, node, start, finish, name=None):
  190. if name is None:
  191. name = self.name
  192. if hasattr(node, "offset"):
  193. node.start = start
  194. node.finish = finish
  195. self.offsets[name, node.offset] = node
  196. if hasattr(node, "parent"):
  197. assert node.parent != node
  198. node.start = start
  199. node.finish = finish
  200. self.last_finish = finish
  201. def preorder(self, node=None):
  202. start = len(self.f.getvalue())
  203. super(SourceWalker, self).preorder(node)
  204. self.set_pos_info(node, start, len(self.f.getvalue()))
  205. return
  206. def table_r_node(self, node):
  207. """General pattern where the last node should should
  208. get the text span attributes of the entire tree"""
  209. start = len(self.f.getvalue())
  210. try:
  211. self.default(node)
  212. except GenericASTTraversalPruningException:
  213. if not hasattr(node[-1], "parent"):
  214. node[-1].parent = node
  215. final = len(self.f.getvalue())
  216. self.set_pos_info(node, start, final)
  217. self.set_pos_info(node[-1], start, final)
  218. raise GenericASTTraversalPruningException
  219. n_slice0 = n_slice1 = n_slice2 = n_slice3 = n_subscript = table_r_node
  220. n_aug_assign_1 = n_print_item = exec_stmt = print_to_item = delete = table_r_node
  221. n_classdefco1 = n_classdefco2 = except_cond1 = except_cond2 = table_r_node
  222. def n_pass(self, node):
  223. start = len(self.f.getvalue()) + len(self.indent)
  224. self.set_pos_info(node, start, start + len("pass"))
  225. self.default(node)
  226. def n_try_except(self, node):
  227. # Note: we could also do this via modifying the
  228. # 5 or so template rules. That is change:
  229. # 'try_except': ( '%|try%:\n%+%c%-%c\n\n', 1, 3 ),
  230. # to:
  231. # 'try_except': ( '%|try%b:\n%+%c%-%c\n\n', 0, 1, 3 ),
  232. start = len(self.f.getvalue()) + len(self.indent)
  233. self.set_pos_info(node[0], start, start + len("try:"))
  234. self.default(node)
  235. n_tryelsestmt = n_tryelsestmtc = n_tryelsestmtl = n_tryfinallystmt = n_try_except
  236. def n_raise_stmt0(self, node):
  237. assert node[0] == "RAISE_VARARGS_0"
  238. start = len(self.f.getvalue()) + len(self.indent)
  239. try:
  240. self.default(node)
  241. except GenericASTTraversalPruningException:
  242. self.set_pos_info(node[0], start, len(self.f.getvalue()))
  243. self.prune()
  244. def n_raise_stmt1(self, node):
  245. assert node[1] == "RAISE_VARARGS_1"
  246. start = len(self.f.getvalue()) + len(self.indent)
  247. try:
  248. self.default(node)
  249. except GenericASTTraversalPruningException:
  250. self.set_pos_info(node[1], start, len(self.f.getvalue()))
  251. self.prune()
  252. def n_raise_stmt2(self, node):
  253. assert node[2] == "RAISE_VARARGS_2"
  254. start = len(self.f.getvalue()) + len(self.indent)
  255. try:
  256. self.default(node)
  257. except GenericASTTraversalPruningException:
  258. self.set_pos_info(node[2], start, len(self.f.getvalue()))
  259. self.prune()
  260. # FIXME: Isolate: only in Python 2.x.
  261. def n_raise_stmt3(self, node):
  262. assert node[3] == "RAISE_VARARGS_3"
  263. start = len(self.f.getvalue()) + len(self.indent)
  264. try:
  265. self.default(node)
  266. except GenericASTTraversalPruningException:
  267. self.set_pos_info(node[3], start, len(self.f.getvalue()))
  268. self.prune()
  269. def n_return(self, node):
  270. start = len(self.f.getvalue()) + len(self.indent)
  271. if self.params["is_lambda"]:
  272. self.preorder(node[0])
  273. if hasattr(node[-1], "offset"):
  274. self.set_pos_info(node[-1], start, len(self.f.getvalue()))
  275. self.prune()
  276. else:
  277. start = len(self.f.getvalue()) + len(self.indent)
  278. self.write(self.indent, "return")
  279. if self.return_none or node != SyntaxTree(
  280. "return", [SyntaxTree("return_expr", [NONE]), Token("RETURN_VALUE")]
  281. ):
  282. self.write(" ")
  283. self.last_finish = len(self.f.getvalue())
  284. self.preorder(node[0])
  285. if hasattr(node[-1], "offset"):
  286. self.set_pos_info(node[-1], start, len(self.f.getvalue()))
  287. pass
  288. pass
  289. else:
  290. for n in node:
  291. self.set_pos_info_recurse(n, start, len(self.f.getvalue()))
  292. pass
  293. pass
  294. self.set_pos_info(node, start, len(self.f.getvalue()))
  295. self.println()
  296. self.prune() # stop recursing
  297. def n_return_if_stmt(self, node):
  298. start = len(self.f.getvalue()) + len(self.indent)
  299. if self.params["is_lambda"]:
  300. node[0].parent = node
  301. self.preorder(node[0])
  302. else:
  303. start = len(self.f.getvalue()) + len(self.indent)
  304. self.write(self.indent, "return")
  305. if self.return_none or node != SyntaxTree(
  306. "return", [SyntaxTree("return_expr", [NONE]), Token("RETURN_END_IF")]
  307. ):
  308. self.write(" ")
  309. self.preorder(node[0])
  310. if hasattr(node[-1], "offset"):
  311. self.set_pos_info(node[-1], start, len(self.f.getvalue()))
  312. self.println()
  313. self.set_pos_info(node, start, len(self.f.getvalue()))
  314. self.prune() # stop recursing
  315. def n_yield(self, node):
  316. start = len(self.f.getvalue())
  317. try:
  318. super(FragmentsWalker, self).n_yield(node)
  319. except GenericASTTraversalPruningException:
  320. pass
  321. if node != SyntaxTree("yield", [NONE, Token("YIELD_VALUE")]):
  322. node[0].parent = node
  323. self.set_pos_info(node[-1], start, len(self.f.getvalue()))
  324. self.set_pos_info(node, start, len(self.f.getvalue()))
  325. self.prune() # stop recursing
  326. # In Python 3.3+ only
  327. def n_yield_from(self, node):
  328. start = len(self.f.getvalue())
  329. try:
  330. super(FragmentsWalker, self).n_yield(node)
  331. except GenericASTTraversalPruningException:
  332. pass
  333. self.preorder(node[0])
  334. self.set_pos_info(node, start, len(self.f.getvalue()))
  335. self.prune() # stop recursing
  336. def n_buildslice3(self, node):
  337. start = len(self.f.getvalue())
  338. try:
  339. super(FragmentsWalker, self).n_buildslice3(node)
  340. except GenericASTTraversalPruningException:
  341. pass
  342. self.set_pos_info(node, start, len(self.f.getvalue()))
  343. self.prune() # stop recursing
  344. def n_buildslice2(self, node):
  345. start = len(self.f.getvalue())
  346. try:
  347. super(FragmentsWalker, self).n_buildslice2(node)
  348. except GenericASTTraversalPruningException:
  349. pass
  350. self.set_pos_info(node, start, len(self.f.getvalue()))
  351. self.prune() # stop recursing
  352. def n_expr(self, node):
  353. start = len(self.f.getvalue())
  354. p = self.prec
  355. if node[0].kind.startswith("bin_op"):
  356. n = node[0][-1][0]
  357. else:
  358. n = node[0]
  359. self.prec = PRECEDENCE.get(n.kind, -2)
  360. if n == "LOAD_CONST" and repr(n.pattr)[0] == "-":
  361. n.parent = node
  362. self.set_pos_info(n, start, len(self.f.getvalue()))
  363. self.prec = 6
  364. if p < self.prec:
  365. self.write("(")
  366. node[0].parent = node
  367. self.last_finish = len(self.f.getvalue())
  368. self.preorder(node[0])
  369. finish = len(self.f.getvalue())
  370. if hasattr(node[0], "offset"):
  371. self.set_pos_info(node[0], start, len(self.f.getvalue()))
  372. self.write(")")
  373. self.last_finish = finish + 1
  374. else:
  375. node[0].parent = node
  376. start = len(self.f.getvalue())
  377. self.preorder(node[0])
  378. if hasattr(node[0], "offset"):
  379. self.set_pos_info(node[0], start, len(self.f.getvalue()))
  380. self.prec = p
  381. self.set_pos_info(node, start, len(self.f.getvalue()))
  382. self.prune()
  383. def n_return_expr(self, node):
  384. start = len(self.f.getvalue())
  385. super(FragmentsWalker, self).n_return_expr(node)
  386. self.set_pos_info(node, start, len(self.f.getvalue()))
  387. def n_bin_op(self, node):
  388. """bin_op (formerly "binary_expr") is the Python AST BinOp"""
  389. start = len(self.f.getvalue())
  390. for n in node:
  391. n.parent = node
  392. self.last_finish = len(self.f.getvalue())
  393. try:
  394. super(FragmentsWalker, self).n_bin_op(node)
  395. except GenericASTTraversalPruningException:
  396. pass
  397. self.set_pos_info(node, start, len(self.f.getvalue()))
  398. self.prune()
  399. def n_LOAD_CONST(self, node):
  400. start = len(self.f.getvalue())
  401. try:
  402. super(FragmentsWalker, self).n_LOAD_CONST(node)
  403. except GenericASTTraversalPruningException:
  404. pass
  405. self.set_pos_info(node, start, len(self.f.getvalue()))
  406. self.prune()
  407. n_LOAD_STR = n_LOAD_CONST
  408. def n_exec_stmt(self, node):
  409. """
  410. exec_stmt ::= expr exprlist DUP_TOP EXEC_STMT
  411. exec_stmt ::= expr exprlist EXEC_STMT
  412. """
  413. start = len(self.f.getvalue()) + len(self.indent)
  414. try:
  415. super(FragmentsWalker, self).n_exec_stmt(node)
  416. except GenericASTTraversalPruningException:
  417. pass
  418. self.set_pos_info(node, start, len(self.f.getvalue()))
  419. self.set_pos_info(node[-1], start, len(self.f.getvalue()))
  420. self.prune() # stop recursing
  421. def n_ifelsestmtr(self, node):
  422. if node[2] == "COME_FROM":
  423. return_stmts_node = node[3]
  424. node.kind = "ifelsestmtr2"
  425. else:
  426. return_stmts_node = node[2]
  427. if len(return_stmts_node) != 2:
  428. self.default(node)
  429. if not (
  430. return_stmts_node[0][0][0] == "ifstmt"
  431. and return_stmts_node[0][0][0][1][0] == "return_if_stmts"
  432. ) and not (
  433. return_stmts_node[0][-1][0] == "ifstmt"
  434. and return_stmts_node[0][-1][0][1][0] == "return_if_stmts"
  435. ):
  436. self.default(node)
  437. return
  438. start = len(self.f.getvalue()) + len(self.indent)
  439. self.write(self.indent, "if ")
  440. self.preorder(node[0])
  441. self.println(":")
  442. self.indent_more()
  443. node[1].parent = node
  444. self.preorder(node[1])
  445. self.indent_less()
  446. if_ret_at_end = False
  447. if len(node[2][0]) >= 3:
  448. node[2][0].parent = node
  449. if (
  450. node[2][0][-1][0] == "ifstmt"
  451. and node[2][0][-1][0][1][0] == "return_if_stmts"
  452. ):
  453. if_ret_at_end = True
  454. past_else = False
  455. prev_stmt_is_if_ret = True
  456. for n in return_stmts_node[0]:
  457. if n[0] == "ifstmt" and n[0][1][0] == "return_if_stmts":
  458. if prev_stmt_is_if_ret:
  459. n[0].kind = "elifstmt"
  460. prev_stmt_is_if_ret = True
  461. else:
  462. prev_stmt_is_if_ret = False
  463. if not past_else and not if_ret_at_end:
  464. self.println(self.indent, "else:")
  465. self.indent_more()
  466. past_else = True
  467. n.parent = node
  468. self.preorder(n)
  469. if not past_else or if_ret_at_end:
  470. self.println(self.indent, "else:")
  471. self.indent_more()
  472. node[2][1].parent = node
  473. self.preorder(node[2][1])
  474. self.set_pos_info(node, start, len(self.f.getvalue()))
  475. self.indent_less()
  476. self.prune()
  477. def n_elifelsestmtr(self, node):
  478. if len(node[2]) != 2:
  479. self.default(node)
  480. for n in node[2][0]:
  481. if not (n[0] == "ifstmt" and n[0][1][0] == "return_if_stmts"):
  482. self.default(node)
  483. return
  484. start = len(self.f.getvalue() + self.indent)
  485. self.write(self.indent, "elif ")
  486. node[0].parent = node
  487. self.preorder(node[0])
  488. self.println(":")
  489. self.indent_more()
  490. node[1].parent = node
  491. self.preorder(node[1])
  492. self.indent_less()
  493. for n in node[2][0]:
  494. n[0].kind = "elifstmt"
  495. n.parent = node
  496. self.preorder(n)
  497. self.println(self.indent, "else:")
  498. self.indent_more()
  499. node[2][1].parent = node
  500. self.preorder(node[2][1])
  501. self.indent_less()
  502. self.set_pos_info(node, start, len(self.f.getvalue()))
  503. self.prune()
  504. def n_alias(self, node):
  505. start = len(self.f.getvalue())
  506. iname = node[0].pattr
  507. store_import_node = node[-1][-1]
  508. assert store_import_node.kind.startswith("STORE_")
  509. sname = store_import_node.pattr
  510. self.write(iname)
  511. finish = len(self.f.getvalue())
  512. if iname == sname or iname.startswith(sname + "."):
  513. self.set_pos_info_recurse(node, start, finish)
  514. else:
  515. self.write(" as ")
  516. sname_start = len(self.f.getvalue())
  517. self.write(sname)
  518. finish = len(self.f.getvalue())
  519. for n in node[-1]:
  520. self.set_pos_info_recurse(n, sname_start, finish)
  521. self.set_pos_info(node, start, finish)
  522. self.prune() # stop recursing
  523. def n_mkfunc(self, node):
  524. start = len(self.f.getvalue())
  525. code_node = find_code_node(node, -2)
  526. func_name = code_node.attr.co_name
  527. self.write(func_name)
  528. self.set_pos_info(code_node, start, len(self.f.getvalue()))
  529. self.indent_more()
  530. start = len(self.f.getvalue())
  531. self.make_function(node, is_lambda=False, code_node=code_node)
  532. self.set_pos_info(node, start, len(self.f.getvalue()))
  533. if len(self.param_stack) > 1:
  534. self.write("\n\n")
  535. else:
  536. self.write("\n\n\n")
  537. self.indent_less()
  538. self.prune() # stop recursing
  539. def comprehension_walk(self, node, iter_index, code_index=-5):
  540. p = self.prec
  541. self.prec = 27
  542. # FIXME: clean this up
  543. if self.version >= (3, 1) and node == "dict_comp":
  544. cn = node[1]
  545. elif self.version > (3, 1) and node in ("generator_exp", "generator_exp_async"):
  546. if node[0] == "load_genexpr":
  547. load_genexpr = node[0]
  548. elif node[1] == "load_genexpr":
  549. load_genexpr = node[1]
  550. cn = load_genexpr[0]
  551. elif hasattr(node[code_index], "attr"):
  552. # Python 2.5+ (and earlier?) does this
  553. cn = node[code_index]
  554. else:
  555. if len(node[1]) > 1 and hasattr(node[1][1], "attr"):
  556. # Python 3.3+ does this
  557. cn = node[1][1]
  558. elif hasattr(node[1][0], "attr"):
  559. # Python 3.2 does this
  560. cn = node[1][0]
  561. else:
  562. assert False, "Can't find code for comprehension"
  563. assert iscode(cn.attr)
  564. code = Code(cn.attr, self.scanner, self.currentclass)
  565. ast = self.build_ast(code._tokens, code._customize, code)
  566. self.MAP_DIRECT = (self.TABLE_DIRECT,)
  567. self.MAP_R = (self.TABLE_R, -1)
  568. self.MAP = {
  569. "stmt": self.MAP_R,
  570. "call": self.MAP_R,
  571. "delete": self.MAP_R,
  572. "store": self.MAP_R,
  573. }
  574. self.customize(code._customize)
  575. # Remove single reductions as in ("stmts", "sstmt"):
  576. while len(ast) == 1:
  577. ast = ast[0]
  578. n = ast[iter_index]
  579. assert n == "comp_iter", n.kind
  580. # Find the comprehension body. It is the inner-most
  581. # node that is not list_.. .
  582. while n == "comp_iter": # list_iter
  583. n = n[0] # recurse one step
  584. if n == "comp_for":
  585. if n[0] == "SETUP_LOOP":
  586. n = n[4]
  587. else:
  588. n = n[3]
  589. elif n == "comp_if":
  590. n = n[2]
  591. elif n == "comp_ifnot":
  592. n = n[2]
  593. assert n == "comp_body", ast
  594. self.preorder(n[0])
  595. if node == "generator_exp_async":
  596. self.write(" async")
  597. iter_var_index = iter_index - 2
  598. else:
  599. iter_var_index = iter_index - 1
  600. self.write(" for ")
  601. start = len(self.f.getvalue())
  602. store = ast[iter_var_index]
  603. self.preorder(store)
  604. self.set_pos_info(ast[iter_index - 1], start, len(self.f.getvalue()))
  605. self.write(" in ")
  606. start = len(self.f.getvalue())
  607. node[-3].parent = node
  608. self.preorder(node[-3])
  609. self.set_pos_info(node[-3], start, len(self.f.getvalue()))
  610. if node[2] == "expr":
  611. iter_expr = node[2]
  612. else:
  613. iter_expr = node[-3]
  614. assert iter_expr == "expr"
  615. iter_expr.parent = node
  616. self.preorder(iter_expr)
  617. self.set_pos_info(iter_expr, start, len(self.f.getvalue()))
  618. start = len(self.f.getvalue())
  619. self.preorder(ast[iter_index])
  620. self.set_pos_info(ast[iter_index], start, len(self.f.getvalue()))
  621. self.prec = p
  622. def comprehension_walk3(self, node, iter_index, code_index=-5):
  623. """
  624. List comprehensions the way they are done in Python3.
  625. They're more other comprehensions, e.g. set comprehensions
  626. See if we can combine code.
  627. """
  628. p = self.prec
  629. self.prec = 27
  630. code = node[code_index].attr
  631. assert iscode(code), node[code_index]
  632. code_name = code.co_name
  633. code = Code(code, self.scanner, self.currentclass, self.debug_opts["asm"])
  634. ast = self.build_ast(code._tokens, code._customize, code)
  635. self.customize(code._customize)
  636. if ast[0] == "sstmt":
  637. ast = ast[0]
  638. # skip over stmt return return_expr
  639. ast = ast[0][0][0]
  640. store = None
  641. if ast in ["set_comp_func", "dict_comp_func"]:
  642. # Offset 0: BUILD_SET should have the span
  643. # of '{'
  644. self.gen_source(ast, code_name, {})
  645. for k in ast:
  646. if k == "comp_iter":
  647. n = k
  648. elif k == "store":
  649. store = k
  650. pass
  651. pass
  652. pass
  653. else:
  654. ast = ast[0][0]
  655. n = ast[iter_index]
  656. assert n == "list_iter", n
  657. # FIXME: I'm not totally sure this is right.
  658. # Find the list comprehension body. It is the inner-most
  659. # node that is not list_.. .
  660. if_node = None
  661. comp_for = None
  662. comp_store = None
  663. if n == "comp_iter":
  664. comp_for = n
  665. comp_store = ast[3]
  666. have_not = False
  667. while n in ("list_iter", "comp_iter"):
  668. n = n[0] # iterate one nesting deeper
  669. if n in ("list_for", "comp_for"):
  670. if n[2] == "store":
  671. store = n[2]
  672. n = n[3]
  673. elif n in ("list_if", "list_if_not", "comp_if", "comp_ifnot"):
  674. have_not = n in ("list_if_not", "comp_ifnot")
  675. if_node = n[0]
  676. if n[1] == "store":
  677. store = n[1]
  678. n = n[2]
  679. pass
  680. pass
  681. # Python 2.7+ starts including set_comp_body
  682. # Python 3.5+ starts including set_comp_func
  683. assert n.kind in ("lc_body", "comp_body", "set_comp_func", "set_comp_body"), ast
  684. assert store, "Couldn't find store in list/set comprehension"
  685. old_name = self.name
  686. self.name = code_name
  687. # Issue created with later Python code generation is that there
  688. # is a lambda set up with a dummy argument name that is then called
  689. # So we can't just translate that as is but need to replace the
  690. # dummy name. Below we are picking out the variable name as seen
  691. # in the code. And trying to generate code for the other parts
  692. # that don't have the dummy argument name in it.
  693. # Another approach might be to be able to pass in the source name
  694. # for the dummy argument.
  695. self.preorder(n[0])
  696. gen_start = len(self.f.getvalue()) + 1
  697. self.write(" for ")
  698. start = len(self.f.getvalue())
  699. if comp_store:
  700. self.preorder(comp_store)
  701. else:
  702. self.preorder(store)
  703. self.set_pos_info(store, start, len(self.f.getvalue()))
  704. # FIXME this is all merely approximate
  705. # from trepan.api import debug; debug()
  706. self.write(" in ")
  707. start = len(self.f.getvalue())
  708. node[-3].parent = node
  709. self.preorder(node[-3])
  710. fin = len(self.f.getvalue())
  711. self.set_pos_info(node[-3], start, fin, old_name)
  712. if ast == "list_comp":
  713. list_iter = ast[1]
  714. assert list_iter == "list_iter"
  715. if list_iter == "list_for":
  716. self.preorder(list_iter[3])
  717. self.prec = p
  718. return
  719. pass
  720. if comp_store:
  721. self.preorder(comp_for)
  722. elif if_node:
  723. self.write(" if ")
  724. if have_not:
  725. self.write("not ")
  726. self.preorder(if_node)
  727. pass
  728. self.prec = p
  729. self.name = old_name
  730. if node[-1].kind.startswith("CALL_FUNCTION"):
  731. self.set_pos_info(node[-1], gen_start, len(self.f.getvalue()))
  732. def listcomprehension_walk2(self, node):
  733. """List comprehensions the way they are done in Python 2 (and
  734. some Python 3?).
  735. They're more other comprehensions, e.g. set comprehensions
  736. See if we can combine code.
  737. """
  738. p = self.prec
  739. self.prec = 27
  740. code = Code(node[1].attr, self.scanner, self.currentclass)
  741. ast = self.build_ast(code._tokens, code._customize, code)
  742. self.customize(code._customize)
  743. if node == "set_comp":
  744. ast = ast[0][0][0]
  745. else:
  746. ast = ast[0][0][0][0][0]
  747. if ast == "expr":
  748. ast = ast[0]
  749. n = ast[1]
  750. collection = node[-3]
  751. list_if = None
  752. assert n == "list_iter"
  753. # Find the list comprehension body. It is the inner-most
  754. # node that is not list_.. .
  755. while n == "list_iter":
  756. n = n[0] # recurse one step
  757. if n == "list_for":
  758. store = n[2]
  759. n = n[3]
  760. elif n in ("list_if", "list_if_not"):
  761. # FIXME: just a guess
  762. if n[0].kind == "expr":
  763. list_if = n
  764. else:
  765. list_if = n[1]
  766. n = n[2]
  767. pass
  768. pass
  769. assert n == "lc_body", ast
  770. self.preorder(n[0])
  771. self.write(" for ")
  772. start = len(self.f.getvalue())
  773. self.preorder(store)
  774. self.set_pos_info(store, start, len(self.f.getvalue()))
  775. self.write(" in ")
  776. start = len(self.f.getvalue())
  777. node[-3].parent = node
  778. self.preorder(collection)
  779. self.set_pos_info(collection, start, len(self.f.getvalue()))
  780. if list_if:
  781. start = len(self.f.getvalue())
  782. self.preorder(list_if)
  783. self.set_pos_info(list_if, start, len(self.f.getvalue()))
  784. self.prec = p
  785. def n_generator_exp(self, node):
  786. start = len(self.f.getvalue())
  787. self.write("(")
  788. code_index = -6 if self.version >= (3, 3) else -5
  789. self.comprehension_walk(node, iter_index=4, code_index=code_index)
  790. self.write(")")
  791. self.set_pos_info(node, start, len(self.f.getvalue()))
  792. self.prune()
  793. def n_set_comp(self, node):
  794. start = len(self.f.getvalue())
  795. self.write("{")
  796. if node[0] in ["LOAD_SETCOMP", "LOAD_DICTCOMP"]:
  797. start = len(self.f.getvalue())
  798. self.set_pos_info(node[0], start - 1, start)
  799. self.comprehension_walk3(node, 1, 0)
  800. elif node[0].kind == "load_closure":
  801. self.closure_walk(node, collection_index=4)
  802. else:
  803. self.comprehension_walk(node, iter_index=4)
  804. self.write("}")
  805. self.set_pos_info(node, start, len(self.f.getvalue()))
  806. self.prune()
  807. # FIXME: Not sure if below is general. Also, add dict_comp_func.
  808. # 'set_comp_func': ("%|lambda %c: {%c for %c in %c%c}\n", 1, 3, 3, 1, 4)
  809. def n_set_comp_func(self, node):
  810. setcomp_start = len(self.f.getvalue())
  811. self.write(self.indent, "lambda ")
  812. param_node = node[1]
  813. start = len(self.f.getvalue())
  814. self.preorder(param_node)
  815. self.set_pos_info(node[0], start, len(self.f.getvalue()))
  816. self.write(": {")
  817. start = len(self.f.getvalue())
  818. assert node[0].kind.startswith("BUILD_SET")
  819. self.set_pos_info(node[0], start - 1, start)
  820. store = node[3]
  821. assert store == "store"
  822. start = len(self.f.getvalue())
  823. self.preorder(store)
  824. fin = len(self.f.getvalue())
  825. self.set_pos_info(store, start, fin)
  826. for_iter_node = node[2]
  827. assert for_iter_node.kind == "FOR_ITER"
  828. self.set_pos_info(for_iter_node, start, fin)
  829. self.write(" for ")
  830. self.preorder(store)
  831. self.write(" in ")
  832. self.preorder(param_node)
  833. start = len(self.f.getvalue())
  834. self.preorder(node[4])
  835. self.set_pos_info(node[4], start, len(self.f.getvalue()))
  836. self.write("}")
  837. fin = len(self.f.getvalue())
  838. self.set_pos_info(node, setcomp_start, fin)
  839. if node[-2] == "RETURN_VALUE":
  840. self.set_pos_info(node[-2], setcomp_start, fin)
  841. self.prune()
  842. def n_list_comp(self, node):
  843. self.write("[")
  844. if node[0].kind == "load_closure":
  845. self.listcomprehension_walk2(node)
  846. else:
  847. if node[0] == "LOAD_LISTCOMP":
  848. start = len(self.f.getvalue())
  849. self.set_pos_info(node[0], start - 1, start)
  850. self.comprehension_walk_newer(node, 1, 0)
  851. self.write("]")
  852. self.prune()
  853. def n__ifstmts_jump_exit(self, node):
  854. if len(node) > 1:
  855. if (
  856. node[0] == "c_stmts_opt"
  857. and node[0][0] == "pass"
  858. and node[1].kind.startswith("JUMP_FORWARD")
  859. ):
  860. self.set_pos_info(node[1], node[0][0].start, node[0][0].finish)
  861. def closure_walk(self, node, collection_index):
  862. """Set comprehensions the way they are done in Python3.
  863. They're more other comprehensions, e.g. set comprehensions
  864. See if we can combine code.
  865. """
  866. p = self.prec
  867. self.prec = 27
  868. code = Code(node[1].attr, self.scanner, self.currentclass)
  869. ast = self.build_ast(code._tokens, code._customize, code)
  870. self.customize(code._customize)
  871. ast = ast[0][0][0]
  872. store = ast[3]
  873. collection = node[collection_index]
  874. n = ast[4]
  875. list_if = None
  876. assert n == "comp_iter"
  877. # find inner-most node
  878. while n == "comp_iter":
  879. n = n[0] # recurse one step
  880. # FIXME: adjust for set comprehension
  881. if n == "list_for":
  882. store = n[2]
  883. n = n[3]
  884. elif n in ("list_if", "list_if_not", "comp_if", "comp_if_not"):
  885. # FIXME: just a guess
  886. if n[0].kind == "expr":
  887. list_if = n
  888. else:
  889. list_if = n[1]
  890. n = n[2]
  891. pass
  892. pass
  893. assert n == "comp_body", ast
  894. self.preorder(n[0])
  895. self.write(" for ")
  896. start = len(self.f.getvalue())
  897. self.preorder(store)
  898. self.set_pos_info(store, start, len(self.f.getvalue()))
  899. self.write(" in ")
  900. start = len(self.f.getvalue())
  901. self.preorder(collection)
  902. self.set_pos_info(collection, start, len(self.f.getvalue()))
  903. if list_if:
  904. start = len(self.f.getvalue())
  905. self.preorder(list_if)
  906. self.set_pos_info(list_if, start, len(self.f.getvalue()))
  907. self.prec = p
  908. def n_classdef(self, node):
  909. # class definition ('class X(A,B,C):')
  910. cclass = self.currentclass
  911. if self.version >= (3, 1):
  912. if node == "classdefdeco2":
  913. currentclass = node[1][2].pattr
  914. buildclass = node
  915. else:
  916. currentclass = node[1][0].pattr
  917. buildclass = node[0]
  918. if buildclass[0] == "LOAD_BUILD_CLASS":
  919. start = len(self.f.getvalue())
  920. self.set_pos_info(buildclass[0], start, start + len("class") + 2)
  921. assert "mkfunc" == buildclass[1]
  922. mkfunc = buildclass[1]
  923. if mkfunc[0] == "kwargs":
  924. for n in mkfunc:
  925. if hasattr(n, "attr") and iscode(n.attr):
  926. subclass = n.attr
  927. break
  928. pass
  929. subclass_info = node if node == "classdefdeco2" else node[0]
  930. elif buildclass[1][0] == "load_closure":
  931. # Python 3 with closures not functions
  932. load_closure = buildclass[1]
  933. if hasattr(load_closure[-3], "attr"):
  934. # Python 3.3 classes with closures work like this.
  935. # Note have to test before 3.2 case because
  936. # index -2 also has an attr.
  937. subclass = load_closure[-3].attr
  938. elif hasattr(load_closure[-2], "attr"):
  939. # Python 3.2 works like this
  940. subclass = load_closure[-2].attr
  941. else:
  942. raise RuntimeError(
  943. "Internal Error n_classdef: cannot find class body"
  944. )
  945. if hasattr(buildclass[3], "__len__"):
  946. subclass_info = buildclass[3]
  947. elif hasattr(buildclass[2], "__len__"):
  948. subclass_info = buildclass[2]
  949. else:
  950. raise RuntimeError(
  951. "Internal Error n_classdef: cannot superclass name"
  952. )
  953. else:
  954. subclass = buildclass[1][0].attr
  955. subclass_info = node[0]
  956. else:
  957. buildclass = node if (node == "classdefdeco2") else node[0]
  958. build_list = buildclass[1][0]
  959. if hasattr(buildclass[-3][0], "attr"):
  960. subclass = buildclass[-3][0].attr
  961. currentclass = buildclass[0].pattr
  962. elif hasattr(node[0][0], "pattr"):
  963. subclass = buildclass[-3][1].attr
  964. currentclass = node[0][0].pattr
  965. else:
  966. raise "Internal Error n_classdef: cannot find class name"
  967. if node == "classdefdeco2":
  968. self.write("\n")
  969. else:
  970. self.write("\n\n")
  971. self.currentclass = str(currentclass)
  972. start = len(self.f.getvalue())
  973. self.write(self.indent, "class ", self.currentclass)
  974. if self.version >= (3, 1):
  975. self.print_super_classes3(subclass_info)
  976. else:
  977. self.print_super_classes(build_list)
  978. self.println(":")
  979. # class body
  980. self.indent_more()
  981. self.build_class(subclass)
  982. self.indent_less()
  983. self.currentclass = cclass
  984. self.set_pos_info(node, start, len(self.f.getvalue()))
  985. if len(self.param_stack) > 1:
  986. self.write("\n\n")
  987. else:
  988. self.write("\n\n\n")
  989. self.prune()
  990. n_classdefdeco2 = n_classdef
  991. def gen_source(
  992. self,
  993. ast,
  994. name,
  995. customize,
  996. is_lambda=False,
  997. returnNone=False,
  998. debug_opts=DEFAULT_DEBUG_OPTS,
  999. ):
  1000. """convert parse tree to Python source code"""
  1001. rn = self.return_none
  1002. self.return_none = returnNone
  1003. old_name = self.name
  1004. self.name = name
  1005. self.debug_opts = debug_opts
  1006. # if code would be empty, append 'pass'
  1007. if len(ast) == 0:
  1008. self.println(self.indent, "pass")
  1009. else:
  1010. self.customize(customize)
  1011. self.text = self.traverse(ast, is_lambda=is_lambda)
  1012. self.name = old_name
  1013. self.return_none = rn
  1014. def build_ast(
  1015. self,
  1016. tokens,
  1017. customize,
  1018. code,
  1019. is_lambda=False,
  1020. noneInNames=False,
  1021. is_top_level_module=False,
  1022. ):
  1023. # FIXME: DRY with pysource.py
  1024. # assert isinstance(tokens[0], Token)
  1025. if is_lambda:
  1026. for t in tokens:
  1027. if t.kind == "RETURN_END_IF":
  1028. t.kind = "RETURN_END_IF_LAMBDA"
  1029. elif t.kind == "RETURN_VALUE":
  1030. t.kind = "RETURN_VALUE_LAMBDA"
  1031. tokens.append(Token("LAMBDA_MARKER"))
  1032. try:
  1033. # FIXME: have p.insts update in a better way
  1034. # modularity is broken here
  1035. p_insts = self.p.insts
  1036. self.p.insts = self.scanner.insts
  1037. self.p.offset2inst_index = self.scanner.offset2inst_index
  1038. ast = parse(self.p, tokens, customize, code)
  1039. self.customize(customize)
  1040. self.p.insts = p_insts
  1041. except (ParserError, AssertionError) as e:
  1042. raise ParserError(e, tokens)
  1043. transform_tree = self.treeTransform.transform(ast, code)
  1044. maybe_show_tree(self, ast)
  1045. return transform_tree
  1046. # The bytecode for the end of the main routine has a
  1047. # "return None". However you can't issue a "return" statement in
  1048. # main. In the other build_ast routine we eliminate the
  1049. # return statement instructions before parsing.
  1050. # But here we want to keep these instructions at the expense of
  1051. # a fully runnable Python program because we
  1052. # my be queried about the role of one of those instructions.
  1053. #
  1054. # NOTE: this differs from behavior in pysource.py
  1055. if len(tokens) >= 2 and not noneInNames:
  1056. if tokens[-1].kind in ("RETURN_VALUE", "RETURN_VALUE_LAMBDA"):
  1057. # Python 3.4's classes can add a "return None" which is
  1058. # invalid syntax.
  1059. if tokens[-2].kind == "LOAD_CONST":
  1060. if is_top_level_module or tokens[-2].pattr is None:
  1061. del tokens[-2:]
  1062. else:
  1063. tokens.append(Token("RETURN_LAST"))
  1064. else:
  1065. tokens.append(Token("RETURN_LAST"))
  1066. if len(tokens) == 0:
  1067. return PASS
  1068. # Build a parse tree from tokenized and massaged disassembly.
  1069. try:
  1070. # FIXME: have p.insts update in a better way
  1071. # modularity is broken here
  1072. p_insts = self.p.insts
  1073. self.p.insts = self.scanner.insts
  1074. self.p.offset2inst_index = self.scanner.offset2inst_index
  1075. self.p.opc = self.scanner.opc
  1076. ast = parse(self.p, tokens, customize, code)
  1077. self.p.insts = p_insts
  1078. except (ParserError, AssertionError) as e:
  1079. raise ParserError(e, tokens, {})
  1080. checker(ast, False, self.ast_errors)
  1081. self.customize(customize)
  1082. transform_tree = self.treeTransform.transform(ast, code)
  1083. maybe_show_tree(self, ast)
  1084. del ast # Save memory
  1085. return transform_tree
  1086. # FIXME: we could provide another customized routine
  1087. # that fixes up parents along a particular path to a node that
  1088. # we are interested in.
  1089. def fixup_parents(self, node, parent):
  1090. """Make sure each node has a parent"""
  1091. start, finish = 0, self.last_finish
  1092. # We assume anything with a start has a finish.
  1093. needs_range = not hasattr(node, "start")
  1094. if not hasattr(node, "parent"):
  1095. node.parent = parent
  1096. for n in node:
  1097. if needs_range and hasattr(n, "start"):
  1098. if n.start < start:
  1099. start = n.start
  1100. if n.finish > finish:
  1101. finish = n.finish
  1102. if hasattr(n, "offset") and not hasattr(n, "parent"):
  1103. n.parent = node
  1104. else:
  1105. self.fixup_parents(n, node)
  1106. pass
  1107. pass
  1108. if needs_range:
  1109. node.start, node.finish = start, finish
  1110. return
  1111. # FIXME: revise to do *once* over the entire tree.
  1112. # So here we should just mark that the subtree
  1113. # needs offset adjustment.
  1114. def fixup_offsets(self, new_start, node):
  1115. """Adjust all offsets under node"""
  1116. if hasattr(node, "start"):
  1117. node.start += new_start
  1118. node.finish += new_start
  1119. for n in node:
  1120. if hasattr(n, "offset"):
  1121. if hasattr(n, "start"):
  1122. n.start += new_start
  1123. n.finish += new_start
  1124. else:
  1125. self.fixup_offsets(new_start, n)
  1126. return
  1127. def set_pos_info_recurse(self, node, start, finish, parent=None):
  1128. """Set positions under node"""
  1129. self.set_pos_info(node, start, finish)
  1130. if parent is None:
  1131. parent = node
  1132. for n in node:
  1133. n.parent = parent
  1134. if hasattr(n, "offset"):
  1135. self.set_pos_info(n, start, finish)
  1136. else:
  1137. n.start = start
  1138. n.finish = finish
  1139. self.set_pos_info_recurse(n, start, finish, parent)
  1140. return
  1141. def node_append(self, before_str, node_text, node):
  1142. self.write(before_str)
  1143. self.last_finish = len(self.f.getvalue())
  1144. self.fixup_offsets(self.last_finish, node)
  1145. self.write(node_text)
  1146. self.last_finish = len(self.f.getvalue())
  1147. # FIXME: duplicated from pysource, since we don't find self.params
  1148. def traverse(self, node, indent=None, is_lambda=False):
  1149. """Builds up fragment which can be used inside a larger
  1150. block of code"""
  1151. self.param_stack.append(self.params)
  1152. if indent is None:
  1153. indent = self.indent
  1154. p = self.pending_newlines
  1155. self.pending_newlines = 0
  1156. self.params = {
  1157. "_globals": {},
  1158. "f": StringIO(),
  1159. "indent": indent,
  1160. "is_lambda": is_lambda,
  1161. }
  1162. self.preorder(node)
  1163. self.f.write("\n" * self.pending_newlines)
  1164. text = self.f.getvalue()
  1165. self.last_finish = len(text)
  1166. self.params = self.param_stack.pop()
  1167. self.pending_newlines = p
  1168. return text
  1169. def extract_node_info(self, nodeInfo):
  1170. # XXX debug
  1171. # print('-' * 30)
  1172. # node = nodeInfo.node
  1173. # print(node)
  1174. # if hasattr(node, 'parent'):
  1175. # print('~' * 30)
  1176. # print(node.parent)
  1177. # else:
  1178. # print("No parent")
  1179. # print('-' * 30)
  1180. start, finish = (nodeInfo.start, nodeInfo.finish)
  1181. text = self.text
  1182. # Ignore trailing blanks
  1183. match = re.search(r"\n+$", text[start:])
  1184. if match:
  1185. text = text[: -len(match.group(0))]
  1186. # Ignore leading blanks
  1187. match = re.search(r"\s*[^ \t\n]", text[start:])
  1188. if match:
  1189. start += len(match.group(0)) - 1
  1190. at_end = False
  1191. if start >= finish:
  1192. at_end = True
  1193. selectedText = text
  1194. else:
  1195. selectedText = text[start:finish]
  1196. # Compute offsets relative to the beginning of the
  1197. # line rather than the beginning of the text.
  1198. try:
  1199. lineStart = text[:start].rindex("\n") + 1
  1200. except ValueError:
  1201. lineStart = 0
  1202. adjustedStart = start - lineStart
  1203. # If selected text is greater than a single line
  1204. # just show the first line plus ellipsis.
  1205. lines = selectedText.split("\n")
  1206. if len(lines) > 1:
  1207. adjustedEnd = len(lines[0]) - adjustedStart
  1208. selectedText = lines[0] + " ...\n" + lines[-1]
  1209. else:
  1210. adjustedEnd = len(selectedText)
  1211. if at_end:
  1212. markerLine = (" " * len(lines[-1])) + "^"
  1213. else:
  1214. markerLine = (" " * adjustedStart) + ("-" * adjustedEnd)
  1215. elided = False
  1216. if len(lines) > 1 and not at_end:
  1217. elided = True
  1218. markerLine += " ..."
  1219. # Get line that the selected text is in and
  1220. # get a line count for that.
  1221. try:
  1222. lineEnd = lineStart + text[lineStart + 1 :].index("\n") - 1
  1223. except ValueError:
  1224. lineEnd = len(text)
  1225. lines = text[:lineEnd].split("\n")
  1226. selectedLine = text[lineStart : lineEnd + 2]
  1227. if elided:
  1228. selectedLine += " ..."
  1229. if isinstance(nodeInfo, Token):
  1230. nodeInfo = nodeInfo.parent
  1231. else:
  1232. nodeInfo = nodeInfo
  1233. if isinstance(nodeInfo, SyntaxTree):
  1234. nonterminal = nodeInfo[0]
  1235. else:
  1236. nonterminal = nodeInfo.node
  1237. return ExtractInfo(
  1238. lineNo=len(lines),
  1239. lineStartOffset=lineStart,
  1240. markerLine=markerLine,
  1241. selectedLine=selectedLine,
  1242. selectedText=selectedText,
  1243. nonterminal=nonterminal,
  1244. )
  1245. def extract_line_info(self, name, offset):
  1246. if (name, offset) not in list(self.offsets.keys()):
  1247. return None
  1248. return self.extract_node_info(self.offsets[name, offset])
  1249. def prev_node(self, node):
  1250. prev = None
  1251. if not hasattr(node, "parent"):
  1252. return prev
  1253. p = node.parent
  1254. for n in p:
  1255. if node == n:
  1256. return prev
  1257. prev = n
  1258. return prev
  1259. def extract_parent_info(self, node):
  1260. if not hasattr(node, "parent"):
  1261. return None, None
  1262. p = node.parent
  1263. orig_parent = p
  1264. # If we can get different text, use that as the parent,
  1265. # otherwise we'll use the immediatate parent.
  1266. while p and (
  1267. hasattr(p, "parent") and p.start == node.start and p.finish == node.finish
  1268. ):
  1269. assert p != node
  1270. node = p
  1271. p = p.parent
  1272. if p is None:
  1273. p = orig_parent
  1274. return self.extract_node_info(p), p
  1275. def print_super_classes(self, node):
  1276. if not (node == "build_list"):
  1277. return
  1278. start = len(self.f.getvalue())
  1279. self.write("(")
  1280. line_separator = ", "
  1281. sep = ""
  1282. for elem in node[:-1]:
  1283. value = self.traverse(elem)
  1284. self.node_append(sep, value, elem)
  1285. # self.write(sep, value)
  1286. sep = line_separator
  1287. self.write(")")
  1288. self.set_pos_info(node, start, len(self.f.getvalue()))
  1289. def print_super_classes3(self, node):
  1290. # FIXME: wrap superclasses onto a node
  1291. # as a custom rule
  1292. start = len(self.f.getvalue())
  1293. n = len(node) - 1
  1294. j = 0
  1295. if node.kind != "expr":
  1296. if node == "kwarg":
  1297. self.template_engine(("(%[0]{attr}=%c)", 1), node)
  1298. return
  1299. kwargs = None
  1300. assert node[n].kind.startswith("CALL_FUNCTION")
  1301. if node[n].kind.startswith("CALL_FUNCTION_KW"):
  1302. if self.is_pypy:
  1303. # FIXME: this doesn't handle positional and keyword args
  1304. # properly. Need to do something more like that below
  1305. # in the non-PYPY 3.6 case.
  1306. self.template_engine(("(%[0]{attr}=%c)", 1), node[n - 1])
  1307. return
  1308. else:
  1309. kwargs = node[n - 1].attr
  1310. assert isinstance(kwargs, tuple)
  1311. i = n - (len(kwargs) + 1)
  1312. j = 1 + n - node[n].attr
  1313. else:
  1314. i = start = n - 2
  1315. for i in range(start, 0, -1):
  1316. if not node[i].kind in ["expr", "call", "LOAD_CLASSNAME"]:
  1317. break
  1318. pass
  1319. if i == start:
  1320. return
  1321. i += 2
  1322. for i in range(n - 2, 0, -1):
  1323. if not node[i].kind in ["expr", "LOAD_CLASSNAME"]:
  1324. break
  1325. pass
  1326. line_separator = ", "
  1327. sep = ""
  1328. i += 1
  1329. self.write("(")
  1330. if kwargs:
  1331. # Last arg is tuple of keyword values: omit
  1332. m = n - 1
  1333. else:
  1334. m = n
  1335. if kwargs:
  1336. # 3.6+ does this
  1337. while j < i:
  1338. self.write(sep)
  1339. value = self.traverse(node[j])
  1340. self.write("%s" % value)
  1341. sep = line_separator
  1342. j += 1
  1343. j = 0
  1344. while i < m:
  1345. self.write(sep)
  1346. value = self.traverse(node[i])
  1347. self.write("%s=%s" % (kwargs[j], value))
  1348. sep = line_separator
  1349. j += 1
  1350. i += 1
  1351. else:
  1352. while i < m:
  1353. value = self.traverse(node[i])
  1354. i += 1
  1355. self.write(sep, value)
  1356. sep = line_separator
  1357. pass
  1358. pass
  1359. else:
  1360. if self.version >= (3, 6) and node[0] == "LOAD_CONST":
  1361. return
  1362. value = self.traverse(node[0])
  1363. self.write("(")
  1364. self.write(value)
  1365. pass
  1366. self.write(")")
  1367. self.set_pos_info(node, start, len(self.f.getvalue()))
  1368. def n_dict(self, node):
  1369. """
  1370. prettyprint a dict
  1371. 'dict' is something like k = {'a': 1, 'b': 42 }"
  1372. """
  1373. p = self.prec
  1374. self.prec = 100
  1375. self.indent_more(INDENT_PER_LEVEL)
  1376. line_seperator = ",\n" + self.indent
  1377. sep = INDENT_PER_LEVEL[:-1]
  1378. start = len(self.f.getvalue())
  1379. if node[0] != "dict_entry":
  1380. self.write("{")
  1381. self.set_pos_info(node[0], start, start + 1)
  1382. if self.version >= (3, 0) and not self.is_pypy:
  1383. if node[0].kind.startswith("kvlist"):
  1384. # Python 3.5+ style key/value list in dict
  1385. kv_node = node[0]
  1386. ll = list(kv_node)
  1387. length = len(ll)
  1388. if kv_node[-1].kind.startswith("BUILD_MAP"):
  1389. length -= 1
  1390. i = 0
  1391. while i < length:
  1392. self.write(sep)
  1393. name = self.traverse(ll[i], indent="")
  1394. ll[i].parent = kv_node
  1395. ll[i + 1].parent = kv_node
  1396. self.write(name, ": ")
  1397. value = self.traverse(
  1398. ll[i + 1], indent=self.indent + (len(name) + 2) * " "
  1399. )
  1400. self.write(sep, name, ": ", value)
  1401. sep = line_seperator
  1402. i += 2
  1403. pass
  1404. pass
  1405. elif len(node) > 1 and node[1].kind.startswith("kvlist"):
  1406. # Python 3.0..3.4 style key/value list in dict
  1407. kv_node = node[1]
  1408. ll = list(kv_node)
  1409. if len(ll) > 0 and ll[0].kind == "kv3":
  1410. # Python 3.2 does this
  1411. kv_node = node[1][0]
  1412. ll = list(kv_node)
  1413. i = 0
  1414. while i < len(ll):
  1415. ll[i].parent = kv_node
  1416. ll[i + 1].parent = kv_node
  1417. key_start = len(self.f.getvalue()) + len(sep)
  1418. name = self.traverse(ll[i + 1], indent="")
  1419. key_finish = key_start + len(name)
  1420. val_start = key_finish + 2
  1421. value = self.traverse(
  1422. ll[i], indent=self.indent + (len(name) + 2) * " "
  1423. )
  1424. self.write(sep, name, ": ", value)
  1425. self.set_pos_info_recurse(ll[i + 1], key_start, key_finish)
  1426. self.set_pos_info_recurse(ll[i], val_start, val_start + len(value))
  1427. sep = line_seperator
  1428. i += 3
  1429. pass
  1430. pass
  1431. elif node[-1].kind.startswith("BUILD_CONST_KEY_MAP"):
  1432. # Python 3.6+ style const map
  1433. keys = node[-2].pattr
  1434. values = node[:-2]
  1435. # FIXME: Line numbers?
  1436. for key, value in zip(keys, values):
  1437. self.write(sep)
  1438. self.write(repr(key))
  1439. line_number = self.line_number
  1440. self.write(":")
  1441. self.write(self.traverse(value[0]))
  1442. sep = ", "
  1443. if line_number != self.line_number:
  1444. sep += "\n" + self.indent + INDENT_PER_LEVEL[:-1]
  1445. line_number = self.line_number
  1446. else:
  1447. sep += " "
  1448. pass
  1449. pass
  1450. if sep.startswith(",\n"):
  1451. self.write(sep[1:])
  1452. pass
  1453. elif node[0].kind.startswith("dict_entry"):
  1454. assert self.version >= (3, 5)
  1455. template = ("%C", (0, len(node[0]), ", **"))
  1456. self.template_engine(template, node[0])
  1457. sep = ""
  1458. elif node[-1].kind.startswith("BUILD_MAP_UNPACK") or node[
  1459. -1
  1460. ].kind.startswith("dict_entry"):
  1461. assert self.version >= (3, 5)
  1462. # FIXME: I think we can intermingle dict_comp's with other
  1463. # dictionary kinds of things. The most common though is
  1464. # a sequence of dict_comp's
  1465. kwargs = node[-1].attr
  1466. template = ("**%C", (0, kwargs, ", **"))
  1467. self.template_engine(template, node)
  1468. sep = ""
  1469. pass
  1470. else:
  1471. # Python 2 style kvlist. Find beginning of kvlist.
  1472. indent = self.indent + " "
  1473. line_number = self.line_number
  1474. if node[0].kind.startswith("BUILD_MAP"):
  1475. if len(node) > 1 and node[1].kind in ("kvlist", "kvlist_n"):
  1476. kv_node = node[1]
  1477. else:
  1478. kv_node = node[1:]
  1479. self.kv_map(kv_node, sep, line_number, indent)
  1480. else:
  1481. sep = ""
  1482. opname = node[-1].kind
  1483. if self.is_pypy and self.version >= (3, 5):
  1484. if opname.startswith("BUILD_CONST_KEY_MAP"):
  1485. keys = node[-2].attr
  1486. # FIXME: DRY this and the above
  1487. for i in range(len(keys)):
  1488. key = keys[i]
  1489. value = self.traverse(node[i], indent="")
  1490. self.write(sep, key, ": ", value)
  1491. sep = ", "
  1492. if line_number != self.line_number:
  1493. sep += "\n" + self.indent + " "
  1494. line_number = self.line_number
  1495. pass
  1496. pass
  1497. pass
  1498. else:
  1499. if opname.startswith("kvlist"):
  1500. list_node = node[0]
  1501. else:
  1502. list_node = node
  1503. assert list_node[-1].kind.startswith("BUILD_MAP")
  1504. for i in range(0, len(list_node) - 1, 2):
  1505. key = self.traverse(list_node[i], indent="")
  1506. value = self.traverse(list_node[i + 1], indent="")
  1507. self.write(sep, key, ": ", value)
  1508. sep = ", "
  1509. if line_number != self.line_number:
  1510. sep += "\n" + self.indent + " "
  1511. line_number = self.line_number
  1512. pass
  1513. pass
  1514. pass
  1515. elif opname.startswith("kvlist"):
  1516. kv_node = node[-1]
  1517. self.kv_map(node[-1], sep, line_number, indent)
  1518. pass
  1519. self.write("}")
  1520. finish = len(self.f.getvalue())
  1521. self.set_pos_info(node, start, finish)
  1522. self.indent_less(INDENT_PER_LEVEL)
  1523. self.prec = p
  1524. self.prune()
  1525. def n_list(self, node):
  1526. """
  1527. prettyprint a list or tuple
  1528. """
  1529. p = self.prec
  1530. self.prec = PRECEDENCE["yield"] - 1
  1531. n = node.pop()
  1532. lastnode = n.kind
  1533. start = len(self.f.getvalue())
  1534. if lastnode.startswith("BUILD_LIST"):
  1535. self.write("[")
  1536. endchar = "]"
  1537. elif lastnode.startswith("BUILD_TUPLE"):
  1538. self.write("(")
  1539. endchar = ")"
  1540. elif lastnode.startswith("BUILD_SET"):
  1541. self.write("{")
  1542. endchar = "}"
  1543. elif lastnode.startswith("ROT_TWO"):
  1544. self.write("(")
  1545. endchar = ")"
  1546. else:
  1547. raise RuntimeError("Internal Error: n_list expects list or tuple")
  1548. flat_elems = []
  1549. for elem in node:
  1550. if elem == "expr1024":
  1551. for subelem in elem:
  1552. for subsubelem in subelem:
  1553. flat_elems.append(subsubelem)
  1554. elif elem == "expr32":
  1555. for subelem in elem:
  1556. flat_elems.append(subelem)
  1557. else:
  1558. flat_elems.append(elem)
  1559. self.indent_more(INDENT_PER_LEVEL)
  1560. if len(node) > 3:
  1561. line_separator = ",\n" + self.indent
  1562. else:
  1563. line_separator = ", "
  1564. sep = INDENT_PER_LEVEL[:-1]
  1565. # FIXME:
  1566. # if flat_elems > some_number, then group
  1567. # do automatic wrapping
  1568. for elem in flat_elems:
  1569. if elem == "ROT_THREE":
  1570. continue
  1571. assert elem == "expr"
  1572. value = self.traverse(elem)
  1573. self.node_append(sep, value, elem)
  1574. sep = line_separator
  1575. if len(node) == 1 and lastnode.startswith("BUILD_TUPLE"):
  1576. self.write(",")
  1577. self.write(endchar)
  1578. finish = len(self.f.getvalue())
  1579. n.parent = node.parent
  1580. self.set_pos_info(n, start, finish)
  1581. self.set_pos_info(node, start, finish)
  1582. self.indent_less(INDENT_PER_LEVEL)
  1583. self.prec = p
  1584. self.prune()
  1585. return
  1586. n_set = n_tuple = n_build_set = n_list
  1587. def template_engine(self, entry, startnode):
  1588. """The format template interpretation engine. See the comment at the
  1589. beginning of this module for how we interpret format
  1590. specifications such as %c, %C, and so on.
  1591. """
  1592. # print("-----")
  1593. # print(startnode.kind)
  1594. # print(entry[0])
  1595. # print('======')
  1596. startnode_start = len(self.f.getvalue())
  1597. start = startnode_start
  1598. fmt = entry[0]
  1599. arg = 1
  1600. i = 0
  1601. lastC = -1
  1602. recurse_node = False
  1603. m = escape.search(fmt)
  1604. while m:
  1605. i = m.end()
  1606. self.write(m.group("prefix"))
  1607. typ = m.group("type") or "{"
  1608. node = startnode
  1609. try:
  1610. if m.group("child"):
  1611. node = node[int(m.group("child"))]
  1612. node.parent = startnode
  1613. except Exception:
  1614. print(node.__dict__)
  1615. raise
  1616. if typ == "%":
  1617. start = len(self.f.getvalue())
  1618. self.write("%")
  1619. self.set_pos_info(node, start, len(self.f.getvalue()))
  1620. elif typ == "+":
  1621. self.indent_more()
  1622. elif typ == "-":
  1623. self.indent_less()
  1624. elif typ == "|":
  1625. self.write(self.indent)
  1626. # no longer used, since BUILD_TUPLE_n is pretty printed:
  1627. elif typ == "r":
  1628. recurse_node = True
  1629. elif typ == ",":
  1630. if lastC == 1:
  1631. self.write(",")
  1632. elif typ == "b":
  1633. finish = len(self.f.getvalue())
  1634. self.set_pos_info(node[entry[arg]], start, finish)
  1635. arg += 1
  1636. elif typ == "c":
  1637. start = len(self.f.getvalue())
  1638. index = entry[arg]
  1639. if isinstance(index, tuple):
  1640. if isinstance(index[1], str):
  1641. # if node[index[0]] != index[1]:
  1642. # from trepan.api import debug; debug()
  1643. assert (
  1644. node[index[0]] == index[1]
  1645. ), "at %s[%d], expected '%s' node; got '%s'" % (
  1646. node.kind,
  1647. arg,
  1648. index[1],
  1649. node[index[0]].kind,
  1650. )
  1651. else:
  1652. assert (
  1653. node[index[0]] in index[1]
  1654. ), "at %s[%d], expected to be in '%s' node; got '%s'" % (
  1655. node.kind,
  1656. arg,
  1657. index[1],
  1658. node[index[0]].kind,
  1659. )
  1660. index = index[0]
  1661. assert isinstance(
  1662. index, int
  1663. ), "at %s[%d], %s should be int or tuple" % (
  1664. node.kind,
  1665. arg,
  1666. type(index),
  1667. )
  1668. self.preorder(node[index])
  1669. finish = len(self.f.getvalue())
  1670. self.set_pos_info(node, start, finish)
  1671. arg += 1
  1672. elif typ == "p":
  1673. p = self.prec
  1674. tup = entry[arg]
  1675. assert isinstance(tup, tuple)
  1676. if len(tup) == 3:
  1677. (index, nonterm_name, self.prec) = tup
  1678. if isinstance(tup[1], str):
  1679. assert (
  1680. node[index] == nonterm_name
  1681. ), "at %s[%d], expected '%s' node; got '%s'" % (
  1682. node.kind,
  1683. arg,
  1684. nonterm_name,
  1685. node[index].kind,
  1686. )
  1687. else:
  1688. assert node[tup[0]] in tup[1], (
  1689. f"at {node.kind}[{tup[0]}], expected to be in '{tup[1]}' "
  1690. f"node; got '{node[tup[0]].kind}'"
  1691. )
  1692. else:
  1693. assert len(tup) == 2
  1694. (index, self.prec) = entry[arg]
  1695. node[index].parent = node
  1696. start = len(self.f.getvalue())
  1697. self.preorder(node[index])
  1698. self.set_pos_info(node, start, len(self.f.getvalue()))
  1699. self.prec = p
  1700. arg += 1
  1701. elif typ == "C":
  1702. low, high, sep = entry[arg]
  1703. lastC = remaining = len(node[low:high])
  1704. start = len(self.f.getvalue())
  1705. for subnode in node[low:high]:
  1706. self.preorder(subnode)
  1707. remaining -= 1
  1708. if remaining > 0:
  1709. self.write(sep)
  1710. self.set_pos_info(node, start, len(self.f.getvalue()))
  1711. arg += 1
  1712. elif typ == "D":
  1713. low, high, sep = entry[arg]
  1714. lastC = remaining = len(node[low:high])
  1715. for subnode in node[low:high]:
  1716. remaining -= 1
  1717. if len(subnode) > 0:
  1718. self.preorder(subnode)
  1719. if remaining > 0:
  1720. self.write(sep)
  1721. pass
  1722. pass
  1723. pass
  1724. arg += 1
  1725. elif typ == "x":
  1726. src, dest = entry[arg]
  1727. for d in dest:
  1728. self.set_pos_info_recurse(
  1729. node[d], node[src].start, node[src].finish
  1730. )
  1731. pass
  1732. arg += 1
  1733. elif typ == "P":
  1734. p = self.prec
  1735. low, high, sep, self.prec = entry[arg]
  1736. lastC = remaining = len(node[low:high])
  1737. start = self.last_finish
  1738. for subnode in node[low:high]:
  1739. self.preorder(subnode)
  1740. remaining -= 1
  1741. if remaining > 0:
  1742. self.write(sep)
  1743. self.prec = p
  1744. arg += 1
  1745. elif typ == "{":
  1746. d = node.__dict__
  1747. expr = m.group("expr")
  1748. # Line mapping stuff
  1749. if (
  1750. hasattr(node, "linestart")
  1751. and node.linestart
  1752. and hasattr(node, "current_line_number")
  1753. ):
  1754. self.source_linemap[self.current_line_number] = node.linestart
  1755. # Additional fragment-position stuff
  1756. try:
  1757. start = len(self.f.getvalue())
  1758. self.write(eval(expr, d, d))
  1759. self.set_pos_info(node, start, len(self.f.getvalue()))
  1760. except Exception:
  1761. print(node)
  1762. raise
  1763. m = escape.search(fmt, i)
  1764. pass
  1765. self.write(fmt[i:])
  1766. fin = len(self.f.getvalue())
  1767. if recurse_node:
  1768. self.set_pos_info_recurse(startnode, startnode_start, fin)
  1769. else:
  1770. self.set_pos_info(startnode, startnode_start, fin)
  1771. # FIXME figure out how to get these cases to be table driven.
  1772. # 2. subroutine calls. It the last op is the call and for purposes of printing
  1773. # we don't need to print anything special there. However it encompasses the
  1774. # entire string of the node fn(...)
  1775. if startnode.kind == "call":
  1776. last_node = startnode[-1]
  1777. self.set_pos_info(last_node, startnode_start, self.last_finish)
  1778. return
  1779. def _get_mapping(self, node):
  1780. if (
  1781. hasattr(node, "data")
  1782. and len(node) > 0
  1783. and isinstance(node[-1], Token)
  1784. and not hasattr(node[-1], "parent")
  1785. ):
  1786. node[-1].parent = node
  1787. return self.MAP.get(node, self.MAP_DIRECT_FRAGMENT)
  1788. pass
  1789. #
  1790. DEFAULT_DEBUG_OPTS = {"asm": False, "tree": False, "grammar": False}
  1791. # This interface is deprecated
  1792. def deparse_code(
  1793. version,
  1794. co,
  1795. out=StringIO(),
  1796. showasm=False,
  1797. showast=False,
  1798. showgrammar=False,
  1799. code_objects={},
  1800. compile_mode="exec",
  1801. is_pypy=IS_PYPY,
  1802. walker=FragmentsWalker,
  1803. ):
  1804. debug_opts = {"asm": showasm, "ast": showast, "grammar": showgrammar}
  1805. return code_deparse(
  1806. co,
  1807. out,
  1808. version=version,
  1809. debug_opts=debug_opts,
  1810. code_objects=code_objects,
  1811. compile_mode=compile_mode,
  1812. is_pypy=is_pypy,
  1813. walker=walker,
  1814. )
  1815. def code_deparse(
  1816. co,
  1817. out=StringIO(),
  1818. version=None,
  1819. is_pypy=IS_PYPY,
  1820. debug_opts=DEFAULT_DEBUG_OPTS,
  1821. code_objects={},
  1822. compile_mode="exec",
  1823. walker=FragmentsWalker,
  1824. start_offset: int = 0,
  1825. stop_offset: int = -1,
  1826. ):
  1827. """
  1828. Convert the code object co into a python source fragment.
  1829. :param version: The python version this code is from as a float, for
  1830. example 2.6, 2.7, 3.2, 3.3, 3.4, 3.5 etc.
  1831. :param co: The code object to parse.
  1832. :param out: File like object to write the output to.
  1833. :param debug_opts: A dictionary with keys
  1834. 'asm': value determines whether to show
  1835. mangled bytecode disassembly
  1836. 'ast': value determines whether to show
  1837. 'grammar': boolean determining whether to show
  1838. grammar reduction rules.
  1839. If value is a file-like object, output that object's write method will
  1840. be used rather than sys.stdout
  1841. :return: The deparsed source fragment.
  1842. """
  1843. assert iscode(co)
  1844. if version is None:
  1845. version = PYTHON_VERSION_TRIPLE
  1846. if is_pypy is None:
  1847. is_pypy = IS_PYPY
  1848. # store final output stream for case of error
  1849. scanner = get_scanner(version, is_pypy=is_pypy, show_asm=debug_opts["asm"])
  1850. show_asm = debug_opts.get("asm", None)
  1851. tokens, customize = scanner.ingest(co, code_objects=code_objects, show_asm=show_asm)
  1852. tokens, customize = scanner.ingest(co)
  1853. if start_offset > 0:
  1854. for i, t in enumerate(tokens):
  1855. # If t.offset is a string, we want to skip this.
  1856. if isinstance(t.offset, int) and t.offset >= start_offset:
  1857. tokens = tokens[i:]
  1858. break
  1859. if stop_offset > -1:
  1860. for i, t in enumerate(tokens):
  1861. # In contrast to the test for start_offset If t.offset is
  1862. # a string, we want to extract the integer offset value.
  1863. if t.off2int() >= stop_offset:
  1864. tokens = tokens[:i]
  1865. break
  1866. maybe_show_asm(show_asm, tokens)
  1867. debug_parser = dict(PARSER_DEFAULT_DEBUG)
  1868. show_grammar = debug_opts.get("grammar", None)
  1869. if show_grammar:
  1870. debug_parser["reduce"] = show_grammar
  1871. debug_parser["errorstack"] = True
  1872. # Build Syntax Tree from tokenized and massaged disassembly.
  1873. # deparsed = FragmentsWalker(out, scanner, showast=showast)
  1874. show_tree = debug_opts.get("tree", False)
  1875. linestarts = dict(scanner.opc.findlinestarts(co))
  1876. deparsed = walker(
  1877. version,
  1878. scanner,
  1879. showast=show_tree,
  1880. debug_parser=debug_parser,
  1881. compile_mode=compile_mode,
  1882. is_pypy=is_pypy,
  1883. linestarts=linestarts,
  1884. )
  1885. is_top_level_module = co.co_name == "<module>"
  1886. deparsed.ast = deparsed.build_ast(
  1887. tokens, customize, co, is_top_level_module=is_top_level_module
  1888. )
  1889. assert deparsed.ast == "stmts", "Should have parsed grammar start"
  1890. # save memory
  1891. del tokens
  1892. # convert leading '__doc__ = "..." into doc string
  1893. assert deparsed.ast == "stmts"
  1894. (deparsed.mod_globs, _) = find_globals_and_nonlocals(
  1895. deparsed.ast, set(), set(), co, version
  1896. )
  1897. # Just when you think we've forgotten about what we
  1898. # were supposed to do: Generate source from the Syntax tree!
  1899. deparsed.gen_source(deparsed.ast, co.co_name, customize)
  1900. deparsed.set_pos_info(deparsed.ast, 0, len(deparsed.text))
  1901. deparsed.fixup_parents(deparsed.ast, None)
  1902. for g in sorted(deparsed.mod_globs):
  1903. deparsed.write("# global %s ## Warning: Unused global\n" % g)
  1904. if deparsed.ast_errors:
  1905. deparsed.write("# NOTE: have decompilation errors.\n")
  1906. deparsed.write("# Use -t option to show full context.")
  1907. for err in deparsed.ast_errors:
  1908. deparsed.write(err)
  1909. deparsed.ERROR = True
  1910. if deparsed.ERROR:
  1911. raise deparsed.ERROR
  1912. # To keep the API consistent with previous releases, convert
  1913. # deparse.offset values into NodeInfo items
  1914. for tup, node in deparsed.offsets.items():
  1915. deparsed.offsets[tup] = NodeInfo(
  1916. node=node, start=node.start, finish=node.finish
  1917. )
  1918. deparsed.scanner = scanner
  1919. return deparsed
  1920. def find_gt(a, x):
  1921. "Find leftmost value greater than x"
  1922. i = bisect_right(a, x)
  1923. if i != len(a):
  1924. return a[i]
  1925. raise ValueError
  1926. def code_deparse_around_offset(
  1927. name,
  1928. offset,
  1929. co,
  1930. out=StringIO(),
  1931. version: Optional[tuple] = None,
  1932. is_pypy: bool = False,
  1933. debug_opts=DEFAULT_DEBUG_OPTS,
  1934. ):
  1935. """
  1936. Like deparse_code(), but given a function/module name and
  1937. offset, finds the node closest to offset. If offset is not an instruction boundary,
  1938. we raise an IndexError.
  1939. """
  1940. assert iscode(co)
  1941. if version is None:
  1942. version = PYTHON_VERSION_TRIPLE
  1943. if is_pypy is None:
  1944. is_pypy = IS_PYPY
  1945. deparsed = code_deparse(co, out, version, is_pypy, debug_opts)
  1946. if (name, offset) in deparsed.offsets.keys():
  1947. # This is the easy case
  1948. return deparsed
  1949. valid_offsets = [t for t in deparsed.offsets if isinstance(t[1], int)]
  1950. offset_list = sorted([t[1] for t in valid_offsets if t[0] == name])
  1951. # FIXME: should check for branching?
  1952. found_offset = find_gt(offset_list, offset)
  1953. deparsed.offsets[name, offset] = deparsed.offsets[name, found_offset]
  1954. return deparsed
  1955. # Deprecated. Here still for compatibility
  1956. def deparse_code_around_offset(
  1957. name,
  1958. offset,
  1959. version,
  1960. co,
  1961. out=StringIO(),
  1962. showasm=False,
  1963. showast=False,
  1964. showgrammar=PARSER_DEFAULT_DEBUG,
  1965. is_pypy=False,
  1966. ):
  1967. debug_opts = {"asm": showasm, "ast": showast, "grammar": showgrammar}
  1968. return code_deparse(name, offset, co, out, version, is_pypy, debug_opts)
  1969. def op_at_code_loc(code, loc, opc):
  1970. """Return the instruction name at code[loc] using
  1971. opc to look up instruction names. Returns 'got IndexError'
  1972. if code[loc] is invalid.
  1973. `code` is instruction bytecode, `loc` is an offset (integer) and
  1974. `opc` is an opcode module from `xdis`.
  1975. """
  1976. try:
  1977. op = code[loc]
  1978. except IndexError:
  1979. return "got IndexError"
  1980. return opc.opname[op]
  1981. def deparsed_find(tup, deparsed, code):
  1982. """Return a NodeInfo nametuple for a fragment-deparsed `deparsed` at `tup`.
  1983. `tup` is a name and offset tuple, `deparsed` is a fragment object
  1984. and `code` is instruction bytecode."""
  1985. nodeInfo = None
  1986. name, last_i = tup
  1987. if not hasattr(deparsed, "offsets"):
  1988. return None
  1989. if (name, last_i) in deparsed.offsets.keys():
  1990. nodeInfo = deparsed.offsets[name, last_i]
  1991. else:
  1992. from uncompyle6.scanner import get_scanner
  1993. scanner = get_scanner(deparsed.version)
  1994. co = code.co_code
  1995. if op_at_code_loc(co, last_i, scanner.opc) == "DUP_TOP":
  1996. offset = deparsed.scanner.next_offset(co[last_i], last_i)
  1997. if (name, offset) in deparsed.offsets:
  1998. nodeInfo = deparsed.offsets[name, offset]
  1999. return nodeInfo
  2000. # if __name__ == "__main__":
  2001. # def deparse_test(co, is_pypy=IS_PYPY):
  2002. # deparsed = code_deparse(co, is_pypy=IS_PYPY)
  2003. # print("deparsed source")
  2004. # print(deparsed.text, "\n")
  2005. # print("------------------------")
  2006. # for name, offset in sorted(deparsed.offsets.keys(), key=lambda x: str(x[0])):
  2007. # print("name %s, offset %s" % (name, offset))
  2008. # nodeInfo = deparsed.offsets[name, offset]
  2009. # nodeInfo2 = deparsed_find((name, offset), deparsed, co)
  2010. # assert nodeInfo == nodeInfo2
  2011. # node = nodeInfo.node
  2012. # extractInfo = deparsed.extract_node_info(node)
  2013. # print("code: %s" % node.kind)
  2014. # # print extractInfo
  2015. # print(extractInfo.selectedText)
  2016. # print(extractInfo.selectedLine)
  2017. # print(extractInfo.markerLine)
  2018. # extractInfo, p = deparsed.extract_parent_info(node)
  2019. # if extractInfo:
  2020. # print("Contained in...")
  2021. # print(extractInfo.selectedLine)
  2022. # print(extractInfo.markerLine)
  2023. # print("code: %s" % p.kind)
  2024. # print("=" * 40)
  2025. # pass
  2026. # pass
  2027. # return
  2028. # def deparse_test_around(offset, name, co, is_pypy=IS_PYPY):
  2029. # deparsed = code_deparse_around_offset(name, offset, co)
  2030. # print("deparsed source")
  2031. # print(deparsed.text, "\n")
  2032. # print("------------------------")
  2033. # for name, offset in sorted(deparsed.offsets.keys(), key=lambda x: str(x[0])):
  2034. # print("name %s, offset %s" % (name, offset))
  2035. # nodeInfo = deparsed.offsets[name, offset]
  2036. # node = nodeInfo.node
  2037. # extractInfo = deparsed.extract_node_info(node)
  2038. # print("code: %s" % node.kind)
  2039. # # print extractInfo
  2040. # print(extractInfo.selectedText)
  2041. # print(extractInfo.selectedLine)
  2042. # print(extractInfo.markerLine)
  2043. # extractInfo, p = deparsed.extract_parent_info(node)
  2044. # if extractInfo:
  2045. # print("Contained in...")
  2046. # print(extractInfo.selectedLine)
  2047. # print(extractInfo.markerLine)
  2048. # print("code: %s" % p.kind)
  2049. # print("=" * 40)
  2050. # pass
  2051. # pass
  2052. # return
  2053. # def get_code_for_fn(fn):
  2054. # return fn.__code__
  2055. # def test():
  2056. # import os, sys
  2057. # def get_dups(li: list) -> set:
  2058. # dups = {}
  2059. # for item in li:
  2060. # dups[item] = dups.get(item, -1) + 1
  2061. # # return dups
  2062. # return {k for k in dups.keys() if dups[k] > 0}
  2063. # def div_test(a, b, c):
  2064. # return a / b / c
  2065. # def gcd(a, b):
  2066. # if a > b:
  2067. # (a, b) = (b, a)
  2068. # pass
  2069. # if a <= 0:
  2070. # return None
  2071. # if a == 1 or a == b:
  2072. # return a
  2073. # return gcd(b - a, a)
  2074. # # check_args(['3', '5'])
  2075. # # deparse_test(get_code_for_fn(gcd))
  2076. # deparse_test(get_code_for_fn(get_dups))
  2077. # # deparse_test(get_code_for_fn(test))
  2078. # # deparse_test(get_code_for_fn(FragmentsWalker.fixup_offsets))
  2079. # # deparse_test(get_code_for_fn(FragmentsWalker.n_list))
  2080. # print("=" * 30)
  2081. # # deparse_test_around(408, 'n_list', get_code_for_fn(FragmentsWalker.n_build_list))
  2082. # # deparse_test(inspect.currentframe().f_code)