From 21920b5852b69172d0cb0928adca6cec59c5edf0 Mon Sep 17 00:00:00 2001 From: rocky Date: Sun, 20 Dec 2015 17:16:05 -0500 Subject: [PATCH] First list comprehensions from Python3. More test makefile hacking --- test/Makefile | 13 +++-- test/bytecode_2.7/01-lc.pyc | Bin 0 -> 197 bytes test/bytecode_3.4/01-lc.pyc | Bin 0 -> 436 bytes test/simple-source/comprehension/01-lc.py | 18 ++++++ test/test_pythonlib.py | 3 + uncompyle6/deparser.py | 60 ++++++++++++++++++-- uncompyle6/parsers/parse3.py | 8 ++- uncompyle6/scanners/scanner34.py | 24 +++++++- uncompyle6/walker.py | 64 ++++++++++++++++++++-- 9 files changed, 172 insertions(+), 18 deletions(-) create mode 100644 test/bytecode_2.7/01-lc.pyc create mode 100644 test/bytecode_3.4/01-lc.pyc create mode 100644 test/simple-source/comprehension/01-lc.py diff --git a/test/Makefile b/test/Makefile index e463313f..b3d82c9a 100644 --- a/test/Makefile +++ b/test/Makefile @@ -10,10 +10,10 @@ NATIVE_CHECK = check-$(PYTHON_VERSION) COMPILE ?= #: Run working tests from Python 2.7 -check-2.7: check-short-2.7 check-bytecode check-2.7-ok +check-2.7: check-short-2.7 check-bytecode-2.5 check-2.7-ok #: Run working tests from Python 3.4 -check-3.4: check-short-3.4 check-short-2.7 check-bytecode +check-3.4: check-short-3.4 check-short-2.7 check-bytecode-2.5 check-bytecode-3.2 check: check-short @$(PYTHON) -V && PYTHON_VERSION=`$(PYTHON) -V 2>&1 | cut -d ' ' -f 2 | cut -d'.' -f1,2`; \ @@ -27,10 +27,15 @@ check-short: check-disasm: $(PYTHON) dis-compare.py -#: Check deparsing only, but from a different Python version -check-bytecode: +#: Check deparsing only, from Python 2.5 +check-bytecode-2.5: $(PYTHON) test_pythonlib.py --bytecode-2.5 + +#: Check deparsing only, from Python 3.2 +check-bytecode-3.2: + $(PYTHON) test_pythonlib.py --bytecode-3.2 + #: short tests for bytecodes only for this version of Python check-native-short: $(PYTHON) test_pythonlib.py --bytecode-$(PYTHON_VERSION) --verify $(COMPILE) diff --git a/test/bytecode_2.7/01-lc.pyc b/test/bytecode_2.7/01-lc.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9ac619fdddf6ed72c0d9273409ec0528c1ad2f27 GIT binary patch literal 197 zcmZSn%*(|!vn(u`0SZ`wv;z0NA`ABme*a literal 0 HcmV?d00001 diff --git a/test/bytecode_3.4/01-lc.pyc b/test/bytecode_3.4/01-lc.pyc new file mode 100644 index 0000000000000000000000000000000000000000..70659be9d6b01d7956e0ca1da18f452e192612cf GIT binary patch literal 436 zcmaFI!^>r$P#!j)k%8ec0}@~bvK@f9SPe*|01+cY3y|bsaA{^_U`%0y32;IM*cpN~ zxsrjVfB_SbWCmhqATAaI66p*KH4F@~JPfsr3^j}l6PXwa*%*RXG8BPyXfoYm(lfZl zmej`FZ*VhPpY)dIgogxNLGVi%UR~ zc03@fK=yJn6oDN75@hmAkibijOJ1@72^9B%1i': + opname = 'LOAD_LAMBDA' + elif const.co_name == '': + opname = 'LOAD_GENEXPR' + elif const.co_name == '': + opname = 'LOAD_DICTCOMP' + elif const.co_name == '': + opname = 'LOAD_SETCOMP' + elif const.co_name == '': + opname = 'LOAD_LISTCOMP' + # verify() uses 'pattr' for comparison, since 'attr' + # now holds Code(const) and thus can not be used + # for comparison (todo: think about changing this) + # pattr = 'code_object @ 0x%x %s->%s' %\ + # (id(const), const.co_filename, const.co_name) + pattr = '' + else: + pattr = const + pass elif opname in ('BUILD_LIST', 'BUILD_TUPLE', 'BUILD_SET', 'BUILD_SLICE', 'UNPACK_SEQUENCE', 'MAKE_FUNCTION', 'MAKE_CLOSURE', diff --git a/uncompyle6/walker.py b/uncompyle6/walker.py index fc2bbae4..e430d80e 100644 --- a/uncompyle6/walker.py +++ b/uncompyle6/walker.py @@ -910,9 +910,12 @@ class Walker(GenericASTTraversal, object): self.prune() # stop recursing def n_list_compr(self, node): + """List comprehensions the way they are done in Python2. + """ p = self.prec self.prec = 27 n = node[-1] + assert n == 'list_iter' # find innerst node while n == 'list_iter': @@ -928,10 +931,10 @@ class Walker(GenericASTTraversal, object): self.prec = p self.prune() # stop recursing - def comprehension_walk(self, node, iter_index): + def comprehension_walk(self, node, iter_index, code_index=-5): p = self.prec self.prec = 27 - code = node[-5].attr + code = node[code_index].attr assert inspect.iscode(code) code = Code(code, self.scanner, self.currentclass) @@ -940,11 +943,10 @@ class Walker(GenericASTTraversal, object): ast = self.build_ast(code._tokens, code._customize) self.customize(code._customize) ast = ast[0][0][0] - n = ast[iter_index] assert n == 'comp_iter' # find innerst node - while n == 'comp_iter': + while n == 'comp_iter': # list_iter n = n[0] # recurse one step if n == 'comp_for': n = n[3] elif n == 'comp_if': n = n[2] @@ -961,16 +963,66 @@ class Walker(GenericASTTraversal, object): def n_genexpr(self, node): self.write('(') - self.comprehension_walk(node, 3) + self.comprehension_walk(node, iter_index=3) self.write(')') self.prune() def n_setcomp(self, node): self.write('{') - self.comprehension_walk(node, 4) + self.comprehension_walk(node, iter_index=4) self.write('}') self.prune() + def listcomprehension_walk3(self, node, iter_index, code_index=-5): + """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 = node[code_index].attr + + assert inspect.iscode(code) + code = Code(code, self.scanner, self.currentclass) + # assert isinstance(code, Code) + + ast = self.build_ast(code._tokens, code._customize) + self.customize(code._customize) + ast = ast[0][0][0][0][0] + + n = ast[iter_index] + 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 == 'list_if': + # FIXME: just a guess + designator = n[1] + + n = n[2] + elif n == 'list_ifnot': + # FIXME: just a guess + designator = n[1] + n = n[2] + assert n == 'lc_body', ast + + self.preorder(n[0]) + self.write(' for ') + self.preorder(designator) + self.write(' in ') + self.preorder(node[-3]) + # self.preorder(ast[iter_index]) + self.prec = p + + def n_listcomp(self, node): + self.write('[') + self.listcomprehension_walk3(node, iter_index=1, code_index=0) + self.write(']') + self.prune() + n_dictcomp = n_setcomp def n_classdef(self, node):