diff --git a/test/bytecode_3.2/11-list-if.pyc b/test/bytecode_3.2/11-list-if.pyc new file mode 100644 index 00000000..f0586861 Binary files /dev/null and b/test/bytecode_3.2/11-list-if.pyc differ diff --git a/test/bytecode_3.5/11-list-if.pyc b/test/bytecode_3.5/11-list-if.pyc new file mode 100644 index 00000000..266c984a Binary files /dev/null and b/test/bytecode_3.5/11-list-if.pyc differ diff --git a/test/simple_source/comprehension/11-list-if.py b/test/simple_source/comprehension/11-list-if.py new file mode 100644 index 00000000..a3b40f94 --- /dev/null +++ b/test/simple_source/comprehension/11-list-if.py @@ -0,0 +1,8 @@ +# Test Python 3.x test semantic handling of +# +# load_closure ::= LOAD_CLOSURE BUILD_TUPLE_1 +# ... +# listcomp ::= load_closure LOAD_LISTCOMP LOAD_CONST MAKE_CLOSURE_0 expr GET_ITER CALL_FUNCTION_1 + +def long_has_args(opt, longopts): + return [o for o in longopts if o.startswith(opt)] diff --git a/uncompyle6/parsers/parse3.py b/uncompyle6/parsers/parse3.py index b933099d..30dac9be 100644 --- a/uncompyle6/parsers/parse3.py +++ b/uncompyle6/parsers/parse3.py @@ -53,6 +53,8 @@ class Python3Parser(PythonParser): list_for ::= expr FOR_ITER designator list_iter JUMP_BACK # See also common Python p_list_comprehension + + load_closure ::= LOAD_CLOSURE BUILD_TUPLE_1 """ def p_setcomp3(self, args): @@ -426,7 +428,7 @@ class Python3Parser(PythonParser): listcomp ::= LOAD_LISTCOMP MAKE_FUNCTION_0 expr GET_ITER CALL_FUNCTION_1 - build_class (see load_build_class) + # build_class (see load_build_class) build_list ::= {expr}^n BUILD_LIST_n build_list ::= {expr}^n BUILD_TUPLE_n @@ -438,7 +440,9 @@ class Python3Parser(PythonParser): mklambda ::= {expr}^n LOAD_LAMBDA MAKE_FUNCTION_n mkfunc ::= {expr}^n load_closure LOAD_CONST MAKE_FUNCTION_n - call_function (see custom_classfunc_rule) + listcomp ::= load_closure expr GET_ITER CALL_FUNCTION_1" + + # call_function (see custom_classfunc_rule) """ # from trepan.api import debug # debug(start_opts={'startup-profile': True}) @@ -503,6 +507,19 @@ class Python3Parser(PythonParser): 'expr GET_ITER CALL_FUNCTION_1' % ('expr ' * token.attr, opname), opname, token.attr, customize) + if self.version >= 3.4: + self.add_unique_rule('listcomp ::= %s load_closure ' + 'LOAD_LISTCOMP LOAD_CONST %s expr ' + 'GET_ITER CALL_FUNCTION_1' % + ('expr ' * token.attr, opname), + opname, token.attr, customize) + else: + self.add_unique_rule('listcomp ::= %s load_closure ' + 'LOAD_LISTCOMP %s expr ' + 'GET_ITER CALL_FUNCTION_1' % + ('expr ' * token.attr, opname), + opname, token.attr, customize) + self.add_unique_rule('setcomp ::= %s load_closure LOAD_SETCOMP %s expr ' 'GET_ITER CALL_FUNCTION_1' % ('expr ' * token.attr, opname), diff --git a/uncompyle6/semantics/pysource.py b/uncompyle6/semantics/pysource.py index b7405754..6502cc0d 100644 --- a/uncompyle6/semantics/pysource.py +++ b/uncompyle6/semantics/pysource.py @@ -1053,16 +1053,12 @@ class SourceWalker(GenericASTTraversal, object): if n == 'list_for': designator = n[2] n = n[3] - elif n == 'list_if': - # FIXME: just a guess - designator = n[1] - - n = n[2] - elif n == 'list_if_not': + elif n in ['list_if', 'list_if_not']: # FIXME: just a guess designator = n[1] n = n[2] - + pass + pass assert n == 'lc_body', ast self.preorder(n[0]) @@ -1070,12 +1066,59 @@ class SourceWalker(GenericASTTraversal, object): self.preorder(designator) self.write(' in ') self.preorder(node[-3]) - # self.preorder(ast[iter_index]) + self.prec = p + + def listcomprehension_walk2(self, node): + """List comprehensions the way they are done in Python3. + They're more other comprehensions, e.g. set comprehensions + See if we can combine code. + """ + p = self.prec + self.prec = 27 + + code = Code(node[1].attr, self.scanner, self.currentclass) + ast = self.build_ast(code._tokens, code._customize) + self.customize(code._customize) + ast = ast[0][0][0][0][0] + + n = ast[1] + collection = node[-3] + list_if = None + assert n == 'list_iter' + + # find innermost node + while n == 'list_iter': # list_iter + n = n[0] # recurse one step + if n == 'list_for': + designator = n[2] + n = n[3] + elif n in ('list_if', 'list_if_not'): + # FIXME: just a guess + if n[0].type == 'expr': + list_if = n + else: + list_if = n[1] + n = n[2] + pass + pass + + assert n == 'lc_body', ast + + self.preorder(n[0]) + self.write(' for ') + self.preorder(designator) + self.write(' in ') + self.preorder(collection) + if list_if: + self.preorder(list_if) self.prec = p def n_listcomp(self, node): self.write('[') - self.listcomprehension_walk3(node, iter_index=1, code_index=0) + if node[0].type == 'load_closure': + self.listcomprehension_walk2(node) + else: + self.listcomprehension_walk3(node, 1, 0) self.write(']') self.prune()