| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134 |
- # Copyright (c) 2022-2024 by Rocky Bernstein
- #
- # This program is free software: you can redistribute it and/or modify
- # it under the terms of the GNU General Public License as published by
- # the Free Software Foundation, either version 3 of the License, or
- # (at your option) any later version.
- #
- # This program is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- # GNU General Public License for more details.
- #
- # You should have received a copy of the GNU General Public License
- # along with this program. If not, see <http://www.gnu.org/licenses/>.
- """
- Custom Nonterminal action functions. See NonterminalActions docstring.
- """
- from uncompyle6.parsers.treenode import SyntaxTree
- from uncompyle6.scanners.tok import Token
- from uncompyle6.semantics.consts import (
- INDENT_PER_LEVEL,
- NO_PARENTHESIS_EVER,
- NONE,
- PARENTHESIS_ALWAYS,
- PRECEDENCE,
- minint,
- )
- from uncompyle6.semantics.helper import find_code_node, flatten_list, print_docstring
- from uncompyle6.util import better_repr, get_code_name
- class NonterminalActions:
- """
- Methods here all start with n_ and the remaining portion should be a nonterminal
- name defined by some rule.
- These methods take priority over names defined in table constants.
- All of the methods should have the same signature: (self, node) and return None.
- node is the subtree of the parse tree the that nonterminal name as the root.
- """
- def __init__(self):
- # Precedence is used to determine when an expression needs
- # parenthesis surrounding it. A high value indicates no
- # parenthesis are needed.
- self.prec = 1000
- self.in_format_string = False
- def n_alias(self, node: SyntaxTree):
- if self.version <= (2, 1):
- if len(node) == 2:
- store = node[1]
- assert store == "store"
- if store[0].pattr == node[0].pattr:
- self.write("import %s\n" % node[0].pattr)
- else:
- self.write("import %s as %s\n" % (node[0].pattr, store[0].pattr))
- pass
- pass
- self.prune() # stop recursing
- store_node = node[-1][-1]
- assert store_node.kind.startswith("STORE_")
- iname = node[0].pattr # import name
- sname = store_node.pattr # store_name
- if iname and iname == sname or iname.startswith(sname + "."):
- self.write(iname)
- else:
- self.write(iname, " as ", sname)
- self.prune() # stop recursing
- n_alias37 = n_alias
- def n_assign(self, node: SyntaxTree):
- # A horrible hack for Python 3.0 .. 3.2
- if (3, 0) <= self.version <= (3, 2) and len(node) == 2:
- if (
- node[0][0] == "LOAD_FAST"
- and node[0][0].pattr == "__locals__"
- and node[1][0].kind == "STORE_LOCALS"
- ):
- self.prune()
- self.default(node)
- def n_assign2(self, node: SyntaxTree):
- for n in node[-2:]:
- if n[0] == "unpack":
- n[0].kind = "unpack_w_parens"
- self.default(node)
- def n_assign3(self, node: SyntaxTree):
- for n in node[-3:]:
- if n[0] == "unpack":
- n[0].kind = "unpack_w_parens"
- self.default(node)
- def n_attribute(self, node: SyntaxTree):
- if node[0] == "LOAD_CONST" or node[0] == "expr" and node[0][0] == "LOAD_CONST":
- # FIXME: I didn't record which constants parenthesis is
- # necessary. However, I suspect that we could further
- # refine this by looking at operator precedence and
- # eval'ing the constant value (pattr) and comparing with
- # the type of the constant.
- node.kind = "attribute_w_parens"
- self.default(node)
- def n_bin_op(self, node: SyntaxTree):
- """bin_op (formerly "binary_expr") is the Python AST BinOp"""
- self.preorder(node[0])
- self.write(" ")
- self.preorder(node[-1])
- self.write(" ")
- # Try to avoid a trailing parentheses by lowering the priority a little
- self.prec -= 1
- self.preorder(node[1])
- self.prec += 1
- self.prune()
- def n_build_slice2(self, node: SyntaxTree):
- p = self.prec
- self.prec = NO_PARENTHESIS_EVER
- if not node[0].isNone():
- self.preorder(node[0])
- self.write(":")
- if not node[1].isNone():
- self.preorder(node[1])
- self.prec = p
- self.prune() # stop recursing
- def n_build_slice3(self, node: SyntaxTree):
- p = self.prec
- self.prec = NO_PARENTHESIS_EVER
- if not node[0].isNone():
- self.preorder(node[0])
- self.write(":")
- if not node[1].isNone():
- self.preorder(node[1])
- self.write(":")
- if not node[2].isNone():
- self.preorder(node[2])
- self.prec = p
- self.prune() # stop recursing
- def n_classdef(self, node: SyntaxTree):
- if self.version >= (3, 6):
- self.n_classdef36(node)
- elif self.version >= (3, 0):
- self.n_classdef3(node)
- # class definition ('class X(A,B,C):')
- cclass = self.currentclass
- # Pick out various needed bits of information
- # * class_name - the name of the class
- # * subclass_info - the parameters to the class e.g.
- # class Foo(bar, baz)
- # ------------
- # * subclass_code - the code for the subclass body
- if node == "classdefdeco2":
- build_class = node
- else:
- build_class = node[0]
- build_list = build_class[1][0]
- if hasattr(build_class[-3][0], "attr"):
- subclass_code = build_class[-3][0].attr
- class_name = build_class[0].pattr
- elif (
- build_class[-3] == "mkfunc"
- and node == "classdefdeco2"
- and build_class[-3][0] == "load_closure"
- ):
- subclass_code = build_class[-3][1].attr
- class_name = build_class[-3][0][0].pattr
- elif hasattr(node[0][0], "pattr"):
- subclass_code = build_class[-3][1].attr
- class_name = node[0][0].pattr
- else:
- raise RuntimeError("Internal Error n_classdef: cannot find class name")
- if node == "classdefdeco2":
- self.write("\n")
- else:
- self.write("\n\n")
- self.currentclass = str(class_name)
- self.write(self.indent, "class ", self.currentclass)
- self.print_super_classes(build_list)
- self.println(":")
- # class body
- self.indent_more()
- self.build_class(subclass_code)
- self.indent_less()
- self.currentclass = cclass
- if len(self.param_stack) > 1:
- self.write("\n\n")
- else:
- self.write("\n\n\n")
- self.prune()
- n_classdefdeco2 = n_classdef
- def n_const_list(self, node: SyntaxTree):
- """
- prettyprint a constant dict, list, set or tuple.
- """
- p = self.prec
- lastnodetype = node[2].kind
- flat_elems = node[1]
- is_dict = lastnodetype.endswith("DICT")
- if lastnodetype.endswith("LIST"):
- self.write("[")
- endchar = "]"
- elif lastnodetype.endswith("SET") or is_dict:
- self.write("{")
- endchar = "}"
- else:
- # from trepan.api import debug; debug()
- raise TypeError(
- (
- "Internal Error: n_const_list expects dict, list set, or set; got "
- f"{lastnodetype}"
- )
- )
- self.indent_more(INDENT_PER_LEVEL)
- sep = ""
- line_len = len(self.indent)
- if is_dict:
- keys = flat_elems[-1].attr
- assert isinstance(keys, tuple)
- assert len(keys) == len(flat_elems) - 1
- for i, elem in enumerate(flat_elems[:-1]):
- assert elem.kind == "ADD_VALUE"
- if elem.optype in ("local", "name"):
- value = elem.attr
- elif elem.optype == "const" and not isinstance(elem.attr, str):
- value = elem.attr
- else:
- try:
- value = "%r" % elem.pattr
- except Exception:
- value = elem.pattr
- if elem.linestart is not None:
- if elem.linestart != self.line_number:
- next_indent = self.indent + INDENT_PER_LEVEL[:-1]
- line_len = len(next_indent)
- sep += "\n" + next_indent
- self.line_number = elem.linestart
- else:
- if sep != "":
- sep += ", "
- elif line_len > 80:
- next_indent = self.indent + INDENT_PER_LEVEL[:-1]
- line_len = len(next_indent)
- sep += "\n" + next_indent
- sep_key_value = f"{sep}{repr(keys[i])}: {value}"
- line_len += len(sep_key_value)
- self.write(sep_key_value)
- sep = ", "
- else:
- for elem in flat_elems:
- if elem == "add_value":
- elem = elem[0]
- if elem == "ADD_VALUE":
- if elem.optype in ("local", "name"):
- value = elem.attr
- elif elem.optype == "const" and not isinstance(elem.attr, str):
- value = elem.attr
- else:
- value = "%s" % repr(elem.attr)
- else:
- assert elem.kind == "ADD_VALUE_VAR"
- value = "%s" % elem.pattr
- if elem.linestart is not None:
- if elem.linestart != self.line_number:
- next_indent = self.indent + INDENT_PER_LEVEL[:-1]
- line_len += len(next_indent)
- sep += "\n" + next_indent
- self.line_number = elem.linestart
- else:
- if sep != "":
- sep += " "
- line_len += len(sep)
- elif line_len > 80:
- next_indent = self.indent + INDENT_PER_LEVEL[:-1]
- line_len = len(next_indent)
- sep += "\n" + next_indent
- line_len += len(sep) + len(str(value)) + 1
- self.write(sep, value)
- sep = ", "
- self.write(endchar)
- self.indent_less(INDENT_PER_LEVEL)
- self.prec = p
- self.prune()
- return
- def n_delete_subscript(self, node: SyntaxTree):
- if node[-2][0] == "build_list" and node[-2][0][-1].kind.startswith(
- "BUILD_TUPLE"
- ):
- if node[-2][0][-1] != "BUILD_TUPLE_0":
- node[-2][0].kind = "build_tuple2"
- self.default(node)
- n_store_subscript = n_subscript = n_delete_subscript
- def n_dict(self, node: SyntaxTree):
- """
- Prettyprint a dict.
- 'dict' is something like k = {'a': 1, 'b': 42}"
- We will use source-code line breaks to guide us when to break.
- """
- if len(node) == 1 and node[0] == "const_list":
- self.preorder(node[0])
- self.prune()
- return
- p = self.prec
- self.prec = PRECEDENCE["dict"]
- self.indent_more(INDENT_PER_LEVEL)
- sep = INDENT_PER_LEVEL[:-1]
- if node[0] != "dict_entry":
- self.write("{")
- line_number = self.line_number
- if self.version >= (3, 0) and not self.is_pypy:
- if node[0].kind.startswith("kvlist"):
- # Python 3.5+ style key/value list in dict
- kv_node = node[0]
- ll = list(kv_node)
- length = len(ll)
- if kv_node[-1].kind.startswith("BUILD_MAP"):
- length -= 1
- i = 0
- # Respect line breaks from source
- while i < length:
- self.write(sep)
- name = self.traverse(ll[i], indent="")
- if i > 0:
- line_number = self.indent_if_source_nl(
- line_number, self.indent + INDENT_PER_LEVEL[:-1]
- )
- line_number = self.line_number
- self.write(name, ": ")
- value = self.traverse(
- ll[i + 1], indent=self.indent + (len(name) + 2) * " "
- )
- self.write(value)
- sep = ", "
- if line_number != self.line_number:
- sep += "\n" + self.indent + INDENT_PER_LEVEL[:-1]
- line_number = self.line_number
- i += 2
- pass
- pass
- elif len(node) > 1 and node[1].kind.startswith("kvlist"):
- # Python 3.0..3.4 style key/value list in dict
- kv_node = node[1]
- ll = list(kv_node)
- if len(ll) > 0 and ll[0].kind == "kv3":
- # Python 3.2 does this
- kv_node = node[1][0]
- ll = list(kv_node)
- i = 0
- while i < len(ll):
- self.write(sep)
- name = self.traverse(ll[i + 1], indent="")
- if i > 0:
- line_number = self.indent_if_source_nl(
- line_number, self.indent + INDENT_PER_LEVEL[:-1]
- )
- pass
- line_number = self.line_number
- self.write(name, ": ")
- value = self.traverse(
- ll[i], indent=self.indent + (len(name) + 2) * " "
- )
- self.write(value)
- sep = ", "
- if line_number != self.line_number:
- sep += "\n" + self.indent + INDENT_PER_LEVEL[:-1]
- line_number = self.line_number
- else:
- sep += " "
- i += 3
- pass
- pass
- elif node[-1].kind.startswith("BUILD_CONST_KEY_MAP"):
- # Python 3.6+ style const map
- keys = node[-2].pattr
- values = node[:-2]
- # FIXME: Line numbers?
- for key, value in zip(keys, values):
- self.write(sep)
- self.write(repr(key))
- line_number = self.line_number
- self.write(":")
- self.write(self.traverse(value[0]))
- sep = ", "
- if line_number != self.line_number:
- sep += "\n" + self.indent + INDENT_PER_LEVEL[:-1]
- line_number = self.line_number
- else:
- sep += " "
- pass
- pass
- if sep.startswith(",\n"):
- self.write(sep[1:])
- pass
- elif node[0].kind.startswith("dict_entry"):
- assert self.version >= (3, 5)
- template = ("%C", (0, len(node[0]), ", **"))
- self.template_engine(template, node[0])
- sep = ""
- elif node[-1].kind.startswith("BUILD_MAP_UNPACK") or node[
- -1
- ].kind.startswith("dict_entry"):
- assert self.version >= (3, 5)
- # FIXME: I think we can intermingle dict_comp's with other
- # dictionary kinds of things. The most common though is
- # a sequence of dict_comp's
- kwargs = node[-1].attr
- template = ("**%C", (0, kwargs, ", **"))
- self.template_engine(template, node)
- sep = ""
- if node[0].kind == "COLLECTION_START":
- key_value_pairs = node[1]
- for key_value_pair in key_value_pairs:
- key, value = key_value_pair
- if key.linestart is not None:
- line_number = key.linestart
- if line_number != self.line_number:
- sep += "\n" + self.indent + INDENT_PER_LEVEL[:-1]
- self.line_number = line_number
- self.write(sep)
- self.write(key.pattr)
- self.write(": ")
- if value.linestart is not None:
- line_number = value.linestart
- if line_number != self.line_number:
- sep += "\n" + self.indent + INDENT_PER_LEVEL[:-1]
- self.line_number = line_number
- else:
- sep += " "
- pass
- self.write(value.pattr)
- sep = ", "
- pass
- if sep.startswith(",\n"):
- self.write(sep[1:])
- pass
- pass
- else:
- # Python 2 style kvlist. Find beginning of kvlist.
- indent = self.indent + " "
- line_number = self.line_number
- if node[0].kind.startswith("BUILD_MAP"):
- if len(node) > 1 and node[1].kind in ("kvlist", "kvlist_n"):
- kv_node = node[1]
- else:
- kv_node = node[1:]
- self.kv_map(kv_node, sep, line_number, indent)
- else:
- sep = ""
- opname = node[-1].kind
- if self.is_pypy and self.version >= (3, 5):
- if opname.startswith("BUILD_CONST_KEY_MAP"):
- keys = node[-2].attr
- # FIXME: DRY this and the above
- for i in range(len(keys)):
- key = keys[i]
- value = self.traverse(node[i], indent="")
- self.write(sep, key, ": ", value)
- sep = ", "
- if line_number != self.line_number:
- sep += "\n" + self.indent + " "
- line_number = self.line_number
- pass
- pass
- pass
- else:
- if opname.startswith("kvlist"):
- list_node = node[0]
- else:
- list_node = node
- assert list_node[-1].kind.startswith("BUILD_MAP")
- for i in range(0, len(list_node) - 1, 2):
- key = self.traverse(list_node[i], indent="")
- value = self.traverse(list_node[i + 1], indent="")
- self.write(sep, key, ": ", value)
- sep = ", "
- if line_number != self.line_number:
- sep += "\n" + self.indent + " "
- line_number = self.line_number
- pass
- pass
- pass
- elif opname.startswith("kvlist"):
- kv_node = node[-1]
- self.kv_map(node[-1], sep, line_number, indent)
- pass
- pass
- if sep.startswith(",\n"):
- self.write(sep[1:])
- if node[0] != "dict_entry":
- self.write("}")
- self.indent_less(INDENT_PER_LEVEL)
- self.prec = p
- self.prune()
- def n_docstring(self, node):
- indent = self.indent
- doc_node = node[0]
- if doc_node.attr:
- docstring = doc_node.attr
- if not isinstance(docstring, str):
- # FIXME: we have mistakenly tagged something as a doc
- # string in transform when it isn't one.
- # The rule in n_mkfunc is pretty flaky.
- self.prune()
- return
- else:
- docstring = node[0].pattr
- print_docstring(self, indent, docstring)
- self.prune()
- def n_elifelsestmtr(self, node: SyntaxTree):
- if node[2] == "COME_FROM":
- return_stmts_node = node[3]
- node.kind = "elifelsestmtr2"
- else:
- return_stmts_node = node[2]
- if len(return_stmts_node) != 2:
- self.default(node)
- for n in return_stmts_node[0]:
- if not (n[0] == "ifstmt" and n[0][1][0] == "return_if_stmts"):
- self.default(node)
- return
- self.write(self.indent, "elif ")
- self.preorder(node[0])
- self.println(":")
- self.indent_more()
- self.preorder(node[1])
- self.indent_less()
- for n in return_stmts_node[0]:
- n[0].kind = "elifstmt"
- self.preorder(n)
- self.println(self.indent, "else:")
- self.indent_more()
- self.preorder(return_stmts_node[1])
- self.indent_less()
- self.prune()
- def n_except_cond2(self, node: SyntaxTree):
- if node[-1] == "come_from_opt":
- unpack_node = -3
- else:
- unpack_node = -2
- if node[unpack_node][0] == "unpack":
- node[unpack_node][0].kind = "unpack_w_parens"
- self.default(node)
- # Note: this node is only in Python 2.x
- # FIXME: figure out how to get this into customization
- # put so that we can get access via super from
- # the fragments routine.
- def n_exec_stmt(self, node: SyntaxTree):
- """
- exec_stmt ::= expr exprlist DUP_TOP EXEC_STMT
- exec_stmt ::= expr exprlist EXEC_STMT
- """
- self.write(self.indent, "exec ")
- self.preorder(node[0])
- if not node[1][0].isNone():
- sep = " in "
- for subnode in node[1]:
- self.write(sep)
- sep = ", "
- self.preorder(subnode)
- self.println()
- self.prune() # stop recursing
- def n_expr(self, node):
- first_child = node[0]
- if first_child == "_lambda_body" and self.in_format_string:
- p = -2
- else:
- p = self.prec
- if first_child.kind.startswith("bin_op"):
- n = node[0][-1][0]
- else:
- n = node[0]
- # if (hasattr(n, 'linestart') and n.linestart and
- # hasattr(self, 'current_line_number')):
- # self.source_linemap[self.current_line_number] = n.linestart
- if n.kind != "expr":
- self.prec = PRECEDENCE.get(n.kind, PARENTHESIS_ALWAYS)
- if n == "LOAD_CONST" and repr(n.pattr)[0] == "-":
- self.prec = 6
- # print("XXX", n.kind, p, "<", self.prec)
- # print(self.f.getvalue())
- if p < self.prec:
- # print(f"PREC {p}, {node[0].kind}")
- self.write("(")
- self.preorder(node[0])
- self.write(")")
- else:
- self.preorder(node[0])
- self.prec = p
- self.prune()
- n_return_expr_or_cond = n_expr
- def n_generator_exp(self, node):
- self.write("(")
- iter_index = 3
- if self.version > (3, 2):
- if self.version >= (3, 3):
- if node[0].kind in (
- "load_closure",
- "load_genexpr",
- ) and self.version >= (3, 8):
- code_index = -6
- is_lambda = self.is_lambda
- if node[0].kind == "load_genexpr":
- self.is_lambda = False
- self.closure_walk(node, collection_index=4)
- self.is_lambda = is_lambda
- else:
- # Python 3.7+ adds optional "come_froms" at node[0] so count from
- # the end.
- if node == "generator_exp_async" and self.version[:2] == (3, 6):
- code_index = 0
- else:
- code_index = -6
- iter_index = (
- 4
- if self.version < (3, 8) and not isinstance(node[4], Token)
- else 3
- )
- self.comprehension_walk(
- node, iter_index=iter_index, code_index=code_index
- )
- pass
- pass
- else:
- code_index = -5
- self.comprehension_walk(node, iter_index=iter_index, code_index=code_index)
- self.write(")")
- self.prune()
- n_genexpr_func = n_generator_exp_async = n_generator_exp
- def n_ifelsestmtr(self, node):
- if node[2] == "COME_FROM":
- return_stmts_node = node[3]
- node.kind = "ifelsestmtr2"
- else:
- return_stmts_node = node[2]
- if len(return_stmts_node) != 2:
- self.default(node)
- if not (
- return_stmts_node[0][0][0] == "ifstmt"
- and return_stmts_node[0][0][0][1][0] == "return_if_stmts"
- ) and not (
- return_stmts_node[0][-1][0] == "ifstmt"
- and return_stmts_node[0][-1][0][1][0] == "return_if_stmts"
- ):
- self.default(node)
- return
- self.write(self.indent, "if ")
- self.preorder(node[0])
- self.println(":")
- self.indent_more()
- self.preorder(node[1])
- self.indent_less()
- if_ret_at_end = False
- if len(return_stmts_node[0]) >= 3:
- if (
- return_stmts_node[0][-1][0] == "ifstmt"
- and return_stmts_node[0][-1][0][1][0] == "return_if_stmts"
- ):
- if_ret_at_end = True
- past_else = False
- prev_stmt_is_if_ret = True
- for n in return_stmts_node[0]:
- if n[0] == "ifstmt" and n[0][1][0] == "return_if_stmts":
- if prev_stmt_is_if_ret:
- n[0].kind = "elifstmt"
- prev_stmt_is_if_ret = True
- else:
- prev_stmt_is_if_ret = False
- if not past_else and not if_ret_at_end:
- self.println(self.indent, "else:")
- self.indent_more()
- past_else = True
- self.preorder(n)
- if not past_else or if_ret_at_end:
- self.println(self.indent, "else:")
- self.indent_more()
- self.preorder(return_stmts_node[1])
- self.indent_less()
- self.prune()
- n_ifelsestmtr2 = n_ifelsestmtr
- def n_import_from(self, node):
- relative_path_index = 0
- if self.version >= (2, 5):
- if node[relative_path_index].pattr > 0:
- node[2].pattr = ("." * node[relative_path_index].attr) + node[2].pattr
- if self.version > (2, 7):
- if isinstance(node[1].pattr, tuple):
- imports = node[1].pattr
- for pattr in imports:
- node[1].pattr = pattr
- self.default(node)
- return
- pass
- self.default(node)
- n_import_from_star = n_import_from
- def n_lambda_body(self, node):
- self.make_function(node, is_lambda=True, code_node=node[-2])
- self.prune() # stop recursing
- def n_list(self, node: SyntaxTree):
- """
- prettyprint a dict, list, set or tuple.
- """
- if len(node) == 1 and node[0] == "const_list":
- self.preorder(node[0])
- self.prune()
- return
- p = self.prec
- self.prec = PRECEDENCE["yield"] - 1
- lastnode = node[-1]
- node = node[:-1]
- lastnodetype = lastnode.kind
- # If this build list is inside a CALL_FUNCTION_VAR,
- # then the first * has already been printed.
- # Until I have a better way to check for CALL_FUNCTION_VAR,
- # will assume that if the text ends in *.
- last_was_star = self.f.getvalue().endswith("*")
- if lastnodetype.endswith("UNPACK"):
- # FIXME: need to handle range of BUILD_LIST_UNPACK
- have_star = True
- # endchar = ''
- else:
- have_star = False
- if lastnodetype.startswith("BUILD_LIST"):
- self.write("[")
- endchar = "]"
- elif lastnodetype.startswith("BUILD_MAP_UNPACK"):
- self.write("{*")
- endchar = "}"
- elif lastnodetype.startswith("BUILD_SET"):
- self.write("{")
- endchar = "}"
- elif lastnodetype.startswith("BUILD_TUPLE") or node == "tuple":
- # Tuples can appear places that can NOT
- # have parenthesis around them, like array
- # subscripts. We check for that by seeing
- # if a tuple item is some sort of slice.
- no_parens = False
- for n in node:
- if n == "expr" and n[0].kind.startswith("build_slice"):
- no_parens = True
- break
- pass
- if no_parens:
- endchar = ""
- else:
- self.write("(")
- endchar = ")"
- pass
- elif lastnodetype.startswith("ROT_TWO"):
- self.write("(")
- endchar = ")"
- else:
- raise TypeError(
- "Internal Error: n_build_list expects list, tuple, set, or unpack"
- )
- flat_elems = flatten_list(node)
- self.indent_more(INDENT_PER_LEVEL)
- sep = ""
- for elem in flat_elems:
- if elem in ("ROT_THREE", "EXTENDED_ARG"):
- continue
- assert elem == "expr"
- line_number = self.line_number
- value = self.traverse(elem)
- if line_number != self.line_number:
- sep += "\n" + self.indent + INDENT_PER_LEVEL[:-1]
- else:
- if sep != "":
- sep += " "
- if not last_was_star:
- if have_star:
- sep += "*"
- pass
- pass
- else:
- last_was_star = False
- self.write(sep, value)
- sep = ","
- if lastnode.attr == 1 and lastnodetype.startswith("BUILD_TUPLE"):
- self.write(",")
- self.write(endchar)
- self.indent_less(INDENT_PER_LEVEL)
- self.prec = p
- self.prune()
- return
- n_set = n_build_set = n_tuple = n_list
- def n_list_comp(self, node):
- self.write("[")
- if node[0].kind == "load_closure":
- assert self.version >= (3, 0)
- self.listcomp_closure3(node)
- else:
- if node == "listcomp_async":
- list_iter_index = 5
- else:
- list_iter_index = 1
- self.comprehension_walk_newer(node, list_iter_index, 0)
- self.write("]")
- self.prune()
- def n_list_comp_pypy27(self, node):
- """List comprehensions in PYPY."""
- p = self.prec
- self.prec = 27
- if node[-1].kind == "list_iter":
- n = node[-1]
- elif self.is_pypy and node[-1] == "JUMP_BACK":
- n = node[-2]
- list_expr = node[1]
- if len(node) >= 3:
- store = node[3]
- elif self.is_pypy and n[0] == "list_for":
- store = n[0][2]
- assert n == "list_iter"
- assert store == "store"
- # Find the list comprehension body. It is the inner-most
- # node.
- # FIXME: DRY with other use
- while n == "list_iter":
- n = n[0] # iterate one nesting deeper
- if n == "list_for":
- n = n[3]
- elif n == "list_if":
- n = n[2]
- elif n == "list_if_not":
- n = n[2]
- assert n == "lc_body"
- self.write("[ ")
- expr = n[0]
- if self.is_pypy and node[-1] == "JUMP_BACK":
- list_iter = node[-2]
- else:
- list_iter = node[-1]
- assert expr == "expr"
- assert list_iter == "list_iter"
- # FIXME: use source line numbers for directing line breaks
- self.preorder(expr)
- self.preorder(list_expr)
- self.write(" ]")
- self.prec = p
- self.prune() # stop recursing
- def n_mkfunc(self, node):
- code_node = find_code_node(node, -2)
- code = code_node.attr
- self.write(get_code_name(code))
- self.indent_more()
- self.make_function(node, is_lambda=False, code_node=code_node)
- if len(self.param_stack) > 1:
- self.write("\n\n")
- else:
- self.write("\n\n\n")
- self.indent_less()
- self.prune() # stop recursing
- def n_return(self, node):
- if self.params["is_lambda"]:
- self.preorder(node[0])
- self.prune()
- else:
- # One reason we worry over whether we use "return None" or "return"
- # is that inside a generator, "return None" is illegal.
- # Thank you, Python!
- if self.return_none or not self.is_return_none(node):
- self.default(node)
- else:
- self.template_engine(("%|return\n",), node)
- self.prune() # stop recursing
- def n_return_expr(self, node):
- if len(node) == 1 and node[0] == "expr":
- # If expr is yield we want parens.
- self.prec = PRECEDENCE["yield"] - 1
- self.n_expr(node[0])
- else:
- self.n_expr(node)
- # Python 3.x can have dead code as a result of its optimization?
- # So we'll add a # at the end of the return lambda so the rest is ignored
- def n_return_expr_lambda(self, node):
- if 1 <= len(node) <= 2:
- self.preorder(node[0])
- self.write(" # Avoid dead code: ")
- self.prune()
- else:
- # We can't comment out like above because there may be a trailing ')'
- # that needs to be written
- assert len(node) == 3 and node[2] in (
- "RETURN_VALUE_LAMBDA",
- "LAMBDA_MARKER",
- )
- self.preorder(node[0])
- self.prune()
- def n_return_if_stmt(self, node):
- if self.params["is_lambda"]:
- self.write(" return ")
- self.preorder(node[0])
- self.prune()
- else:
- self.write(self.indent, "return")
- if self.return_none or not self.is_return_none(node):
- self.write(" ")
- self.preorder(node[0])
- self.println()
- self.prune() # stop recursing
- def n_set_comp(self, node):
- self.write("{")
- if node[0] in ["LOAD_SETCOMP", "LOAD_DICTCOMP"]:
- if self.version == (3, 0):
- if len(node) >= 6:
- iter_index = 6
- else:
- assert node[1].kind.startswith("MAKE_FUNCTION")
- iter_index = 2
- pass
- else:
- iter_index = 1
- self.comprehension_walk_newer(node, iter_index=iter_index, code_index=0)
- elif node[0].kind == "load_closure" and self.version >= (3, 0):
- self.closure_walk(node, collection_index=4)
- else:
- self.comprehension_walk(node, iter_index=4)
- self.write("}")
- self.prune()
- n_dict_comp = n_set_comp
- # In the old days this node would never get called because
- # it was embedded inside some sort of comprehension
- # Nowadays, we allow starting any code object, not just
- # a top-level module. In doing so we can
- # now encounter this outside of the embedding of
- # a comprehension.
- def n_set_comp_async(self, node):
- self.write("{")
- if node[0] in ["BUILD_SET_0", "BUILD_MAP_0"]:
- self.comprehension_walk_newer(node[1], 3, 0, collection_node=node[1])
- if node[0] in ["LOAD_SETCOMP", "LOAD_DICTCOMP"]:
- get_aiter = node[3]
- assert get_aiter == "get_aiter", node.kind
- self.comprehension_walk_newer(node, 1, 0, collection_node=get_aiter[0])
- self.write("}")
- self.prune()
- n_dict_comp_async = n_set_comp_async
- def n_str(self, node):
- self.write(node[0].pattr)
- self.prune()
- def n_store(self, node):
- expr = node[0]
- if expr == "expr" and expr[0] == "LOAD_CONST" and node[1] == "STORE_ATTR":
- # FIXME: I didn't record which constants parenthesis is
- # necessary. However, I suspect that we could further
- # refine this by looking at operator precedence and
- # eval'ing the constant value (pattr) and comparing with
- # the type of the constant.
- node.kind = "store_w_parens"
- self.default(node)
- def n_unpack(self, node):
- if node[0].kind.startswith("UNPACK_EX"):
- # Python 3+
- before_count, after_count = node[0].attr
- for i in range(before_count + 1):
- self.preorder(node[i])
- if i != 0:
- self.write(", ")
- self.write("*")
- for i in range(1, after_count + 2):
- self.preorder(node[before_count + i])
- if i != after_count + 1:
- self.write(", ")
- self.prune()
- return
- if node[0] == "UNPACK_SEQUENCE_0":
- self.write("[]")
- self.prune()
- return
- for n in node[1:]:
- if n[0].kind == "unpack":
- n[0].kind = "unpack_w_parens"
- # In Python 2.4, unpack is used in (a, b, c) of:
- # except RuntimeError, (a, b, c):
- if self.version < (2, 7):
- node.kind = "unpack_w_parens"
- self.default(node)
- n_unpack_w_parens = n_unpack
- def n_yield(self, node):
- if node != SyntaxTree("yield", [NONE, Token("YIELD_VALUE")]):
- self.template_engine(("yield %c", 0), node)
- elif self.version <= (2, 4):
- # Early versions of Python don't allow a plain "yield"
- self.write("yield None")
- else:
- self.write("yield")
- self.prune() # stop recursing
- def n_LOAD_CONST(self, node):
- attr = node.attr
- data = node.pattr
- datatype = type(data)
- if isinstance(data, float):
- self.write(better_repr(data, self.version))
- elif isinstance(data, complex):
- self.write(better_repr(data, self.version))
- elif isinstance(datatype, int) and data == minint:
- # convert to hex, since decimal representation
- # would result in 'LOAD_CONST; UNARY_NEGATIVE'
- # change:hG/2002-02-07: this was done for all negative integers
- # todo: check whether this is necessary in Python 2.1
- self.write(hex(data))
- elif datatype is type(Ellipsis):
- self.write("...")
- elif attr is None:
- # LOAD_CONST 'None' only occurs, when None is
- # implicit e.g. in 'return' w/o params
- # pass
- self.write("None")
- elif isinstance(data, tuple):
- self.pp_tuple(data)
- elif isinstance(attr, bool):
- self.write(repr(attr))
- elif self.FUTURE_UNICODE_LITERALS:
- # The FUTURE_UNICODE_LITERALS compiler flag
- # in 2.6 on change the way
- # strings are interpreted:
- # u'xxx' -> 'xxx'
- # xxx' -> b'xxx'
- if isinstance(data, str):
- self.write("b" + repr(data))
- else:
- self.write(repr(data))
- else:
- self.write(repr(data))
- # LOAD_CONST is a terminal, so stop processing/recursing early
- self.prune()
|