gencomp.py 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742
  1. # Copyright (c) 2022 by Rocky Bernstein
  2. #
  3. # This program is free software: you can redistribute it and/or modify
  4. # it under the terms of the GNU General Public License as published by
  5. # the Free Software Foundation, either version 3 of the License, or
  6. # (at your option) any later version.
  7. #
  8. # This program is distributed in the hope that it will be useful,
  9. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. # GNU General Public License for more details.
  12. #
  13. # You should have received a copy of the GNU General Public License
  14. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  15. """
  16. Generators and comprehension functions
  17. """
  18. from typing import Optional
  19. from xdis import co_flags_is_async, iscode
  20. from uncompyle6.parser import get_python_parser
  21. from uncompyle6.scanner import Code
  22. from uncompyle6.scanners.tok import Token
  23. from uncompyle6.semantics.consts import PRECEDENCE
  24. from uncompyle6.semantics.helper import is_lambda_mode
  25. class ComprehensionMixin:
  26. """
  27. These functions hand nonterminal common actions that occur
  28. when encountering a generator or some sort of comprehension.
  29. What is common about these is that often the nonterminal has
  30. a code object whose decompilation needs to be melded into the resulting
  31. Python source code. In source code, the implicit function calls
  32. are not seen.
  33. """
  34. def closure_walk(self, node, collection_index):
  35. """
  36. Dictionary and comprehensions using closure the way they are done in Python3.
  37. """
  38. p = self.prec
  39. self.prec = PRECEDENCE["lambda_body"] - 1
  40. code_index = 0 if node[0] == "load_genexpr" else 1
  41. tree = self.get_comprehension_function(node, code_index=code_index)
  42. # Remove single reductions as in ("stmts", "sstmt"):
  43. while len(tree) == 1:
  44. tree = tree[0]
  45. store = tree[3]
  46. collection = node[collection_index]
  47. iter_index = 3 if tree == "genexpr_func_async" else 4
  48. n = tree[iter_index]
  49. list_if = None
  50. assert n == "comp_iter"
  51. # Pick out important parts of the comprehension:
  52. # * the variables we iterate over: "stores"
  53. # * the results we accumulate: "n"
  54. # Find inner-most node.
  55. while n == "comp_iter":
  56. n = n[0] # recurse one step
  57. # FIXME: adjust for set comprehension
  58. if n == "list_for":
  59. store = n[2]
  60. n = n[3]
  61. elif n in ("list_if", "list_if_not", "comp_if", "comp_if_not"):
  62. # FIXME: just a guess
  63. if n[0].kind == "expr":
  64. list_if = n
  65. else:
  66. list_if = n[1]
  67. n = n[-1]
  68. pass
  69. elif n == "list_if37":
  70. list_if.append(n)
  71. n = n[-1]
  72. pass
  73. pass
  74. assert n == "comp_body", tree
  75. self.preorder(n[0])
  76. self.write(" for ")
  77. self.preorder(store)
  78. self.write(" in ")
  79. self.preorder(collection)
  80. if list_if:
  81. self.preorder(list_if)
  82. self.prec = p
  83. def comprehension_walk(
  84. self,
  85. node,
  86. iter_index: Optional[int],
  87. code_index: int = -5,
  88. ):
  89. p: int = self.prec
  90. self.prec = PRECEDENCE["lambda_body"] - 1
  91. # FIXME: clean this up
  92. if self.version >= (3, 0) and node == "dict_comp":
  93. cn = node[1]
  94. elif self.version <= (2, 7) and node == "generator_exp":
  95. if node[0] == "LOAD_GENEXPR":
  96. cn = node[0]
  97. elif node[0] == "load_closure":
  98. cn = node[1]
  99. elif self.version >= (3, 0) and node in (
  100. "generator_exp",
  101. "generator_exp_async",
  102. ):
  103. if node[0] == "load_genexpr":
  104. load_genexpr = node[0]
  105. elif node[1] == "load_genexpr":
  106. load_genexpr = node[1]
  107. cn = load_genexpr[0]
  108. elif hasattr(node[code_index], "attr"):
  109. # Python 2.5+ (and earlier?) does this
  110. cn = node[code_index]
  111. else:
  112. if len(node[1]) > 1 and hasattr(node[1][1], "attr"):
  113. # Python 3.3+ does this
  114. cn = node[1][1]
  115. elif hasattr(node[1][0], "attr"):
  116. # Python 3.2 does this
  117. cn = node[1][0]
  118. else:
  119. assert False, "Can't find code for comprehension"
  120. assert iscode(cn.attr)
  121. code = Code(cn.attr, self.scanner, self.currentclass, self.debug_opts["asm"])
  122. # FIXME: is there a way we can avoid this?
  123. # The problem is that in filter in top-level list comprehensions we can
  124. # encounter comprehensions of other kinds, and lambdas
  125. if is_lambda_mode(self.compile_mode):
  126. p_save = self.p
  127. self.p = get_python_parser(
  128. self.version,
  129. compile_mode="exec",
  130. is_pypy=self.is_pypy,
  131. )
  132. tree = self.build_ast(code._tokens, code._customize, code)
  133. self.p = p_save
  134. else:
  135. tree = self.build_ast(code._tokens, code._customize, code)
  136. self.customize(code._customize)
  137. # Remove single reductions as in ("stmts", "sstmt"):
  138. while len(tree) == 1:
  139. tree = tree[0]
  140. if tree == "stmts":
  141. # FIXME: rest is a return None?
  142. # Verify this
  143. # rest = tree[1:]
  144. tree = tree[0]
  145. elif tree == "lambda_start":
  146. assert len(tree) <= 3
  147. tree = tree[-2]
  148. if tree == "return_expr_lambda":
  149. tree = tree[1]
  150. pass
  151. if tree in (
  152. "genexpr_func",
  153. "genexpr_func_async",
  154. ):
  155. for i in range(3, 5):
  156. if tree[i] == "comp_iter":
  157. iter_index = i
  158. break
  159. n = tree[iter_index]
  160. assert n == "comp_iter", n.kind
  161. # Find the comprehension body. It is the inner-most
  162. # node that is not list_.. .
  163. while n == "comp_iter": # list_iter
  164. n = n[0] # recurse one step
  165. if n == "comp_for":
  166. if n[0] == "SETUP_LOOP":
  167. n = n[4]
  168. else:
  169. n = n[3]
  170. elif n == "comp_if":
  171. n = n[2]
  172. elif n == "comp_if_not":
  173. n = n[2]
  174. assert n == "comp_body", n
  175. self.preorder(n[0])
  176. if node == "generator_exp_async":
  177. self.write(" async")
  178. iter_var_index = iter_index - 2
  179. else:
  180. iter_var_index = iter_index - 1
  181. self.write(" for ")
  182. self.preorder(tree[iter_var_index])
  183. self.write(" in ")
  184. if node[2] == "expr":
  185. iter_expr = node[2]
  186. elif node[3] in ("expr", "get_aiter"):
  187. iter_expr = node[3]
  188. else:
  189. iter_expr = node[-3]
  190. assert iter_expr in ("expr", "get_aiter"), iter_expr
  191. self.preorder(iter_expr)
  192. self.preorder(tree[iter_index])
  193. self.prec = p
  194. def comprehension_walk_newer(
  195. self,
  196. node,
  197. iter_index: Optional[int],
  198. code_index: int = -5,
  199. collection_node=None,
  200. ):
  201. """Non-closure-based comprehensions the way they are done in Python3
  202. and some Python 2.7. Note: there are also other set comprehensions.
  203. Note: there are also other comprehensions.
  204. """
  205. # FIXME: DRY with listcomp_closure3
  206. p = self.prec
  207. self.prec = PRECEDENCE["lambda_body"] - 1
  208. comp_for = None
  209. # FIXME? Nonterminals in grammar maybe should be split out better?
  210. # Maybe test on self.compile_mode?
  211. if (
  212. isinstance(node[0], Token)
  213. and node[0].kind.startswith("LOAD")
  214. and iscode(node[0].attr)
  215. ):
  216. if node[3] == "get_aiter":
  217. compile_mode = self.compile_mode
  218. self.compile_mode = "genexpr"
  219. is_lambda = self.is_lambda
  220. self.is_lambda = True
  221. tree = self.get_comprehension_function(node, code_index)
  222. self.compile_mode = compile_mode
  223. self.is_lambda = is_lambda
  224. else:
  225. tree = self.get_comprehension_function(node, code_index)
  226. elif (
  227. len(node) > 2
  228. and isinstance(node[2], Token)
  229. and node[2].kind.startswith("LOAD")
  230. and iscode(node[2].attr)
  231. ):
  232. tree = self.get_comprehension_function(node, 2)
  233. else:
  234. tree = node
  235. # Pick out important parts of the comprehension:
  236. # * the variable we iterate over: "store"
  237. # * the results we accumulate: "n"
  238. is_30_dict_comp = False
  239. store = None
  240. if node == "list_comp_async":
  241. # We have two different kinds of grammar rules:
  242. # list_comp_async ::= LOAD_LISTCOMP LOAD_STR MAKE_FUNCTION_0 expr ...
  243. # and:
  244. # list_comp_async ::= BUILD_LIST_0 LOAD_ARG list_afor2
  245. if tree[0] == "expr" and tree[0][0] == "list_comp_async":
  246. tree = tree[0][0]
  247. if tree[0] == "BUILD_LIST_0":
  248. list_afor2 = tree[2]
  249. assert list_afor2 == "list_afor2"
  250. store = list_afor2[1]
  251. assert store == "store"
  252. n = list_afor2[3] if list_afor2[3] == "list_iter" else list_afor2[2]
  253. else:
  254. # ???
  255. pass
  256. elif node.kind in ("dict_comp_async", "set_comp_async"):
  257. # We have two different kinds of grammar rules:
  258. # dict_comp_async ::= LOAD_DICTCOMP LOAD_STR MAKE_FUNCTION_0 expr ...
  259. # set_comp_async ::= LOAD_SETCOMP LOAD_STR MAKE_FUNCTION_0 expr ...
  260. # and:
  261. # dict_comp_async ::= BUILD_MAP_0 genexpr_func_async
  262. # set_comp_async ::= BUILD_SET_0 genexpr_func_async
  263. if tree[0] == "expr":
  264. tree = tree[0]
  265. if tree[0].kind in ("BUILD_MAP_0", "BUILD_SET_0"):
  266. genexpr_func_async = tree[1]
  267. if genexpr_func_async == "genexpr_func_async":
  268. store = genexpr_func_async[2]
  269. assert store.kind.startswith("store")
  270. n = genexpr_func_async[4]
  271. assert n == "comp_iter"
  272. comp_for = collection_node
  273. else:
  274. set_afor2 = genexpr_func_async
  275. assert set_afor2 == "set_afor2"
  276. n = set_afor2[1]
  277. store = n[1]
  278. comp_for = node[3]
  279. else:
  280. # ???
  281. pass
  282. elif node == "list_afor":
  283. comp_for = node[0]
  284. list_afor2 = node[1]
  285. assert list_afor2 == "list_afor2"
  286. store = list_afor2[1]
  287. assert store == "store"
  288. n = list_afor2[2]
  289. elif node == "set_afor2":
  290. comp_for = node[0]
  291. set_iter_async = node[1]
  292. assert set_iter_async == "set_iter_async"
  293. store = set_iter_async[1]
  294. assert store == "store"
  295. n = set_iter_async[2]
  296. elif node == "list_comp" and tree[0] == "expr":
  297. list_iter = None
  298. for list_iter_try in tree:
  299. if list_iter_try == "list_iter":
  300. list_iter = list_iter_try
  301. break
  302. if not list_iter_try:
  303. tree = tree[0][0]
  304. n = tree[iter_index]
  305. else:
  306. n = list_iter
  307. pass
  308. pass
  309. pass
  310. else:
  311. n = tree[iter_index]
  312. if tree in (
  313. "dict_comp_func",
  314. "genexpr_func_async",
  315. "generator_exp",
  316. "list_comp",
  317. "set_comp",
  318. "set_comp_func",
  319. "set_comp_func_header",
  320. ):
  321. for k in tree:
  322. if k.kind in ("comp_iter", "list_iter", "set_iter", "await_expr"):
  323. n = k
  324. elif k == "store":
  325. store = k
  326. pass
  327. pass
  328. pass
  329. elif tree.kind in ("list_comp_async", "dict_comp_async", "set_afor2"):
  330. if self.version == (3, 0):
  331. for k in tree:
  332. if k in ("dict_comp_header", "set_comp_header"):
  333. n = k
  334. elif k == "store":
  335. store = k
  336. elif k == "dict_comp_iter":
  337. is_30_dict_comp = True
  338. n = (k[3], k[1])
  339. pass
  340. elif k == "comp_iter":
  341. n = k[0]
  342. pass
  343. pass
  344. elif tree == "list_comp_async":
  345. store = tree[2][1]
  346. else:
  347. if n.kind in ("RETURN_VALUE_LAMBDA", "return_expr_lambda"):
  348. self.prune()
  349. assert n in ("list_iter", "comp_iter"), n
  350. # FIXME: I'm not totally sure this is right.
  351. # Find the list comprehension body. It is the inner-most
  352. # node that is not list_.. .
  353. if_node = None
  354. comp_store = None
  355. if n == "comp_iter" and store is None:
  356. comp_for = n
  357. comp_store = tree[3]
  358. have_not = False
  359. # Iterate to find the inner-most "store".
  360. # We'll come back to the list iteration below.
  361. while n in ("list_iter", "list_afor", "list_afor2", "comp_iter"):
  362. # iterate one nesting deeper
  363. if self.version == (3, 0) and len(n) == 3:
  364. assert n[0] == "expr" and n[1] == "expr"
  365. n = n[1]
  366. elif n == "list_afor":
  367. n = n[1]
  368. elif n == "list_afor2":
  369. if n[1] == "store":
  370. store = n[1]
  371. n = n[3]
  372. else:
  373. n = n[0]
  374. if n in ("list_for", "comp_for"):
  375. if n == "list_for" and not comp_for and n[0] == "expr":
  376. comp_for = n[0]
  377. n_index = 3
  378. if (
  379. (n[2] == "store")
  380. or (self.version == (3, 0) and n[4] == "store")
  381. and not store
  382. ):
  383. if self.version == (3, 0):
  384. store = n[4]
  385. n_index = 5
  386. else:
  387. store = n[2]
  388. if not comp_store:
  389. comp_store = store
  390. n = n[n_index]
  391. elif n in (
  392. "list_if",
  393. "list_if_not",
  394. "list_if37",
  395. "list_if37_not",
  396. "comp_if",
  397. "comp_if_not",
  398. ):
  399. have_not = n in ("list_if_not", "comp_if_not", "list_if37_not")
  400. if n in ("list_if37", "list_if37_not"):
  401. n = n[1]
  402. else:
  403. if_node = n[0]
  404. if n[1] == "store":
  405. store = n[1]
  406. n = n[2]
  407. pass
  408. pass
  409. # Python 2.7+ starts including set_comp_body
  410. # Python 3.5+ starts including set_comp_func
  411. # Python 3.0 is yet another snowflake
  412. if self.version != (3, 0) and self.version < (3, 7):
  413. assert n.kind in (
  414. "lc_body",
  415. "list_if37",
  416. "comp_body",
  417. "set_comp_func",
  418. "set_comp_body",
  419. ), tree
  420. assert store, "Couldn't find store in list/set comprehension"
  421. # A problem created with later Python code generation is that there
  422. # is a lambda set up with a dummy argument name that is then called
  423. # So we can't just translate that as is but need to replace the
  424. # dummy name. Below we are picking out the variable name as seen
  425. # in the code. And trying to generate code for the other parts
  426. # that don't have the dummy argument name in it.
  427. # Another approach might be to be able to pass in the source name
  428. # for the dummy argument.
  429. if is_30_dict_comp:
  430. self.preorder(n[0])
  431. self.write(": ")
  432. self.preorder(n[1])
  433. else:
  434. if self.version == (3, 0):
  435. if isinstance(n, Token):
  436. body = store
  437. elif len(n) > 1:
  438. body = n[1]
  439. else:
  440. body = n[0]
  441. else:
  442. body = n[0]
  443. self.preorder(body)
  444. if node == "list_comp_async":
  445. self.write(" async")
  446. in_node_index = 3
  447. else:
  448. in_node_index = -3
  449. self.write(" for ")
  450. if comp_store:
  451. self.preorder(comp_store)
  452. comp_store = None
  453. else:
  454. self.preorder(store)
  455. self.write(" in ")
  456. if comp_for:
  457. self.preorder(comp_for)
  458. else:
  459. try:
  460. node[in_node_index]
  461. except:
  462. from trepan.api import debug
  463. debug()
  464. self.preorder(node[in_node_index])
  465. # Here is where we handle nested list iterations.
  466. if tree == "list_comp" and self.version != (3, 0):
  467. list_iter = None
  468. for list_iter_try in tree:
  469. if list_iter_try == "list_iter":
  470. list_iter = list_iter_try
  471. break
  472. assert list_iter == "list_iter"
  473. if list_iter[0] == "list_for":
  474. self.preorder(list_iter[0][3])
  475. self.prec = p
  476. return
  477. pass
  478. if comp_store:
  479. self.preorder(comp_for)
  480. if if_node:
  481. self.write(" if ")
  482. if have_not:
  483. self.write("not ")
  484. pass
  485. self.prec = PRECEDENCE["lambda_body"] - 1
  486. self.preorder(if_node)
  487. pass
  488. self.prec = p
  489. def get_comprehension_function(self, node, code_index: int):
  490. """
  491. Build the body of a comprehension function and then
  492. find the comprehension node buried in the tree which may
  493. be surrounded with start-like symbols or dominiators,.
  494. """
  495. self.prec = PRECEDENCE["lambda_body"] - 1
  496. code_node = node[code_index]
  497. if code_node == "load_genexpr":
  498. code_node = code_node[0]
  499. code_obj = code_node.attr
  500. assert iscode(code_obj), code_node
  501. code = Code(code_obj, self.scanner, self.currentclass, self.debug_opts["asm"])
  502. # FIXME: is there a way we can avoid this?
  503. # The problem is that in filterint top-level list comprehensions we can
  504. # encounter comprehensions of other kinds, and lambdas
  505. if self.compile_mode in ("listcomp",): # add other comprehensions to this list
  506. p_save = self.p
  507. self.p = get_python_parser(
  508. self.version,
  509. compile_mode="exec",
  510. is_pypy=self.is_pypy,
  511. )
  512. tree = self.build_ast(
  513. code._tokens, code._customize, code, is_lambda=self.is_lambda
  514. )
  515. self.p = p_save
  516. else:
  517. tree = self.build_ast(
  518. code._tokens, code._customize, code, is_lambda=self.is_lambda
  519. )
  520. self.customize(code._customize)
  521. # skip over: sstmt, stmt, return, return_expr
  522. # and other singleton derivations
  523. if tree == "lambda_start":
  524. if tree[0] in ("dom_start", "dom_start_opt"):
  525. tree = tree[1]
  526. while len(tree) == 1 or (tree in ("stmt", "sstmt", "return", "return_expr")):
  527. self.prec = 100
  528. tree = tree[1] if tree[0] in ("dom_start", "dom_start_opt") else tree[0]
  529. return tree
  530. def listcomp_closure3(self, node):
  531. """
  532. List comprehensions in Python 3 when handled as a closure.
  533. See if we can combine code.
  534. """
  535. # FIXME: DRY with comprehension_walk_newer
  536. p = self.prec
  537. self.prec = 27
  538. code_obj = node[1].attr
  539. assert iscode(code_obj), node[1]
  540. code = Code(code_obj, self.scanner, self.currentclass, self.debug_opts["asm"])
  541. tree = self.build_ast(code._tokens, code._customize, code)
  542. self.customize(code._customize)
  543. # skip over: sstmt, stmt, return, return_expr
  544. # and other singleton derivations
  545. while len(tree) == 1 or (
  546. tree in ("sstmt", "return") and tree[-1] in ("RETURN_LAST", "RETURN_VALUE")
  547. ):
  548. self.prec = 100
  549. tree = tree[0]
  550. n = tree[1]
  551. # Pick out important parts of the comprehension:
  552. # * the variables we iterate over: "stores"
  553. # * the results we accumulate: "n"
  554. # collections is the name of the expression(s) we are iterating over
  555. collections = [node[-3]]
  556. list_ifs = []
  557. if self.version[:2] == (3, 0) and n.kind != "list_iter":
  558. # FIXME 3.0 is a snowflake here. We need
  559. # special code for this. Not sure if this is totally
  560. # correct.
  561. stores = [tree[3]]
  562. assert tree[4] == "comp_iter"
  563. n = tree[4]
  564. # Find the list comprehension body. It is the inner-most
  565. # node that is not comp_.. .
  566. while n == "comp_iter":
  567. if n[0] == "comp_for":
  568. n = n[0]
  569. stores.append(n[2])
  570. n = n[3]
  571. elif n[0] in ("comp_if", "comp_if_not"):
  572. n = n[0]
  573. # FIXME: just a guess
  574. if n[0].kind == "expr":
  575. list_ifs.append(n)
  576. else:
  577. list_ifs.append([1])
  578. n = n[2]
  579. pass
  580. else:
  581. break
  582. pass
  583. # Skip over n[0] which is something like: _[1]
  584. self.preorder(n[1])
  585. else:
  586. assert n == "list_iter"
  587. stores = []
  588. # Find the list comprehension body. It is the inner-most
  589. # node that is not list_.. .
  590. while n == "list_iter":
  591. # recurse one step
  592. n = n[0]
  593. # FIXME: adjust for set comprehension
  594. if n == "list_for":
  595. stores.append(n[2])
  596. if self.version[:2] == (3, 0):
  597. body_index = 5
  598. else:
  599. body_index = 3
  600. n = n[body_index]
  601. if n[0] == "list_for":
  602. # Dog-paddle down largely singleton reductions
  603. # to find the collection (expr)
  604. c = n[0][0]
  605. if c == "expr":
  606. c = c[0]
  607. # FIXME: grammar is wonky here? Is this really an attribute?
  608. if c == "attribute":
  609. c = c[0]
  610. collections.append(c)
  611. pass
  612. elif n in ("list_if", "list_if_not", "list_if_or_not"):
  613. if n[0].kind == "expr":
  614. list_ifs.append(n)
  615. else:
  616. list_ifs.append([1])
  617. if self.version[:2] == (3, 0) and n[2] == "list_iter":
  618. n = n[2]
  619. else:
  620. n = n[-2] if n[-1] == "come_from_opt" else n[-1]
  621. pass
  622. elif n == "list_if37":
  623. list_ifs.append(n)
  624. n = n[-1]
  625. pass
  626. elif n == "list_afor":
  627. collections.append(n[0][0])
  628. n = n[1]
  629. stores.append(n[1][0])
  630. n = n[2] if n[2].kind == "list_iter" else n[3]
  631. pass
  632. assert n == "lc_body", tree
  633. if self.version[:2] == (3, 0):
  634. body_index = 1
  635. else:
  636. body_index = 0
  637. self.preorder(n[body_index])
  638. # FIXME: add indentation around "for"'s and "in"'s
  639. n_colls = len(collections)
  640. for i, store in enumerate(stores):
  641. if i >= n_colls:
  642. break
  643. token = collections[i]
  644. if not isinstance(token, Token):
  645. token = token.first_child()
  646. if token == "LOAD_DEREF" and co_flags_is_async(code_obj.co_flags):
  647. self.write(" async")
  648. pass
  649. self.write(" for ")
  650. if self.version[:2] == (3, 0):
  651. store = token
  652. self.preorder(store)
  653. self.write(" in ")
  654. self.preorder(collections[i])
  655. if i < len(list_ifs):
  656. self.preorder(list_ifs[i])
  657. pass
  658. pass
  659. self.prec = p