From 293e7b036766e6a1c3fbde0ac5408b8d664282bd Mon Sep 17 00:00:00 2001 From: rocky Date: Thu, 2 May 2019 06:43:53 -0400 Subject: [PATCH] store_subscript precedence fix and... Allow format specifier "%p" to indicate a nonterminal name, like "%c" allows. store_subscr -> store_subscript to match Python AST a little closer. --- pytest/test_pysource.py | 14 +- test/bytecode_2.7_run/04_subscript.pyc | Bin 0 -> 332 bytes test/bytecode_3.6_run/04_subscript.pyc | Bin 0 -> 263 bytes test/simple_source/expression/04_subscript.py | 11 ++ uncompyle6/parser.py | 18 +- uncompyle6/semantics/consts.py | 160 +++++++++--------- uncompyle6/semantics/pysource.py | 18 +- 7 files changed, 127 insertions(+), 94 deletions(-) create mode 100644 test/bytecode_2.7_run/04_subscript.pyc create mode 100644 test/bytecode_3.6_run/04_subscript.pyc create mode 100644 test/simple_source/expression/04_subscript.py diff --git a/pytest/test_pysource.py b/pytest/test_pysource.py index 902222c0..bc866671 100644 --- a/pytest/test_pysource.py +++ b/pytest/test_pysource.py @@ -127,11 +127,17 @@ def test_tables(): "Full entry: %s" % (name, k, arg, typ, entry[arg], type(entry[arg]), entry) ) - assert len(tup) == 2 + assert 2 <= len(tup) <= 3 for j, x in enumerate(tup): - assert isinstance(x, int), ( - "%s[%s][%d][%d] type '%s' is '%s should be an int but is %s. Full entry: %s" % - (name, k, arg, j, typ, x, type(x), entry) + if len(tup) == 3 and j == 1: + assert isinstance(x, str), ( + "%s[%s][%d][%d] type '%s' is '%s should be an string but is %s. Full entry: %s" % + (name, k, arg, j, typ, x, type(x), entry) + ) + else: + assert isinstance(x, int), ( + "%s[%s][%d][%d] type '%s' is '%s should be an int but is %s. Full entry: %s" % + (name, k, arg, j, typ, x, type(x), entry) ) pass arg += 1 diff --git a/test/bytecode_2.7_run/04_subscript.pyc b/test/bytecode_2.7_run/04_subscript.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3cd38e7c2ed88a0e40ba6f9424738e4c8d5b4082 GIT binary patch literal 332 zcmYLC!AiqG6r9Z_R*LcN$!kEc9z-aJDCkKjhzB85icMZ+F-_vW7fi2ymuCt_)Hb-fCFW>ZYx@F)A@oX8ui4i mfyvqVg1aRb-nDYtZe(8{U#^?7tLQ59@mL`>sj=q397lfu{6SR! literal 0 HcmV?d00001 diff --git a/test/bytecode_3.6_run/04_subscript.pyc b/test/bytecode_3.6_run/04_subscript.pyc new file mode 100644 index 0000000000000000000000000000000000000000..baaf71a487ced9490fdf2e1a545bab0a2ae886c7 GIT binary patch literal 263 zcmX|(u}T9$5Qb-V_l_eFOTor6n8pZ7DiH)ZGa))qd( zwpMuqvC3%S!2kVI%r~d~e((L`bNUScUy}bZvRz8}R^Y&4jrO;dG # Copyright (c) 2000-2002 by hartmut Goebel # Copyright (c) 1999 John Aycock @@ -589,14 +589,14 @@ class PythonParser(GenericASTBuilder): ## designLists ::= ## Will need to redo semantic actiion - store ::= STORE_FAST - store ::= STORE_NAME - store ::= STORE_GLOBAL - store ::= STORE_DEREF - store ::= expr STORE_ATTR - store ::= store_subscr - store_subscr ::= expr expr STORE_SUBSCR - store ::= unpack + store ::= STORE_FAST + store ::= STORE_NAME + store ::= STORE_GLOBAL + store ::= STORE_DEREF + store ::= expr STORE_ATTR + store ::= store_subscript + store_subscript ::= expr expr STORE_SUBSCR + store ::= unpack ''' diff --git a/uncompyle6/semantics/consts.py b/uncompyle6/semantics/consts.py index 6183e060..719b5789 100644 --- a/uncompyle6/semantics/consts.py +++ b/uncompyle6/semantics/consts.py @@ -27,6 +27,78 @@ else: maxint = sys.maxint +# Operator precidence +# See https://docs.python.org/2/reference/expressions.html +# or https://docs.python.org/3/reference/expressions.html +# for a list. + +# Things at the top of this list below with low-value precidence will +# tend to have parenthesis around them. Things at the bottom +# of the list will tend not to have parenthesis around them. +PRECEDENCE = { + 'list': 0, + 'dict': 0, + 'unary_convert': 0, + 'dict_comp': 0, + 'set_comp': 0, + 'set_comp_expr': 0, + 'list_comp': 0, + 'generator_exp': 0, + + 'attribute': 2, + 'subscript': 2, + 'subscript2': 2, + 'store_subscript': 2, + 'delete_subscr': 2, + 'slice0': 2, + 'slice1': 2, + 'slice2': 2, + 'slice3': 2, + 'buildslice2': 2, + 'buildslice3': 2, + 'call': 2, + + 'BINARY_POWER': 4, + + 'unary_expr': 6, + + 'BINARY_MULTIPLY': 8, + 'BINARY_DIVIDE': 8, + 'BINARY_TRUE_DIVIDE': 8, + 'BINARY_FLOOR_DIVIDE': 8, + 'BINARY_MODULO': 8, + + 'BINARY_ADD': 10, + 'BINARY_SUBTRACT': 10, + + 'BINARY_LSHIFT': 12, + 'BINARY_RSHIFT': 12, + + 'BINARY_AND': 14, + 'BINARY_XOR': 16, + 'BINARY_OR': 18, + + 'compare': 20, + 'unary_not': 22, + 'and': 24, + 'ret_and': 24, + + 'or': 26, + 'ret_or': 26, + + 'conditional': 28, + 'conditional_lamdba': 28, + 'conditional_not_lamdba': 28, + 'conditionalnot': 28, + 'ret_cond': 28, + 'ret_cond_not': 28, + + '_mklambda': 30, + + 'yield': 101, + 'yield_from': 101 +} + LINE_LENGTH = 80 # Some parse trees created below are used for comparing code @@ -150,15 +222,17 @@ TABLE_DIRECT = { 'DELETE_FAST': ( '%|del %{pattr}\n', ), 'DELETE_NAME': ( '%|del %{pattr}\n', ), 'DELETE_GLOBAL': ( '%|del %{pattr}\n', ), - 'delete_subscr': ( '%|del %c[%c]\n', - (0, 'expr'), (1, 'expr') ), - 'subscript': ( '%c[%p]', - (0, 'expr'), - (1, 100) ), - 'subscript2': ( '%c[%c]', - (0, 'expr'), + 'delete_subscr': ( '%|del %p[%c]\n', + (0, 'expr', PRECEDENCE['subscript']), (1, 'expr') ), + 'subscript': ( '%p[%c]', + (0, 'expr', PRECEDENCE['subscript']), (1, 'expr') ), - 'store_subscr': ( '%c[%c]', 0, 1), + 'subscript2': ( '%p[%c]', + (0, 'expr', PRECEDENCE['subscript']), + (1, 'expr') ), + 'store_subscript': ( '%p[%c]', + (0, 'expr', PRECEDENCE['subscript']), + (1, 'expr') ), 'STORE_FAST': ( '%{pattr}', ), 'STORE_NAME': ( '%{pattr}', ), 'STORE_GLOBAL': ( '%{pattr}', ), @@ -337,76 +411,6 @@ MAP = { 'exprlist': MAP_R0, } -# Operator precidence -# See https://docs.python.org/2/reference/expressions.html -# or https://docs.python.org/3/reference/expressions.html -# for a list. - -# Things at the top of this list below with low-value precidence will -# tend to have parenthesis around them. Things at the bottom -# of the list will tend not to have parenthesis around them. -PRECEDENCE = { - 'list': 0, - 'dict': 0, - 'unary_convert': 0, - 'dict_comp': 0, - 'set_comp': 0, - 'set_comp_expr': 0, - 'list_comp': 0, - 'generator_exp': 0, - - 'attribute': 2, - 'subscript': 2, - 'subscript2': 2, - 'slice0': 2, - 'slice1': 2, - 'slice2': 2, - 'slice3': 2, - 'buildslice2': 2, - 'buildslice3': 2, - 'call': 2, - - 'BINARY_POWER': 4, - - 'unary_expr': 6, - - 'BINARY_MULTIPLY': 8, - 'BINARY_DIVIDE': 8, - 'BINARY_TRUE_DIVIDE': 8, - 'BINARY_FLOOR_DIVIDE': 8, - 'BINARY_MODULO': 8, - - 'BINARY_ADD': 10, - 'BINARY_SUBTRACT': 10, - - 'BINARY_LSHIFT': 12, - 'BINARY_RSHIFT': 12, - - 'BINARY_AND': 14, - 'BINARY_XOR': 16, - 'BINARY_OR': 18, - - 'compare': 20, - 'unary_not': 22, - 'and': 24, - 'ret_and': 24, - - 'or': 26, - 'ret_or': 26, - - 'conditional': 28, - 'conditional_lamdba': 28, - 'conditional_not_lamdba': 28, - 'conditionalnot': 28, - 'ret_cond': 28, - 'ret_cond_not': 28, - - '_mklambda': 30, - - 'yield': 101, - 'yield_from': 101 -} - ASSIGN_TUPLE_PARAM = lambda param_name: \ SyntaxTree('expr', [ Token('LOAD_FAST', pattr=param_name) ]) diff --git a/uncompyle6/semantics/pysource.py b/uncompyle6/semantics/pysource.py index b254b22d..4edeaaca 100644 --- a/uncompyle6/semantics/pysource.py +++ b/uncompyle6/semantics/pysource.py @@ -88,7 +88,8 @@ Python. # # %p like %c but sets the operator precedence. # Its argument then is a tuple indicating the node -# index and the precidence value, an integer. +# index and the precedence value, an integer. If 3 items are given, +# the second item is the nonterminal name and the precedence is given last. # # %C evaluate children recursively, with sibling children separated by the # given string. It needs a 3-tuple: a starting node, the maximimum @@ -616,7 +617,7 @@ class SourceWalker(GenericASTTraversal, object): node[-2][0].kind = 'build_tuple2' self.default(node) - n_store_subscr = n_subscript = n_delete_subscr + n_store_subscript = n_subscript = n_delete_subscr # Note: this node is only in Python 2.x # FIXME: figure out how to get this into customization @@ -1873,7 +1874,18 @@ class SourceWalker(GenericASTTraversal, object): arg += 1 elif typ == 'p': p = self.prec - (index, self.prec) = entry[arg] + tup = entry[arg] + assert isinstance(tup, tuple) + if len(tup) == 3: + (index, nonterm_name, self.prec) = tup + assert node[index] == nonterm_name, ( + "at %s[%d], expected '%s' node; got '%s'" % ( + node.kind, arg, nonterm_name, node[index].kind) + ) + else: + assert len(tup) == 2 + (index, self.prec) = entry[arg] + self.preorder(node[index]) self.prec = p arg += 1