Commits

gstarnberger committed a5b9295

Rebranded back to 'uncompyle'

Comments (0)

Files changed (56)

 
-                 uncompyle2 -- A Python byte-code decompiler
+                 uncompyle -- A Python byte-code decompiler
                           0.11
                           2011-1-9
 
 Introduction
 ------------
 
-'uncompyle2' converts Python byte-code back into equivalent Python
+'uncompyle' converts Python byte-code back into equivalent Python
 source. It accepts byte-code from Python version 2.7 only.
 
 The generated source is very readable: docstrings, lists, tuples and
 hashes get pretty-printed.
 
-'uncompyle2' may also verify the equivalence of the generated source by
+'uncompyle' may also verify the equivalence of the generated source by
 by compiling it and comparing both byte-codes.
 
-'uncompyle2' is based on John Aycock's generic small languages compiler
+'uncompyle' is based on John Aycock's generic small languages compiler
 'spark' (http://www.csr.uvic.ca/~aycock/python/) and his prior work on
-'uncompyle'.
+'decompyle'.
 
 Additional note (3 July 2004, Ben Burton):
 
   * decompiles byte-code from Python version 2.7
 
   * pretty-prints docstrings, hashes, lists and tuples
-  
+
   * reads directly from .pyc/.pyo files, bulk-decompile whole
     directories
 
 Usage
 -----
 
-uncompyle2 -h		prints short usage
-uncompyle2 --help	prints long usage
+uncompyler -h		prints short usage
+uncompyler --help	prints long usage
 
 
 Known Bugs/Restrictions

scripts/uncompyle.py

-#!/usr/bin/env python2.7
-# Mode: -*- python -*-
-#
-# Copyright (c) 2000-2002 by hartmut Goebel <hartmut@goebel.noris.de>
-#
-"""
-Usage: uncompyle [OPTIONS]... [ FILE | DIR]...
-
-Examples:
-  uncompyle      foo.pyc bar.pyc       # uncompyle foo.pyc, bar.pyc to stdout
-  uncompyle -o . foo.pyc bar.pyc       # uncompyle to ./foo.dis and ./bar.dis
-  uncompyle -o /tmp /usr/lib/python1.5 # uncompyle whole library
-
-Options:
-  -o <path>     output decompiled files to this path:
-                if multiple input files are decompiled, the common prefix
-                is stripped from these names and the remainder appended to
-                <path>
-                  uncompyle -o /tmp bla/fasel.pyc bla/foo.pyc
-                    -> /tmp/fasel.dis, /tmp/foo.dis
-                  uncompyle -o /tmp bla/fasel.pyc bar/foo.pyc
-                    -> /tmp/bla/fasel.dis, /tmp/bar/foo.dis
-                  uncompyle -o /tmp /usr/lib/python1.5
-                    -> /tmp/smtplib.dis ... /tmp/lib-tk/FixTk.dis
-  -c <file>     attempts a disassembly after compiling <file>
-  -d            do not print timestamps
-  -p <integer>  use <integer> number of processes
-  -r            recurse directories looking for .pyc and .pyo files
-  --verify      compare generated source with input byte-code
-                (requires -o)
-  --help        show this message
-
-Debugging Options:
-  --showasm   -a  include byte-code                  (disables --verify)
-  --showast   -t  include AST (abstract syntax tree) (disables --verify)
-
-Extensions of generated files:
-  '.dis'             successfully decompiled (and verified if --verify)
-  '.dis_unverified'  successfully decompile but --verify failed
-  '.nodis'           uncompyle failed (contact author for enhancement)
-"""
-from threading import Thread
-from multiprocessing import Process, Queue
-from Queue import Empty
-from uncompyle2 import main, verify
-
-def process_func(src_base, out_base, codes, outfile, showasm, showast, do_verify, fqueue, rqueue):
-    try:
-      (tot_files, okay_files, failed_files, verify_failed_files) = (0,0,0,0)
-      while 1:
-          f = fqueue.get()
-          if f == None:
-              break
-          (t, o, f, v) = \
-              main(src_base, out_base, [f], codes, outfile, showasm, showast, do_verify)
-          tot_files += t
-          okay_files += o
-          failed_files += f
-          verify_failed_files += v
-    except (Empty, KeyboardInterrupt, OSError):
-      pass
-    rqueue.put((tot_files, okay_files, failed_files, verify_failed_files))
-    rqueue.close()
-
-if __name__ == '__main__':
-    Usage_short = \
-    "decomyple [--help] [--verify] [--showasm] [--showast] [-o <path>] FILE|DIR..."
-
-    import sys, os, getopt
-    import os.path
-    import time
-
-    showasm = showast = do_verify = numproc = recurse_dirs = 0
-    outfile = '-'
-    out_base = None
-    codes = []
-    timestamp = True
-    timestampfmt = "# %Y.%m.%d %H:%M:%S %Z"
-
-    try:
-        opts, files = getopt.getopt(sys.argv[1:], 'hatdro:c:p:',
-                               ['help', 'verify', 'showast', 'showasm'])
-    except getopt.GetoptError, e:
-        print >>sys.stderr, '%s: %s' % (os.path.basename(sys.argv[0]), e)
-        sys.exit(-1)    
-
-    for opt, val in opts:
-        if opt in ('-h', '--help'):
-            print __doc__
-            sys.exit(0)
-        elif opt == '--verify':
-            do_verify = 1
-        elif opt in ('--showasm', '-a'):
-            showasm = 1
-            do_verify = 0
-        elif opt in ('--showast', '-t'):
-            showast = 1
-            do_verify = 0
-        elif opt == '-o':
-            outfile = val
-        elif opt == '-d':
-            timestamp = False
-        elif opt == '-c':
-            codes.append(val)
-        elif opt == '-p':
-            numproc = int(val)
-        elif opt == '-r':
-            recurse_dirs = 1
-        else:
-            print opt
-            print Usage_short
-            sys.exit(1)
-
-    # expand directory if specified
-    if recurse_dirs:
-        expanded_files = []
-        for f in files:
-            if os.path.isdir(f):
-                for root, _, dir_files in os.walk(f):
-                    for df in dir_files:
-                        if df.endswith('.pyc') or df.endswith('.pyo'):
-                            expanded_files.append(os.path.join(root, df))
-        files = expanded_files
-
-    # argl, commonprefix works on strings, not on path parts,
-    # thus we must handle the case with files in 'some/classes'
-    # and 'some/cmds'
-    src_base = os.path.commonprefix(files)
-    if src_base[-1:] != os.sep:
-        src_base = os.path.dirname(src_base)
-    if src_base:
-        sb_len = len( os.path.join(src_base, '') )
-        files = map(lambda f: f[sb_len:], files)
-        del sb_len
-        
-    if outfile == '-':
-        outfile = None # use stdout
-    elif outfile and os.path.isdir(outfile):
-        out_base = outfile; outfile = None
-    elif outfile and len(files) > 1:
-        out_base = outfile; outfile = None
-
-    if timestamp:
-        print time.strftime(timestampfmt)
-    if numproc <= 1:
-        try:
-            result = main(src_base, out_base, files, codes, outfile, showasm, showast, do_verify)
-            print '# decompiled %i files: %i okay, %i failed, %i verify failed' % result
-        except (KeyboardInterrupt, OSError):
-            pass
-        except verify.VerifyCmpError:
-            raise
-    else:
-        fqueue = Queue(len(files)+numproc)
-        for f in files:
-            fqueue.put(f)
-        for i in range(numproc):
-            fqueue.put(None)
-            
-        rqueue = Queue(numproc)
-        
-        try:
-            procs = [Process(target=process_func, args=(src_base, out_base, codes, outfile, showasm, showast, do_verify, fqueue, rqueue)) for i in range(numproc)]
-            for p in procs:
-                p.start()
-            for p in procs:
-                p.join()
-            try:
-                (tot_files, okay_files, failed_files, verify_failed_files) = (0,0,0,0)
-                while 1:
-                    (t, o, f, v) = rqueue.get(False)
-                    tot_files += t
-                    okay_files += o
-                    failed_files += f
-                    verify_failed_files += v
-            except Empty:
-                pass
-            print '# decompiled %i files: %i okay, %i failed, %i verify failed' % \
-                  (tot_files, okay_files, failed_files, verify_failed_files)
-        except (KeyboardInterrupt, OSError):
-            pass
-            
-
-    if timestamp:
-        print time.strftime(timestampfmt)

scripts/uncompyler.py

+#!/usr/bin/env python2.7
+# Mode: -*- python -*-
+#
+# Copyright (c) 2000-2002 by hartmut Goebel <hartmut@goebel.noris.de>
+#
+"""
+Usage: uncompyler [OPTIONS]... [ FILE | DIR]...
+
+Examples:
+  uncompyler      foo.pyc bar.pyc       # uncompyle foo.pyc, bar.pyc to stdout
+  uncompyler -o . foo.pyc bar.pyc       # uncompyle to ./foo.dis and ./bar.dis
+  uncompyler -o /tmp /usr/lib/python1.5 # uncompyle whole library
+
+Options:
+  -o <path>     output decompiled files to this path:
+                if multiple input files are decompiled, the common prefix
+                is stripped from these names and the remainder appended to
+                <path>
+                  uncompyler -o /tmp bla/fasel.pyc bla/foo.pyc
+                    -> /tmp/fasel.dis, /tmp/foo.dis
+                  uncompyler -o /tmp bla/fasel.pyc bar/foo.pyc
+                    -> /tmp/bla/fasel.dis, /tmp/bar/foo.dis
+                  uncompyler -o /tmp /usr/lib/python1.5
+                    -> /tmp/smtplib.dis ... /tmp/lib-tk/FixTk.dis
+  -c <file>     attempts a disassembly after compiling <file>
+  -d            do not print timestamps
+  -p <integer>  use <integer> number of processes
+  -r            recurse directories looking for .pyc and .pyo files
+  --verify      compare generated source with input byte-code
+                (requires -o)
+  --help        show this message
+
+Debugging Options:
+  --showasm   -a  include byte-code                  (disables --verify)
+  --showast   -t  include AST (abstract syntax tree) (disables --verify)
+
+Extensions of generated files:
+  '.dis'             successfully decompiled (and verified if --verify)
+  '.dis_unverified'  successfully decompile but --verify failed
+  '.nodis'           uncompyle failed (contact author for enhancement)
+"""
+from threading import Thread
+from multiprocessing import Process, Queue
+from Queue import Empty
+from uncompyle import main, verify
+
+def process_func(src_base, out_base, codes, outfile, showasm, showast, do_verify, fqueue, rqueue):
+    try:
+      (tot_files, okay_files, failed_files, verify_failed_files) = (0,0,0,0)
+      while 1:
+          f = fqueue.get()
+          if f == None:
+              break
+          (t, o, f, v) = \
+              main(src_base, out_base, [f], codes, outfile, showasm, showast, do_verify)
+          tot_files += t
+          okay_files += o
+          failed_files += f
+          verify_failed_files += v
+    except (Empty, KeyboardInterrupt, OSError):
+      pass
+    rqueue.put((tot_files, okay_files, failed_files, verify_failed_files))
+    rqueue.close()
+
+if __name__ == '__main__':
+    Usage_short = \
+    "decomyple [--help] [--verify] [--showasm] [--showast] [-o <path>] FILE|DIR..."
+
+    import sys, os, getopt
+    import os.path
+    import time
+
+    showasm = showast = do_verify = numproc = recurse_dirs = 0
+    outfile = '-'
+    out_base = None
+    codes = []
+    timestamp = True
+    timestampfmt = "# %Y.%m.%d %H:%M:%S %Z"
+
+    try:
+        opts, files = getopt.getopt(sys.argv[1:], 'hatdro:c:p:',
+                               ['help', 'verify', 'showast', 'showasm'])
+    except getopt.GetoptError, e:
+        print >>sys.stderr, '%s: %s' % (os.path.basename(sys.argv[0]), e)
+        sys.exit(-1)    
+
+    for opt, val in opts:
+        if opt in ('-h', '--help'):
+            print __doc__
+            sys.exit(0)
+        elif opt == '--verify':
+            do_verify = 1
+        elif opt in ('--showasm', '-a'):
+            showasm = 1
+            do_verify = 0
+        elif opt in ('--showast', '-t'):
+            showast = 1
+            do_verify = 0
+        elif opt == '-o':
+            outfile = val
+        elif opt == '-d':
+            timestamp = False
+        elif opt == '-c':
+            codes.append(val)
+        elif opt == '-p':
+            numproc = int(val)
+        elif opt == '-r':
+            recurse_dirs = 1
+        else:
+            print opt
+            print Usage_short
+            sys.exit(1)
+
+    # expand directory if specified
+    if recurse_dirs:
+        expanded_files = []
+        for f in files:
+            if os.path.isdir(f):
+                for root, _, dir_files in os.walk(f):
+                    for df in dir_files:
+                        if df.endswith('.pyc') or df.endswith('.pyo'):
+                            expanded_files.append(os.path.join(root, df))
+        files = expanded_files
+
+    # argl, commonprefix works on strings, not on path parts,
+    # thus we must handle the case with files in 'some/classes'
+    # and 'some/cmds'
+    src_base = os.path.commonprefix(files)
+    if src_base[-1:] != os.sep:
+        src_base = os.path.dirname(src_base)
+    if src_base:
+        sb_len = len( os.path.join(src_base, '') )
+        files = map(lambda f: f[sb_len:], files)
+        del sb_len
+        
+    if outfile == '-':
+        outfile = None # use stdout
+    elif outfile and os.path.isdir(outfile):
+        out_base = outfile; outfile = None
+    elif outfile and len(files) > 1:
+        out_base = outfile; outfile = None
+
+    if timestamp:
+        print time.strftime(timestampfmt)
+    if numproc <= 1:
+        try:
+            result = main(src_base, out_base, files, codes, outfile, showasm, showast, do_verify)
+            print '# decompiled %i files: %i okay, %i failed, %i verify failed' % result
+        except (KeyboardInterrupt, OSError):
+            pass
+        except verify.VerifyCmpError:
+            raise
+    else:
+        fqueue = Queue(len(files)+numproc)
+        for f in files:
+            fqueue.put(f)
+        for i in range(numproc):
+            fqueue.put(None)
+            
+        rqueue = Queue(numproc)
+        
+        try:
+            procs = [Process(target=process_func, args=(src_base, out_base, codes, outfile, showasm, showast, do_verify, fqueue, rqueue)) for i in range(numproc)]
+            for p in procs:
+                p.start()
+            for p in procs:
+                p.join()
+            try:
+                (tot_files, okay_files, failed_files, verify_failed_files) = (0,0,0,0)
+                while 1:
+                    (t, o, f, v) = rqueue.get(False)
+                    tot_files += t
+                    okay_files += o
+                    failed_files += f
+                    verify_failed_files += v
+            except Empty:
+                pass
+            print '# decompiled %i files: %i okay, %i failed, %i verify failed' % \
+                  (tot_files, okay_files, failed_files, verify_failed_files)
+        except (KeyboardInterrupt, OSError):
+            pass
+            
+
+    if timestamp:
+        print time.strftime(timestampfmt)
 
 from distutils.core import setup, Extension
 
-setup (name = "uncompyle2",
+setup (name = "uncompyle",
        version = "1.1",
        description = "Python byte-code to source-code converter",
        author = "Hartmut Goebel",
        author_email = "hartmut@oberon.noris.de",
-       url = "http://github.com/sysfrog/uncompyle",
-       packages=['uncompyle2', 'uncompyle2.dis', 'uncompyle2.opcode'],
-       scripts=['scripts/uncompyle.py'],
+       url = "http://github.com/gstarnberger/uncompyle",
+       packages=['uncompyle', 'uncompyle.dis', 'uncompyle.opcode'],
+       scripts=['scripts/uncompyler.py'],
       )

uncompyle/Parser.py

+#  Copyright (c) 1999 John Aycock
+#  Copyright (c) 2000-2002 by hartmut Goebel <hartmut@goebel.noris.de>
+#  Copyright (c) 2005 by Dan Pascu <dan@windowmaker.org>
+#
+#  See main module for license.
+#
+
+__all__ = ['parse', 'AST', 'ParserError', 'Parser']
+
+from spark import GenericASTBuilder
+import string, exceptions, sys
+from UserList import UserList
+
+from Scanner import Token
+
+class AST(UserList):
+    def __init__(self, type, kids=[]):
+        self.type = intern(type)
+        UserList.__init__(self, kids)
+
+    def __getslice__(self, low, high):    return self.data[low:high]
+    def __eq__(self, o):
+        if isinstance(o, AST):
+            return self.type == o.type \
+                   and UserList.__eq__(self, o)
+        else:
+            return self.type == o
+
+    def __hash__(self):            return hash(self.type)
+
+    def __repr__(self, indent=''):
+        rv = str(self.type)
+        for k in self:
+            rv = rv + '\n' + string.replace(str(k), '\n', '\n   ')
+        return rv
+
+
+class ParserError(Exception):
+    def __init__(self, token, offset):
+        self.token = token
+        self.offset = offset
+
+    def __str__(self):
+        return "Syntax error at or near `%r' token at offset %s" % \
+               (self.token, self.offset)
+    
+
+class Parser(GenericASTBuilder):
+    def __init__(self):
+        GenericASTBuilder.__init__(self, AST, 'stmts')
+        self.customized = {}
+
+    def cleanup(self):
+        """
+        Remove recursive references to allow garbage
+        collector to collect this object.
+        """
+        for dict in (self.rule2func, self.rules, self.rule2name):
+            for i in dict.keys():
+                dict[i] = None
+        for i in dir(self):
+            setattr(self, i, None)
+
+    def error(self, token):
+            raise ParserError(token, token.offset)
+
+    def typestring(self, token):
+        return token.type
+    
+    def p_funcdef(self, args):
+        '''
+        stmt ::= funcdef
+        funcdef ::= mkfunc designator
+        stmt ::= funcdefdeco
+        funcdefdeco ::= mkfuncdeco designator
+        mkfuncdeco ::= expr mkfuncdeco CALL_FUNCTION_1
+        mkfuncdeco ::= expr mkfuncdeco0 CALL_FUNCTION_1
+        mkfuncdeco0 ::= mkfunc
+        load_closure ::= load_closure LOAD_CLOSURE
+        load_closure ::= LOAD_CLOSURE
+        '''
+
+    def p_list_comprehension(self, args):
+        '''
+        expr ::= list_compr
+        list_compr ::= BUILD_LIST_0 list_iter
+
+
+        list_iter ::= list_for
+        list_iter ::= list_if
+        list_iter ::= list_if_not
+        list_iter ::= lc_body
+
+        _come_from ::= COME_FROM
+        _come_from ::= 
+
+        
+        list_for ::= expr _for designator list_iter JUMP_BACK COME_FROM
+        list_if ::= expr jmp_false list_iter               
+        list_if_not ::= expr jmp_true list_iter               
+
+        lc_body ::= expr LIST_APPEND
+        '''
+        
+    def p_setcomp(self, args):
+        '''
+        expr ::= setcomp
+        
+        setcomp ::= LOAD_SETCOMP MAKE_FUNCTION_0 expr GET_ITER CALL_FUNCTION_1
+        
+        stmt ::= setcomp_func
+        
+        setcomp_func ::= BUILD_SET_0 LOAD_FAST FOR_ITER designator comp_iter
+                JUMP_BACK COME_FROM RETURN_VALUE RETURN_LAST
+        
+        comp_iter ::= comp_if
+        comp_iter ::= comp_ifnot
+        comp_iter ::= comp_for
+        comp_iter ::= comp_body
+        comp_body ::= set_comp_body
+        comp_body ::= gen_comp_body
+        comp_body ::= dict_comp_body
+        set_comp_body ::= expr SET_ADD
+        gen_comp_body ::= expr YIELD_VALUE POP_TOP
+        dict_comp_body ::= expr expr MAP_ADD
+
+        comp_if ::= expr jmp_false comp_iter
+        comp_ifnot ::= expr jmp_true comp_iter
+        comp_for ::= expr _for designator comp_iter JUMP_BACK COME_FROM
+        '''
+
+
+    def p_genexpr(self, args):
+        '''
+        expr ::= genexpr
+        
+        genexpr ::= LOAD_GENEXPR MAKE_FUNCTION_0 expr GET_ITER CALL_FUNCTION_1
+        
+        stmt ::= genexpr_func
+        
+        genexpr_func ::= LOAD_FAST FOR_ITER designator comp_iter JUMP_BACK COME_FROM
+        '''
+
+
+    def p_dictcomp(self, args):
+        '''
+        expr ::= dictcomp
+        dictcomp ::= LOAD_DICTCOMP MAKE_FUNCTION_0 expr GET_ITER CALL_FUNCTION_1
+        stmt ::= dictcomp_func
+        
+        dictcomp_func ::= BUILD_MAP LOAD_FAST FOR_ITER designator
+                comp_iter JUMP_BACK COME_FROM RETURN_VALUE RETURN_LAST
+
+        '''
+
+
+    def p_augmented_assign(self, args):
+        '''
+        stmt ::= augassign1
+        stmt ::= augassign2
+        augassign1 ::= expr expr inplace_op designator
+        augassign1 ::= expr expr inplace_op ROT_THREE STORE_SUBSCR
+        augassign1 ::= expr expr inplace_op ROT_TWO   STORE_SLICE+0
+        augassign1 ::= expr expr inplace_op ROT_THREE STORE_SLICE+1
+        augassign1 ::= expr expr inplace_op ROT_THREE STORE_SLICE+2
+        augassign1 ::= expr expr inplace_op ROT_FOUR  STORE_SLICE+3
+        augassign2 ::= expr DUP_TOP LOAD_ATTR expr
+                inplace_op ROT_TWO   STORE_ATTR
+
+        inplace_op ::= INPLACE_ADD
+        inplace_op ::= INPLACE_SUBTRACT
+        inplace_op ::= INPLACE_MULTIPLY
+        inplace_op ::= INPLACE_DIVIDE
+        inplace_op ::= INPLACE_TRUE_DIVIDE
+        inplace_op ::= INPLACE_FLOOR_DIVIDE
+        inplace_op ::= INPLACE_MODULO
+        inplace_op ::= INPLACE_POWER
+        inplace_op ::= INPLACE_LSHIFT
+        inplace_op ::= INPLACE_RSHIFT
+        inplace_op ::= INPLACE_AND
+        inplace_op ::= INPLACE_XOR
+        inplace_op ::= INPLACE_OR 
+        '''
+
+    def p_assign(self, args):
+        '''
+        stmt ::= assign
+        assign ::= expr DUP_TOP designList
+        assign ::= expr designator
+
+        stmt ::= _25_assign2
+        stmt ::= _25_assign3
+        _25_assign2 ::= expr expr ROT_TWO designator designator
+        _25_assign3 ::= expr expr expr ROT_THREE ROT_TWO designator designator designator
+        '''
+
+    def p_print(self, args):
+        '''
+        stmt ::= print_stmt
+        stmt ::= print_stmt_nl
+        stmt ::= print_nl_stmt
+        print_stmt ::= expr PRINT_ITEM
+        print_nl_stmt ::= PRINT_NEWLINE
+        print_stmt_nl ::= print_stmt print_nl_stmt
+        '''
+
+    def p_print_to(self, args):
+        '''
+        stmt ::= print_to
+        stmt ::= print_to_nl
+        stmt ::= print_nl_to
+        print_to ::= expr print_to_items POP_TOP
+        print_to_nl ::= expr print_to_items PRINT_NEWLINE_TO
+        print_nl_to ::= expr PRINT_NEWLINE_TO
+        print_to_items ::= print_to_items print_to_item
+        print_to_items ::= print_to_item
+        print_to_item ::= DUP_TOP expr ROT_TWO PRINT_ITEM_TO
+        '''
+        # expr   print_to*   POP_TOP
+        # expr { print_to* } PRINT_NEWLINE_TO
+
+    def p_import15(self, args):
+        '''
+        stmt ::= importstmt
+        stmt ::= importfrom
+
+        importstmt ::= IMPORT_NAME STORE_FAST
+        importstmt ::= IMPORT_NAME STORE_NAME
+
+        importfrom ::= IMPORT_NAME importlist POP_TOP
+        importlist ::= importlist IMPORT_FROM
+        importlist ::= IMPORT_FROM
+        '''
+
+    def p_import20(self, args):
+        '''
+        stmt ::= importstmt2
+        stmt ::= importfrom2
+        stmt ::= importstar2
+        
+        stmt ::= _25_importstmt
+        stmt ::= _25_importfrom
+        stmt ::= _25_importstar
+
+        importstmt2 ::= LOAD_CONST import_as
+        importstar2 ::= LOAD_CONST IMPORT_NAME IMPORT_STAR
+
+        importfrom2 ::= LOAD_CONST IMPORT_NAME importlist2 POP_TOP
+        importlist2 ::= importlist2 import_as
+        importlist2 ::= import_as
+        import_as ::= IMPORT_NAME designator
+        import_as ::= IMPORT_NAME LOAD_ATTR designator
+        import_as ::= IMPORT_NAME LOAD_ATTR LOAD_ATTR designator
+        import_as ::= IMPORT_NAME LOAD_ATTR LOAD_ATTR LOAD_ATTR designator
+        import_as ::= IMPORT_FROM designator
+
+        _25_importstmt ::= LOAD_CONST LOAD_CONST import_as 
+        _25_importstar ::= LOAD_CONST LOAD_CONST IMPORT_NAME IMPORT_STAR 
+        _25_importfrom ::= LOAD_CONST LOAD_CONST IMPORT_NAME importlist2 POP_TOP
+        '''
+
+    def p_grammar(self, args):
+        '''
+        stmts ::= stmts sstmt
+        stmts ::= sstmt
+        sstmt ::= stmt
+        sstmt ::= return_stmt
+        sstmt ::= ifelsestmtr
+        sstmt ::= return_stmt RETURN_LAST
+        
+        stmts_opt ::= stmts
+        stmts_opt ::= passstmt
+        passstmt ::= 
+        
+        _stmts ::= _stmts stmt
+        _stmts ::= stmt
+
+        c_stmts ::= _stmts
+        c_stmts ::= _stmts lastc_stmt
+        c_stmts ::= lastc_stmt
+        c_stmts ::= _stmts lastl_stmt continue_stmt
+        c_stmts ::= lastl_stmt continue_stmt
+        c_stmts ::= continue_stmt
+        
+        lastc_stmt ::= iflaststmt
+        lastc_stmt ::= whileelselaststmt
+        lastc_stmt ::= forelselaststmt
+        lastc_stmt ::= ifelsestmtr
+        lastc_stmt ::= c_trystmt
+
+        c_stmts_opt ::= c_stmts
+        c_stmts_opt ::= passstmt
+        
+        l_stmts ::= _stmts
+        l_stmts ::= return_stmts
+        l_stmts ::= _stmts lastl_stmt
+        l_stmts ::= lastl_stmt
+        
+        lastl_stmt ::= iflaststmtl
+        lastl_stmt ::= ifelsestmtl
+        lastl_stmt ::= c_trystmt
+
+        l_stmts_opt ::= l_stmts
+        l_stmts_opt ::= passstmt
+
+        designList ::= designator designator
+        designList ::= designator DUP_TOP designList
+
+        designator ::= STORE_FAST
+        designator ::= STORE_NAME
+        designator ::= STORE_GLOBAL
+        designator ::= STORE_DEREF
+        designator ::= expr STORE_ATTR
+        designator ::= expr STORE_SLICE+0
+        designator ::= expr expr STORE_SLICE+1
+        designator ::= expr expr STORE_SLICE+2
+        designator ::= expr expr expr STORE_SLICE+3
+        designator ::= store_subscr
+        store_subscr ::= expr expr STORE_SUBSCR
+        designator ::= unpack
+        designator ::= unpack_list
+        
+        stmt ::= classdef
+        stmt ::= call_stmt
+        call_stmt ::= expr POP_TOP
+
+        return_stmt ::= expr RETURN_VALUE
+
+        stmt ::= yield_stmt
+        yield_stmt ::= expr YIELD_STMT
+        yield_stmt ::= expr YIELD_STMT POP_TOP
+
+        yield_stmt ::= expr YIELD_VALUE
+        yield_stmt ::= expr YIELD_VALUE POP_TOP
+
+        stmt ::= break_stmt
+        break_stmt ::= BREAK_LOOP
+        
+        continue_stmt ::= JUMP_BACK
+        continue_stmt ::= CONTINUE_LOOP
+        
+        stmt ::= raise_stmt
+        raise_stmt ::= exprlist RAISE_VARARGS
+        raise_stmt ::= nullexprlist RAISE_VARARGS
+        
+        stmt ::= exec_stmt
+        exec_stmt ::= expr exprlist DUP_TOP EXEC_STMT
+        exec_stmt ::= expr exprlist EXEC_STMT
+        
+        stmt ::= assert
+        stmt ::= assert2
+        stmt ::= ifstmt
+        stmt ::= ifelsestmt
+        
+        stmt ::= whilestmt
+        stmt ::= whilenotstmt
+        stmt ::= while1stmt
+        stmt ::= whileelsestmt
+        stmt ::= while1elsestmt
+        stmt ::= forstmt
+        stmt ::= forelsestmt
+        stmt ::= trystmt
+        stmt ::= tryfinallystmt
+        stmt ::= withstmt
+        stmt ::= withasstmt
+        
+        stmt ::= del_stmt
+        del_stmt ::= DELETE_FAST
+        del_stmt ::= DELETE_NAME
+        del_stmt ::= DELETE_GLOBAL
+        del_stmt ::= expr DELETE_SLICE+0
+        del_stmt ::= expr expr DELETE_SLICE+1
+        del_stmt ::= expr expr DELETE_SLICE+2
+        del_stmt ::= expr expr expr DELETE_SLICE+3
+        del_stmt ::= delete_subscr
+        delete_subscr ::= expr expr DELETE_SUBSCR
+        del_stmt ::= expr DELETE_ATTR
+        
+        kwarg   ::= LOAD_CONST expr
+        
+        classdef ::= LOAD_CONST expr mkfunc
+                    CALL_FUNCTION_0 BUILD_CLASS designator
+        
+        stmt ::= classdefdeco
+        classdefdeco ::= classdefdeco1 designator
+        classdefdeco1 ::= expr classdefdeco1 CALL_FUNCTION_1
+        classdefdeco1 ::= expr classdefdeco2 CALL_FUNCTION_1
+        classdefdeco2 ::= LOAD_CONST expr mkfunc CALL_FUNCTION_0 BUILD_CLASS
+
+        assert ::= assert_expr POP_JUMP_IF_TRUE
+                LOAD_ASSERT RAISE_VARARGS
+                
+        assert2 ::= assert_expr POP_JUMP_IF_TRUE
+                LOAD_ASSERT expr RAISE_VARARGS
+                
+        assert_expr ::= expr
+        assert_expr ::= assert_expr_or
+        assert_expr ::= assert_expr_and
+        assert_expr_or ::= assert_expr POP_JUMP_IF_TRUE expr
+        assert_expr_and ::= assert_expr POP_JUMP_IF_FALSE expr
+
+
+        _jump ::= JUMP_ABSOLUTE
+        _jump ::= JUMP_FORWARD
+        _jump ::= JUMP_BACK
+        _jump ::= JUMP_BACK JUMP_BACK_ELSE
+
+        jmp_false    ::= POP_JUMP_IF_FALSE
+        jmp_true    ::= POP_JUMP_IF_TRUE
+        
+        ifstmt ::= testexpr _ifstmts_jump
+        
+        
+        testexpr ::= testfalse
+        testexpr ::= testtrue
+        testfalse ::= expr jmp_false
+        testtrue ::= expr jmp_true
+        
+        _ifstmts_jump ::= return_stmts
+        _ifstmts_jump ::= c_stmts_opt JUMP_FORWARD COME_FROM
+        
+        iflaststmt ::= testexpr c_stmts_opt JUMP_ABSOLUTE
+        
+        iflaststmtl ::= testexpr l_stmts_opt JUMP_ABSOLUTE
+        iflaststmtl ::= testexpr l_stmts_opt JUMP_BACK
+
+        ifelsestmt ::= testexpr c_stmts_opt JUMP_FORWARD c_stmts COME_FROM
+        ifelsestmt ::= testexpr c_stmts_opt JUMP_FORWARD return_stmts COME_FROM
+        ifelsestmt ::= testexpr c_stmts_opt JUMP_ABSOLUTE c_stmts
+        ifelsestmt ::= testexpr c_stmts_opt JUMP_ABSOLUTE return_stmts
+        ifelsestmtr ::= testexpr return_stmts return_stmts
+
+        ifelsestmtl ::= testexpr l_stmts_opt JUMP_ABSOLUTE l_stmts
+        ifelsestmtl ::= testexpr l_stmts_opt _jump_back_jump_back_else l_stmts
+        _jump_back_jump_back_else ::= JUMP_BACK JUMP_BACK_ELSE
+        
+        
+        trystmt ::= SETUP_EXCEPT stmts_opt
+                POP_BLOCK JUMP_FORWARD
+                COME_FROM except_stmts
+
+        trystmt ::= SETUP_EXCEPT stmts_opt
+                POP_BLOCK 
+                COME_FROM JUMP_FORWARD except_stmts
+
+        except_stmts ::= except_cond1 except_sub_stmts 
+        except_stmts ::= except_cond2 except_sub_stmts 
+        except_stmts ::= except JUMP_FORWARD try_end COME_FROM
+        except_stmts ::= except2 END_FINALLY COME_FROM
+        except_stmts ::= END_FINALLY COME_FROM
+
+        except_stmts_a ::= except_cond1 except_sub_stmts_a 
+        except_stmts_a ::= except_cond2 except_sub_stmts_a 
+        except_stmts_a ::= except JUMP_FORWARD try_end COME_FROM
+        except_stmts_a ::= except2 try_end
+        except_stmts_a ::= try_end
+
+        except_sub_stmts ::= c_stmts_opt JUMP_FORWARD except_stmts_a COME_FROM
+        except_sub_stmts ::= return_stmts except_stmts
+        except_sub_stmts ::= continue_stmts jmp_back except_stmts
+        
+        except_sub_stmts_a ::= c_stmts_opt JUMP_FORWARD except_stmts_a COME_FROM
+        except_sub_stmts_a ::= return_stmts except_stmts_a
+        except_sub_stmts_a ::= continue_stmts jmp_back except_stmts_a
+        
+        jmp_back ::= JUMP_BACK
+        jmp_back ::= JUMP_BACK JUMP_BACK_ELSE
+        continue_stmts ::= continue_stmt
+        continue_stmts ::=_stmts continue_stmt
+        
+        try_end  ::= END_FINALLY COME_FROM
+        try_end  ::= except_else
+        except_else ::= END_FINALLY COME_FROM stmts
+
+        except_cond1 ::= DUP_TOP expr COMPARE_OP
+                POP_JUMP_IF_FALSE POP_TOP POP_TOP POP_TOP
+                
+
+        except_cond2 ::= DUP_TOP expr COMPARE_OP
+                POP_JUMP_IF_FALSE POP_TOP designator POP_TOP
+                 
+        except  ::=  POP_TOP POP_TOP POP_TOP c_stmts_opt
+
+        except2  ::=  POP_TOP POP_TOP POP_TOP return_stmts
+
+
+        c_trystmt ::= SETUP_EXCEPT stmts_opt
+                POP_BLOCK JUMP_FORWARD
+                COME_FROM c_except_stmts
+
+        c_trystmt ::= SETUP_EXCEPT stmts_opt
+                POP_BLOCK
+                COME_FROM JUMP_FORWARD c_except_stmts
+
+        c_trystmt ::= SETUP_EXCEPT stmts_opt
+                POP_BLOCK jmp_abs
+                COME_FROM c_except_stmts2
+
+        c_trystmt ::= SETUP_EXCEPT stmts_opt
+                POP_BLOCK
+                COME_FROM jmp_abs c_except_stmts2
+
+        c_except_stmts ::= except_cond1 c_except_sub_stmts
+        c_except_stmts ::= except_cond2 c_except_sub_stmts
+        c_except_stmts ::= except jmp_abs try_end3
+        c_except_stmts ::= except2 END_FINALLY COME_FROM
+        c_except_stmts ::= END_FINALLY COME_FROM
+
+        c_except_stmts_a ::= except_cond1 c_except_sub_stmts_a
+        c_except_stmts_a ::= except_cond2 c_except_sub_stmts_a
+        c_except_stmts_a ::= except jmp_abs try_end3
+        c_except_stmts_a ::= except2 try_end3
+        c_except_stmts_a ::= try_end3
+
+        try_end3  ::= END_FINALLY COME_FROM
+        try_end3  ::= except_else3
+        except_else3 ::= END_FINALLY COME_FROM c_stmts
+        except_else3 ::= END_FINALLY COME_FROM l_stmts
+
+        c_except_sub_stmts ::= c_stmts_opt jmp_abs c_except_stmts_a
+        c_except_sub_stmts ::= return_stmts c_except_stmts
+
+        c_except_sub_stmts_a ::= c_stmts_opt jmp_abs c_except_stmts_a
+        c_except_sub_stmts_a ::= return_stmts c_except_stmts_a
+
+        c_except_stmts2 ::= except_cond1 c_except_sub_stmts2
+        c_except_stmts2 ::= except_cond2 c_except_sub_stmts2
+        c_except_stmts2 ::= except jmp_abs try_end2
+        c_except_stmts2 ::= except2 END_FINALLY
+        c_except_stmts2 ::= END_FINALLY
+
+        c_except_stmts2_a ::= except_cond1 c_except_sub_stmts2_a
+        c_except_stmts2_a ::= except_cond2 c_except_sub_stmts2_a
+        c_except_stmts2_a ::= except jmp_abs try_end2
+        c_except_stmts2_a ::= except2 try_end2
+        c_except_stmts2_a ::= try_end2
+
+        c_except_sub_stmts2 ::= c_stmts_opt jmp_abs c_except_stmts2_a
+        c_except_sub_stmts2 ::= return_stmts c_except_stmts2
+
+        c_except_sub_stmts2_a ::= c_stmts_opt jmp_abs c_except_stmts2_a
+        c_except_sub_stmts2_a ::= return_stmts c_except_stmts2_a
+
+        try_end2  ::= END_FINALLY
+        try_end2  ::= except_else2
+        except_else2 ::= END_FINALLY c_stmts
+        except_else2 ::= END_FINALLY l_stmts
+
+        jmp_abs ::= JUMP_ABSOLUTE
+        jmp_abs ::= JUMP_BACK
+        jmp_abs ::= JUMP_BACK JUMP_BACK_ELSE
+        
+
+
+        tryfinallystmt ::= SETUP_FINALLY stmts
+                POP_BLOCK LOAD_CONST
+                COME_FROM stmts_opt END_FINALLY
+                
+        withstmt ::= expr SETUP_WITH POP_TOP stmts_opt
+                POP_BLOCK LOAD_CONST COME_FROM
+                WITH_CLEANUP END_FINALLY
+
+        withasstmt ::= expr SETUP_WITH designator stmts
+                POP_BLOCK LOAD_CONST COME_FROM
+                WITH_CLEANUP END_FINALLY
+
+        whilestmt ::= SETUP_LOOP
+                testexpr
+                l_stmts_opt _jump_back
+                POP_BLOCK COME_FROM
+
+        _jump_back ::= JUMP_BACK
+        _jump_back ::= JUMP_BACK JUMP_BACK_ELSE
+        
+        whilestmt ::= SETUP_LOOP
+                testexpr
+                return_stmts
+                POP_BLOCK COME_FROM
+
+        while1stmt ::= SETUP_LOOP l_stmts _jump_back COME_FROM
+        while1stmt ::= SETUP_LOOP return_stmts COME_FROM
+        whileelsestmt ::= SETUP_LOOP testexpr
+                l_stmts_opt _jump_back
+                POP_BLOCK
+                stmts COME_FROM
+
+        whileelselaststmt ::= SETUP_LOOP testexpr
+                l_stmts_opt _jump_back
+                POP_BLOCK
+                c_stmts COME_FROM
+
+        _for ::= GET_ITER FOR_ITER
+        _for ::= LOAD_CONST FOR_LOOP
+
+        forstmt ::= SETUP_LOOP expr _for designator
+                l_stmts_opt _jump_back
+                COME_FROM POP_BLOCK COME_FROM
+        forstmt ::= SETUP_LOOP expr _for designator
+                return_stmts 
+                COME_FROM POP_BLOCK COME_FROM
+
+        forelsestmt ::= SETUP_LOOP expr _for designator
+                l_stmts_opt _jump_back
+                COME_FROM POP_BLOCK stmts COME_FROM
+        forelsestmt ::= SETUP_LOOP expr _for designator
+                return_stmts _come_from
+                COME_FROM POP_BLOCK stmts COME_FROM
+
+        forelselaststmt ::= SETUP_LOOP expr _for designator
+                l_stmts_opt _jump_back
+                COME_FROM POP_BLOCK c_stmts COME_FROM
+        forelselaststmt ::= SETUP_LOOP expr _for designator
+                return_stmts _come_from
+                COME_FROM POP_BLOCK c_stmts COME_FROM
+
+        return_stmts ::= return_stmt
+        return_stmts ::= _stmts return_stmt
+        
+        '''
+
+    def p_expr(self, args):
+        '''
+        expr ::= _mklambda
+        expr ::= SET_LINENO
+        expr ::= LOAD_FAST
+        expr ::= LOAD_NAME
+        expr ::= LOAD_CONST
+        expr ::= LOAD_ASSERT
+        expr ::= LOAD_GLOBAL
+        expr ::= LOAD_DEREF
+        expr ::= LOAD_LOCALS
+        expr ::= load_attr
+        expr ::= binary_expr
+        expr ::= binary_expr_na
+        expr ::= build_list
+        expr ::= cmp
+        expr ::= mapexpr
+        expr ::= and
+        expr ::= and2
+        expr ::= or
+        expr ::= unary_expr
+        expr ::= call_function
+        expr ::= unary_not
+        expr ::= unary_convert
+        expr ::= binary_subscr
+        expr ::= binary_subscr2
+        expr ::= load_attr
+        expr ::= get_iter
+        expr ::= slice0
+        expr ::= slice1
+        expr ::= slice2
+        expr ::= slice3
+        
+        
+        binary_expr ::= expr expr binary_op
+        binary_op ::= BINARY_ADD
+        binary_op ::= BINARY_MULTIPLY
+        binary_op ::= BINARY_AND
+        binary_op ::= BINARY_OR
+        binary_op ::= BINARY_XOR
+        binary_op ::= BINARY_SUBTRACT
+        binary_op ::= BINARY_DIVIDE
+        binary_op ::= BINARY_TRUE_DIVIDE
+        binary_op ::= BINARY_FLOOR_DIVIDE
+        binary_op ::= BINARY_MODULO
+        binary_op ::= BINARY_LSHIFT
+        binary_op ::= BINARY_RSHIFT
+        binary_op ::= BINARY_POWER
+
+        unary_expr ::= expr unary_op
+        unary_op ::= UNARY_POSITIVE
+        unary_op ::= UNARY_NEGATIVE
+        unary_op ::= UNARY_INVERT
+
+        unary_not ::= expr UNARY_NOT
+        unary_convert ::= expr UNARY_CONVERT
+
+        binary_subscr ::= expr expr BINARY_SUBSCR
+        binary_subscr2 ::= expr expr DUP_TOPX_2 BINARY_SUBSCR
+
+        load_attr ::= expr LOAD_ATTR
+        get_iter ::= expr GET_ITER
+        slice0 ::= expr SLICE+0
+        slice0 ::= expr DUP_TOP SLICE+0
+        slice1 ::= expr expr SLICE+1
+        slice1 ::= expr expr DUP_TOPX_2 SLICE+1
+        slice2 ::= expr expr SLICE+2
+        slice2 ::= expr expr DUP_TOPX_2 SLICE+2
+        slice3 ::= expr expr expr SLICE+3
+        slice3 ::= expr expr expr DUP_TOPX_3 SLICE+3
+
+        _mklambda ::= load_closure mklambda
+        _mklambda ::= mklambda
+
+        or   ::= expr POP_JUMP_IF_TRUE expr COME_FROM
+        or   ::= expr JUMP_IF_TRUE_OR_POP expr COME_FROM
+        and  ::= expr POP_JUMP_IF_FALSE expr COME_FROM
+        and  ::= expr JUMP_IF_FALSE_OR_POP expr COME_FROM
+        and2 ::= _jump POP_JUMP_IF_FALSE COME_FROM expr COME_FROM
+        
+        expr ::= conditional
+        conditional ::= expr POP_JUMP_IF_FALSE expr JUMP_FORWARD expr COME_FROM
+        conditional ::= expr POP_JUMP_IF_FALSE expr JUMP_ABSOLUTE expr
+        expr ::= conditionalnot
+        conditionalnot ::= expr POP_JUMP_IF_TRUE expr _jump expr COME_FROM
+
+        stmt ::= return_lambda
+        stmt ::= conditional_lambda
+        stmt ::= conditional_lambda2
+        
+        return_lambda ::= expr RETURN_VALUE LAMBDA_MARKER
+        conditional_lambda ::= expr POP_JUMP_IF_FALSE return_stmt return_stmt LAMBDA_MARKER 
+        conditional_lambda2 ::= expr POP_JUMP_IF_FALSE expr POP_JUMP_IF_FALSE 
+            return_stmt return_stmt LAMBDA_MARKER 
+
+        cmp ::= cmp_list
+        cmp ::= compare
+        compare ::= expr expr COMPARE_OP
+        cmp_list ::= expr cmp_list1 ROT_TWO POP_TOP
+                _come_from
+        cmp_list1 ::= expr DUP_TOP ROT_THREE
+                COMPARE_OP JUMP_IF_FALSE_OR_POP
+                cmp_list1 COME_FROM
+        cmp_list1 ::= expr DUP_TOP ROT_THREE
+                COMPARE_OP JUMP_IF_FALSE_OR_POP
+                cmp_list2 COME_FROM
+        cmp_list2 ::= expr COMPARE_OP JUMP_FORWARD
+        cmp_list2 ::= expr COMPARE_OP RETURN_VALUE
+        mapexpr ::= BUILD_MAP kvlist
+
+        kvlist ::= kvlist kv
+        kvlist ::= kvlist kv2
+        kvlist ::= kvlist kv3
+        kvlist ::=
+
+        kv ::= DUP_TOP expr ROT_TWO expr STORE_SUBSCR
+        kv2 ::= DUP_TOP expr expr ROT_THREE STORE_SUBSCR
+        kv3 ::= expr expr STORE_MAP
+
+        exprlist ::= exprlist expr
+        exprlist ::= expr
+
+        nullexprlist ::=
+        '''
+
+    def nonterminal(self, nt, args):
+        collect = ('stmts', 'exprlist', 'kvlist', '_stmts')
+
+        if nt in collect and len(args) > 1:
+            #
+            #  Collect iterated thingies together.
+            #
+            rv = args[0]
+            rv.append(args[1])
+        else:
+           rv = GenericASTBuilder.nonterminal(self, nt, args)
+        return rv
+
+    def __ambiguity(self, children):
+        # only for debugging! to be removed hG/2000-10-15
+        print children
+        return GenericASTBuilder.ambiguity(self, children)
+
+    def resolve(self, list):
+        if len(list) == 2 and 'funcdef' in list and 'assign' in list:
+            return 'funcdef'
+        if 'grammar' in list and 'expr' in list:
+            return 'expr'
+        #print >> sys.stderr, 'resolve', str(list)
+        return GenericASTBuilder.resolve(self, list)
+
+nop = lambda self, args: None
+
+p = Parser()
+
+def parse(tokens, customize):
+    #
+    #  Special handling for opcodes that take a variable number
+    #  of arguments -- we add a new rule for each:
+    #
+    #    expr ::= {expr}^n BUILD_LIST_n
+    #    expr ::= {expr}^n BUILD_TUPLE_n
+    #    expr ::= {expr}^n BUILD_SLICE_n
+    #    unpack_list ::= UNPACK_LIST {expr}^n
+    #    unpack ::= UNPACK_TUPLE {expr}^n
+    #    unpack ::= UNPACK_SEQEUENE {expr}^n
+    #    mkfunc ::= {expr}^n LOAD_CONST MAKE_FUNCTION_n
+    #    mkfunc ::= {expr}^n load_closure LOAD_CONST MAKE_FUNCTION_n
+    #    expr ::= expr {expr}^n CALL_FUNCTION_n
+    #    expr ::= expr {expr}^n CALL_FUNCTION_VAR_n POP_TOP
+    #    expr ::= expr {expr}^n CALL_FUNCTION_VAR_KW_n POP_TOP
+    #    expr ::= expr {expr}^n CALL_FUNCTION_KW_n POP_TOP
+    #
+    global p
+    for k, v in customize.items():
+        # avoid adding the same rule twice to this parser
+        if p.customized.has_key(k):
+            continue
+        p.customized[k] = None
+
+        #nop = lambda self, args: None
+        op = k[:string.rfind(k, '_')]
+        if op in ('BUILD_LIST', 'BUILD_TUPLE', 'BUILD_SET'):
+            rule = 'build_list ::= ' + 'expr '*v + k
+        elif op == 'BUILD_SLICE':
+            rule = 'expr ::= ' + 'expr '*v + k
+        elif op in ('UNPACK_TUPLE', 'UNPACK_SEQUENCE'):
+            rule = 'unpack ::= ' + k + ' designator'*v
+        elif op == 'UNPACK_LIST':
+            rule = 'unpack_list ::= ' + k + ' designator'*v
+        elif op == 'DUP_TOPX':
+            # no need to add a rule
+            continue
+            #rule = 'dup_topx ::= ' + 'expr '*v + k
+        elif op == 'MAKE_FUNCTION':
+            p.addRule('mklambda ::= %s LOAD_LAMBDA %s' %
+                  ('expr '*v, k), nop)
+            rule = 'mkfunc ::= %s LOAD_CONST %s' % ('expr '*v, k)
+        elif op == 'MAKE_CLOSURE':
+            p.addRule('mklambda ::= %s load_closure LOAD_LAMBDA %s' %
+                  ('expr '*v, k), nop)
+            p.addRule('genexpr ::= %s load_closure LOAD_GENEXPR %s expr GET_ITER CALL_FUNCTION_1' %
+                  ('expr '*v, k), nop)
+            p.addRule('dictcomp ::= %s load_closure LOAD_DICTCOMP %s expr GET_ITER CALL_FUNCTION_1' %
+                  ('expr '*v, k), nop)
+            rule = 'mkfunc ::= %s load_closure LOAD_CONST %s' % ('expr '*v, k)
+#            rule = 'mkfunc ::= %s closure_list LOAD_CONST %s' % ('expr '*v, k)
+        elif op in ('CALL_FUNCTION', 'CALL_FUNCTION_VAR',
+                'CALL_FUNCTION_VAR_KW', 'CALL_FUNCTION_KW'):
+            na = (v & 0xff)           # positional parameters
+            nk = (v >> 8) & 0xff      # keyword parameters
+            # number of apply equiv arguments:
+            nak = ( len(op)-len('CALL_FUNCTION') ) / 3
+            rule = 'call_function ::= expr ' + 'expr '*na + 'kwarg '*nk \
+                   + 'expr ' * nak + k
+        else:
+            raise Exception('unknown customize token %s' % k)
+        p.addRule(rule, nop)
+    ast = p.parse(tokens)
+#    p.cleanup()
+    return ast

uncompyle/Scanner.py

+#  Copyright (c) 1999 John Aycock
+#  Copyright (c) 2000-2002 by hartmut Goebel <hartmut@goebel.noris.de>
+#  Copyright (c) 2005 by Dan Pascu <dan@windowmaker.org>
+#
+#  See main module for license.
+#
+
+__all__ = ['Token', 'Scanner', 'getscanner']
+
+import types
+
+class Token:
+    """
+    Class representing a byte-code token.
+    
+    A byte-code token is equivalent to the contents of one line
+    as output by dis.dis().
+    """
+    def __init__(self, type, attr=None, pattr=None, offset=-1):
+        self.type = intern(type)
+        self.attr = attr
+        self.pattr = pattr
+        self.offset = offset
+        
+    def __cmp__(self, o):
+        if isinstance(o, Token):
+            # both are tokens: compare type and pattr 
+            return cmp(self.type, o.type) or cmp(self.pattr, o.pattr)
+        else:
+            return cmp(self.type, o)
+
+    def __repr__(self):		return str(self.type)
+    def __str__(self):
+        pattr = self.pattr or ''
+        return '%s\t%-17s %r' % (self.offset, self.type, pattr)
+    def __hash__(self):		return hash(self.type)
+    def __getitem__(self, i):	raise IndexError
+
+
+class Code:
+    """
+    Class for representing code-objects.
+
+    This is similar to the original code object, but additionally
+    the diassembled code is stored in the attribute '_tokens'.
+    """
+    def __init__(self, co, scanner):
+        for i in dir(co):
+            if i.startswith('co_'):
+                setattr(self, i, getattr(co, i))
+        self._tokens, self._customize = scanner.disassemble(co)
+
+class Scanner:
+    def __init__(self, version):
+        self.__version = version
+
+        from sys import version_info
+        self.__pyversion = float('%d.%d' % version_info[0:2])
+
+        # use module 'dis' for this version
+        import dis.dis_files as dis_files
+        self.dis = dis_files.by_version[version]
+        self.resetTokenClass()
+
+        dis = self.dis
+        self.JUMP_OPs = map(lambda op: dis.opname[op],
+                            dis.hasjrel + dis.hasjabs)
+
+        # setup an opmap if we don't have one
+        try:
+            dis.opmap
+        except:
+            opmap = {}
+            opname = dis.opname
+            for i in range(len(opname)):
+                opmap[opname[i]] = i
+            dis.opmap = opmap
+        copmap = {}
+        for i in range(len(dis.cmp_op)):
+            copmap[dis.cmp_op[i]] = i
+        dis.copmap = copmap
+        
+
+    def setShowAsm(self, showasm, out=None):
+        self.showasm = showasm
+        self.out = out
+            
+    def setTokenClass(self, tokenClass):
+        assert type(tokenClass) == types.ClassType
+        self.Token = tokenClass
+        
+    def resetTokenClass(self):
+        self.setTokenClass(Token)
+        
+    def disassemble(self, co):
+        """
+        Disassemble a code object, returning a list of 'Token'.
+
+        The main part of this procedure is modelled after
+        dis.disassemble().
+        """
+        rv = []
+        customize = {}
+        dis = self.dis # shortcut
+        Token = self.Token # shortcut
+        code = co.co_code
+        n = len(code)
+        self.prev = [0]
+        i=0
+        while i < n:
+            c = code[i]
+            op = ord(code[i])
+            if op >= dis.HAVE_ARGUMENT:
+                self.prev.append(i)
+                self.prev.append(i)
+                self.prev.append(i)
+                i = i + 3
+            else:
+                self.prev.append(i)
+                i = i + 1
+                
+        self.lines = []
+        self.if_lines = {}
+        j = 0
+        linestarts = list(dis.findlinestarts(co))
+        (prev_start_byte, prev_line_no) = linestarts[0]
+        for (start_byte, line_no) in linestarts[1:]:
+            while j < start_byte:
+                self.lines.append((prev_line_no, start_byte))
+                j += 1
+            last_opname = dis.opname[ord(code[self.prev[start_byte]])]
+            if last_opname in ('POP_JUMP_IF_FALSE', 'POP_JUMP_IF_TRUE'):
+                self.if_lines[prev_line_no] = True
+            else:
+                self.if_lines[prev_line_no] = False
+            (prev_start_byte, prev_line_no) = (start_byte, line_no)
+        while j < n:
+            self.lines.append((prev_line_no, n))
+            j+=1
+        self.if_lines[prev_line_no] = False
+        
+        cf = self.find_jump_targets(code)
+        POP_JUMP_IF_FALSE = self.dis.opmap['POP_JUMP_IF_FALSE']
+        POP_JUMP_IF_TRUE  = self.dis.opmap['POP_JUMP_IF_TRUE']
+
+        i = 0
+        extended_arg = 0
+        free = None
+        while i < n:
+            offset = i
+            k = 0
+            if cf.has_key(offset):
+                for j in cf[offset]:
+                    rv.append(Token('COME_FROM', None, repr(j),
+                                    offset="%s_%d" % (offset, k) ))
+                    k += 1
+                    
+#            if self.__end_if_line.has_key(offset):
+#                rv.append(Token('END_IF_LINE', None, repr(self.__end_if_line[offset]), 
+#                            offset="%s_" % offset ))
+            c = code[i]
+            op = ord(c)
+            opname = dis.opname[op]
+            i += 1
+            oparg = None; pattr = None
+            if op >= dis.HAVE_ARGUMENT:
+                oparg = ord(code[i]) + ord(code[i+1]) * 256 + extended_arg
+                extended_arg = 0
+                i += 2
+                if op == dis.EXTENDED_ARG:
+                    extended_arg = oparg * 65536L
+                if op in dis.hasconst:
+                    const = co.co_consts[oparg]
+                    if type(const) == types.CodeType:
+                        oparg = const
+                        if const.co_name == '<lambda>':
+                            assert opname == 'LOAD_CONST'
+                            opname = 'LOAD_LAMBDA'
+                        elif const.co_name == '<genexpr>':
+                            opname = 'LOAD_GENEXPR'
+                        elif const.co_name == '<dictcomp>':
+                            opname = 'LOAD_DICTCOMP'
+                        elif const.co_name == '<setcomp>':
+                            opname = 'LOAD_SETCOMP'
+                        # verify uses 'pattr' for comparism, since 'attr'
+                        # now holds Code(const) and thus can not be used
+                        # for comparism (todo: think about changing this)
+                        #pattr = 'code_object @ 0x%x %s->%s' %\
+                        #	(id(const), const.co_filename, const.co_name)
+                        pattr = '<code_object ' + const.co_name + '>'
+                    else:
+                        pattr = const
+                elif op in dis.hasname:
+                    pattr = co.co_names[oparg]
+                elif op in dis.hasjrel:
+                    pattr = repr(i + oparg)
+                elif op in dis.hasjabs:
+                    pattr = repr(oparg)
+                elif op in dis.haslocal:
+                    pattr = co.co_varnames[oparg]
+                elif op in dis.hascompare:
+                    pattr = dis.cmp_op[oparg]
+                elif op in dis.hasfree:
+                    if free is None:
+                        free = co.co_cellvars + co.co_freevars
+                    pattr = free[oparg]
+
+            if opname == 'SET_LINENO':
+                continue
+            elif opname in ('BUILD_LIST', 'BUILD_TUPLE', 'BUILD_SLICE', 'BUILD_SET',
+                            'UNPACK_LIST', 'UNPACK_TUPLE', 'UNPACK_SEQUENCE',
+                            'MAKE_FUNCTION', 'CALL_FUNCTION', 'MAKE_CLOSURE',
+                            'CALL_FUNCTION_VAR', 'CALL_FUNCTION_KW',
+                            'CALL_FUNCTION_VAR_KW', 'DUP_TOPX',
+                            ):
+                # CE - Hack for >= 2.5
+                #      Now all values loaded via LOAD_CLOSURE are packed into
+                #      a tuple before calling MAKE_CLOSURE.
+                if opname == 'BUILD_TUPLE' and \
+                   dis.opname[ord(code[offset-3])] == 'LOAD_CLOSURE':
+                    continue
+                else:
+                    opname = '%s_%d' % (opname, oparg)
+                    customize[opname] = oparg
+#            elif opname == 'POP_JUMP_IF_TRUE':
+#                target = self.__get_target(code, offset)
+#                if (dis.opname[ord(code[target])] == 'FOR_ITER') and (self.lines[offset] != self.lines[offset+3]):
+#                    opname = 'CONTINUE_IF_TRUE'
+            elif opname == 'JUMP_ABSOLUTE':
+                target = self.__get_target(code, offset)
+                if target < offset:
+                    opname = 'JUMP_BACK'
+
+            elif opname == 'LOAD_GLOBAL':
+                try:
+                    if pattr == 'AssertionError' and rv and rv[-1] == 'POP_JUMP_IF_TRUE':
+                        opname = 'LOAD_ASSERT'
+                except AttributeError:
+                    pass
+
+            elif opname == 'IMPORT_NAME':
+                if pattr == '':
+                    pattr = '.'
+
+            rv.append(Token(opname, oparg, pattr, offset))
+
+            if self.__jump_back_else.get(offset, False):
+                rv.append(Token('JUMP_BACK_ELSE', None, None, 
+                    offset="%s_" % offset ))
+            
+        if self.showasm:
+            out = self.out # shortcut
+            for t in rv:
+                print >>out, t
+            print >>out
+
+        return rv, customize
+
+    def __get_target(self, code, pos, op=None):
+        if op is None:
+            op = ord(code[pos])
+        target = ord(code[pos+1]) + ord(code[pos+2]) * 256
+        if op in self.dis.hasjrel:
+            target += pos + 3
+        return target
+
+    def __first_instr(self, code, start, end, instr, target=None, exact=True):
+        """
+        Find the first <instr> in the block from start to end.
+        <instr> is any python bytecode instruction or a list of opcodes
+        If <instr> is an opcode with a target (like a jump), a target
+        destination can be specified which must match precisely if exact
+        is True, or if exact is False, the instruction which has a target
+        closest to <target> will be returned.
+
+        Return index to it or None if not found.
+        """
+
+        assert(start>=0 and end<len(code))
+
+        HAVE_ARGUMENT = self.dis.HAVE_ARGUMENT
+
+        try:    instr[0]
+        except: instr = [instr]
+
+        pos = None
+        distance = len(code)
+        i = start
+        while i < end:
+            op = ord(code[i])
+            if op in instr:
+                if target is None:
+                    return i
+                dest = self.__get_target(code, i, op)
+                if dest == target:
+                    return i
+                elif not exact:
+                    _distance = abs(target - dest)
+                    if _distance < distance:
+                        distance = _distance
+                        pos = i
+            if op < HAVE_ARGUMENT:
+                i += 1
+            else:
+                i += 3
+        return pos
+
+    def __last_instr(self, code, start, end, instr, target=None, exact=True):
+        """
+        Find the last <instr> in the block from start to end.
+        <instr> is any python bytecode instruction or a list of opcodes
+        If <instr> is an opcode with a target (like a jump), a target
+        destination can be specified which must match precisely if exact
+        is True, or if exact is False, the instruction which has a target
+        closest to <target> will be returned.
+
+        Return index to it or None if not found.
+        """
+
+        if not (start>=0 and end<len(code)):
+            return None
+
+        HAVE_ARGUMENT = self.dis.HAVE_ARGUMENT
+
+        try:    instr[0]
+        except: instr = [instr]
+
+        pos = None
+        distance = len(code)
+        i = start
+        while i < end:
+            op = ord(code[i])
+            if op in instr:
+                if target is None:
+                    pos = i
+                else:
+                    dest = self.__get_target(code, i, op)
+                    if dest == target:
+                        distance = 0
+                        pos = i
+                    elif not exact:
+                        _distance = abs(target - dest)
+                        if _distance <= distance:
+                            distance = _distance
+                            pos = i
+            if op < HAVE_ARGUMENT:
+                i += 1
+            else:
+                i += 3
+        return pos
+
+    def __all_instr(self, code, start, end, instr, target=None):
+        """
+        Find all <instr> in the block from start to end.
+        <instr> is any python bytecode instruction or a list of opcodes
+        If <instr> is an opcode with a target (like a jump), a target
+        destination can be specified which must match precisely.
+
+        Return a list with indexes to them or [] if none found.
+        """
+
+        assert(start>=0 and end<len(code))
+
+        HAVE_ARGUMENT = self.dis.HAVE_ARGUMENT
+
+        try:    instr[0]
+        except: instr = [instr]
+
+        result = []
+        i = start
+        while i < end:
+            op = ord(code[i])
+            if op in instr:
+                if target is None:
+                    result.append(i)
+                elif target == self.__get_target(code, i, op):
+                    result.append(i)
+            if op < HAVE_ARGUMENT:
+                i += 1
+            else:
+                i += 3
+        return result
+
+    def __next_except_jump(self, code, start, end, target):
+        """
+        Return the next jump that was generated by an except SomeException:
+        construct in a try...except...else clause or None if not found.
+        """
+        HAVE_ARGUMENT = self.dis.HAVE_ARGUMENT
+        JUMP_FORWARD  = self.dis.opmap['JUMP_FORWARD']
+        JUMP_ABSOLUTE = self.dis.opmap['JUMP_ABSOLUTE']
+        END_FINALLY   = self.dis.opmap['END_FINALLY']
+        POP_TOP       = self.dis.opmap['POP_TOP']
+        DUP_TOP       = self.dis.opmap['DUP_TOP']
+        try:    SET_LINENO = self.dis.opmap['SET_LINENO']
+        except: SET_LINENO = None
+
+        lookup = [JUMP_ABSOLUTE, JUMP_FORWARD]
+        while start < end:
+            jmp = self.__first_instr(code, start, end, lookup, target)
+            if jmp is None:
+                return None
+            if jmp == end-3:
+                return jmp
+            after = jmp + 3
+            ops = [None, None, None, None]
+            opp = [0, 0, 0, 0]
+            pos = 0
+            x = jmp+3
+            while x <= end and pos < 4:
+                op = ord(code[x])
+                if op == SET_LINENO:
+                    x += 3
+                    continue
+                elif op >= HAVE_ARGUMENT:
+                    break
+                ops[pos] = op
+                opp[pos] = x
+                pos += 1
+                x += 1
+            if ops[0] == END_FINALLY and opp[0] == end:
+                return jmp
+            if ops[0] == DUP_TOP:
+                return jmp
+            if ops[0] == ops[1] == ops[2] == POP_TOP:
+                return jmp
+            start = jmp + 3
+        return None
+
+    def __list_comprehension(self, code, pos, op=None):
+        """
+        Determine if there is a list comprehension structure starting at pos
+        """
+        BUILD_LIST = self.dis.opmap['BUILD_LIST']
+        DUP_TOP    = self.dis.opmap['DUP_TOP']
+        LOAD_ATTR  = self.dis.opmap['LOAD_ATTR']
+        if op is None:
+            op = ord(code[pos])
+        if op != BUILD_LIST:
+            return 0
+        try:
+            elems = ord(code[pos+1]) + ord(code[pos+2])*256
+            codes = (op, elems, ord(code[pos+3]), ord(code[pos+4]))
+        except IndexError:
+            return 0
+        return (codes==(BUILD_LIST, 0, DUP_TOP, LOAD_ATTR))
+
+    def __ignore_if(self, code, pos):
+        """
+        Return true if this 'if' is to be ignored.
+        """
+        POP_TOP      = self.dis.opmap['POP_TOP']
+        COMPARE_OP   = self.dis.opmap['COMPARE_OP']
+        EXCEPT_MATCH = self.dis.copmap['exception match']
+
+        ## If that was added by a while loop
+        if pos in self.__ignored_ifs:
+            return 1
+
+        # Check if we can test only for POP_TOP for this -Dan
+        # Maybe need to be done as above (skip SET_LINENO's)
+        if (ord(code[pos-3])==COMPARE_OP and
+            (ord(code[pos-2]) + ord(code[pos-1])*256)==EXCEPT_MATCH and
+            ord(code[pos+3])==POP_TOP and
+            ord(code[pos+4])==POP_TOP and
+            ord(code[pos+5])==POP_TOP and
+            ord(code[pos+6])==POP_TOP):
+            return 1 ## Exception match
+        return 0
+
+    def __fix_parent(self, code, target, parent):
+        """Fix parent boundaries if needed"""
+        JUMP_ABSOLUTE = self.dis.opmap['JUMP_ABSOLUTE']
+        start = parent['start']
+        end = parent['end']
+        ## Map the second start point for 'while 1:' in python 2.3+ to start
+        try:    target = self.__while1[target]
+        except: pass
+        if target >= start or end-start < 3 or target not in self.__loops:
+            return
+        if ord(code[end-3])==JUMP_ABSOLUTE:
+            cont_target = self.__get_target(code, end-3, JUMP_ABSOLUTE)
+            if target == cont_target:
+                parent['end'] = end-3
+
+    def __restrict_to_parent(self, target, parent):
+        """Restrict pos to parent boundaries."""
+        if not (parent['start'] < target < parent['end']):
+            target = parent['end']
+        return target
+
+        
+    def __detect_structure(self, code, pos, op=None):
+        """
+        Detect structures and their boundaries to fix optimizied jumps
+        in python2.3+
+        """
+
+        # TODO: check the struct boundaries more precisely -Dan
+
+        SETUP_LOOP    = self.dis.opmap['SETUP_LOOP']
+        BUILD_LIST    = self.dis.opmap['BUILD_LIST']
+        FOR_ITER      = self.dis.opmap['FOR_ITER']
+        GET_ITER      = self.dis.opmap['GET_ITER']
+        SETUP_EXCEPT  = self.dis.opmap['SETUP_EXCEPT']
+        JUMP_FORWARD  = self.dis.opmap['JUMP_FORWARD']
+        JUMP_ABSOLUTE = self.dis.opmap['JUMP_ABSOLUTE']
+        POP_JUMP_IF_FALSE = self.dis.opmap['POP_JUMP_IF_FALSE']
+        POP_JUMP_IF_TRUE  = self.dis.opmap['POP_JUMP_IF_TRUE']
+        JUMP_IF_FALSE_OR_POP = self.dis.opmap['JUMP_IF_FALSE_OR_POP']
+        JUMP_IF_TRUE_OR_POP = self.dis.opmap['JUMP_IF_TRUE_OR_POP']
+        END_FINALLY   = self.dis.opmap['END_FINALLY']
+        POP_TOP       = self.dis.opmap['POP_TOP']
+        POP_BLOCK       = self.dis.opmap['POP_BLOCK']
+        RETURN_VALUE = self.dis.opmap['RETURN_VALUE']
+        CONTINUE_LOOP = self.dis.opmap['CONTINUE_LOOP']
+        LOAD_ATTR = self.dis.opmap['LOAD_ATTR']
+        LOAD_FAST = self.dis.opmap['LOAD_FAST']
+        RAISE_VARARGS = self.dis.opmap['RAISE_VARARGS']
+        # Ev remove this test and make op a mandatory argument -Dan
+        if op is None:
+            op = ord(code[pos])
+
+        ## Detect parent structure
+        parent = self.__structs[0]
+        start  = parent['start']
+        end    = parent['end']
+        for s in self.__structs:
+            _start = s['start']
+            _end   = s['end']
+            if (_start <= pos < _end) and (_start >= start and _end <= end):
+                start  = _start
+                end    = _end
+                parent = s
+
+        ## We need to know how many new structures were added in this run
+        origStructCount = len(self.__structs)
+
+        if op == SETUP_LOOP:
+            start = pos+3
+#            if ord(code[start])==JUMP_FORWARD:
+                ## This is a while 1 (has a particular structure)
+#                start = self.__get_target(code, start, JUMP_FORWARD)
+#                start = self.__restrict_to_parent(start, parent)
+#                self.__while1[pos+3] = start ## map between the 2 start points
+            target = self.__get_target(code, pos, op)
+            end    = self.__restrict_to_parent(target, parent)
+            if target != end:
+                self.__fixed_jumps[pos] = end
+            
+            (line_no, next_line_byte) = self.lines[pos]
+            jump_back = self.__last_instr(code, start, end, JUMP_ABSOLUTE,
+                                          next_line_byte, False)
+            if not jump_back:
+                return
+
+            if self.__get_target(code, jump_back) >= next_line_byte:
+                jump_back = self.__last_instr(code, start, end, JUMP_ABSOLUTE,
+                                          start, False)
+
+                
+            if end > jump_back+4 and ord(code[end]) in (JUMP_FORWARD, JUMP_ABSOLUTE):
+                if ord(code[jump_back+4]) in (JUMP_ABSOLUTE,):
+                    if self.__get_target(code, jump_back+4) == self.__get_target(code, end):
+                        self.__fixed_jumps[pos] = jump_back+4
+                        end = jump_back+4
+            elif target < pos:
+                self.__fixed_jumps[pos] = jump_back+4
+                end = jump_back+4
+             
+            target = self.__get_target(code, jump_back, JUMP_ABSOLUTE)
+
+            if ord(code[target]) in (FOR_ITER, GET_ITER):
+                loop_type = 'for'
+            else:
+                loop_type = 'while'
+                (line_no, next_line_byte) = self.lines[pos]
+                test = self.prev[next_line_byte]
+                assert(test is not None)
+                self.__ignored_ifs.append(test)
+                test_target = self.__get_target(code, test)
+                if test_target > (jump_back+3):
+                    jump_back = test_target
+                 
+            self.__loops.append(target)
+            self.__structs.append({'type': loop_type + '-loop',
+                                   'start': target,
+                                   'end':   jump_back})
+            self.__structs.append({'type': loop_type + '-else',
+                                   'start': jump_back+3,
+                                   'end':   end})
+        elif self.__list_comprehension(code, pos, op):
+            get_iter = self.__first_instr(code, pos+7, end, GET_ITER)
+            for_iter = self.__first_instr(code, get_iter, end, FOR_ITER)
+            assert(get_iter is not None and for_iter is not None)
+            start  = get_iter+1
+            target = self.__get_target(code, for_iter, FOR_ITER)
+            end    = self.__restrict_to_parent(target, parent)
+            jump_back = self.__last_instr(code, start, end, JUMP_ABSOLUTE,
+                                          start, False)
+            assert(jump_back is not None)
+            target = self.__get_target(code, jump_back, JUMP_ABSOLUTE)
+            start = self.__restrict_to_parent(target, parent)
+            self.__structs.append({'type': 'list-comprehension',
+                                   'start': start,
+                                   'end':   jump_back})
+        elif op == SETUP_EXCEPT:
+            start  = pos+3
+            target = self.__get_target(code, pos, op)
+            end    = self.__restrict_to_parent(target, parent)
+            if target != end:
+                self.__fixed_jumps[pos] = end
+                #print target, end, parent
+            ## Add the try block
+            self.__structs.append({'type':  'try',
+                                   'start': start,
+                                   'end':   end-4})
+            ## Now isolate the except and else blocks
+            start  = end
+            target = self.__get_target(code, self.prev[start])
+            self.__fix_parent(code, target, parent)
+            end    = self.__restrict_to_parent(target, parent)
+            #if target != end:
+            #    self.__fixed_jumps[self.prev[start]] = end
+
+            end_finally = self.__last_instr(code, start, end, END_FINALLY)
+            if end_finally is None:
+                return
+            lookup = [JUMP_FORWARD]
+            jump_end = self.__last_instr(code, start, end, lookup)
+            if jump_end:
+                target = self.__get_target(code, jump_end)
+                end = self.__restrict_to_parent(target, parent)
+            #    if target != end:
+            #        self.__fixed_jumps[jump_end] = end
+           ## Add the try-else block
+            self.__structs.append({'type':  'try-else',
+                                   'start': end_finally+1,
+                                   'end':   end})
+            ## Add the except blocks
+            i = start
+            while i < end_finally:
+                jmp = self.__next_except_jump(code, i, end_finally, target)
+                if jmp is None:
+                    break
+                self.__structs.append({'type':  'except',
+                                       'start': i,
+                                       'end':   jmp})
+       #         if target != end:
+       #             self.__fixed_jumps[jmp] = end
+                i = jmp+3
+        elif op in (POP_JUMP_IF_FALSE, POP_JUMP_IF_TRUE):
+
+#            if self.__ignore_if(code, pos):
+#                return
+            start = pos+3 
+            target = self.__get_target(code, pos, op)
+            rtarget = self.__restrict_to_parent(target, parent)
+            
+            (line_no, next_line_byte) = self.lines[pos]
+            
+            if target == rtarget:
+                prev_target = self.prev[target]
+                prev_target_op = ord(code[prev_target])
+                target_op = ord(code[target])
+                if prev_target_op == JUMP_ABSOLUTE and target_op != POP_BLOCK:
+                    if self.__get_target(code, prev_target) < pos:
+                        self.__jump_back_else[prev_target] = True
+
+            #is this part of a larger expression
+            if (ord(code[self.prev[target]]) in (JUMP_IF_FALSE_OR_POP, JUMP_IF_TRUE_OR_POP,
+                    POP_JUMP_IF_FALSE, POP_JUMP_IF_TRUE)) and (target > pos):  
+                self.__fixed_jumps[pos] = self.prev[target]
+                if op == POP_JUMP_IF_TRUE:
+                    self.pjit_tgt[self.prev[target]] = True
+                return
+                
+            #is this not at the end of a line
+            if line_no == self.lines[start][0]:
+                #is this a one line if with multiple tests
+                good_op = False
+                prev = self.prev[next_line_byte]
+                p_op = ord(code[prev])
+                if op == POP_JUMP_IF_FALSE:
+                    if target == next_line_byte:
+                        if p_op == JUMP_FORWARD:
+                            if self.__get_target(code, prev) == target:
+                                good_op = True
+                        if p_op == RETURN_VALUE:
+                            good_op = True
+                    else:
+                        if start < target < next_line_byte:
+                            if ord(code[self.prev[target]]) in (JUMP_ABSOLUTE, JUMP_FORWARD):
+                                good_op = True
+                        while p_op in (JUMP_ABSOLUTE, JUMP_FORWARD, POP_BLOCK):
+                            if p_op in (JUMP_ABSOLUTE, JUMP_FORWARD):
+                                if self.__get_target(code, prev) == target:
+                                    good_op = True
+                                    break
+                            prev = self.prev[prev]
+                            p_op = ord(code[prev])
+                    if good_op:
+                        last = self.__last_instr(code, start, next_line_byte,
+                                (POP_JUMP_IF_FALSE, POP_JUMP_IF_TRUE), target)
+                        if last:
+                            self.__fixed_jumps[pos] = last
+                            return
+                else:
+                    while p_op in (JUMP_ABSOLUTE, JUMP_FORWARD, POP_BLOCK):
+                        if p_op in (JUMP_ABSOLUTE, JUMP_FORWARD):
+                            if self.__get_target(code, prev) == target:
+                                last = self.__last_instr(code, start, next_line_byte,
+                                        (POP_JUMP_IF_FALSE, POP_JUMP_IF_TRUE))
+                                if last:
+                                    self.__fixed_jumps[pos] = last
+                                    return
+                                break
+                        prev = self.prev[prev]
+                        p_op = ord(code[prev])
+                 
+                #if ifline
+                if self.if_lines.get(line_no, False):
+                    if (target >= next_line_byte) or (target < pos):
+                        if not (line_no == self.lines[target][0]):
+                            self.__fixed_jumps[pos] = self.prev[next_line_byte]
+                    return
+                if self.if_lines.get(line_no+1, False):
+                    next_if = self.prev[self.lines[next_line_byte][1]]
+                    if target == self.__get_target(code, next_if):
+                        self.__fixed_jumps[pos] = next_if
+                    elif (op == POP_JUMP_IF_TRUE) and (ord(code[next_if+3]) == JUMP_ABSOLUTE) and (target == self.__get_target(code, next_if+3)) and (target < pos):
+                        self.__fixed_jumps[pos] = next_if
+                    return
+                else:
+                    if self.lines[target][0] > line_no:
+                        next = self.__first_instr(code, start, target, POP_JUMP_IF_FALSE, target)
+                        j = self.__first_instr(code, start, target, JUMP_ABSOLUTE, target)
+                        if next and not j:
+                            self.__fixed_jumps[pos] = next
+                    return
+                return
+                    
+            if op == POP_JUMP_IF_FALSE:
+                i = self.lines[next_line_byte][0]
+                k = j = next_line_byte
+                num_pj = 1
+                while ((self.if_lines.get(i, False)
+                        and ((self.__get_target(code, self.lines[j][1]-3) == target)
+                             or ((ord(code[self.lines[j][1]-3]) == POP_JUMP_IF_TRUE)
+                                 and (ord(code[self.__get_target(code, self.lines[j][1]-3)-3]) == POP_JUMP_IF_FALSE)
+                                 and (self.__get_target(code, self.__get_target(code, self.lines[j][1]-3)-3) == target))))
+                       or (ord(code[self.prev[self.lines[j][1]]]) in (LOAD_ATTR, LOAD_FAST))):
+                    if (self.if_lines.get(i, False) and (self.__get_target(code, self.lines[j][1]-3) == target)):
+                        num_pj += 1
+                    j = self.lines[j][1]
+                    i = self.lines[j][0]
+                    if (ord(code[self.prev[j]]) not in (LOAD_ATTR, LOAD_FAST)):
+                        k = j
+                if k > next_line_byte:
+                    if num_pj > 1 and target > pos:
+                        prev_end = self.prev[rtarget]
+                        num_pj += len({ self.lines[a][0] for a in self.__all_instr(code, k, prev_end, (POP_JUMP_IF_FALSE, POP_JUMP_IF_TRUE), target)})
+                        num_pr = len({ self.lines[a][0] for a in self.__all_instr(code, k, prev_end, (POP_JUMP_IF_FALSE, POP_JUMP_IF_TRUE), rtarget)})
+                        num_jumps = 0
+                        while ord(code[prev_end]) in (JUMP_FORWARD, JUMP_ABSOLUTE) and self.__get_target(code, prev_end) == target:
+                            num_pr += len({ self.lines[a][0] for a in self.__all_instr(code, k, prev_end, (POP_JUMP_IF_FALSE, POP_JUMP_IF_TRUE), prev_end)})
+                            num_jumps += 1
+                            prev_end = self.prev[prev_end]
+                        if ord(code[prev_end]) == RETURN_VALUE:
+                            num_jumps += 1
+                            num_pj += num_pr
+                        num_pj += len(self.__all_instr(code, k, prev_end, (POP_JUMP_IF_FALSE, POP_JUMP_IF_TRUE), target))
+                        if num_pj > num_jumps:
+                            self.__fixed_jumps[pos] = k-3
+                            return
+                    else:
+                        self.__fixed_jumps[pos] = k-3
+                        return
+                
+#            elif op == POP_JUMP_IF_TRUE and target > pos:
+#                i = self.lines[next_line_byte][0]
+#                j = next_line_byte
+#                while (self.if_lines.get(i, False)
+#                       and ((self.__get_target(code, self.lines[j][1]-3) == target)
+#                            and (ord(code[self.lines[j][1]-3]) == POP_JUMP_IF_TRUE))):
+#                       j = self.lines[j][1]
+#                       i = self.lines[j][0]
+#                if j > next_line_byte:
+#                    self.__fixed_jumps[pos] = j-3
+#                    return
+            elif op == POP_JUMP_IF_TRUE:
+                def equaljumps(jump1, jump2):
+                    jump_ops = (JUMP_ABSOLUTE, JUMP_FORWARD)
+                    while ord(code[jump1]) in jump_ops:
+                        jump1 = self.__get_target(code, jump1)
+                    while ord(code[jump2]) in jump_ops:
+                        jump2 = self.__get_target(code, jump2)
+                    return jump1 == jump2
+                i = self.lines[next_line_byte][0]
+                j = next_line_byte
+                while self.if_lines.get(i, False):
+                       j = self.lines[j][1]
+                       i = self.lines[j][0]
+                if j > next_line_byte:
+                    if ord(code[j]) == JUMP_ABSOLUTE and equaljumps(j, target):
+                        self.__fixed_jumps[pos] = j-3
+                        return
+                
+            if (target < pos) and ((ord(code[target]) == FOR_ITER) or (ord(code[self.prev[target]]) == SETUP_LOOP)):
+#                self.__end_if_line[start] = 0
+                
+                if ord(code[self.prev[end]]) == JUMP_ABSOLUTE:
+                    if self.__get_target(code, self.prev[end]) == target:
+                        self.__structs.append({'type':  'if-then',
+                                       'start': pos,
+                                       'end':   self.prev[end]})
+#                        print self.__structs[-1]
+                return
+
+            #does the if jump just beyond a jump op, then this is probably an if statement
+            if ord(code[self.prev[rtarget]]) in (JUMP_ABSOLUTE, JUMP_FORWARD):
+                if_end = self.__get_target(code, self.prev[rtarget])
+
+                if (if_end < self.prev[rtarget]) and (ord(code[self.prev[if_end]]) == SETUP_LOOP):
+                    loopjump = self.__last_instr(code, start, end, JUMP_ABSOLUTE, if_end)
+                    if(if_end > start):