#
# decompile.py - decompile Python code objects
#
# Copyright (c) 2001 Jonathan Patrick Giddy
#
# Permission is hereby granted, free of charge, to any person obtaining 
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
# 
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

# Send comments to:
# Jonathan Giddy, jongiddy@pobox.co.uk

# version 0.9
# Date 21 January 2005
# - no code changes, added MIT licence, updated email address
# - there will be no v1.0 - try decompyle instead

# version 0.8
# Date 25 January 2001

__version__ = '0.9'

import dis, string, sys, types

VARARGS = 4
KWARGS = 8

PRECEDENCE_ATOM = 24
PRECEDENCE_POWER = 17
PRECEDENCE_UNARY = 16
PRECEDENCE_MULT = 15
PRECEDENCE_ADD = 14
PRECEDENCE_SHIFT = 13
PRECEDENCE_BAND = 12
PRECEDENCE_BXOR = 11
PRECEDENCE_BOR = 10
PRECEDENCE_CMP = 9
PRECEDENCE_IS = 8
PRECEDENCE_IN = 7
PRECEDENCE_NOT = 6
PRECEDENCE_AND = 5
PRECEDENCE_OR = 4
PRECEDENCE_LAMBDA = 3
PRECEDENCE_ARG = PRECEDENCE_LAMBDA
PRECEDENCE_COMMA = 1
PRECEDENCE_NONE = 0

def current_line(code, i):
    tab = code.co_lnotab
    line = code.co_firstlineno
    stopat = i
    addr = 0
    for i in range(0, len(tab), 2):
        addr = addr + ord(tab[i])
        if addr > stopat:
            break
        line = line + ord(tab[i+1])
    return line

class Expression:

    def __init__(self, value, precedence):
        self.value = value
        self.precedence = precedence

    def __repr__(self):
        return 'Expression(%s, %s)' % (`self.value`, `self.precedence`)
    
    def __str__(self):
        return str(self.value)

    def Precedence(self):
        return self.precedence
    
    def Value(self):
        return self.value

    def GetString(self, precedence):
        if self.Precedence() < precedence:
            return '(%s)' % self
        else:
            return str(self)

class Atom(Expression):

    def __init__(self, value):
        Expression.__init__(self, value, PRECEDENCE_ATOM)

class Constant(Atom):

    def __str__(self):
        if self.value is Ellipsis:
            return '...'
        else:
            return repr(self.value)
        
class Local(Atom):
    pass

class Global(Atom):
    pass

class Map(Atom):

    def __init__(self):
        Atom.__init__(self, [])

    def __str__(self):
        return '{%s}' % string.join(self.value, ', ')

    def SetAttr(self, name, value):
        self.value.append("%s: %s" % (name, value))

class Tuple(Atom):

    def __init__(self, values):
        Atom.__init__(self, values)

    def __str__(self):
        values = self.Value()
        if len(values) == 0:
            return '()'
        elif len(values) == 1:
            # This doesn't need the parens immediately, but
            # it emphasises the prescence of the comma, and
            # confirms that it is a 1-tuple
            return '(%s,)' % values[0]
        else:
            return string.join(values, ', ')

    def Precedence(self):
        if len(self.value) <= 1:
            return PRECEDENCE_ATOM
        else:
            return PRECEDENCE_COMMA

    def Value(self):
        return tuple(self.value)
            
class CodeCursor:

    def __init__(self, code):
        self.code = code  # code object
        self.i = 0        # instruction pointer
        self.extend = 0   # extended opcodes
        self.lineno = 1   # minimum possible line number
        self.lastop = 0   # pointer to last operator read
        self.stopi = [len(code.co_code)]

    def GetPosition(self):
        return self.i

    def AtEnd(self):
        return self.i == self.stopi[0]
    
    def GetLine(self):
        return max(self.lineno, current_line(self.code, self.lastop))

    def SetLine(self, lineno):
        assert lineno >= self.lineno, `lineno, self.lineno`
        self.lineno = lineno
        
    def PushStop(self, i):
        self.stopi.append(i)

    def PopStop(self):
        return self.stopi.pop()

    def NextOpcode(self):
        if self.i < self.stopi[-1]:
            c = self.code.co_code[self.i]
            op = ord(c)
            opcode = dis.opname[op]
            if opcode == 'EXTENDED_ARG':
                self.i = self.i + 1
                self.extend = self.ReadOperand()
                return self.NextOpcode()
        else:
            opcode = None
        return opcode

    def ReadOpcode(self, *args):
        opcode = self.NextOpcode()
        assert opcode is not None
        if args:
            assert opcode in args, `self.i, opcode`
        self.lastop = self.i
        self.i = self.i + 1
        return opcode

    def ReadOperand(self):
        assert self.i + 1 < self.stopi[-1], `self.i, self.stopi`
        co_code = self.code.co_code
        operand = ord(co_code[self.i]) + ord(co_code[self.i+1])*256 + \
                  (self.extend << 16)
        self.extend = 0
        self.i = self.i + 2
        return operand

    def GetConstant(self, n):
        return self.code.co_consts[n]

    def GetLocal(self, n):
        assert n < len(self.code.co_varnames), `n, self.code.co_varnames`
        return self.code.co_varnames[n]
    
    def GetName(self, n):
        assert n < len(self.code.co_names), `n, self.code.co_names`
        return self.code.co_names[n]
    
class Decompiler:

    def __init__(self, version):
        self.version = version
        self.stack = []
        self.lines = {}
        self.global_decl = {}
        self.loop = None
    
    def decompile(self, code, *termop):
        try:
            self.code = code
            opcode = code.NextOpcode()
            while opcode is not None and opcode not in termop:
                opcode = string.replace(opcode, '+', '_')
                method = getattr(self, opcode)
                apply(method, (code,))
                opcode = code.NextOpcode()
        except:
            dis.dis(code.code)
            print
            raise

    def getstack(self):
        return self.stack
    
    def getsource(self, indent):
        assert not self.stack, `self.stack`
        lines = {}
        if not self.lines:
            self.lines[self.code.GetLine()] = 'pass'
        for key, value in self.lines.items():
            lines[key] = '    ' * indent + value
        return lines

    def addline(self, lineno, line):
        assert type(line) == type(''), `line`
        prev = self.lines.get(lineno)
        if prev is None:
            self.lines[lineno] = line
        else:
            self.lines[lineno] = '%s; %s' % (prev, line)

    def addclause(self, lineno, head, body):
        if body.has_key(lineno):
            if 0:
                assert len(body) == 1, `body`
                self.addline(lineno, "%s %s" % (head, string.strip(body[lineno])))
            else:
                line = body[lineno]
                self.addline(lineno, "%s %s" % (head, string.strip(line)))
                del body[lineno]
                self.lines.update(body)
                body[lineno] = line
        else:
            self.addline(lineno, head)
            self.lines.update(body)
        self.code.SetLine(max(body.keys()) + 1)

    def SET_LINENO(self, code):
        code.ReadOpcode('SET_LINENO')
        code.ReadOperand()

    def BINARY_ADD(self, code):
        code.ReadOpcode('BINARY_ADD')
        y = self.stack.pop().GetString(PRECEDENCE_ADD+1)
        x = self.stack.pop().GetString(PRECEDENCE_ADD)
        self.stack.append(Expression('%s + %s' % (x, y), PRECEDENCE_ADD))

    def BINARY_AND(self, code):
        code.ReadOpcode('BINARY_AND')
        y = self.stack.pop().GetString(PRECEDENCE_BAND+1)
        x = self.stack.pop().GetString(PRECEDENCE_BAND)
        self.stack.append(Expression('%s & %s' % (x, y), PRECEDENCE_BAND))

    def BINARY_DIVIDE(self, code):
        code.ReadOpcode('BINARY_DIVIDE')
        y = self.stack.pop().GetString(PRECEDENCE_MULT+1)
        x = self.stack.pop().GetString(PRECEDENCE_MULT)
        self.stack.append(Expression('%s / %s' % (x, y), PRECEDENCE_MULT))

    def BINARY_LSHIFT(self, code):
        code.ReadOpcode('BINARY_LSHIFT')
        y = self.stack.pop().GetString(PRECEDENCE_SHIFT+1)
        x = self.stack.pop().GetString(PRECEDENCE_SHIFT)
        self.stack.append(Expression('%s << %s' % (x, y), PRECEDENCE_SHIFT))

    def BINARY_MODULO(self, code):
        code.ReadOpcode('BINARY_MODULO')
        y = self.stack.pop().GetString(PRECEDENCE_MULT+1)
        x = self.stack.pop().GetString(PRECEDENCE_MULT)
        self.stack.append(Expression('%s %% %s' % (x, y), PRECEDENCE_MULT))

    def BINARY_MULTIPLY(self, code):
        code.ReadOpcode('BINARY_MULTIPLY')
        y = self.stack.pop().GetString(PRECEDENCE_MULT+1)
        x = self.stack.pop().GetString(PRECEDENCE_MULT)
        self.stack.append(Expression('%s * %s' % (x, y), PRECEDENCE_MULT))

    def BINARY_OR(self, code):
        code.ReadOpcode('BINARY_OR')
        y = self.stack.pop().GetString(PRECEDENCE_BOR+1)
        x = self.stack.pop().GetString(PRECEDENCE_BOR)
        self.stack.append(Expression('%s | %s' % (x, y), PRECEDENCE_BOR))

    def BINARY_POWER(self, code):
        code.ReadOpcode('BINARY_POWER')
        y = self.stack.pop()
        if y.Precedence() == PRECEDENCE_POWER:
            # include ** in parentheses because the correct order is poorly
            # understood, and is the opposite of the other binary operators
            y = y.GetString(PRECEDENCE_ATOM)
        else:
            y = y.GetString(PRECEDENCE_UNARY)
        x = self.stack.pop().GetString(PRECEDENCE_ATOM)
        self.stack.append(Expression('%s ** %s' % (x, y), PRECEDENCE_POWER))

    def BINARY_RSHIFT(self, code):
        code.ReadOpcode()
        y = self.stack.pop().GetString(PRECEDENCE_SHIFT+1)
        x = self.stack.pop().GetString(PRECEDENCE_SHIFT)
        self.stack.append(Expression('%s >> %s' % (x, y), PRECEDENCE_SHIFT))

    def BINARY_SUBSCR(self, code):
        code.ReadOpcode()
        y = self.stack.pop().GetString(PRECEDENCE_NONE)
        x = self.stack.pop().GetString(PRECEDENCE_ATOM)
        self.stack.append(Expression('%s[%s]' % (x, y), PRECEDENCE_ATOM))

    def BINARY_SUBTRACT(self, code):
        code.ReadOpcode()
        y = self.stack.pop().GetString(PRECEDENCE_ADD+1)
        x = self.stack.pop().GetString(PRECEDENCE_ADD)
        self.stack.append(Expression('%s - %s' % (x, y), PRECEDENCE_ADD))

    def BINARY_XOR(self, code):
        code.ReadOpcode()
        y = self.stack.pop().GetString(PRECEDENCE_BXOR+1)
        x = self.stack.pop().GetString(PRECEDENCE_BXOR)
        self.stack.append(Expression('%s ^ %s' % (x, y), PRECEDENCE_BXOR))

    def BREAK_LOOP(self, code):
        code.ReadOpcode('BREAK_LOOP')
        self.addline(code.GetLine(), 'break')
        
    def BUILD_LIST(self, code):
        code.ReadOpcode('BUILD_LIST')
        oparg = code.ReadOperand()
        values = []
        for i in range(oparg):
            value = self.stack.pop()
            if value.Precedence() < PRECEDENCE_ARG:
                value = '(%s)' % value
            values.append(str(value))
        values.reverse()
        valuelist = string.join(values, ', ')
        self.stack.append(Expression('[%s]' % valuelist, PRECEDENCE_ATOM))

    def BUILD_MAP(self, code):
        code.ReadOpcode('BUILD_MAP')
        code.ReadOperand()
        self.stack.append(Map())

    def BUILD_SLICE(self, code):
        code.ReadOpcode()
        code.ReadOperand()
        z = self.stack.pop()
        if isinstance(z, Constant) and z.Value() is None:
            z = ''
        else:
            z = z.GetString(PRECEDENCE_ARG)
        y = self.stack.pop()
        if isinstance(y, Constant) and y.Value() is None:
            y = ''
        else:
            y = y.GetString(PRECEDENCE_ARG)
        x = self.stack.pop()
        if isinstance(x, Constant) and x.Value() is None:
            x = ''
        else:
            x = x.GetString(PRECEDENCE_ARG)
        # always goes into BINARY_SUBSCR, so precedence is irrelevant
        self.stack.append(Expression('%s:%s:%s' % (x, y, z), PRECEDENCE_NONE))
        
    def BUILD_TUPLE(self, code):
        code.ReadOpcode()
        oparg = code.ReadOperand()
        values = []
        for i in range(oparg):
            value = self.stack.pop().GetString(PRECEDENCE_ARG)
            values.append(value)
        values.reverse()
        self.stack.append(Tuple(values))

    def CALL_FUNCTION(self, code):
        opcode = code.ReadOpcode('CALL_FUNCTION', 'CALL_FUNCTION_VAR',
                                 'CALL_FUNCTION_KW', 'CALL_FUNCTION_VAR_KW')
        oparg = code.ReadOperand()
        nkw, nargs = divmod(oparg, 256)
        args = []
        if opcode in ('CALL_FUNCTION_KW', 'CALL_FUNCTION_VAR_KW'):
            name = self.stack.pop()
            args.append('**%s' % name)
        if opcode in ('CALL_FUNCTION_VAR', 'CALL_FUNCTION_VAR_KW'):
            name = self.stack.pop()
            args.append('*%s' % name)
        for i in range(nkw):
            value = self.stack.pop().GetString(PRECEDENCE_ARG)
            name = self.stack.pop().Value()
            args.append('%s=%s' % (name, value))
        for i in range(nargs):
            arg = self.stack.pop().GetString(PRECEDENCE_ARG)
            args.append(str(arg))
        args.reverse()
        arglist = string.join(args, ', ')
        func = self.stack.pop().GetString(PRECEDENCE_ATOM)
        self.stack.append(Expression('%s(%s)' % (func, arglist),
                                     PRECEDENCE_ATOM))

    CALL_FUNCTION_VAR = CALL_FUNCTION
    CALL_FUNCTION_KW = CALL_FUNCTION
    CALL_FUNCTION_VAR_KW = CALL_FUNCTION
    
    def COMPARE_OP(self, code):
        code.ReadOpcode()
        oparg = code.ReadOperand()
        if len(self.stack) == 1:
            y = self.stack.pop()
            x = None
        else:
            y = self.stack.pop()
            x = self.stack.pop()
        op = dis.cmp_op[oparg]
        if op[0] in '!<=>':
            prec = PRECEDENCE_CMP
        elif op[-2:] == 'in':
            prec = PRECEDENCE_IN
        else:
            assert op[:2] == 'is', `op`
            prec = PRECEDENCE_IS
        if y.Precedence() <= prec:
            y = '(%s)' % y
        if x is None:
            self.stack.append(Chain('%s %s' % (op, y)))
        else:
            if x.Precedence() < prec:
                x = '(%s)' % x
            self.stack.append(Expression('%s %s %s' % (x, op, y), prec))

    def DELETE_ATTR(self, code):
        code.ReadOpcode()
        oparg = code.ReadOperand()
        attr = code.GetName(oparg)
        x = self.stack.pop().GetString(PRECEDENCE_ATOM)
        self.addline(code.GetLine(), 'del %s.%s' % (x, attr))
        
    def DELETE_FAST(self, code):
        names = []
        lastlineno = -1
        while code.NextOpcode() in ('DELETE_FAST', 'DELETE_GLOBAL',
                                    'DELETE_NAME'):
            opcode = code.ReadOpcode()
            if lastlineno == -1:
                lineno = lastlineno = code.GetLine()
            else:
                lineno = code.GetLine()
            if lineno != lastlineno:
                self.addline(lastlineno, 'del %s' % string.join(names, ', '))
                names = []
                lastlineno = lineno
            oparg = code.ReadOperand()
            if opcode == 'DELETE_FAST':
                names.append(code.GetLocal(oparg))
            elif opcode == 'DELETE_GLOBAL':
                names.append(code.GetName(oparg))
            else:
                assert opcode == 'DELETE_NAME', `opcode`
                names.append(code.GetName(oparg))
        self.addline(lastlineno, 'del %s' % string.join(names, ', '))

    DELETE_GLOBAL = DELETE_FAST
    DELETE_NAME = DELETE_FAST

    def DELETE_SLICE_0(self, code):
        code.ReadOpcode()
        x = self.stack.pop().GetString(PRECEDENCE_ATOM)
        self.addline(code.GetLine(), 'del %s[:]' % x)

    def DELETE_SLICE_1(self, code):
        code.ReadOpcode()
        y = self.stack.pop().GetString(PRECEDENCE_ARG)
        x = self.stack.pop().GetString(PRECEDENCE_ATOM)
        self.addline(code.GetLine(), 'del %s[%s:]' % (x, y))

    def DELETE_SLICE_2(self, code):
        code.ReadOpcode()
        z = self.stack.pop().GetString(PRECEDENCE_ARG)
        x = self.stack.pop().GetString(PRECEDENCE_ATOM)
        self.addline(code.GetLine(), 'del %s[:%s]' % (x, z))
        
    def DELETE_SLICE_3(self, code):
        code.ReadOpcode()
        z = self.stack.pop().GetString(PRECEDENCE_ARG)
        y = self.stack.pop().GetString(PRECEDENCE_ARG)
        x = self.stack.pop().GetString(PRECEDENCE_ATOM)
        self.addline(code.GetLine(), 'del %s[%s:%s]' % (x, y, z))
        
    def DELETE_SUBSCR(self, code):
        code.ReadOpcode()
        y = self.stack.pop().GetString(PRECEDENCE_NONE)
        x = self.stack.pop().GetString(PRECEDENCE_ATOM)
        self.addline(code.GetLine(), 'del %s[%s]' % (x, y))

    def DUP_TOP(self, code):
        code.ReadOpcode('DUP_TOP')
        self.stack.append(self.stack[-1])
        
    def DUP_TOPX(self, code):
        code.ReadOpcode('DUP_TOPX')
        n = code.ReadOperand()
        self.stack = self.stack + self.stack[-n:]
        
    def EXEC_STMT(self, code):
        code.ReadOpcode('EXEC_STMT')
        lineno = code.GetLine()
        locals = self.stack.pop()
        globals = self.stack.pop()
        stmt = self.stack.pop().GetString(PRECEDENCE_IN)
        if isinstance(globals, Constant) and globals.Value() is None:
            self.addline(lineno, 'exec %s' % stmt)
        else:
            if locals is globals:
                globals = globals.GetString(PRECEDENCE_ARG)
                self.addline(lineno, 'exec %s in %s' % (stmt, globals))
            else:
                globals = globals.GetString(PRECEDENCE_ARG)
                locals = locals.GetString(PRECEDENCE_ARG)
                self.addline(lineno,
                             'exec %s in %s, %s' % (stmt, globals, locals))

    def FOR_LOOP(self, code):
        code.ReadOpcode('FOR_LOOP')
        leap = code.ReadOperand()
        loopcleanup = code.GetPosition() + leap
        self.stack.pop()  # sequence index
        forlist = self.stack.pop()
        forvar = self.build_target(code).GetString(PRECEDENCE_NONE)
        head = "for %s in %s:" % (forvar, forlist)
        lineno = code.GetLine()
        d = Decompiler(self.version)
        d.decompile(code, 'JUMP_ABSOLUTE')
        self.addclause(lineno, head, d.getsource(1))
        code.ReadOpcode('JUMP_ABSOLUTE')
        oparg = code.ReadOperand()  # to FOR_LOOP (or SET_LINENO)
        assert code.GetPosition() == loopcleanup
        code.ReadOpcode('POP_BLOCK')
        end = self.loop[1]
        self.loop = None
        if code.GetPosition() < end:
            lineno = code.GetLine()
            code.PushStop(end)
            d = Decompiler(self.version)
            d.decompile(code)
            code.PopStop()
            self.addclause(lineno, "else:", d.getsource(1))
        assert code.GetPosition() == end
        
    def IMPORT_NAME(self, code):
        names = []
        while code.NextOpcode() == 'IMPORT_NAME':
            code.ReadOpcode('IMPORT_NAME')
            if self.version >= (2, 0):
                self.stack.pop()
            oparg = code.ReadOperand()
            module = code.GetName(oparg)
            opname = code.ReadOpcode('IMPORT_FROM', 'IMPORT_STAR',
                                     'STORE_FAST', 'STORE_NAME')
            if opname in ('IMPORT_FROM', 'IMPORT_STAR'):
                if names:
                    self.addline('import %s' % string.join(names, ', '))
                    names = []
                if opname == 'IMPORT_STAR':
                    objs = '*'
                else:
                    while opname == 'IMPORT_FROM':
                        oparg = code.ReadOperand()
                        name1 = code.GetName(oparg)
                        if self.version >= (2, 0):
                            opname = code.ReadOpcode('STORE_FAST', 'STORE_NAME')
                            oparg = code.ReadOperand()
                            if opname == 'STORE_FAST':
                                name2 = code.GetLocal(oparg)
                            else:
                                name2 = code.GetName(oparg)
                        else:
                            name2 = name1
                        if name1 == name2:
                            names.append(name1)
                        else:
                            names.append('%s as %s' % (name1, name2))
                        opname = code.ReadOpcode('IMPORT_FROM', 'POP_TOP')
                    objs = string.join(names, ', ')
                self.addline(code.GetLine(),
                             'from %s import %s' %
                             (module, objs))
                names = []
            else:
                assert opname in ('STORE_FAST', 'STORE_NAME'), `opname`
                oparg = code.ReadOperand()
                if opname == 'STORE_FAST':
                    name = code.GetLocal(oparg)
                else:
                    name = code.GetName(oparg)
                if module == name:
                    names.append(module)
                else:
                    names.append("%s as %s" % (module, name))
        if names:
            self.addline(code.GetLine(),
                         'import %s' % string.join(names, ', '))

    def INPLACE_ADD(self, code):
        opcode = code.ReadOpcode(
            'INPLACE_ADD', 'INPLACE_AND', 'INPLACE_DIVIDE', 'INPLACE_LSHIFT',
            'INPLACE_MODULO', 'INPLACE_MULTIPLY', 'INPLACE_OR', 'INPLACE_POWER',
            'INPLACE_RSHIFT', 'INPLACE_SUBTRACT', 'INPLACE_XOR')
        if opcode == 'INPLACE_ADD':
            op = '+='
        elif opcode == 'INPLACE_AND':
            op = '&='
        elif opcode == 'INPLACE_DIVIDE':
            op = '/='
        elif opcode == 'INPLACE_LSHIFT':
            op = '<<='
        elif opcode == 'INPLACE_MODULO':
            op = '%='
        elif opcode == 'INPLACE_MULTIPLY':
            op = '*='
        elif opcode == 'INPLACE_OR':
            op = '|='
        elif opcode == 'INPLACE_POWER':
            op = '**='
        elif opcode == 'INPLACE_RSHIFT':
            op = '>>='
        elif opcode == 'INPLACE_SUBTRACT':
            op = '-='
        else:
            assert opcode == 'INPLACE_XOR', `opcode`
            op = '^='
        y = self.stack.pop().GetString(PRECEDENCE_NONE)
        x = self.stack.pop().GetString(PRECEDENCE_NONE)
        opcode = code.ReadOpcode('ROT_THREE', 'ROT_TWO', 'STORE_FAST',
                                 'STORE_GLOBAL')
        if opcode == 'STORE_FAST':
            code.ReadOperand()
        elif opcode == 'STORE_GLOBAL':
            code.ReadOperand()
        elif opcode == 'STORE_NAME':
            code.ReadOperand()
        elif opcode == 'ROT_THREE':
            code.ReadOpcode('STORE_SUBSCR')
            self.stack.pop()
            self.stack.pop()
        elif opcode == 'ROT_TWO':
            code.ReadOpcode('STORE_ATTR')
            code.ReadOperand()
            self.stack.pop()
        self.addline(code.GetLine(), '%s %s %s' % (x, op, y))

    INPLACE_AND = INPLACE_ADD
    INPLACE_DIVIDE = INPLACE_ADD
    INPLACE_LSHIFT = INPLACE_ADD
    INPLACE_MODULO = INPLACE_ADD
    INPLACE_MULTIPLY = INPLACE_ADD
    INPLACE_OR = INPLACE_ADD
    INPLACE_POWER = INPLACE_ADD
    INPLACE_RSHIFT = INPLACE_ADD
    INPLACE_SUBTRACT = INPLACE_ADD
    INPLACE_XOR = INPLACE_ADD
    
    def JUMP_ABSOLUTE(self, code):
        code.ReadOpcode('JUMP_ABSOLUTE')
        code.ReadOperand()
        self.addline(code.GetLine(), 'continue')
        
    def JUMP_IF_FALSE(self, code):
        code.ReadOpcode('JUMP_IF_FALSE')
        leap = code.ReadOperand()
        endcond = code.GetPosition() + leap
        opcode = code.ReadOpcode('POP_TOP')
        assert opcode == 'POP_TOP', `opcode`
        lineno = code.GetLine()
        code.PushStop(endcond)
        d = Decompiler(self.version)
        if self.loop is None:
            d.decompile(code, 'JUMP_FORWARD')
        else:
            d.decompile(code, 'JUMP_ABSOLUTE')
        code.PopStop()
        stack = d.getstack()
        if stack:
            if len(stack) == 1:
                assert code.GetPosition() == endcond
                # and
                x = self.stack.pop().GetString(PRECEDENCE_AND+1)
                y = stack.pop().GetString(PRECEDENCE_AND)
                self.stack.append(
                    Expression('%s and %s' % (x, y), PRECEDENCE_AND))
            else:
                # assert
                self.stack.pop()
                test = stack.pop().GetString(PRECEDENCE_ARG)
                value = stack.pop()
                if value is None:
                    self.addline(lineno, 'assert %s' % test)
                else:
                    value = value.GetString(PRECEDENCE_ARG)
                    self.addline(lineno, 'assert %s, %s' % (test, value))
                code.ReadOpcode('POP_TOP')
        else:
            condition = self.stack.pop()
            body = d.getsource(1)
            if self.loop is None:
                # if
                self.addclause(lineno, 'if %s:' % condition, body)
                code.ReadOpcode('JUMP_FORWARD')
                leap = code.ReadOperand()
                end = code.GetPosition() + leap
                code.ReadOpcode('POP_TOP')
                while code.GetPosition() < end:
                    lineno = code.GetLine()
                    code.PushStop(end)
                    d = Decompiler(self.version)
                    d.decompile(code, 'JUMP_FORWARD')
                    code.PopStop()
                    body = d.getsource(1)
                    if body.has_key(lineno):
                        if body[lineno][-1] == ':':
                            # elif
                            body = d.getsource(0)
                            body[lineno] = 'el' + body[lineno]
                            self.lines.update(body)
                        else:
                            #assert len(body) == 1, `body`
                            line = body[lineno]
                            self.addline(lineno, "else: %s" %
                                         string.strip(line))
                            del body[lineno]
                            self.lines.update(body)
                            body[lineno] = line
                    else:
                        self.addline(lineno, "else:")
                        self.lines.update(body)
                    code.SetLine(max(body.keys()) + 1)
            else:
                # while
                self.addclause(lineno, "while %s:" % condition, body)
                code.ReadOpcode('JUMP_ABSOLUTE')
                oparg = code.ReadOperand()
                assert oparg == self.loop[0], `(oparg, self.loop)`
                code.ReadOpcode('POP_TOP')
                code.ReadOpcode('POP_BLOCK')
                end = self.loop[1]
                self.loop = None
                if code.GetPosition() < end:
                    lineno = code.GetLine()
                    code.PushStop(end)
                    d = Decompiler(self.version)
                    d.decompile(code)
                    code.PopStop()
                    self.addclause(lineno, "else:", d.getsource(1))
            assert code.GetPosition() == end

    def JUMP_IF_TRUE(self, code):
        code.ReadOpcode('JUMP_IF_TRUE')
        leap = code.ReadOperand()
        end = code.GetPosition() + leap
        code.ReadOpcode('POP_TOP')
        code.PushStop(end)
        d = Decompiler(self.version)
        d.decompile(code, 'RAISE_VARARGS')
        code.PopStop()
        stack = d.getstack()
        assert stack
        if code.GetPosition() == end:
            # or expression
            x = self.stack.pop().GetString(PRECEDENCE_OR+1)
            y = stack.pop().GetString(PRECEDENCE_OR)
            self.stack.append(Expression('%s or %s' % (x, y), PRECEDENCE_OR))
        else:
            # raise AssertionError, exp
            test = self.stack.pop()
            code.ReadOpcode('RAISE_VARARGS')
            oparg = code.ReadOperand()
            if oparg == 1:
                self.stack.append(None)
            else:
                value = stack.pop()
                self.stack.append(value)
            self.stack.append(test)
            assert len(self.stack) == 2, `self.stack`
        assert code.GetPosition() == end

    def LOAD_ATTR(self, code):
        code.ReadOpcode('LOAD_ATTR')
        oparg = code.ReadOperand()
        attr = code.GetName(oparg)
        x = self.stack.pop()
        if x.Precedence() < PRECEDENCE_ATOM:
            x = '(%s)' % x
        self.stack.append(Expression('%s.%s' % (x, attr), PRECEDENCE_ATOM))
        
    def LOAD_CONST(self, code):
        code.ReadOpcode('LOAD_CONST')
        oparg = code.ReadOperand()
        self.stack.append(Constant(code.GetConstant(oparg)))

    def LOAD_FAST(self, code):
        code.ReadOpcode('LOAD_FAST')
        oparg = code.ReadOperand()
        self.stack.append(Local(code.GetLocal(oparg)))
        
    def LOAD_GLOBAL(self, code):
        code.ReadOpcode('LOAD_GLOBAL')
        oparg = code.ReadOperand()
        self.stack.append(Global(code.GetName(oparg)))

    def LOAD_LOCALS(self, code):
        code.ReadOpcode('LOAD_LOCALS')
        self.stack.append(Constant(None))
        
    def LOAD_NAME(self, code):
        code.ReadOpcode('LOAD_NAME')
        oparg = code.ReadOperand()
        self.stack.append(Local(code.GetName(oparg)))

    def MAKE_FUNCTION(self, code):
        code.ReadOpcode('MAKE_FUNCTION')
        defaultcount = code.ReadOperand()
        co = self.stack.pop().Value()
        if co.co_name == '<lambda>':
            # lambda
            # Get the function def part
            params = []
            argcount = co.co_argcount
            while argcount:
                argcount = argcount - 1
                name = co.co_varnames[argcount]
                if defaultcount:
                    defaultcount = defaultcount - 1
                    default = self.stack.pop().GetString(PRECEDENCE_ARG)
                    params.append('%s=%s' % (name, default))
                else:
                    params.append(name)
            params.reverse()
            argcount = co.co_argcount
            if co.co_flags & VARARGS:
                params.append('*' + co.co_varnames[argcount])
                argcount = argcount + 1
            if co.co_flags & KWARGS:
                params.append('**' + co.co_varnames[argcount])
            paramlist = string.join(params, ', ')
            # get the function body
            d = Decompiler(self.version)
            d.decompile(CodeCursor(co), 'RETURN_VALUE')
            stack = d.getstack()
            assert len(stack) == 1, `stack`
            y = stack.pop().GetString(PRECEDENCE_LAMBDA)
            self.stack.append(
                Expression('lambda %s: %s' % (paramlist, y), PRECEDENCE_LAMBDA))
        else:
            opcode = code.ReadOpcode('CALL_FUNCTION', 'STORE_FAST',
                                     'STORE_NAME')
            if opcode == 'CALL_FUNCTION':
                # class
                oparg = code.ReadOperand()
                assert oparg == 0, `oparg`
                code.ReadOpcode('BUILD_CLASS')
                super = self.stack.pop().Value()
                name = self.stack.pop().Value()
                opcode = code.ReadOpcode('STORE_FAST', 'STORE_NAME')
                oparg = code.ReadOperand()
                if opcode == 'STORE_FAST':
                    classname = code.GetLocal(oparg)
                else:
                    classname = code.GetName(oparg)
                assert name == classname, `name, classname`
                if super:
                    classname = '%s(%s)' % (classname, string.join(super, ', '))
                lineno = code.GetLine()
                d = Decompiler(self.version)
                d.decompile(CodeCursor(co))
                body = d.getsource(1)
                if body.has_key(lineno):
                    if len(body) == 1:
                        self.addline(lineno, "class %s: %s" % (classname,
                                      string.strip(body[lineno])))
                    else:
                        assert 0
                        # __doc__ string appears in 0th row
                        assert not body.has_key(lineno+1), `body`
                        body[lineno+1] = body[lineno]
                        del body[lineno]
                        self.addline(lineno, "class %s:" % classname)
                        self.lines.update(body)
                else:
                    self.addline(lineno, "class %s:" % classname)
                    self.lines.update(body)
                code.SetLine(max(body.keys()) + 1)
            else:
                assert opcode in ('STORE_FAST', 'STORE_NAME'), `opcode`
                # def
                oparg = code.ReadOperand()
                if opcode == 'STORE_FAST':
                    funcname = code.GetLocal(oparg)
                else:
                    funcname = code.GetName(oparg)
                # Get the function def part
                params = []
                argcount = co.co_argcount
                while argcount:
                    argcount = argcount - 1
                    name = co.co_varnames[argcount]
                    if defaultcount:
                        defaultcount = defaultcount - 1
                        default = self.stack.pop()
                        if default.Precedence() < PRECEDENCE_ARG:
                            default = '(%s)' % default
                        params.append('%s=%s' % (name, default))
                    else:
                        params.append(name)
                params.reverse()
                argcount = co.co_argcount
                if co.co_flags & VARARGS:
                    params.append('*' + co.co_varnames[argcount])
                    argcount = argcount + 1
                if co.co_flags & KWARGS:
                    params.append('**' + co.co_varnames[argcount])
                paramlist = string.join(params, ', ')
                head = "def %s(%s):" % (funcname, paramlist)
                # get the function body
                lineno = code.GetLine()
                d = Decompiler(self.version)
                d.decompile(CodeCursor(co))
                self.addclause(lineno, head, d.getsource(1))

    def PRINT_ITEM(self, code):
        code.ReadOpcode('PRINT_ITEM')
        x = self.stack.pop().GetString(PRECEDENCE_ARG)
        if code.NextOpcode() == 'PRINT_NEWLINE':
            code.ReadOpcode('PRINT_NEWLINE')
            self.addline(code.GetLine(), 'print %s' % x)
        else:
            self.addline(code.GetLine(), 'print %s,' % x)

    def PRINT_ITEM_TO(self, code):
        # XXX - if file is an expression, it gets evaluated multiple times.
        code.ReadOpcode('PRINT_ITEM_TO')
        file = self.stack.pop()
        x = self.stack.pop().GetString(PRECEDENCE_ARG)
        if code.NextOpcode() == 'PRINT_NEWLINE_TO' and self.stack[-1] is file:
            code.ReadOpcode('PRINT_NEWLINE_TO')
            self.stack.pop()
            if file.Precedence() < PRECEDENCE_ARG:
                file = '(%s)' % file
            self.addline('print >> %s, %s' % (file, x))
        else:
            if file.Precedence() < PRECEDENCE_ARG:
                file = '(%s)' % file
            self.addline('print >> %s, %s,' % (file, x))

    def PRINT_NEWLINE(self, code):
        code.ReadOpcode('PRINT_NEWLINE')
        self.addline(code.GetLine(), 'print')
        
    def PRINT_NEWLINE_TO(self, code):
        code.ReadOpcode('PRINT_NEWLINE')
        file = self.stack.pop().GetString(PRECEDENCE_ARG)
        self.addline(code.GetLine(), 'print >> %s' % file)
        
    def POP_TOP(self, code):
        code.ReadOpcode('POP_TOP')
        self.addline(code.GetLine(),
                     self.stack.pop().GetString(PRECEDENCE_NONE))

    def RAISE_VARARGS(self, code):
        code.ReadOpcode('RAISE_VARARGS')
        argcount = code.ReadOperand()
        args = []
        for i in range(argcount):
            arg = self.stack.pop().GetString(PRECEDENCE_ARG)
            args.append(arg)
        args.reverse()
        self.addline(code.GetLine(), 'raise %s' % string.join(args, ', '))
        
    def RETURN_VALUE(self, code):
        code.ReadOpcode()
        y = self.stack.pop()
        if isinstance(y, Constant) and y.Value() is None:
            if not code.AtEnd():
                self.addline(code.GetLine(), 'return')
        else:
            value = y.GetString(PRECEDENCE_NONE)
            self.addline(code.GetLine(), 'return %s' % value)

    def ROT_THREE(self, code):
        code.ReadOpcode('ROT_THREE')
        assert len(self.stack) >= 3, `code.GetPosition(), self.stack`
        self.stack.pop()  # duplicate of y
        y = self.stack.pop().GetString(PRECEDENCE_CMP+1)
        x = self.stack.pop().GetString(PRECEDENCE_CMP+1)
        code.ReadOpcode('COMPARE_OP')
        oparg = code.ReadOperand()
        op = dis.cmp_op[oparg]
        chain = '%s %s %s' % (x, op, y)
        opcode = code.ReadOpcode('JUMP_IF_FALSE')
        leap = code.ReadOperand()
        stop1 = code.GetPosition() + leap
        while opcode == 'JUMP_IF_FALSE':
            assert code.GetPosition() + leap == stop1, \
                   `code.GetPosition(), leap, stop1`
            code.ReadOpcode('POP_TOP')
            code.PushStop(stop1 - 6)
            d = Decompiler(self.version)
            d.decompile(code, 'ROT_THREE')
            code.PopStop()
            stack = d.getstack()
            y = stack.pop().GetString(PRECEDENCE_CMP+1)
            opcode = code.ReadOpcode('COMPARE_OP', 'ROT_THREE')
            if opcode == 'ROT_THREE':
                opcode = code.ReadOpcode('COMPARE_OP')
            oparg = code.ReadOperand()
            op = dis.cmp_op[oparg]
            chain = '%s %s %s' % (chain, op, y)
            opcode = code.ReadOpcode('JUMP_IF_FALSE', 'JUMP_FORWARD')
            leap = code.ReadOperand()
        assert leap == 2, `leap`
        assert code.GetPosition() == stop1, `code.GetPosition(), stop1`
        code.ReadOpcode('ROT_TWO')
        code.ReadOpcode('POP_TOP')
        self.stack.append(Expression(chain, PRECEDENCE_CMP))

    def ROT_TWO(self, code):
        code.ReadOpcode('ROT_TWO')
        n1 = self.stack.pop()
        n2 = self.stack.pop()
        self.stack.append(n1)
        self.stack.append(n2)

    def handle_except_clause(self, code):
        opcode = code.ReadOpcode('DUP_TOP', 'POP_TOP', 'SET_LINENO')
        if opcode == 'SET_LINENO':
            code.ReadOperand()
            opcode = code.ReadOpcode('DUP_TOP', 'POP_TOP')
        lineno = code.GetLine()
        if opcode == 'DUP_TOP':
            d = Decompiler(self.version)
            d.decompile(code, 'COMPARE_OP')
            stack = d.getstack()
            exc_type = stack.pop().GetString(PRECEDENCE_ARG)
            code.ReadOpcode('COMPARE_OP')
            oparg = code.ReadOperand()
            assert oparg == 10, `oparg`  # 10 -> exception match
            code.ReadOpcode('JUMP_IF_FALSE')
            leap = code.ReadOperand()
            nextclause = code.GetPosition() + leap
            code.ReadOpcode('POP_TOP')  # result of test
            code.ReadOpcode('POP_TOP')  # exc_type
            opcode = code.NextOpcode()
            if opcode == 'POP_TOP':  # exc_value
                code.ReadOpcode('POP_TOP')
                head = 'except %s:' % exc_type
            else:
                exc_value = self.build_target(code).GetString(PRECEDENCE_ARG)
                head = 'except %s, %s:' % (exc_type, exc_value)
        else:
            code.ReadOpcode('POP_TOP')  # exc_value
            head = 'except:'
            nextclause = None
        code.ReadOpcode('POP_TOP')  # exc_tb
        d = Decompiler(self.version)
        d.decompile(code, 'JUMP_FORWARD')
        self.addclause(lineno, head, d.getsource(1))
        code.ReadOpcode('JUMP_FORWARD')
        leap = code.ReadOperand()
        end = code.GetPosition() + leap
        if nextclause is not None:
            assert code.GetPosition() == nextclause
            code.ReadOpcode('POP_TOP')
        return end
        
    def SETUP_EXCEPT(self, code):
        code.ReadOpcode('SETUP_EXCEPT')
        leap = code.ReadOperand()
        firstexceptclause = code.GetPosition() + leap
        lineno = code.GetLine()
        d = Decompiler(self.version)
        d.decompile(code, 'POP_BLOCK')
        self.addclause(lineno, "try:", d.getsource(1))
        code.ReadOpcode('POP_BLOCK')
        code.ReadOpcode('JUMP_FORWARD')
        leap = code.ReadOperand()
        elseclause = code.GetPosition() + leap
        assert code.GetPosition() == firstexceptclause
        end = self.handle_except_clause(code)
        while code.NextOpcode() != 'END_FINALLY':
            end1 = self.handle_except_clause(code)
            assert end1 == end, `end1, end`
        code.ReadOpcode('END_FINALLY')
        assert code.GetPosition() == elseclause, \
               `code.GetPosition(), elseclause`
        if elseclause < end:
            lineno = code.GetLine()
            code.PushStop(end)
            d = Decompiler(self.version)
            d.decompile(code)
            code.PopStop()
            self.addclause(lineno, "else:", d.getsource(1))
        assert code.GetPosition() == end
        
    def SETUP_FINALLY(self, code):
        code.ReadOpcode('SETUP_FINALLY')
        leap = code.ReadOperand()
        finallyclause = code.GetPosition() + leap
        lineno = code.GetLine()
        d = Decompiler(self.version)
        d.decompile(code, 'POP_BLOCK')
        body = d.getsource(1)
        self.addclause(lineno, "try:", body)
        code.ReadOpcode('POP_BLOCK')
        code.ReadOpcode('LOAD_CONST')
        oparg = code.ReadOperand()
        assert oparg == 0, `oparg`
        assert code.GetPosition() == finallyclause
        lineno = code.GetLine()
        d = Decompiler(self.version)
        d.decompile(code, 'END_FINALLY')
        body = d.getsource(1)
        self.addclause(lineno, "finally:", body)
        code.ReadOpcode('END_FINALLY')
       
    def SETUP_LOOP(self, code):
        code.ReadOpcode('SETUP_LOOP')
        leap = code.ReadOperand()
        assert self.loop is None, `self.loop`
        i = code.GetPosition()
        self.loop = i, i + leap

    def SLICE_0(self, code):
        code.ReadOpcode()
        x = self.stack.pop().GetString(PRECEDENCE_ATOM)
        self.stack.append(Expression('%s[:]' % x, PRECEDENCE_ATOM))

    def SLICE_1(self, code):
        code.ReadOpcode()
        y = self.stack.pop().GetString(PRECEDENCE_ARG)
        x = self.stack.pop().GetString(PRECEDENCE_ATOM)
        self.stack.append(Expression('%s[%s:]' % (x, y), PRECEDENCE_ATOM))

    def SLICE_2(self, code):
        code.ReadOpcode()
        z = self.stack.pop().GetString(PRECEDENCE_ARG)
        x = self.stack.pop().GetString(PRECEDENCE_ATOM)
        self.stack.append(Expression('%s[:%s]' % (x, z), PRECEDENCE_ATOM))
        
    def SLICE_3(self, code):
        code.ReadOpcode()
        z = self.stack.pop().GetString(PRECEDENCE_ARG)
        y = self.stack.pop().GetString(PRECEDENCE_ARG)
        x = self.stack.pop().GetString(PRECEDENCE_ATOM)
        self.stack.append(Expression('%s[%s:%s]' % (x, y, z), PRECEDENCE_ATOM))
        
    def STORE_ATTR(self, code):
        code.ReadOpcode()
        oparg = code.ReadOperand()
        attr = code.GetName(oparg)
        name = self.stack.pop().GetString(PRECEDENCE_ATOM)
        value = self.stack.pop().GetString(PRECEDENCE_NONE)
        self.addline(code.GetLine(), '%s.%s = %s' % (name, attr, value))

    def STORE_FAST(self, code):
        code.ReadOpcode()
        oparg = code.ReadOperand()
        name = code.GetLocal(oparg)
        value = self.stack.pop().GetString(PRECEDENCE_NONE)
        self.addline(code.GetLine(), '%s = %s' % (name, value))

    def STORE_GLOBAL(self, code):
        # XXX - need to put in global statement
        code.ReadOpcode()
        oparg = code.ReadOperand()
        name = code.GetName(oparg)
        value = self.stack.pop().GetString(PRECEDENCE_NONE)
        self.addline(code.GetLine(), '%s = %s' % (name, value))

    def STORE_NAME(self, code):
        code.ReadOpcode()
        oparg = code.ReadOperand()
        name = code.GetName(oparg)
        value = self.stack.pop().GetString(PRECEDENCE_NONE)
        self.addline(code.GetLine(), '%s = %s' % (name, value))

    def STORE_SLICE_0(self, code):
        code.ReadOpcode()
        x = self.stack.pop().GetString(PRECEDENCE_ATOM)
        value = self.stack.pop().GetString(PRECEDENCE_NONE)
        self.addline(code.GetLine(), '%s[:] = %s' % (x, value))

    def STORE_SLICE_1(self, code):
        code.ReadOpcode()
        y = self.stack.pop().GetString(PRECEDENCE_ARG)
        x = self.stack.pop().GetString(PRECEDENCE_ATOM)
        value = self.stack.pop().GetString(PRECEDENCE_NONE)
        self.addline(code.GetLine(), '%s[%s:] = %s' % (x, y, value))

    def STORE_SLICE_2(self, code):
        code.ReadOpcode()
        z = self.stack.pop().GetString(PRECEDENCE_ARG)
        x = self.stack.pop().GetString(PRECEDENCE_ATOM)
        value = self.stack.pop().GetString(PRECEDENCE_NONE)
        self.addline(code.GetLine(), '%s[:%s] = %s' % (x, z, value))
        
    def STORE_SLICE_3(self, code):
        code.ReadOpcode()
        z = self.stack.pop().GetString(PRECEDENCE_ARG)
        y = self.stack.pop().GetString(PRECEDENCE_ARG)
        x = self.stack.pop().GetString(PRECEDENCE_ATOM)
        value = self.stack.pop().GetString(PRECEDENCE_NONE)
        self.addline(code.GetLine(), '%s[%s:%s] = %s' % (x, y, z, value))
        
    def STORE_SUBSCR(self, code):
        code.ReadOpcode()
        key = self.stack.pop()
        obj = self.stack.pop()
        if isinstance(obj, Map):
            value = self.stack.pop().GetString(PRECEDENCE_ARG)
            if key.Precedence() < PRECEDENCE_ARG:
                key = '(%s)' % key
            obj.SetAttr(key, value)
        else:
            obj = obj.GetString(PRECEDENCE_ATOM)
            value = self.stack.pop().GetString(PRECEDENCE_NONE)
            self.addline(code.GetLine(),
                         '%s[%s] = %s' % (obj, key, value))

    def UNARY_CONVERT(self, code):
        code.ReadOpcode()
        value = self.stack.pop().GetString(PRECEDENCE_NONE)
        self.stack.append(Expression('`%s`' % value, PRECEDENCE_ATOM))
        
    def UNARY_INVERT(self, code):
        code.ReadOpcode()
        # only requires PRECEDENCE_UNARY, but both powers and other
        # unary operators are confusing without parentheses
        y = self.stack.pop().GetString(PRECEDENCE_ATOM)
        self.stack.append(Expression('~%s' % y, PRECEDENCE_UNARY))

    def UNARY_NEGATIVE(self, code):
        code.ReadOpcode()
        # only requires PRECEDENCE_UNARY, but both powers and other
        # unary operators are confusing without parentheses
        y = self.stack.pop().GetString(PRECEDENCE_ATOM)
        self.stack.append(Expression('-%s' % y, PRECEDENCE_UNARY))

    def UNARY_NOT(self, code):
        code.ReadOpcode()
        y = self.stack.pop().GetString(PRECEDENCE_NOT)
        self.stack.append(Expression('not %s' % y, PRECEDENCE_NOT))

    def UNARY_POSITIVE(self, code):
        code.ReadOpcode()
        # only requires PRECEDENCE_UNARY, but both powers and other
        # unary operators are confusing without parentheses
        y = self.stack.pop().GetString(PRECEDENCE_ATOM)
        self.stack.append(Expression('+%s' % y, PRECEDENCE_UNARY))

    def build_target(self, code):
        if code.NextOpcode() not in ('STORE_FAST', 'STORE_GLOBAL', 'STORE_NAME',
                                     'UNPACK_SEQUENCE', 'UNPACK_TUPLE'):
            d = Decompiler(self.version)
            d.decompile(code, 'STORE_ATTR', 'STORE_SLICE+0', 'STORE_SLICE+1',\
                        'STORE_SLICE+2', 'STORE_SLICE+3', 'STORE_SUBSCR')
        opcode = code.ReadOpcode()
        if opcode == 'STORE_ATTR':
            stack = d.getstack()
            assert len(stack) == 1, `stack`
            name = stack.pop().GetString(PRECEDENCE_ATOM)
            oparg = code.ReadOperand()
            attr = code.GetName(oparg)
            target = Expression('%s.%s' % (name, attr), PRECEDENCE_ATOM)
        elif opcode == 'STORE_FAST':
            oparg = code.ReadOperand()
            target = Local(code.GetLocal(oparg))
        elif opcode == 'STORE_GLOBAL':
            oparg = code.ReadOperand()
            target = Global(code.GetName(oparg))
        elif opcode == 'STORE_NAME':
            oparg = code.ReadOperand()
            target = Local(code.GetName(oparg))
        elif opcode == 'STORE_SLICE+0':
            stack = d.getstack()
            assert len(stack) == 1, `stack`
            x = stack.pop().GetString(PRECEDENCE_ATOM)
            target = Expression('%s[:]' % x, PRECEDENCE_ATOM)
        elif opcode == 'STORE_SLICE+1':
            stack = d.getstack()
            assert len(stack) == 2, `stack`
            y = stack.pop().GetString(PRECEDENCE_ARG)
            x = stack.pop().GetString(PRECEDENCE_ATOM)
            target = Expression('%s[%s:]' % (x, y), PRECEDENCE_ATOM)
        elif opcode == 'STORE_SLICE+2':
            stack = d.getstack()
            assert len(stack) == 2, `stack`
            z = stack.pop().GetString(PRECEDENCE_ARG)
            x = stack.pop().GetString(PRECEDENCE_ATOM)
            target = Expression('%s[:%s]' % (x, z), PRECEDENCE_ATOM)
        elif opcode == 'STORE_SLICE+3':
            stack = d.getstack()
            assert len(stack) == 3, `stack`
            z = stack.pop().GetString(PRECEDENCE_ARG)
            y = stack.pop().GetString(PRECEDENCE_ARG)
            x = stack.pop().GetString(PRECEDENCE_ATOM)
            target = Expression('%s[%s:%s]' % (x, y, z), PRECEDENCE_ATOM)
        elif opcode == 'STORE_SUBSCR':
            stack = d.getstack()
            assert len(stack) == 2, `stack`
            key = stack.pop().GetString(PRECEDENCE_NONE)
            name = stack.pop().GetString(PRECEDENCE_ATOM)
            target = Expression('%s[%s]' % (name, key), PRECEDENCE_ATOM)
        else:
            assert opcode in ('UNPACK_SEQUENCE', 'UNPACK_TUPLE'), `opcode`
            count = code.ReadOperand()
            values = []
            while count > 0:
                value = self.build_target(code).GetString(PRECEDENCE_ARG)
                values.append(value)
                count = count - 1
            target = Tuple(values)
        return target

    def UNPACK_SEQUENCE(self, code):
        seq = self.build_target(code).GetString(PRECEDENCE_NONE)
        rhs = self.stack.pop().GetString(PRECEDENCE_NONE)
        self.addline(code.GetLine(), '%s = %s' % (seq, rhs))
    
    UNPACK_TUPLE = UNPACK_SEQUENCE

# These tests need to be more complete, however, the things that are
# known to be broken are represented
tests = [
    '3\n'           # constant expression
    'a\n'           # name expression
    'a[3][4]\n'     # subscr expression
    'a.b.c\n'       # attr expression
    'a(b, c, d=3, e="d")\n', # function call
    'a = b\n',      # simple assignment
    'a.b.c.d = 2\n',  # attr target
    'a[b][c][d] = 3\n', # subscr target
    'a[:] = 4\n',  # slice targets
    'a[b:] = 5\n',
    'a[:b] = 6\n',
    'a[b:c] = 7\n',
    'a, b, c.d, e[f], g[:], h[i:], j[:k], l[m:n] = z\n', # tuple target
    '(a, b), (c, (d, e)) = t\n', # nested tuple target
    'import spam',
    'import string, spam as ham, sys',
    'from spam import ham, eggs',
    'import spam.ham',    # BROKEN: this type of import
    'from package.spam import ham, eggs as milk',
    'from string import *',
    'def f(a, (b, c)):\n    del f\n', # BROKEN: tuple func args
    'a = b.d = c[3] = a, b = f()',    # BROKEN: chained assignment
    'print >> file, a, b',
    'a = [2*i for i in x]',           # BROKEN: list comprehension
    'if 1:\n  pass\nelif 2: pass\nelse: 3', # BROKEN, swap conditions JUMP_IF_FALSE
    
##    for i in r:
##        print
##    print hello, b
##    a &= 3; b^=9
##    c|=8 ; d <<=2
##    def g(a, b, c=5, d=10, *args, **kw):
##        pass
##    class G(foo):
##        def __init__(self, b=5):
##            "hello"
##            x.k = x.k & (7 * (3 + 4))
##            return x
##    x, y[5][g+4], z.x = 1, 2, 3
##    f(a, b, x=1, **args)
##    try: 1
##    finally: 2
##    if a < b*2 <= c > d:
##        print
##    x = lambda y: (2 * y, 3)
##    class C: pass
##    if 3:
##        print
##    elif 5:
##        del e
##    else:
##        print
##    import sys
##    if 3:
##        print
##    else:
##        i = j + -2 ** -3 ** -2
##        if stuff[0] == '!':
##            stuff = '[^' + stuff[1:] + ']'
##        elif stuff == 'g':
##            stuff = '\\^'
##        else:
##            while stuff[0] == '^':
##                stuff = stuff[1:] + stuff[0]
##            stuff = ('[' + stuff + ']'),
##            
##    a, b, c.b, a[p] = c
##    x = 1 - 2 + 3 - (4 + 5) - 6, 6
##    try:
##        print 2
##    except RuntimeError, exc:
##        del a
##    except IOError:
##        del g
##    except:
##        del b
##    else:
##        del elses
##    if a and b and c:
##        del a
##    if (a and b) and c:
##        del b
##    if a and (b and c):
##        del c
    ]

def test():
    import traceback
    for osrc in tests:
        code1 = compile(osrc, '<string>', 'exec')
        d = Decompiler((2, 0))
        try:
            d.decompile(CodeCursor(code1))
        except:
            print osrc
            print 'FAILS'
            traceback.print_exc(file=sys.stdout)
            print '---'
            continue
        source = d.getsource(0)
        lastline = max(source.keys())
        lines = []
        for lineno in range(1, lastline+2):
            lines.append(source.get(lineno, ''))
        dsrc = string.join(lines, '\n')
        if dsrc == osrc:
            continue
        try:
            code2 = compile(dsrc, '<string>', 'exec')
        except SyntaxError:
            code2 = None
        if code2 and code2.co_code == code1.co_code:
            continue
        print osrc
        print 'BECOMES'
        print dsrc
        print '---'

def f():
    a(b=3, d=3)
    a[x:], b[:] = 1
    # don't work:
##    import spam.ham
##    def f(a, (b, c)):
##        del f
##    a = b.d = c[3] = a, b = f()
##    a = [2*i for i in x]
    
##    import string, x as y
##    from spam.ham import eggs, foo as bar
##    from d import *
##    for i in r:
##        print
    print hello, b
##    a &= 3; b^=9
##    c|=8 ; d <<=2
##    def g(a, b, c=5, d=10, *args, **kw):
##        pass
##    class G(foo):
##        def __init__(self, b=5):
##            "hello"
##            x.k = x.k & (7 * (3 + 4))
##            return x
##    x, y[5][g+4], z.x = 1, 2, 3
##    f(a, b, x=1, **args)
##    try: 1
##    finally: 2
##    if a < b*2 <= c > d:
##        print
##    x = lambda y: (2 * y, 3)
##    class C: pass
##    if 3:
##        print
##    elif 5:
##        del e
##    else:
##        print
##    import sys
##    if 3:
##        print
##    else:
##        i = j + -2 ** -3 ** -2
##        if stuff[0] == '!':
##            stuff = '[^' + stuff[1:] + ']'
##        elif stuff == 'g':
##            stuff = '\\^'
##        else:
##            while stuff[0] == '^':
##                stuff = stuff[1:] + stuff[0]
##            stuff = ('[' + stuff + ']'),
##            
##    a, b, c.b, a[p] = c
##    x = 1 - 2 + 3 - (4 + 5) - 6, 6
##    try:
##        print 2
##    except RuntimeError, exc:
##        del a
##    except IOError:
##        del g
##    except:
##        del b
##    else:
##        del elses
##    if a and b and c:
##        del a
##    if (a and b) and c:
##        del b
##    if a and (b and c):
##        del c
        
if __name__ == '__main__':
  if 1:
    test()
  else:
    out = open(r'E:\output.txt', 'wt')
    try:
        stdout = sys.stdout
        sys.stdout = out
        if 1:
            import marshal
            m = open(r'C:\Python20\Lib\code.pyc', 'rb')
            magic = m.read(4)
            if magic == '\207\306\015\012':
                version = (2, 0)
            elif magic == '\231N\015\012':
                version = (1, 5, 2)
            else:
                raise RuntimeError, 'unrecognised magic: %s' % `magic`
            timestamp = m.read(4)
            s = m.read()
            code = marshal.loads(s)
            m.close()
        else:
            version = (2, 0)
            print f.func_code.co_names
            print f.func_code.co_varnames
            print f.func_code.co_consts
            code = f.func_code
        c = CodeCursor(code)
        d = Decompiler(version)
        d.decompile(c)
        lines = d.getsource(0)
        keys = lines.keys()
        keys.sort()
        for lineno in range(keys[0], keys[-1] + 1):
            print '%4d %s' % (lineno, lines.get(lineno, ''))
        print
        sys.stdout = stdout
    finally:
        out.close()
    sys.stderr.write('DONE\n')
    
