diff --git a/test/bytecode_2.4/07_try_except.pyc b/test/bytecode_2.4/07_try_except.pyc new file mode 100644 index 00000000..7c6a6566 Binary files /dev/null and b/test/bytecode_2.4/07_try_except.pyc differ diff --git a/test/ok_lib2.7/bsddb/dbshelve.py b/test/ok_lib2.7/bsddb/dbshelve.py index 7d0daa2f..60469b6a 100644 --- a/test/ok_lib2.7/bsddb/dbshelve.py +++ b/test/ok_lib2.7/bsddb/dbshelve.py @@ -29,6 +29,7 @@ storage. #------------------------------------------------------------------------ import sys + absolute_import = (sys.version_info[0] >= 3) if absolute_import : # Because this syntaxis is not valid before Python 2.5 @@ -229,7 +230,7 @@ class DBShelf(MutableMapping): def associate(self, secondaryDB, callback, flags=0): def _shelf_callback(priKey, priData, realCallback=callback): - # Safe in Python 2.x because expresion short circuit + # Safe in Python 2.x because expression short circuit if sys.version_info[0] < 3 or isinstance(priData, bytes) : data = cPickle.loads(priData) else : @@ -366,7 +367,7 @@ class DBShelfCursor: return None else: key, data = rec - # Safe in Python 2.x because expresion short circuit + # Safe in Python 2.x because expression short circuit if sys.version_info[0] < 3 or isinstance(data, bytes) : return key, cPickle.loads(data) else : diff --git a/test/simple_source/bug26/03_weird26.py b/test/simple_source/bug26/03_weird26.py index 9b583eed..b0c97df1 100644 --- a/test/simple_source/bug26/03_weird26.py +++ b/test/simple_source/bug26/03_weird26.py @@ -3,10 +3,10 @@ # Grammar allows multiple adjacent 'if's in listcomps and genexps, # even though it's silly. Make sure it works (ifelse broke this.) -[ x for x in range(10) if x % 2 if x % 3 ] +[x for x in range(10) if x % 2 if x % 3] list(x for x in range(10) if x % 2 if x % 3) -# expresion which evaluates True unconditionally, +# expression which evaluates True unconditionally, # but leave dead code or junk around that we have to match on. # Tests "if_exp_true" rule 5 if 1 else 2 diff --git a/test/simple_source/bug26/07_try_except.py b/test/simple_source/bug26/07_try_except.py new file mode 100644 index 00000000..50fbe4e8 --- /dev/null +++ b/test/simple_source/bug26/07_try_except.py @@ -0,0 +1,34 @@ +# Bug portion of Issue #405 https://github.com/rocky/python-uncompyle6/issues/405 +# Bug was detecting if/else as the last item in a "try: .. except" block. +class Saveframe(object): + """A saveframe. Use the classmethod from_scratch to create one.""" + + frame_list = {} + + def frame_dict(self): + return + + # Next line is 1477 + def __setitem__(self, key, item): + # Next line is 1481 + if isinstance(item, Saveframe): + try: + self.frame_list[key] = item + except TypeError: + if key in (self.frame_dict()): + dict((frame.name, frame) for frame in self.frame_list) + for pos, frame in enumerate(self.frame_list): + if frame.name == key: + self.frame_list[pos] = item + else: + raise KeyError( + "Saveframe with name '%s' does not exist and " + "therefore cannot be written to. Use the add_saveframe method to add new saveframes." + % key + ) + # Next line is 1498 + raise ValueError("You can only assign an entry to a saveframe splice.") + + +x = Saveframe() +x.__setitem__("foo", 5) diff --git a/test/simple_source/bug27+/01_module_doc.py b/test/simple_source/bug27+/01_module_doc.py index 0ceef372..d2e3282d 100644 --- a/test/simple_source/bug27+/01_module_doc.py +++ b/test/simple_source/bug27+/01_module_doc.py @@ -1,8 +1,8 @@ # From 2.7.17 test_bdb.py -# The problem was detecting a docstring at the begining of the module +# The problem was detecting a docstring at the beginning of the module # It must be detected and change'd or else the "from __future__" below # is invalid. -# Note that this has to be compiled with optimation < 2 or else optimization +# Note that this has to be compiled with optimization < 2 or else optimization # will remove the docstring """Rational, infinite-precision, real numbers.""" diff --git a/test/simple_source/bug30/01_ops.py b/test/simple_source/bug30/01_ops.py index c984b3dd..6536b180 100644 --- a/test/simple_source/bug30/01_ops.py +++ b/test/simple_source/bug30/01_ops.py @@ -1,20 +1,20 @@ # Statements to beef up grammar coverage rules # Force "inplace" ops # Note this is like simple_source/bug22/01_ops.py -# But we don't ahve the UNARY_CONVERT which dropped +# But we don't have the UNARY_CONVERT which dropped # out around 2.7 y = +10 # UNARY_POSITIVE -y /= 1 # INPLACE_DIVIDE -y %= 4 # INPLACE_MODULO +y /= 1 # INPLACE_DIVIDE +y %= 4 # INPLACE_MODULO y **= 1 # INPLACE POWER y >>= 2 # INPLACE_RSHIFT y <<= 2 # INPLACE_LSHIFT y //= 1 # INPLACE_TRUE_DIVIDE -y &= 1 # INPLACE_AND -y ^= 1 # INPLACE_XOR +y &= 1 # INPLACE_AND +y ^= 1 # INPLACE_XOR # Beef up aug_assign and STORE_SLICE+3 -x = [1,2,3,4,5] +x = [1, 2, 3, 4, 5] x[0:1] = 1 x[0:3] += 1, 2, 3 diff --git a/test/simple_source/bug33/04_lambda_star_default.py b/test/simple_source/bug33/04_lambda_star_default.py index 879b8960..a1e2db67 100644 --- a/test/simple_source/bug33/04_lambda_star_default.py +++ b/test/simple_source/bug33/04_lambda_star_default.py @@ -1,18 +1,20 @@ # From 3.x test_audiop.py # Bug is handling default value after * argument in a lambda. -# That's a mouthful of desciption; I am not sure if the really +# That's a mouthful of description; I am not sure if the really # hacky fix to the code is even correct. # # FIXME: try and test with more than one default argument. + # RUNNABLE def pack(width, data): return (width, data) + packs = {w: (lambda *data, width=w: pack(width, data)) for w in (1, 2, 4)} -assert packs[1]('a') == (1, ('a',)) -assert packs[2]('b') == (2, ('b',)) -assert packs[4]('c') == (4, ('c',)) +assert packs[1]("a") == (1, ("a",)) +assert packs[2]("b") == (2, ("b",)) +assert packs[4]("c") == (4, ("c",)) diff --git a/test/simple_source/bug33/08_if_else.py b/test/simple_source/bug33/08_if_else.py index abb104de..d16da5b8 100644 --- a/test/simple_source/bug33/08_if_else.py +++ b/test/simple_source/bug33/08_if_else.py @@ -1,16 +1,19 @@ # From python 3.3.7 trace # Bug was not having not having semantic rule for conditional not + # RUNNABLE! def init(modules=None): mods = set() if not modules else set(modules) return mods + assert init() == set() assert init([1, 2, 3]) == set([1, 2, 3]) + # From 3.6 sre_parse -# Bug was in handling multple COME_FROMS from nested if's +# Bug was in handling multiple COME_FROMS from nested if's def _escape(a, b, c, d, e): if a: if b: @@ -24,15 +27,16 @@ def _escape(a, b, c, d, e): return raise -assert _escape(False, True, True, True, True) is None -assert _escape(True, True, True, False, True) is None -assert _escape(True, True, False, False, True) is None + +assert _escape(False, True, True, True, True) is None +assert _escape(True, True, True, False, True) is None +assert _escape(True, True, False, False, True) is None for args in ( - (True, True, True, False, True), - (True, False, True, True, True), - (True, False, True, True, False), - ): + (True, True, True, False, True), + (True, False, True, True, True), + (True, False, True, True, False), +): try: _escape(*args) assert False, args diff --git a/test/simple_source/bug35/06_while_return.py b/test/simple_source/bug35/06_while_return.py index 735065f7..a08949de 100644 --- a/test/simple_source/bug35/06_while_return.py +++ b/test/simple_source/bug35/06_while_return.py @@ -1,8 +1,9 @@ # From Python 3.4 asynchat.py -# Tests presence or absense of +# Tests presence or absence of # SETUP_LOOP testexpr return_stmts POP_BLOCK COME_FROM_LOOP # Note: that there is no JUMP_BACK because of the return_stmts. + def initiate_send(a, b, c, num_sent): while a and b: try: @@ -24,6 +25,7 @@ def initiate_send2(a, b): return 2 + assert initiate_send(1, 1, 2, False) == 1 assert initiate_send(1, 2, 3, False) == 3 assert initiate_send(1, 2, 3, True) == 2 diff --git a/test/simple_source/bug36/03_fn_defaults.py b/test/simple_source/bug36/03_fn_defaults.py index 6167ce0c..af27ee25 100644 --- a/test/simple_source/bug36/03_fn_defaults.py +++ b/test/simple_source/bug36/03_fn_defaults.py @@ -1,13 +1,20 @@ -# Python 3.6 changes, yet again, the way deafult pairs are handled +# Python 3.6 changes, yet again, the way default pairs are handled def foo1(bar, baz=1): return 1 + + def foo2(bar, baz, qux=1): return 2 + + def foo3(bar, baz=1, qux=2): return 3 + + def foo4(bar, baz, qux=1, quux=2): return 4 + # From 3.6 compileall. # Bug was in omitting default which when used in an "if" # are treated as False would be diff --git a/test/simple_source/bug36/04_class_kwargs.py b/test/simple_source/bug36/04_class_kwargs.py index 55797a27..36072659 100644 --- a/test/simple_source/bug36/04_class_kwargs.py +++ b/test/simple_source/bug36/04_class_kwargs.py @@ -1,17 +1,23 @@ # From 3.6 test_abc.py -# Bug was Reciever() class definition +# Bug was Receiver() class definition import abc import unittest + + class TestABCWithInitSubclass(unittest.TestCase): def test_works_with_init_subclass(self): class ReceivesClassKwargs: def __init_subclass__(cls, **kwargs): super().__init_subclass__() + class Receiver(ReceivesClassKwargs, abc.ABC, x=1, y=2, z=3): pass + def test_abstractmethod_integration(self): for abstractthing in [abc.abstractmethod]: + class C(metaclass=abc.ABCMeta): @abstractthing - def foo(self): pass # abstract + def foo(self): + pass # abstract diff --git a/test/simple_source/bug36/05_if_and_comp.py b/test/simple_source/bug36/05_if_and_comp.py index 9c4bc61d..b2959451 100644 --- a/test/simple_source/bug36/05_if_and_comp.py +++ b/test/simple_source/bug36/05_if_and_comp.py @@ -1,12 +1,12 @@ # From 3.6 base64.py -# Bug was handling "and" condition in the presense of POP_JUMP_IF_FALSE +# Bug was handling "and" condition in the presence of POP_JUMP_IF_FALSE # locations def _85encode(foldnuls, words): - return ['z' if foldnuls and word - else 'y' - for word in words] + return ["z" if foldnuls and word else "y" for word in words] + # From Python 3.6 enum.py + def __new__(metacls, cls, bases, classdict): {k: classdict[k] for k in classdict._member_names} diff --git a/test/simple_source/bug36/10_fstring.py b/test/simple_source/bug36/10_fstring.py index 52f79481..9a93db7e 100644 --- a/test/simple_source/bug36/10_fstring.py +++ b/test/simple_source/bug36/10_fstring.py @@ -14,6 +14,7 @@ assert ( assert "def0" == f"{abc}0" assert "defdef" == f"{abc}{abc!s}" + # From 3.8 test/test_string.py # We had the precedence of yield vs. lambda incorrect. def fn(x): @@ -97,9 +98,10 @@ else: (x, y, width) = ("foo", 2, 10) assert f"x={x*y:{width}}" == "x=foofoo " + # Why the fact that the distinction of docstring versus stmt is a # string expression is important academic, but we will decompile an -# equivalent thing. For compatiblity with older Python we'll use "%" +# equivalent thing. For compatibility with older Python we'll use "%" # instead of a format string def f(): f"""Not a docstring""" # noqa diff --git a/test/simple_source/bug36/10_long_pop_jump.py b/test/simple_source/bug36/10_long_pop_jump.py index ad6c4de7..da6a1981 100644 --- a/test/simple_source/bug36/10_long_pop_jump.py +++ b/test/simple_source/bug36/10_long_pop_jump.py @@ -1,26 +1,27 @@ # From 3.6 _markupbase.py -# Bug is that the routine is long enough that POP_JUMP_IF_FALSE instruciton has an -# EXTENDED_ARG intruction before it and we weren't picking out the jump offset properly +# Bug is that the routine is long enough that POP_JUMP_IF_FALSE instruction has an +# EXTENDED_ARG instruction before it and we weren't picking out the jump offset properly + def parse_declaration(self, i): if rawdata[j:j] in ("-", ""): return -1 n = len(rawdata) - if rawdata[j:j+2] == '-': + if rawdata[j : j + 2] == "-": return self.parse_comment(i) - elif rawdata[j] == '[': + elif rawdata[j] == "[": return self.parse_marked_section(i) else: decltype, j = self._scan_name(j, i) if j < 0: return j if decltype == "d": - self._decl_otherchars = '' + self._decl_otherchars = "" while j < n: c = rawdata[j] if c == ">": - data = rawdata[i+2:j] + data = rawdata[i + 2 : j] if decltype == "d": self.handle_decl(data) else: @@ -43,8 +44,7 @@ def parse_declaration(self, i): else: self.error("unexpected '[' char in declaration") else: - self.error( - "unexpected %r char in declaration" % rawdata[j]) + self.error("unexpected %r char in declaration" % rawdata[j]) if j < 0: return j return -1 diff --git a/test/simple_source/calls/01_positional.py b/test/simple_source/calls/01_positional.py index 3cc17e25..9527bf89 100644 --- a/test/simple_source/calls/01_positional.py +++ b/test/simple_source/calls/01_positional.py @@ -1,5 +1,5 @@ # Tests custom added grammar rule: # expr ::= expr {expr}^n CALL_FUNCTION_n -# which in the specifc case below is: +# which in the specific case below is: # expr ::= expr expr expr CALL_FUNCTION_2 max(1, 2) diff --git a/test/simple_source/looping/04_while1_while1.py b/test/simple_source/looping/04_while1_while1.py index 41106b0c..bb8762cc 100644 --- a/test/simple_source/looping/04_while1_while1.py +++ b/test/simple_source/looping/04_while1_while1.py @@ -27,7 +27,7 @@ while 1: else: raise RuntimeError -# Degenerate case. Note: we can't run becase this causes an infinite loop. +# Degenerate case. Note: we can't run because this causes an infinite loop. # Suggested in issue #172 while 1: pass diff --git a/test/simple_source/operation_logic/05_control_flow_bugs.py b/test/simple_source/operation_logic/05_control_flow_bugs.py index f756f2ad..5d89100d 100644 --- a/test/simple_source/operation_logic/05_control_flow_bugs.py +++ b/test/simple_source/operation_logic/05_control_flow_bugs.py @@ -1,7 +1,8 @@ # From 3.6.10 test_binascii.py -# Bug was getting "while c and noise" parsed correclty +# Bug was getting "while c and noise" parsed correctly # and not put into the "ifelsesmt" + # RUNNABLE! def addnoise(c, noise): while c and noise: @@ -12,6 +13,7 @@ def addnoise(c, noise): noise = False return c + assert addnoise(0, True) == 0 assert addnoise(1, False) == 1 assert addnoise(2, True) == 2 @@ -19,9 +21,10 @@ assert addnoise(3, True) == 3 assert addnoise(4, True) == 3 assert addnoise(5, False) == 5 + # From 3.6.10 test_dbm_dumb.py # Bug was getting attaching "else" to the right "if" in the -# presense of a loop. +# presence of a loop. def test_random(a, r): x = 0 for dummy in r: @@ -32,11 +35,13 @@ def test_random(a, r): x += 1 return x + assert test_random(True, [1]) == 2 assert test_random(True, [1, 1]) == 4 assert test_random(False, [1]) == 0 assert test_random(False, [1, 1]) == 0 + # From 2.7.17 test_frozen.py # Bug was getting making sure we have "try" not # "try"/"else" @@ -53,11 +58,13 @@ def test_frozen(a, b): return x + assert test_frozen(1, 1) == 4.0 assert test_frozen(0, 1) == 5.0 assert test_frozen(0.5, 0) == 6.0 assert test_frozen(0, 0.5) == 8.0 + # From 3.6.10 test_binop.py # Bug was getting "other += 3" outside of "if"/"else. def __floordiv__(a, b): @@ -70,6 +77,7 @@ def __floordiv__(a, b): other += 3 return other + assert __floordiv__(True, True) == 4 assert __floordiv__(True, False) == 4 assert __floordiv__(False, True) == 3 diff --git a/test/simple_source/operation_logic/10_mixed_boolean.py b/test/simple_source/operation_logic/10_mixed_boolean.py index d1849cda..35d64bef 100644 --- a/test/simple_source/operation_logic/10_mixed_boolean.py +++ b/test/simple_source/operation_logic/10_mixed_boolean.py @@ -1,19 +1,19 @@ # Self-checking test. -# Mixed boolean expresions +# Mixed boolean expressions b = True -assert b, 'b = True' +assert b, "b = True" c = False -assert not c, 'c = False' +assert not c, "c = False" d = True a = b and c or d -assert a, 'b and c or d' +assert a, "b and c or d" a = (b or c) and d -assert a, '(b or c) and d' +assert a, "(b or c) and d" a = b or c or d -assert a, 'b or c or d' +assert a, "b or c or d" a = b and c and d -assert not a, 'b and c and d' +assert not a, "b and c and d" a = b or c and d assert a a = b and (c or d) diff --git a/test/simple_source/stmts/03_if_elif.py b/test/simple_source/stmts/03_if_elif.py index 615a1b72..df51f39c 100644 --- a/test/simple_source/stmts/03_if_elif.py +++ b/test/simple_source/stmts/03_if_elif.py @@ -1,5 +1,5 @@ # 2.6.9 symbols.py -# Bug in 2.6 is having multple COME_FROMs due to the +# Bug in 2.6 is having multiple COME_FROMs due to the # "and" in the "if" clause # RUNNABLE @@ -10,7 +10,7 @@ if __name__: assert False # 2.6.9 transformer.py -# Bug in 2.6 is multple COME_FROMs as a result +# Bug in 2.6 is multiple COME_FROMs as a result # of the "or" in the "assert" # In PyPy the assert is handled via PyPy's unique JUMP_IF_NOT_DEBUG @@ -24,6 +24,7 @@ elif __file__: else: pass + # From 3.3.7 test_binop.py # Bug was in ifelsestmt(c) ensuring b+=5 is not in "else" # Also note: ifelsetmtc should not have been used since this @@ -36,6 +37,7 @@ def __floordiv__(a, b): b += 5 return b + assert __floordiv__(1, 1) == 7 assert __floordiv__(1, 0) == 6 assert __floordiv__(0, 3) == 8 diff --git a/uncompyle6/parsers/parse26.py b/uncompyle6/parsers/parse26.py index a8a247bc..c8b30f63 100644 --- a/uncompyle6/parsers/parse26.py +++ b/uncompyle6/parsers/parse26.py @@ -130,6 +130,10 @@ class Python26Parser(Python2Parser): # Semantic actions want else_suitel to be at index 3 ifelsestmtl ::= testexpr c_stmts_opt cf_jb_cf_pop else_suitel ifelsestmtc ::= testexpr c_stmts_opt ja_cf_pop else_suitec + ifelsestmt ::= testexpr stmts_opt ja_cf_pop else_suite + + # The last except of a "try: ... except" can do this... + except_suite ::= stmts_opt COME_FROM JUMP_ABSOLUTE POP_TOP # Semantic actions want suite_stmts_opt to be at index 3 with ::= expr setupwith SETUP_FINALLY suite_stmts_opt diff --git a/uncompyle6/parsers/parse3.py b/uncompyle6/parsers/parse3.py index e1097082..f8673bc0 100644 --- a/uncompyle6/parsers/parse3.py +++ b/uncompyle6/parsers/parse3.py @@ -711,7 +711,7 @@ class Python3Parser(PythonParser): # Note: BUILD_TUPLE_UNPACK_WITH_CALL gets considered by # default because it starts with BUILD. So we'll set to ignore it from # the start. - custom_ops_processed = set(("BUILD_TUPLE_UNPACK_WITH_CALL",)) + custom_ops_processed = {"BUILD_TUPLE_UNPACK_WITH_CALL"} # A set of instruction operation names that exist in the token stream. # We use this customize the grammar that we create. diff --git a/uncompyle6/parsers/parse37base.py b/uncompyle6/parsers/parse37base.py index 00d8062e..83b5b3b1 100644 --- a/uncompyle6/parsers/parse37base.py +++ b/uncompyle6/parsers/parse37base.py @@ -138,7 +138,7 @@ class Python37BaseParser(PythonParser): # Note: BUILD_TUPLE_UNPACK_WITH_CALL gets considered by # default because it starts with BUILD. So we'll set to ignore it from # the start. - custom_ops_processed = set(("BUILD_TUPLE_UNPACK_WITH_CALL",)) + custom_ops_processed = {"BUILD_TUPLE_UNPACK_WITH_CALL"} # A set of instruction operation names that exist in the token stream. # We use this customize the grammar that we create. diff --git a/uncompyle6/scanners/scanner26.py b/uncompyle6/scanners/scanner26.py index be5425fc..b32e881a 100755 --- a/uncompyle6/scanners/scanner26.py +++ b/uncompyle6/scanners/scanner26.py @@ -77,9 +77,15 @@ class Scanner26(Scanner2): # show_asm = 'after' if show_asm in ("both", "before"): print("\n# ---- disassembly:") - for instr in bytecode.get_instructions(co): - print(instr.disassemble(self.opc)) - + bytecode.disassemble_bytes( + co.co_code, + varnames=co.co_varnames, + names=co.co_names, + constants=co.co_consts, + cells=bytecode._cell_names, + line_starts=bytecode._linestarts, + asm_format="extended", + ) # Container for tokens tokens = [] diff --git a/uncompyle6/semantics/consts.py b/uncompyle6/semantics/consts.py index ce227760..715fe30f 100644 --- a/uncompyle6/semantics/consts.py +++ b/uncompyle6/semantics/consts.py @@ -43,7 +43,7 @@ maxint = sys.maxint # children. For example, "call" has precedence 2 so we don't get # additional the additional parenthesis of: ".. op (call())". However # for call's children, it parameters, we set the the precedence high, -# say to 100, to make sure we avoid additional prenthesis in +# say to 100, to make sure we avoid additional parenthesis in # call((.. op ..)). NO_PARENTHESIS_EVER = 100 @@ -56,6 +56,8 @@ PRECEDENCE = { "list_unpack": 38, # *args "yield_from": 38, "tuple_list_starred": 38, # *x, *y, *z - about at the level of yield? + "unpack": 38, # A guess. Used in "async with ... as ... + # This might also get used in tuple assignment? "_lambda_body": 30, "lambda_body": 32, # lambda ... : lambda_body @@ -129,7 +131,7 @@ LINE_LENGTH = 80 # Some parse trees created below are used for comparing code # fragments (like "return None" at the end of functions). -ASSIGN_DOC_STRING = lambda doc_string, doc_load: SyntaxTree( +ASSIGN_DOC_STRING = lambda doc_string, doc_load: SyntaxTree( # noqa "assign", [ SyntaxTree( @@ -246,12 +248,17 @@ TABLE_DIRECT = { "assert_expr_or": ("%c or %c", 0, 2), "assert_expr_and": ("%c and %c", 0, 2), + "assign": ( + "%|%c = %p\n", + -1, + (0, ("expr", "branch_op"), PRECEDENCE["tuple_list_starred"] + 1) + ), + "attribute": ("%c.%[1]{pattr}", (0, "expr")), # This nonterminal we create on the fly in semantic routines "attribute_w_parens": ("(%c).%[1]{pattr}", (0, "expr")), - "assign": ("%|%c = %p\n", -1, (0, 200)), # The 2nd parameter should have a = suffix. # There is a rule with a 4th parameter "store" # which we don't use here. @@ -267,6 +274,15 @@ TABLE_DIRECT = { (0, -1, ", ", NO_PARENTHESIS_EVER) ), + "call_stmt": ( "%|%p\n", + # When a call statement contains only a named_expr (:=) + # the named_expr should have parenthesis around it. + (0, PRECEDENCE["named_expr"]-1)), + + # "classdef": (), # handled by n_classdef() + # A custom rule in n_function def distinguishes whether to call this or + # function_def_async + "classdefdeco": ("\n\n%c", 0), "classdefdeco1": ("%|@%c\n%c", 0, 1), @@ -282,20 +298,30 @@ TABLE_DIRECT = { "continue": ("%|continue\n",), - # "classdef": (), # handled by n_classdef() - # A custom rule in n_function def distinguishes whether to call this or - # function_def_async - "delete_subscript": ( "%|del %p[%c]\n", (0, "expr", PRECEDENCE["subscript"]), (1, "expr"), ), + "designList": ("%c = %c", 0, -1), + "dict_comp_body": ("%c: %c", 1, 0), + + "elifelifstmt": ("%|elif %c:\n%+%c%-%c", 0, 1, 3), + "elifelsestmt": ("%|elif %c:\n%+%c%-%|else:\n%+%c%-", 0, 1, 3), + "elifelsestmtr": ("%|elif %c:\n%+%c%-%|else:\n%+%c%-\n\n", 0, 1, 2), + "elifelsestmtr2": ( + "%|elif %c:\n%+%c%-%|else:\n%+%c%-\n\n", + 0, + 1, + 3, + ), # has COME_FROM + "elifstmt": ("%|elif %c:\n%+%c%-", 0, 1), "except": ("%|except:\n%+%c%-", 3), "except_cond1": ("%|except %c:\n", 1), "except_cond2": ("%|except %c as %c:\n", (1, "expr"), (5, "store")), "except_suite": ("%+%c%-%C", 0, (1, maxint, "")), + # In Python 3.6+, this is more complicated in the presence of "returns" "except_suite_finalize": ("%+%c%-%C", 1, (3, maxint, "")), @@ -304,7 +330,7 @@ TABLE_DIRECT = { # When a statement contains only a named_expr (:=) # the named_expr should have parenthesis around it. (0, "expr", PRECEDENCE["named_expr"] - 1) - ), + ), # Note: Python 3.8+ changes this "for": ("%|for %c in %c:\n%+%c%-\n\n", (3, "store"), (1, "expr"), (4, "for_block")), @@ -331,15 +357,12 @@ TABLE_DIRECT = { -2, ), + "function_def": ("\n\n%|def %c\n", -2), # -2 to handle closures + "function_def_deco": ("\n\n%c", 0), + + "gen_comp_body": ("%c", 0), "get_iter": ("iter(%c)", (0, "expr"),), - "set_comp_body": ("%c", 0), - "gen_comp_body": ("%c", 0), - "dict_comp_body": ("%c: %c", 1, 0), - "designList": ("%c = %c", 0, -1), - "ret_and": ("%c and %c", 0, 2), - "or": ("%p or %p", (0, PRECEDENCE["or"]), (1, PRECEDENCE["or"])), - "ret_or": ("%c or %c", 0, 2), "if_exp": ("%p if %c else %c", (2, "expr", 27), 0, 4), "if_exp_lambda": ("%p if %c else %c", (2, "expr", 27), (0, "expr"), 4), "if_exp_true": ("%p if 1 else %c", (0, "expr", 27), 2), @@ -352,28 +375,6 @@ TABLE_DIRECT = { ), "if_exp_not_lambda": ("%p if not %c else %c", (2, "expr", 27), 0, 4), - "function_def": ("\n\n%|def %c\n", -2), # -2 to handle closures - "function_def_deco": ("\n\n%c", 0), - - # This is only generated by transform - # it is a string at the beginning of a function that is *not* a docstring - # 3.7 test_fstring.py tests for this kind of crap. - # For compatibility with older Python, we'll use "%" instead of - # a format string. - "string_at_beginning": ('%|"%%s" %% %c\n', 0), - "call_stmt": ( "%|%p\n", - # When a call statement contains only a named_expr (:=) - # the named_expr should have parenthesis around it. - (0, PRECEDENCE["named_expr"]-1)), - - "ifstmt": ( - "%|if %c:\n%+%c%-", - 0, # "testexpr" or "testexpr_then" - 1, # "_ifstmts_jump" or "return_stmts" - ), - "iflaststmt": ("%|if %c:\n%+%c%-", 0, 1), - "iflaststmtl": ("%|if %c:\n%+%c%-", 0, 1), - "testtrue": ("not %p", (0, PRECEDENCE["unary_not"])), # Generally the args here are 0: (some sort of) "testexpr", # 1: (some sort of) "cstmts_opt", # 2 or 3: "else_suite" @@ -383,20 +384,21 @@ TABLE_DIRECT = { "ifelsestmt": ("%|if %c:\n%+%c%-%|else:\n%+%c%-", 0, 1, 3), "ifelsestmtc": ("%|if %c:\n%+%c%-%|else:\n%+%c%-", 0, 1, 3), "ifelsestmtl": ("%|if %c:\n%+%c%-%|else:\n%+%c%-", 0, 1, 3), - # These are created only via transformation + + # This is created only via transformation. "ifelifstmt": ("%|if %c:\n%+%c%-%c", 0, 1, 3), # "testexpr" or "testexpr_then" - "elifelifstmt": ("%|elif %c:\n%+%c%-%c", 0, 1, 3), - "elifstmt": ("%|elif %c:\n%+%c%-", 0, 1), - "elifelsestmt": ("%|elif %c:\n%+%c%-%|else:\n%+%c%-", 0, 1, 3), + "ifelsestmtr": ("%|if %c:\n%+%c%-%|else:\n%+%c%-", 0, 1, 2), "ifelsestmtr2": ("%|if %c:\n%+%c%-%|else:\n%+%c%-\n\n", 0, 1, 3), # has COME_FROM - "elifelsestmtr": ("%|elif %c:\n%+%c%-%|else:\n%+%c%-\n\n", 0, 1, 2), - "elifelsestmtr2": ( - "%|elif %c:\n%+%c%-%|else:\n%+%c%-\n\n", - 0, - 1, - 3, - ), # has COME_FROM + "iflaststmt": ("%|if %c:\n%+%c%-", 0, 1), + + "iflaststmtl": ("%|if %c:\n%+%c%-", 0, 1), + + "ifstmt": ( + "%|if %c:\n%+%c%-", + 0, # "testexpr" or "testexpr_then" + 1, # "_ifstmts_jump" or "return_stmts" + ), "import": ("%|import %c\n", 2), "importlist": ("%C", (0, maxint, ", ")), @@ -414,6 +416,7 @@ TABLE_DIRECT = { "kv": ("%c: %c", 3, 1), "kv2": ("%c: %c", 1, 2), + "kwarg": ("%[0]{pattr}=%c", 1), # Change when Python 2 does LOAD_STR "kwargs": ("%D", (0, maxint, ", ")), "kwargs1": ("%D", (0, maxint, ", ")), @@ -424,10 +427,10 @@ TABLE_DIRECT = { "list_if": (" if %p%c", (0, "expr", 27), 2), "list_if_not": (" if not %p%c", (0, "expr", PRECEDENCE["unary_not"]), 2), - "mkfuncdeco": ("%|@%c\n%c", 0, 1), + "mkfuncdeco": ("%|@%c\n%c", (0, "expr"), 1), # A custom rule in n_function def distinguishes whether to call this or # function_def_async - "mkfuncdeco0": ("%|def %c\n", 0), + "mkfuncdeco0": ("%|def %c\n", (0, "mkfunc")), # In cases where we desire an explict new line. # After docstrings which are followed by a "def" is @@ -435,6 +438,10 @@ TABLE_DIRECT = { # and this is added, as a transformation rule. "newline": ("\n"), + "or": ("%p or %p", (0, PRECEDENCE["or"]), (1, PRECEDENCE["or"])), + + "pass": ("%|pass\n",), + "print_item": (", %c", 0), "print_items_nl_stmt": ("%|print %c%c\n", 0, 2), "print_items_stmt": ("%|print %c%c,\n", 0, 2), # Python 2 only @@ -444,16 +451,19 @@ TABLE_DIRECT = { "print_to_items": ("%C", (0, 2, ", ")), "print_to_nl": ("%|print >> %c, %c\n", 0, 1), - "pass": ("%|pass\n",), - "raise_stmt0": ("%|raise\n",), "raise_stmt1": ("%|raise %c\n", 0), "raise_stmt3": ("%|raise %c, %c, %c\n", 0, 1, 2), + "ret_and": ("%c and %c", 0, 2), + "ret_or": ("%c or %c", 0, 2), + # Note: we have a custom rule, which calls when we don't # have "return None" "return": ( "%|return %c\n", 0), + "set_comp_body": ("%c", 0), + "set_iter": ( "%c", 0 ), "return_if_stmt": ("return %c\n", 0), @@ -490,6 +500,13 @@ TABLE_DIRECT = { (0, "expr") ), + # This is only generated by transform + # it is a string at the beginning of a function that is *not* a docstring + # 3.7 test_fstring.py tests for this kind of crap. + # For compatibility with older Python, we'll use "%" instead of + # a format string. + "string_at_beginning": ('%|"%%s" %% %c\n', 0), + "subscript": ( "%p[%p]", (0, "expr", PRECEDENCE["subscript"]), @@ -502,13 +519,16 @@ TABLE_DIRECT = { (1, "expr", NO_PARENTHESIS_EVER) ), + "testtrue": ("not %p", (0, PRECEDENCE["unary_not"])), + + # Note: this is generated generated by grammar rules but in this phase. + "tf_try_except": ("%c%-%c%+", 1, 3), + "tf_tryelsestmt": ("%c%-%c%|else:\n%+%c", 1, 3, 4), + "try_except": ("%|try:\n%+%c%-%c\n\n", 1, 3), "tryelsestmt": ("%|try:\n%+%c%-%c%|else:\n%+%c%-\n\n", 1, 3, 4), "tryelsestmtc": ("%|try:\n%+%c%-%c%|else:\n%+%c%-", 1, 3, 4), "tryelsestmtl": ("%|try:\n%+%c%-%c%|else:\n%+%c%-", 1, 3, 4), - # Note: this is generated generated by grammar rules but in this phase. - "tf_try_except": ("%c%-%c%+", 1, 3), - "tf_tryelsestmt": ("%c%-%c%|else:\n%+%c", 1, 3, 4), "tryfinallystmt": ("%|try:\n%+%c%-%|finally:\n%+%c%-\n\n", 1, 5), # unary_op (formerly "unary_expr") is the Python AST UnaryOp @@ -541,6 +561,7 @@ TABLE_DIRECT = { # "yield": ( "yield %c", 0), } +# fmt: on MAP_DIRECT = (TABLE_DIRECT,) @@ -553,7 +574,7 @@ MAP = { "store": MAP_R, } -ASSIGN_TUPLE_PARAM = lambda param_name: SyntaxTree( +ASSIGN_TUPLE_PARAM = lambda param_name: SyntaxTree( # noqa "expr", [Token("LOAD_FAST", pattr=param_name)] )