mirror of
				https://github.com/python/cpython.git
				synced 2025-11-03 23:21:29 +00:00 
			
		
		
		
	a with statement; previously, executing code containing a with statement compiled by the compiler package crashed the interpreter.
		
			
				
	
	
		
			1531 lines
		
	
	
	
		
			46 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			1531 lines
		
	
	
	
		
			46 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
import imp
 | 
						|
import os
 | 
						|
import marshal
 | 
						|
import struct
 | 
						|
import sys
 | 
						|
from cStringIO import StringIO
 | 
						|
 | 
						|
from compiler import ast, parse, walk, syntax
 | 
						|
from compiler import pyassem, misc, future, symbols
 | 
						|
from compiler.consts import SC_LOCAL, SC_GLOBAL, SC_FREE, SC_CELL
 | 
						|
from compiler.consts import (CO_VARARGS, CO_VARKEYWORDS, CO_NEWLOCALS,
 | 
						|
     CO_NESTED, CO_GENERATOR, CO_FUTURE_DIVISION,
 | 
						|
     CO_FUTURE_ABSIMPORT, CO_FUTURE_WITH_STATEMENT)
 | 
						|
from compiler.pyassem import TupleArg
 | 
						|
 | 
						|
# XXX The version-specific code can go, since this code only works with 2.x.
 | 
						|
# Do we have Python 1.x or Python 2.x?
 | 
						|
try:
 | 
						|
    VERSION = sys.version_info[0]
 | 
						|
except AttributeError:
 | 
						|
    VERSION = 1
 | 
						|
 | 
						|
callfunc_opcode_info = {
 | 
						|
    # (Have *args, Have **args) : opcode
 | 
						|
    (0,0) : "CALL_FUNCTION",
 | 
						|
    (1,0) : "CALL_FUNCTION_VAR",
 | 
						|
    (0,1) : "CALL_FUNCTION_KW",
 | 
						|
    (1,1) : "CALL_FUNCTION_VAR_KW",
 | 
						|
}
 | 
						|
 | 
						|
LOOP = 1
 | 
						|
EXCEPT = 2
 | 
						|
TRY_FINALLY = 3
 | 
						|
END_FINALLY = 4
 | 
						|
 | 
						|
def compileFile(filename, display=0):
 | 
						|
    f = open(filename, 'U')
 | 
						|
    buf = f.read()
 | 
						|
    f.close()
 | 
						|
    mod = Module(buf, filename)
 | 
						|
    try:
 | 
						|
        mod.compile(display)
 | 
						|
    except SyntaxError:
 | 
						|
        raise
 | 
						|
    else:
 | 
						|
        f = open(filename + "c", "wb")
 | 
						|
        mod.dump(f)
 | 
						|
        f.close()
 | 
						|
 | 
						|
def compile(source, filename, mode, flags=None, dont_inherit=None):
 | 
						|
    """Replacement for builtin compile() function"""
 | 
						|
    if flags is not None or dont_inherit is not None:
 | 
						|
        raise RuntimeError, "not implemented yet"
 | 
						|
 | 
						|
    if mode == "single":
 | 
						|
        gen = Interactive(source, filename)
 | 
						|
    elif mode == "exec":
 | 
						|
        gen = Module(source, filename)
 | 
						|
    elif mode == "eval":
 | 
						|
        gen = Expression(source, filename)
 | 
						|
    else:
 | 
						|
        raise ValueError("compile() 3rd arg must be 'exec' or "
 | 
						|
                         "'eval' or 'single'")
 | 
						|
    gen.compile()
 | 
						|
    return gen.code
 | 
						|
 | 
						|
class AbstractCompileMode:
 | 
						|
 | 
						|
    mode = None # defined by subclass
 | 
						|
 | 
						|
    def __init__(self, source, filename):
 | 
						|
        self.source = source
 | 
						|
        self.filename = filename
 | 
						|
        self.code = None
 | 
						|
 | 
						|
    def _get_tree(self):
 | 
						|
        tree = parse(self.source, self.mode)
 | 
						|
        misc.set_filename(self.filename, tree)
 | 
						|
        syntax.check(tree)
 | 
						|
        return tree
 | 
						|
 | 
						|
    def compile(self):
 | 
						|
        pass # implemented by subclass
 | 
						|
 | 
						|
    def getCode(self):
 | 
						|
        return self.code
 | 
						|
 | 
						|
class Expression(AbstractCompileMode):
 | 
						|
 | 
						|
    mode = "eval"
 | 
						|
 | 
						|
    def compile(self):
 | 
						|
        tree = self._get_tree()
 | 
						|
        gen = ExpressionCodeGenerator(tree)
 | 
						|
        self.code = gen.getCode()
 | 
						|
 | 
						|
class Interactive(AbstractCompileMode):
 | 
						|
 | 
						|
    mode = "single"
 | 
						|
 | 
						|
    def compile(self):
 | 
						|
        tree = self._get_tree()
 | 
						|
        gen = InteractiveCodeGenerator(tree)
 | 
						|
        self.code = gen.getCode()
 | 
						|
 | 
						|
class Module(AbstractCompileMode):
 | 
						|
 | 
						|
    mode = "exec"
 | 
						|
 | 
						|
    def compile(self, display=0):
 | 
						|
        tree = self._get_tree()
 | 
						|
        gen = ModuleCodeGenerator(tree)
 | 
						|
        if display:
 | 
						|
            import pprint
 | 
						|
            print pprint.pprint(tree)
 | 
						|
        self.code = gen.getCode()
 | 
						|
 | 
						|
    def dump(self, f):
 | 
						|
        f.write(self.getPycHeader())
 | 
						|
        marshal.dump(self.code, f)
 | 
						|
 | 
						|
    MAGIC = imp.get_magic()
 | 
						|
 | 
						|
    def getPycHeader(self):
 | 
						|
        # compile.c uses marshal to write a long directly, with
 | 
						|
        # calling the interface that would also generate a 1-byte code
 | 
						|
        # to indicate the type of the value.  simplest way to get the
 | 
						|
        # same effect is to call marshal and then skip the code.
 | 
						|
        mtime = os.path.getmtime(self.filename)
 | 
						|
        mtime = struct.pack('<i', mtime)
 | 
						|
        return self.MAGIC + mtime
 | 
						|
 | 
						|
class LocalNameFinder:
 | 
						|
    """Find local names in scope"""
 | 
						|
    def __init__(self, names=()):
 | 
						|
        self.names = misc.Set()
 | 
						|
        self.globals = misc.Set()
 | 
						|
        for name in names:
 | 
						|
            self.names.add(name)
 | 
						|
 | 
						|
    # XXX list comprehensions and for loops
 | 
						|
 | 
						|
    def getLocals(self):
 | 
						|
        for elt in self.globals.elements():
 | 
						|
            if self.names.has_elt(elt):
 | 
						|
                self.names.remove(elt)
 | 
						|
        return self.names
 | 
						|
 | 
						|
    def visitDict(self, node):
 | 
						|
        pass
 | 
						|
 | 
						|
    def visitGlobal(self, node):
 | 
						|
        for name in node.names:
 | 
						|
            self.globals.add(name)
 | 
						|
 | 
						|
    def visitFunction(self, node):
 | 
						|
        self.names.add(node.name)
 | 
						|
 | 
						|
    def visitLambda(self, node):
 | 
						|
        pass
 | 
						|
 | 
						|
    def visitImport(self, node):
 | 
						|
        for name, alias in node.names:
 | 
						|
            self.names.add(alias or name)
 | 
						|
 | 
						|
    def visitFrom(self, node):
 | 
						|
        for name, alias in node.names:
 | 
						|
            self.names.add(alias or name)
 | 
						|
 | 
						|
    def visitClass(self, node):
 | 
						|
        self.names.add(node.name)
 | 
						|
 | 
						|
    def visitAssName(self, node):
 | 
						|
        self.names.add(node.name)
 | 
						|
 | 
						|
def is_constant_false(node):
 | 
						|
    if isinstance(node, ast.Const):
 | 
						|
        if not node.value:
 | 
						|
            return 1
 | 
						|
    return 0
 | 
						|
 | 
						|
class CodeGenerator:
 | 
						|
    """Defines basic code generator for Python bytecode
 | 
						|
 | 
						|
    This class is an abstract base class.  Concrete subclasses must
 | 
						|
    define an __init__() that defines self.graph and then calls the
 | 
						|
    __init__() defined in this class.
 | 
						|
 | 
						|
    The concrete class must also define the class attributes
 | 
						|
    NameFinder, FunctionGen, and ClassGen.  These attributes can be
 | 
						|
    defined in the initClass() method, which is a hook for
 | 
						|
    initializing these methods after all the classes have been
 | 
						|
    defined.
 | 
						|
    """
 | 
						|
 | 
						|
    optimized = 0 # is namespace access optimized?
 | 
						|
    __initialized = None
 | 
						|
    class_name = None # provide default for instance variable
 | 
						|
 | 
						|
    def __init__(self):
 | 
						|
        if self.__initialized is None:
 | 
						|
            self.initClass()
 | 
						|
            self.__class__.__initialized = 1
 | 
						|
        self.checkClass()
 | 
						|
        self.locals = misc.Stack()
 | 
						|
        self.setups = misc.Stack()
 | 
						|
        self.last_lineno = None
 | 
						|
        self._setupGraphDelegation()
 | 
						|
        self._div_op = "BINARY_DIVIDE"
 | 
						|
 | 
						|
        # XXX set flags based on future features
 | 
						|
        futures = self.get_module().futures
 | 
						|
        for feature in futures:
 | 
						|
            if feature == "division":
 | 
						|
                self.graph.setFlag(CO_FUTURE_DIVISION)
 | 
						|
                self._div_op = "BINARY_TRUE_DIVIDE"
 | 
						|
            elif feature == "absolute_import":
 | 
						|
                self.graph.setFlag(CO_FUTURE_ABSIMPORT)
 | 
						|
            elif feature == "with_statement":
 | 
						|
                self.graph.setFlag(CO_FUTURE_WITH_STATEMENT)
 | 
						|
 | 
						|
    def initClass(self):
 | 
						|
        """This method is called once for each class"""
 | 
						|
 | 
						|
    def checkClass(self):
 | 
						|
        """Verify that class is constructed correctly"""
 | 
						|
        try:
 | 
						|
            assert hasattr(self, 'graph')
 | 
						|
            assert getattr(self, 'NameFinder')
 | 
						|
            assert getattr(self, 'FunctionGen')
 | 
						|
            assert getattr(self, 'ClassGen')
 | 
						|
        except AssertionError, msg:
 | 
						|
            intro = "Bad class construction for %s" % self.__class__.__name__
 | 
						|
            raise AssertionError, intro
 | 
						|
 | 
						|
    def _setupGraphDelegation(self):
 | 
						|
        self.emit = self.graph.emit
 | 
						|
        self.newBlock = self.graph.newBlock
 | 
						|
        self.startBlock = self.graph.startBlock
 | 
						|
        self.nextBlock = self.graph.nextBlock
 | 
						|
        self.setDocstring = self.graph.setDocstring
 | 
						|
 | 
						|
    def getCode(self):
 | 
						|
        """Return a code object"""
 | 
						|
        return self.graph.getCode()
 | 
						|
 | 
						|
    def mangle(self, name):
 | 
						|
        if self.class_name is not None:
 | 
						|
            return misc.mangle(name, self.class_name)
 | 
						|
        else:
 | 
						|
            return name
 | 
						|
 | 
						|
    def parseSymbols(self, tree):
 | 
						|
        s = symbols.SymbolVisitor()
 | 
						|
        walk(tree, s)
 | 
						|
        return s.scopes
 | 
						|
 | 
						|
    def get_module(self):
 | 
						|
        raise RuntimeError, "should be implemented by subclasses"
 | 
						|
 | 
						|
    # Next five methods handle name access
 | 
						|
 | 
						|
    def isLocalName(self, name):
 | 
						|
        return self.locals.top().has_elt(name)
 | 
						|
 | 
						|
    def storeName(self, name):
 | 
						|
        self._nameOp('STORE', name)
 | 
						|
 | 
						|
    def loadName(self, name):
 | 
						|
        self._nameOp('LOAD', name)
 | 
						|
 | 
						|
    def delName(self, name):
 | 
						|
        self._nameOp('DELETE', name)
 | 
						|
 | 
						|
    def _nameOp(self, prefix, name):
 | 
						|
        name = self.mangle(name)
 | 
						|
        scope = self.scope.check_name(name)
 | 
						|
        if scope == SC_LOCAL:
 | 
						|
            if not self.optimized:
 | 
						|
                self.emit(prefix + '_NAME', name)
 | 
						|
            else:
 | 
						|
                self.emit(prefix + '_FAST', name)
 | 
						|
        elif scope == SC_GLOBAL:
 | 
						|
            if not self.optimized:
 | 
						|
                self.emit(prefix + '_NAME', name)
 | 
						|
            else:
 | 
						|
                self.emit(prefix + '_GLOBAL', name)
 | 
						|
        elif scope == SC_FREE or scope == SC_CELL:
 | 
						|
            self.emit(prefix + '_DEREF', name)
 | 
						|
        else:
 | 
						|
            raise RuntimeError, "unsupported scope for var %s: %d" % \
 | 
						|
                  (name, scope)
 | 
						|
 | 
						|
    def _implicitNameOp(self, prefix, name):
 | 
						|
        """Emit name ops for names generated implicitly by for loops
 | 
						|
 | 
						|
        The interpreter generates names that start with a period or
 | 
						|
        dollar sign.  The symbol table ignores these names because
 | 
						|
        they aren't present in the program text.
 | 
						|
        """
 | 
						|
        if self.optimized:
 | 
						|
            self.emit(prefix + '_FAST', name)
 | 
						|
        else:
 | 
						|
            self.emit(prefix + '_NAME', name)
 | 
						|
 | 
						|
    # The set_lineno() function and the explicit emit() calls for
 | 
						|
    # SET_LINENO below are only used to generate the line number table.
 | 
						|
    # As of Python 2.3, the interpreter does not have a SET_LINENO
 | 
						|
    # instruction.  pyassem treats SET_LINENO opcodes as a special case.
 | 
						|
 | 
						|
    def set_lineno(self, node, force=False):
 | 
						|
        """Emit SET_LINENO if necessary.
 | 
						|
 | 
						|
        The instruction is considered necessary if the node has a
 | 
						|
        lineno attribute and it is different than the last lineno
 | 
						|
        emitted.
 | 
						|
 | 
						|
        Returns true if SET_LINENO was emitted.
 | 
						|
 | 
						|
        There are no rules for when an AST node should have a lineno
 | 
						|
        attribute.  The transformer and AST code need to be reviewed
 | 
						|
        and a consistent policy implemented and documented.  Until
 | 
						|
        then, this method works around missing line numbers.
 | 
						|
        """
 | 
						|
        lineno = getattr(node, 'lineno', None)
 | 
						|
        if lineno is not None and (lineno != self.last_lineno
 | 
						|
                                   or force):
 | 
						|
            self.emit('SET_LINENO', lineno)
 | 
						|
            self.last_lineno = lineno
 | 
						|
            return True
 | 
						|
        return False
 | 
						|
 | 
						|
    # The first few visitor methods handle nodes that generator new
 | 
						|
    # code objects.  They use class attributes to determine what
 | 
						|
    # specialized code generators to use.
 | 
						|
 | 
						|
    NameFinder = LocalNameFinder
 | 
						|
    FunctionGen = None
 | 
						|
    ClassGen = None
 | 
						|
 | 
						|
    def visitModule(self, node):
 | 
						|
        self.scopes = self.parseSymbols(node)
 | 
						|
        self.scope = self.scopes[node]
 | 
						|
        self.emit('SET_LINENO', 0)
 | 
						|
        if node.doc:
 | 
						|
            self.emit('LOAD_CONST', node.doc)
 | 
						|
            self.storeName('__doc__')
 | 
						|
        lnf = walk(node.node, self.NameFinder(), verbose=0)
 | 
						|
        self.locals.push(lnf.getLocals())
 | 
						|
        self.visit(node.node)
 | 
						|
        self.emit('LOAD_CONST', None)
 | 
						|
        self.emit('RETURN_VALUE')
 | 
						|
 | 
						|
    def visitExpression(self, node):
 | 
						|
        self.set_lineno(node)
 | 
						|
        self.scopes = self.parseSymbols(node)
 | 
						|
        self.scope = self.scopes[node]
 | 
						|
        self.visit(node.node)
 | 
						|
        self.emit('RETURN_VALUE')
 | 
						|
 | 
						|
    def visitFunction(self, node):
 | 
						|
        self._visitFuncOrLambda(node, isLambda=0)
 | 
						|
        if node.doc:
 | 
						|
            self.setDocstring(node.doc)
 | 
						|
        self.storeName(node.name)
 | 
						|
 | 
						|
    def visitLambda(self, node):
 | 
						|
        self._visitFuncOrLambda(node, isLambda=1)
 | 
						|
 | 
						|
    def _visitFuncOrLambda(self, node, isLambda=0):
 | 
						|
        if not isLambda and node.decorators:
 | 
						|
            for decorator in node.decorators.nodes:
 | 
						|
                self.visit(decorator)
 | 
						|
            ndecorators = len(node.decorators.nodes)
 | 
						|
        else:
 | 
						|
            ndecorators = 0
 | 
						|
 | 
						|
        gen = self.FunctionGen(node, self.scopes, isLambda,
 | 
						|
                               self.class_name, self.get_module())
 | 
						|
        walk(node.code, gen)
 | 
						|
        gen.finish()
 | 
						|
        self.set_lineno(node)
 | 
						|
        for default in node.defaults:
 | 
						|
            self.visit(default)
 | 
						|
        self._makeClosure(gen, len(node.defaults))
 | 
						|
        for i in range(ndecorators):
 | 
						|
            self.emit('CALL_FUNCTION', 1)
 | 
						|
 | 
						|
    def visitClass(self, node):
 | 
						|
        gen = self.ClassGen(node, self.scopes,
 | 
						|
                            self.get_module())
 | 
						|
        walk(node.code, gen)
 | 
						|
        gen.finish()
 | 
						|
        self.set_lineno(node)
 | 
						|
        self.emit('LOAD_CONST', node.name)
 | 
						|
        for base in node.bases:
 | 
						|
            self.visit(base)
 | 
						|
        self.emit('BUILD_TUPLE', len(node.bases))
 | 
						|
        self._makeClosure(gen, 0)
 | 
						|
        self.emit('CALL_FUNCTION', 0)
 | 
						|
        self.emit('BUILD_CLASS')
 | 
						|
        self.storeName(node.name)
 | 
						|
 | 
						|
    # The rest are standard visitor methods
 | 
						|
 | 
						|
    # The next few implement control-flow statements
 | 
						|
 | 
						|
    def visitIf(self, node):
 | 
						|
        end = self.newBlock()
 | 
						|
        numtests = len(node.tests)
 | 
						|
        for i in range(numtests):
 | 
						|
            test, suite = node.tests[i]
 | 
						|
            if is_constant_false(test):
 | 
						|
                # XXX will need to check generator stuff here
 | 
						|
                continue
 | 
						|
            self.set_lineno(test)
 | 
						|
            self.visit(test)
 | 
						|
            nextTest = self.newBlock()
 | 
						|
            self.emit('JUMP_IF_FALSE', nextTest)
 | 
						|
            self.nextBlock()
 | 
						|
            self.emit('POP_TOP')
 | 
						|
            self.visit(suite)
 | 
						|
            self.emit('JUMP_FORWARD', end)
 | 
						|
            self.startBlock(nextTest)
 | 
						|
            self.emit('POP_TOP')
 | 
						|
        if node.else_:
 | 
						|
            self.visit(node.else_)
 | 
						|
        self.nextBlock(end)
 | 
						|
 | 
						|
    def visitWhile(self, node):
 | 
						|
        self.set_lineno(node)
 | 
						|
 | 
						|
        loop = self.newBlock()
 | 
						|
        else_ = self.newBlock()
 | 
						|
 | 
						|
        after = self.newBlock()
 | 
						|
        self.emit('SETUP_LOOP', after)
 | 
						|
 | 
						|
        self.nextBlock(loop)
 | 
						|
        self.setups.push((LOOP, loop))
 | 
						|
 | 
						|
        self.set_lineno(node, force=True)
 | 
						|
        self.visit(node.test)
 | 
						|
        self.emit('JUMP_IF_FALSE', else_ or after)
 | 
						|
 | 
						|
        self.nextBlock()
 | 
						|
        self.emit('POP_TOP')
 | 
						|
        self.visit(node.body)
 | 
						|
        self.emit('JUMP_ABSOLUTE', loop)
 | 
						|
 | 
						|
        self.startBlock(else_) # or just the POPs if not else clause
 | 
						|
        self.emit('POP_TOP')
 | 
						|
        self.emit('POP_BLOCK')
 | 
						|
        self.setups.pop()
 | 
						|
        if node.else_:
 | 
						|
            self.visit(node.else_)
 | 
						|
        self.nextBlock(after)
 | 
						|
 | 
						|
    def visitFor(self, node):
 | 
						|
        start = self.newBlock()
 | 
						|
        anchor = self.newBlock()
 | 
						|
        after = self.newBlock()
 | 
						|
        self.setups.push((LOOP, start))
 | 
						|
 | 
						|
        self.set_lineno(node)
 | 
						|
        self.emit('SETUP_LOOP', after)
 | 
						|
        self.visit(node.list)
 | 
						|
        self.emit('GET_ITER')
 | 
						|
 | 
						|
        self.nextBlock(start)
 | 
						|
        self.set_lineno(node, force=1)
 | 
						|
        self.emit('FOR_ITER', anchor)
 | 
						|
        self.visit(node.assign)
 | 
						|
        self.visit(node.body)
 | 
						|
        self.emit('JUMP_ABSOLUTE', start)
 | 
						|
        self.nextBlock(anchor)
 | 
						|
        self.emit('POP_BLOCK')
 | 
						|
        self.setups.pop()
 | 
						|
        if node.else_:
 | 
						|
            self.visit(node.else_)
 | 
						|
        self.nextBlock(after)
 | 
						|
 | 
						|
    def visitBreak(self, node):
 | 
						|
        if not self.setups:
 | 
						|
            raise SyntaxError, "'break' outside loop (%s, %d)" % \
 | 
						|
                  (node.filename, node.lineno)
 | 
						|
        self.set_lineno(node)
 | 
						|
        self.emit('BREAK_LOOP')
 | 
						|
 | 
						|
    def visitContinue(self, node):
 | 
						|
        if not self.setups:
 | 
						|
            raise SyntaxError, "'continue' outside loop (%s, %d)" % \
 | 
						|
                  (node.filename, node.lineno)
 | 
						|
        kind, block = self.setups.top()
 | 
						|
        if kind == LOOP:
 | 
						|
            self.set_lineno(node)
 | 
						|
            self.emit('JUMP_ABSOLUTE', block)
 | 
						|
            self.nextBlock()
 | 
						|
        elif kind == EXCEPT or kind == TRY_FINALLY:
 | 
						|
            self.set_lineno(node)
 | 
						|
            # find the block that starts the loop
 | 
						|
            top = len(self.setups)
 | 
						|
            while top > 0:
 | 
						|
                top = top - 1
 | 
						|
                kind, loop_block = self.setups[top]
 | 
						|
                if kind == LOOP:
 | 
						|
                    break
 | 
						|
            if kind != LOOP:
 | 
						|
                raise SyntaxError, "'continue' outside loop (%s, %d)" % \
 | 
						|
                      (node.filename, node.lineno)
 | 
						|
            self.emit('CONTINUE_LOOP', loop_block)
 | 
						|
            self.nextBlock()
 | 
						|
        elif kind == END_FINALLY:
 | 
						|
            msg = "'continue' not allowed inside 'finally' clause (%s, %d)"
 | 
						|
            raise SyntaxError, msg % (node.filename, node.lineno)
 | 
						|
 | 
						|
    def visitTest(self, node, jump):
 | 
						|
        end = self.newBlock()
 | 
						|
        for child in node.nodes[:-1]:
 | 
						|
            self.visit(child)
 | 
						|
            self.emit(jump, end)
 | 
						|
            self.nextBlock()
 | 
						|
            self.emit('POP_TOP')
 | 
						|
        self.visit(node.nodes[-1])
 | 
						|
        self.nextBlock(end)
 | 
						|
 | 
						|
    def visitAnd(self, node):
 | 
						|
        self.visitTest(node, 'JUMP_IF_FALSE')
 | 
						|
 | 
						|
    def visitOr(self, node):
 | 
						|
        self.visitTest(node, 'JUMP_IF_TRUE')
 | 
						|
 | 
						|
    def visitIfExp(self, node):
 | 
						|
        endblock = self.newBlock()
 | 
						|
        elseblock = self.newBlock()
 | 
						|
        self.visit(node.test)
 | 
						|
        self.emit('JUMP_IF_FALSE', elseblock)
 | 
						|
        self.emit('POP_TOP')
 | 
						|
        self.visit(node.then)
 | 
						|
        self.emit('JUMP_FORWARD', endblock)
 | 
						|
        self.nextBlock(elseblock)
 | 
						|
        self.emit('POP_TOP')
 | 
						|
        self.visit(node.else_)
 | 
						|
        self.nextBlock(endblock)
 | 
						|
 | 
						|
    def visitCompare(self, node):
 | 
						|
        self.visit(node.expr)
 | 
						|
        cleanup = self.newBlock()
 | 
						|
        for op, code in node.ops[:-1]:
 | 
						|
            self.visit(code)
 | 
						|
            self.emit('DUP_TOP')
 | 
						|
            self.emit('ROT_THREE')
 | 
						|
            self.emit('COMPARE_OP', op)
 | 
						|
            self.emit('JUMP_IF_FALSE', cleanup)
 | 
						|
            self.nextBlock()
 | 
						|
            self.emit('POP_TOP')
 | 
						|
        # now do the last comparison
 | 
						|
        if node.ops:
 | 
						|
            op, code = node.ops[-1]
 | 
						|
            self.visit(code)
 | 
						|
            self.emit('COMPARE_OP', op)
 | 
						|
        if len(node.ops) > 1:
 | 
						|
            end = self.newBlock()
 | 
						|
            self.emit('JUMP_FORWARD', end)
 | 
						|
            self.startBlock(cleanup)
 | 
						|
            self.emit('ROT_TWO')
 | 
						|
            self.emit('POP_TOP')
 | 
						|
            self.nextBlock(end)
 | 
						|
 | 
						|
    # list comprehensions
 | 
						|
    __list_count = 0
 | 
						|
 | 
						|
    def visitListComp(self, node):
 | 
						|
        self.set_lineno(node)
 | 
						|
        # setup list
 | 
						|
        tmpname = "$list%d" % self.__list_count
 | 
						|
        self.__list_count = self.__list_count + 1
 | 
						|
        self.emit('BUILD_LIST', 0)
 | 
						|
        self.emit('DUP_TOP')
 | 
						|
        self._implicitNameOp('STORE', tmpname)
 | 
						|
 | 
						|
        stack = []
 | 
						|
        for i, for_ in zip(range(len(node.quals)), node.quals):
 | 
						|
            start, anchor = self.visit(for_)
 | 
						|
            cont = None
 | 
						|
            for if_ in for_.ifs:
 | 
						|
                if cont is None:
 | 
						|
                    cont = self.newBlock()
 | 
						|
                self.visit(if_, cont)
 | 
						|
            stack.insert(0, (start, cont, anchor))
 | 
						|
 | 
						|
        self._implicitNameOp('LOAD', tmpname)
 | 
						|
        self.visit(node.expr)
 | 
						|
        self.emit('LIST_APPEND')
 | 
						|
 | 
						|
        for start, cont, anchor in stack:
 | 
						|
            if cont:
 | 
						|
                skip_one = self.newBlock()
 | 
						|
                self.emit('JUMP_FORWARD', skip_one)
 | 
						|
                self.startBlock(cont)
 | 
						|
                self.emit('POP_TOP')
 | 
						|
                self.nextBlock(skip_one)
 | 
						|
            self.emit('JUMP_ABSOLUTE', start)
 | 
						|
            self.startBlock(anchor)
 | 
						|
        self._implicitNameOp('DELETE', tmpname)
 | 
						|
 | 
						|
        self.__list_count = self.__list_count - 1
 | 
						|
 | 
						|
    def visitListCompFor(self, node):
 | 
						|
        start = self.newBlock()
 | 
						|
        anchor = self.newBlock()
 | 
						|
 | 
						|
        self.visit(node.list)
 | 
						|
        self.emit('GET_ITER')
 | 
						|
        self.nextBlock(start)
 | 
						|
        self.set_lineno(node, force=True)
 | 
						|
        self.emit('FOR_ITER', anchor)
 | 
						|
        self.nextBlock()
 | 
						|
        self.visit(node.assign)
 | 
						|
        return start, anchor
 | 
						|
 | 
						|
    def visitListCompIf(self, node, branch):
 | 
						|
        self.set_lineno(node, force=True)
 | 
						|
        self.visit(node.test)
 | 
						|
        self.emit('JUMP_IF_FALSE', branch)
 | 
						|
        self.newBlock()
 | 
						|
        self.emit('POP_TOP')
 | 
						|
 | 
						|
    def _makeClosure(self, gen, args):
 | 
						|
        frees = gen.scope.get_free_vars()
 | 
						|
        if frees:
 | 
						|
            for name in frees:
 | 
						|
                self.emit('LOAD_CLOSURE', name)
 | 
						|
            self.emit('BUILD_TUPLE', len(frees))
 | 
						|
            self.emit('LOAD_CONST', gen)
 | 
						|
            self.emit('MAKE_CLOSURE', args)
 | 
						|
        else:
 | 
						|
            self.emit('LOAD_CONST', gen)
 | 
						|
            self.emit('MAKE_FUNCTION', args)
 | 
						|
 | 
						|
    def visitGenExpr(self, node):
 | 
						|
        gen = GenExprCodeGenerator(node, self.scopes, self.class_name,
 | 
						|
                                   self.get_module())
 | 
						|
        walk(node.code, gen)
 | 
						|
        gen.finish()
 | 
						|
        self.set_lineno(node)
 | 
						|
        self._makeClosure(gen, 0)
 | 
						|
        # precomputation of outmost iterable
 | 
						|
        self.visit(node.code.quals[0].iter)
 | 
						|
        self.emit('GET_ITER')
 | 
						|
        self.emit('CALL_FUNCTION', 1)
 | 
						|
 | 
						|
    def visitGenExprInner(self, node):
 | 
						|
        self.set_lineno(node)
 | 
						|
        # setup list
 | 
						|
 | 
						|
        stack = []
 | 
						|
        for i, for_ in zip(range(len(node.quals)), node.quals):
 | 
						|
            start, anchor, end = self.visit(for_)
 | 
						|
            cont = None
 | 
						|
            for if_ in for_.ifs:
 | 
						|
                if cont is None:
 | 
						|
                    cont = self.newBlock()
 | 
						|
                self.visit(if_, cont)
 | 
						|
            stack.insert(0, (start, cont, anchor, end))
 | 
						|
 | 
						|
        self.visit(node.expr)
 | 
						|
        self.emit('YIELD_VALUE')
 | 
						|
        self.emit('POP_TOP')
 | 
						|
 | 
						|
        for start, cont, anchor, end in stack:
 | 
						|
            if cont:
 | 
						|
                skip_one = self.newBlock()
 | 
						|
                self.emit('JUMP_FORWARD', skip_one)
 | 
						|
                self.startBlock(cont)
 | 
						|
                self.emit('POP_TOP')
 | 
						|
                self.nextBlock(skip_one)
 | 
						|
            self.emit('JUMP_ABSOLUTE', start)
 | 
						|
            self.startBlock(anchor)
 | 
						|
            self.emit('POP_BLOCK')
 | 
						|
            self.setups.pop()
 | 
						|
            self.startBlock(end)
 | 
						|
 | 
						|
        self.emit('LOAD_CONST', None)
 | 
						|
 | 
						|
    def visitGenExprFor(self, node):
 | 
						|
        start = self.newBlock()
 | 
						|
        anchor = self.newBlock()
 | 
						|
        end = self.newBlock()
 | 
						|
 | 
						|
        self.setups.push((LOOP, start))
 | 
						|
        self.emit('SETUP_LOOP', end)
 | 
						|
 | 
						|
        if node.is_outmost:
 | 
						|
            self.loadName('.0')
 | 
						|
        else:
 | 
						|
            self.visit(node.iter)
 | 
						|
            self.emit('GET_ITER')
 | 
						|
 | 
						|
        self.nextBlock(start)
 | 
						|
        self.set_lineno(node, force=True)
 | 
						|
        self.emit('FOR_ITER', anchor)
 | 
						|
        self.nextBlock()
 | 
						|
        self.visit(node.assign)
 | 
						|
        return start, anchor, end
 | 
						|
 | 
						|
    def visitGenExprIf(self, node, branch):
 | 
						|
        self.set_lineno(node, force=True)
 | 
						|
        self.visit(node.test)
 | 
						|
        self.emit('JUMP_IF_FALSE', branch)
 | 
						|
        self.newBlock()
 | 
						|
        self.emit('POP_TOP')
 | 
						|
 | 
						|
    # exception related
 | 
						|
 | 
						|
    def visitAssert(self, node):
 | 
						|
        # XXX would be interesting to implement this via a
 | 
						|
        # transformation of the AST before this stage
 | 
						|
        if __debug__:
 | 
						|
            end = self.newBlock()
 | 
						|
            self.set_lineno(node)
 | 
						|
            # XXX AssertionError appears to be special case -- it is always
 | 
						|
            # loaded as a global even if there is a local name.  I guess this
 | 
						|
            # is a sort of renaming op.
 | 
						|
            self.nextBlock()
 | 
						|
            self.visit(node.test)
 | 
						|
            self.emit('JUMP_IF_TRUE', end)
 | 
						|
            self.nextBlock()
 | 
						|
            self.emit('POP_TOP')
 | 
						|
            self.emit('LOAD_GLOBAL', 'AssertionError')
 | 
						|
            if node.fail:
 | 
						|
                self.visit(node.fail)
 | 
						|
                self.emit('RAISE_VARARGS', 2)
 | 
						|
            else:
 | 
						|
                self.emit('RAISE_VARARGS', 1)
 | 
						|
            self.nextBlock(end)
 | 
						|
            self.emit('POP_TOP')
 | 
						|
 | 
						|
    def visitRaise(self, node):
 | 
						|
        self.set_lineno(node)
 | 
						|
        n = 0
 | 
						|
        if node.expr1:
 | 
						|
            self.visit(node.expr1)
 | 
						|
            n = n + 1
 | 
						|
        if node.expr2:
 | 
						|
            self.visit(node.expr2)
 | 
						|
            n = n + 1
 | 
						|
        if node.expr3:
 | 
						|
            self.visit(node.expr3)
 | 
						|
            n = n + 1
 | 
						|
        self.emit('RAISE_VARARGS', n)
 | 
						|
 | 
						|
    def visitTryExcept(self, node):
 | 
						|
        body = self.newBlock()
 | 
						|
        handlers = self.newBlock()
 | 
						|
        end = self.newBlock()
 | 
						|
        if node.else_:
 | 
						|
            lElse = self.newBlock()
 | 
						|
        else:
 | 
						|
            lElse = end
 | 
						|
        self.set_lineno(node)
 | 
						|
        self.emit('SETUP_EXCEPT', handlers)
 | 
						|
        self.nextBlock(body)
 | 
						|
        self.setups.push((EXCEPT, body))
 | 
						|
        self.visit(node.body)
 | 
						|
        self.emit('POP_BLOCK')
 | 
						|
        self.setups.pop()
 | 
						|
        self.emit('JUMP_FORWARD', lElse)
 | 
						|
        self.startBlock(handlers)
 | 
						|
 | 
						|
        last = len(node.handlers) - 1
 | 
						|
        for i in range(len(node.handlers)):
 | 
						|
            expr, target, body = node.handlers[i]
 | 
						|
            self.set_lineno(expr)
 | 
						|
            if expr:
 | 
						|
                self.emit('DUP_TOP')
 | 
						|
                self.visit(expr)
 | 
						|
                self.emit('COMPARE_OP', 'exception match')
 | 
						|
                next = self.newBlock()
 | 
						|
                self.emit('JUMP_IF_FALSE', next)
 | 
						|
                self.nextBlock()
 | 
						|
                self.emit('POP_TOP')
 | 
						|
            self.emit('POP_TOP')
 | 
						|
            if target:
 | 
						|
                self.visit(target)
 | 
						|
            else:
 | 
						|
                self.emit('POP_TOP')
 | 
						|
            self.emit('POP_TOP')
 | 
						|
            self.visit(body)
 | 
						|
            self.emit('JUMP_FORWARD', end)
 | 
						|
            if expr:
 | 
						|
                self.nextBlock(next)
 | 
						|
            else:
 | 
						|
                self.nextBlock()
 | 
						|
            if expr: # XXX
 | 
						|
                self.emit('POP_TOP')
 | 
						|
        self.emit('END_FINALLY')
 | 
						|
        if node.else_:
 | 
						|
            self.nextBlock(lElse)
 | 
						|
            self.visit(node.else_)
 | 
						|
        self.nextBlock(end)
 | 
						|
 | 
						|
    def visitTryFinally(self, node):
 | 
						|
        body = self.newBlock()
 | 
						|
        final = self.newBlock()
 | 
						|
        self.set_lineno(node)
 | 
						|
        self.emit('SETUP_FINALLY', final)
 | 
						|
        self.nextBlock(body)
 | 
						|
        self.setups.push((TRY_FINALLY, body))
 | 
						|
        self.visit(node.body)
 | 
						|
        self.emit('POP_BLOCK')
 | 
						|
        self.setups.pop()
 | 
						|
        self.emit('LOAD_CONST', None)
 | 
						|
        self.nextBlock(final)
 | 
						|
        self.setups.push((END_FINALLY, final))
 | 
						|
        self.visit(node.final)
 | 
						|
        self.emit('END_FINALLY')
 | 
						|
        self.setups.pop()
 | 
						|
 | 
						|
    __with_count = 0
 | 
						|
 | 
						|
    def visitWith(self, node):
 | 
						|
        body = self.newBlock()
 | 
						|
        final = self.newBlock()
 | 
						|
        exitvar = "$exit%d" % self.__with_count
 | 
						|
        valuevar = "$value%d" % self.__with_count
 | 
						|
        self.__with_count += 1
 | 
						|
        self.set_lineno(node)
 | 
						|
        self.visit(node.expr)
 | 
						|
        self.emit('DUP_TOP')
 | 
						|
        self.emit('LOAD_ATTR', '__exit__')
 | 
						|
        self._implicitNameOp('STORE', exitvar)
 | 
						|
        self.emit('LOAD_ATTR', '__enter__')
 | 
						|
        self.emit('CALL_FUNCTION', 0)
 | 
						|
        if node.vars is None:
 | 
						|
            self.emit('POP_TOP')
 | 
						|
        else:
 | 
						|
            self._implicitNameOp('STORE', valuevar)
 | 
						|
        self.emit('SETUP_FINALLY', final)
 | 
						|
        self.nextBlock(body)
 | 
						|
        self.setups.push((TRY_FINALLY, body))
 | 
						|
        if node.vars is not None:
 | 
						|
            self._implicitNameOp('LOAD', valuevar)
 | 
						|
            self._implicitNameOp('DELETE', valuevar)
 | 
						|
            self.visit(node.vars)
 | 
						|
        self.visit(node.body)
 | 
						|
        self.emit('POP_BLOCK')
 | 
						|
        self.setups.pop()
 | 
						|
        self.emit('LOAD_CONST', None)
 | 
						|
        self.nextBlock(final)
 | 
						|
        self.setups.push((END_FINALLY, final))
 | 
						|
        self._implicitNameOp('LOAD', exitvar)
 | 
						|
        self._implicitNameOp('DELETE', exitvar)
 | 
						|
        self.emit('WITH_CLEANUP')
 | 
						|
        self.emit('END_FINALLY')
 | 
						|
        self.setups.pop()
 | 
						|
        self.__with_count -= 1
 | 
						|
 | 
						|
    # misc
 | 
						|
 | 
						|
    def visitDiscard(self, node):
 | 
						|
        self.set_lineno(node)
 | 
						|
        self.visit(node.expr)
 | 
						|
        self.emit('POP_TOP')
 | 
						|
 | 
						|
    def visitConst(self, node):
 | 
						|
        self.emit('LOAD_CONST', node.value)
 | 
						|
 | 
						|
    def visitKeyword(self, node):
 | 
						|
        self.emit('LOAD_CONST', node.name)
 | 
						|
        self.visit(node.expr)
 | 
						|
 | 
						|
    def visitGlobal(self, node):
 | 
						|
        # no code to generate
 | 
						|
        pass
 | 
						|
 | 
						|
    def visitName(self, node):
 | 
						|
        self.set_lineno(node)
 | 
						|
        self.loadName(node.name)
 | 
						|
 | 
						|
    def visitPass(self, node):
 | 
						|
        self.set_lineno(node)
 | 
						|
 | 
						|
    def visitImport(self, node):
 | 
						|
        self.set_lineno(node)
 | 
						|
        level = 0 if self.graph.checkFlag(CO_FUTURE_ABSIMPORT) else -1
 | 
						|
        for name, alias in node.names:
 | 
						|
            if VERSION > 1:
 | 
						|
                self.emit('LOAD_CONST', level)
 | 
						|
                self.emit('LOAD_CONST', None)
 | 
						|
            self.emit('IMPORT_NAME', name)
 | 
						|
            mod = name.split(".")[0]
 | 
						|
            if alias:
 | 
						|
                self._resolveDots(name)
 | 
						|
                self.storeName(alias)
 | 
						|
            else:
 | 
						|
                self.storeName(mod)
 | 
						|
 | 
						|
    def visitFrom(self, node):
 | 
						|
        self.set_lineno(node)
 | 
						|
        level = node.level
 | 
						|
        if level == 0 and not self.graph.checkFlag(CO_FUTURE_ABSIMPORT):
 | 
						|
            level = -1
 | 
						|
        fromlist = map(lambda (name, alias): name, node.names)
 | 
						|
        if VERSION > 1:
 | 
						|
            self.emit('LOAD_CONST', level)
 | 
						|
            self.emit('LOAD_CONST', tuple(fromlist))
 | 
						|
        self.emit('IMPORT_NAME', node.modname)
 | 
						|
        for name, alias in node.names:
 | 
						|
            if VERSION > 1:
 | 
						|
                if name == '*':
 | 
						|
                    self.namespace = 0
 | 
						|
                    self.emit('IMPORT_STAR')
 | 
						|
                    # There can only be one name w/ from ... import *
 | 
						|
                    assert len(node.names) == 1
 | 
						|
                    return
 | 
						|
                else:
 | 
						|
                    self.emit('IMPORT_FROM', name)
 | 
						|
                    self._resolveDots(name)
 | 
						|
                    self.storeName(alias or name)
 | 
						|
            else:
 | 
						|
                self.emit('IMPORT_FROM', name)
 | 
						|
        self.emit('POP_TOP')
 | 
						|
 | 
						|
    def _resolveDots(self, name):
 | 
						|
        elts = name.split(".")
 | 
						|
        if len(elts) == 1:
 | 
						|
            return
 | 
						|
        for elt in elts[1:]:
 | 
						|
            self.emit('LOAD_ATTR', elt)
 | 
						|
 | 
						|
    def visitGetattr(self, node):
 | 
						|
        self.visit(node.expr)
 | 
						|
        self.emit('LOAD_ATTR', self.mangle(node.attrname))
 | 
						|
 | 
						|
    # next five implement assignments
 | 
						|
 | 
						|
    def visitAssign(self, node):
 | 
						|
        self.set_lineno(node)
 | 
						|
        self.visit(node.expr)
 | 
						|
        dups = len(node.nodes) - 1
 | 
						|
        for i in range(len(node.nodes)):
 | 
						|
            elt = node.nodes[i]
 | 
						|
            if i < dups:
 | 
						|
                self.emit('DUP_TOP')
 | 
						|
            if isinstance(elt, ast.Node):
 | 
						|
                self.visit(elt)
 | 
						|
 | 
						|
    def visitAssName(self, node):
 | 
						|
        if node.flags == 'OP_ASSIGN':
 | 
						|
            self.storeName(node.name)
 | 
						|
        elif node.flags == 'OP_DELETE':
 | 
						|
            self.set_lineno(node)
 | 
						|
            self.delName(node.name)
 | 
						|
        else:
 | 
						|
            print "oops", node.flags
 | 
						|
 | 
						|
    def visitAssAttr(self, node):
 | 
						|
        self.visit(node.expr)
 | 
						|
        if node.flags == 'OP_ASSIGN':
 | 
						|
            self.emit('STORE_ATTR', self.mangle(node.attrname))
 | 
						|
        elif node.flags == 'OP_DELETE':
 | 
						|
            self.emit('DELETE_ATTR', self.mangle(node.attrname))
 | 
						|
        else:
 | 
						|
            print "warning: unexpected flags:", node.flags
 | 
						|
            print node
 | 
						|
 | 
						|
    def _visitAssSequence(self, node, op='UNPACK_SEQUENCE'):
 | 
						|
        if findOp(node) != 'OP_DELETE':
 | 
						|
            self.emit(op, len(node.nodes))
 | 
						|
        for child in node.nodes:
 | 
						|
            self.visit(child)
 | 
						|
 | 
						|
    if VERSION > 1:
 | 
						|
        visitAssTuple = _visitAssSequence
 | 
						|
        visitAssList = _visitAssSequence
 | 
						|
    else:
 | 
						|
        def visitAssTuple(self, node):
 | 
						|
            self._visitAssSequence(node, 'UNPACK_TUPLE')
 | 
						|
 | 
						|
        def visitAssList(self, node):
 | 
						|
            self._visitAssSequence(node, 'UNPACK_LIST')
 | 
						|
 | 
						|
    # augmented assignment
 | 
						|
 | 
						|
    def visitAugAssign(self, node):
 | 
						|
        self.set_lineno(node)
 | 
						|
        aug_node = wrap_aug(node.node)
 | 
						|
        self.visit(aug_node, "load")
 | 
						|
        self.visit(node.expr)
 | 
						|
        self.emit(self._augmented_opcode[node.op])
 | 
						|
        self.visit(aug_node, "store")
 | 
						|
 | 
						|
    _augmented_opcode = {
 | 
						|
        '+=' : 'INPLACE_ADD',
 | 
						|
        '-=' : 'INPLACE_SUBTRACT',
 | 
						|
        '*=' : 'INPLACE_MULTIPLY',
 | 
						|
        '/=' : 'INPLACE_DIVIDE',
 | 
						|
        '//=': 'INPLACE_FLOOR_DIVIDE',
 | 
						|
        '%=' : 'INPLACE_MODULO',
 | 
						|
        '**=': 'INPLACE_POWER',
 | 
						|
        '>>=': 'INPLACE_RSHIFT',
 | 
						|
        '<<=': 'INPLACE_LSHIFT',
 | 
						|
        '&=' : 'INPLACE_AND',
 | 
						|
        '^=' : 'INPLACE_XOR',
 | 
						|
        '|=' : 'INPLACE_OR',
 | 
						|
        }
 | 
						|
 | 
						|
    def visitAugName(self, node, mode):
 | 
						|
        if mode == "load":
 | 
						|
            self.loadName(node.name)
 | 
						|
        elif mode == "store":
 | 
						|
            self.storeName(node.name)
 | 
						|
 | 
						|
    def visitAugGetattr(self, node, mode):
 | 
						|
        if mode == "load":
 | 
						|
            self.visit(node.expr)
 | 
						|
            self.emit('DUP_TOP')
 | 
						|
            self.emit('LOAD_ATTR', self.mangle(node.attrname))
 | 
						|
        elif mode == "store":
 | 
						|
            self.emit('ROT_TWO')
 | 
						|
            self.emit('STORE_ATTR', self.mangle(node.attrname))
 | 
						|
 | 
						|
    def visitAugSlice(self, node, mode):
 | 
						|
        if mode == "load":
 | 
						|
            self.visitSlice(node, 1)
 | 
						|
        elif mode == "store":
 | 
						|
            slice = 0
 | 
						|
            if node.lower:
 | 
						|
                slice = slice | 1
 | 
						|
            if node.upper:
 | 
						|
                slice = slice | 2
 | 
						|
            if slice == 0:
 | 
						|
                self.emit('ROT_TWO')
 | 
						|
            elif slice == 3:
 | 
						|
                self.emit('ROT_FOUR')
 | 
						|
            else:
 | 
						|
                self.emit('ROT_THREE')
 | 
						|
            self.emit('STORE_SLICE+%d' % slice)
 | 
						|
 | 
						|
    def visitAugSubscript(self, node, mode):
 | 
						|
        if mode == "load":
 | 
						|
            self.visitSubscript(node, 1)
 | 
						|
        elif mode == "store":
 | 
						|
            self.emit('ROT_THREE')
 | 
						|
            self.emit('STORE_SUBSCR')
 | 
						|
 | 
						|
    def visitExec(self, node):
 | 
						|
        self.visit(node.expr)
 | 
						|
        if node.locals is None:
 | 
						|
            self.emit('LOAD_CONST', None)
 | 
						|
        else:
 | 
						|
            self.visit(node.locals)
 | 
						|
        if node.globals is None:
 | 
						|
            self.emit('DUP_TOP')
 | 
						|
        else:
 | 
						|
            self.visit(node.globals)
 | 
						|
        self.emit('EXEC_STMT')
 | 
						|
 | 
						|
    def visitCallFunc(self, node):
 | 
						|
        pos = 0
 | 
						|
        kw = 0
 | 
						|
        self.set_lineno(node)
 | 
						|
        self.visit(node.node)
 | 
						|
        for arg in node.args:
 | 
						|
            self.visit(arg)
 | 
						|
            if isinstance(arg, ast.Keyword):
 | 
						|
                kw = kw + 1
 | 
						|
            else:
 | 
						|
                pos = pos + 1
 | 
						|
        if node.star_args is not None:
 | 
						|
            self.visit(node.star_args)
 | 
						|
        if node.dstar_args is not None:
 | 
						|
            self.visit(node.dstar_args)
 | 
						|
        have_star = node.star_args is not None
 | 
						|
        have_dstar = node.dstar_args is not None
 | 
						|
        opcode = callfunc_opcode_info[have_star, have_dstar]
 | 
						|
        self.emit(opcode, kw << 8 | pos)
 | 
						|
 | 
						|
    def visitPrint(self, node, newline=0):
 | 
						|
        self.set_lineno(node)
 | 
						|
        if node.dest:
 | 
						|
            self.visit(node.dest)
 | 
						|
        for child in node.nodes:
 | 
						|
            if node.dest:
 | 
						|
                self.emit('DUP_TOP')
 | 
						|
            self.visit(child)
 | 
						|
            if node.dest:
 | 
						|
                self.emit('ROT_TWO')
 | 
						|
                self.emit('PRINT_ITEM_TO')
 | 
						|
            else:
 | 
						|
                self.emit('PRINT_ITEM')
 | 
						|
        if node.dest and not newline:
 | 
						|
            self.emit('POP_TOP')
 | 
						|
 | 
						|
    def visitPrintnl(self, node):
 | 
						|
        self.visitPrint(node, newline=1)
 | 
						|
        if node.dest:
 | 
						|
            self.emit('PRINT_NEWLINE_TO')
 | 
						|
        else:
 | 
						|
            self.emit('PRINT_NEWLINE')
 | 
						|
 | 
						|
    def visitReturn(self, node):
 | 
						|
        self.set_lineno(node)
 | 
						|
        self.visit(node.value)
 | 
						|
        self.emit('RETURN_VALUE')
 | 
						|
 | 
						|
    def visitYield(self, node):
 | 
						|
        self.set_lineno(node)
 | 
						|
        self.visit(node.value)
 | 
						|
        self.emit('YIELD_VALUE')
 | 
						|
 | 
						|
    # slice and subscript stuff
 | 
						|
 | 
						|
    def visitSlice(self, node, aug_flag=None):
 | 
						|
        # aug_flag is used by visitAugSlice
 | 
						|
        self.visit(node.expr)
 | 
						|
        slice = 0
 | 
						|
        if node.lower:
 | 
						|
            self.visit(node.lower)
 | 
						|
            slice = slice | 1
 | 
						|
        if node.upper:
 | 
						|
            self.visit(node.upper)
 | 
						|
            slice = slice | 2
 | 
						|
        if aug_flag:
 | 
						|
            if slice == 0:
 | 
						|
                self.emit('DUP_TOP')
 | 
						|
            elif slice == 3:
 | 
						|
                self.emit('DUP_TOPX', 3)
 | 
						|
            else:
 | 
						|
                self.emit('DUP_TOPX', 2)
 | 
						|
        if node.flags == 'OP_APPLY':
 | 
						|
            self.emit('SLICE+%d' % slice)
 | 
						|
        elif node.flags == 'OP_ASSIGN':
 | 
						|
            self.emit('STORE_SLICE+%d' % slice)
 | 
						|
        elif node.flags == 'OP_DELETE':
 | 
						|
            self.emit('DELETE_SLICE+%d' % slice)
 | 
						|
        else:
 | 
						|
            print "weird slice", node.flags
 | 
						|
            raise
 | 
						|
 | 
						|
    def visitSubscript(self, node, aug_flag=None):
 | 
						|
        self.visit(node.expr)
 | 
						|
        for sub in node.subs:
 | 
						|
            self.visit(sub)
 | 
						|
        if len(node.subs) > 1:
 | 
						|
            self.emit('BUILD_TUPLE', len(node.subs))
 | 
						|
        if aug_flag:
 | 
						|
            self.emit('DUP_TOPX', 2)
 | 
						|
        if node.flags == 'OP_APPLY':
 | 
						|
            self.emit('BINARY_SUBSCR')
 | 
						|
        elif node.flags == 'OP_ASSIGN':
 | 
						|
            self.emit('STORE_SUBSCR')
 | 
						|
        elif node.flags == 'OP_DELETE':
 | 
						|
            self.emit('DELETE_SUBSCR')
 | 
						|
 | 
						|
    # binary ops
 | 
						|
 | 
						|
    def binaryOp(self, node, op):
 | 
						|
        self.visit(node.left)
 | 
						|
        self.visit(node.right)
 | 
						|
        self.emit(op)
 | 
						|
 | 
						|
    def visitAdd(self, node):
 | 
						|
        return self.binaryOp(node, 'BINARY_ADD')
 | 
						|
 | 
						|
    def visitSub(self, node):
 | 
						|
        return self.binaryOp(node, 'BINARY_SUBTRACT')
 | 
						|
 | 
						|
    def visitMul(self, node):
 | 
						|
        return self.binaryOp(node, 'BINARY_MULTIPLY')
 | 
						|
 | 
						|
    def visitDiv(self, node):
 | 
						|
        return self.binaryOp(node, self._div_op)
 | 
						|
 | 
						|
    def visitFloorDiv(self, node):
 | 
						|
        return self.binaryOp(node, 'BINARY_FLOOR_DIVIDE')
 | 
						|
 | 
						|
    def visitMod(self, node):
 | 
						|
        return self.binaryOp(node, 'BINARY_MODULO')
 | 
						|
 | 
						|
    def visitPower(self, node):
 | 
						|
        return self.binaryOp(node, 'BINARY_POWER')
 | 
						|
 | 
						|
    def visitLeftShift(self, node):
 | 
						|
        return self.binaryOp(node, 'BINARY_LSHIFT')
 | 
						|
 | 
						|
    def visitRightShift(self, node):
 | 
						|
        return self.binaryOp(node, 'BINARY_RSHIFT')
 | 
						|
 | 
						|
    # unary ops
 | 
						|
 | 
						|
    def unaryOp(self, node, op):
 | 
						|
        self.visit(node.expr)
 | 
						|
        self.emit(op)
 | 
						|
 | 
						|
    def visitInvert(self, node):
 | 
						|
        return self.unaryOp(node, 'UNARY_INVERT')
 | 
						|
 | 
						|
    def visitUnarySub(self, node):
 | 
						|
        return self.unaryOp(node, 'UNARY_NEGATIVE')
 | 
						|
 | 
						|
    def visitUnaryAdd(self, node):
 | 
						|
        return self.unaryOp(node, 'UNARY_POSITIVE')
 | 
						|
 | 
						|
    def visitUnaryInvert(self, node):
 | 
						|
        return self.unaryOp(node, 'UNARY_INVERT')
 | 
						|
 | 
						|
    def visitNot(self, node):
 | 
						|
        return self.unaryOp(node, 'UNARY_NOT')
 | 
						|
 | 
						|
    def visitBackquote(self, node):
 | 
						|
        return self.unaryOp(node, 'UNARY_CONVERT')
 | 
						|
 | 
						|
    # bit ops
 | 
						|
 | 
						|
    def bitOp(self, nodes, op):
 | 
						|
        self.visit(nodes[0])
 | 
						|
        for node in nodes[1:]:
 | 
						|
            self.visit(node)
 | 
						|
            self.emit(op)
 | 
						|
 | 
						|
    def visitBitand(self, node):
 | 
						|
        return self.bitOp(node.nodes, 'BINARY_AND')
 | 
						|
 | 
						|
    def visitBitor(self, node):
 | 
						|
        return self.bitOp(node.nodes, 'BINARY_OR')
 | 
						|
 | 
						|
    def visitBitxor(self, node):
 | 
						|
        return self.bitOp(node.nodes, 'BINARY_XOR')
 | 
						|
 | 
						|
    # object constructors
 | 
						|
 | 
						|
    def visitEllipsis(self, node):
 | 
						|
        self.emit('LOAD_CONST', Ellipsis)
 | 
						|
 | 
						|
    def visitTuple(self, node):
 | 
						|
        self.set_lineno(node)
 | 
						|
        for elt in node.nodes:
 | 
						|
            self.visit(elt)
 | 
						|
        self.emit('BUILD_TUPLE', len(node.nodes))
 | 
						|
 | 
						|
    def visitList(self, node):
 | 
						|
        self.set_lineno(node)
 | 
						|
        for elt in node.nodes:
 | 
						|
            self.visit(elt)
 | 
						|
        self.emit('BUILD_LIST', len(node.nodes))
 | 
						|
 | 
						|
    def visitSliceobj(self, node):
 | 
						|
        for child in node.nodes:
 | 
						|
            self.visit(child)
 | 
						|
        self.emit('BUILD_SLICE', len(node.nodes))
 | 
						|
 | 
						|
    def visitDict(self, node):
 | 
						|
        self.set_lineno(node)
 | 
						|
        self.emit('BUILD_MAP', 0)
 | 
						|
        for k, v in node.items:
 | 
						|
            self.emit('DUP_TOP')
 | 
						|
            self.visit(k)
 | 
						|
            self.visit(v)
 | 
						|
            self.emit('ROT_THREE')
 | 
						|
            self.emit('STORE_SUBSCR')
 | 
						|
 | 
						|
class NestedScopeMixin:
 | 
						|
    """Defines initClass() for nested scoping (Python 2.2-compatible)"""
 | 
						|
    def initClass(self):
 | 
						|
        self.__class__.NameFinder = LocalNameFinder
 | 
						|
        self.__class__.FunctionGen = FunctionCodeGenerator
 | 
						|
        self.__class__.ClassGen = ClassCodeGenerator
 | 
						|
 | 
						|
class ModuleCodeGenerator(NestedScopeMixin, CodeGenerator):
 | 
						|
    __super_init = CodeGenerator.__init__
 | 
						|
 | 
						|
    scopes = None
 | 
						|
 | 
						|
    def __init__(self, tree):
 | 
						|
        self.graph = pyassem.PyFlowGraph("<module>", tree.filename)
 | 
						|
        self.futures = future.find_futures(tree)
 | 
						|
        self.__super_init()
 | 
						|
        walk(tree, self)
 | 
						|
 | 
						|
    def get_module(self):
 | 
						|
        return self
 | 
						|
 | 
						|
class ExpressionCodeGenerator(NestedScopeMixin, CodeGenerator):
 | 
						|
    __super_init = CodeGenerator.__init__
 | 
						|
 | 
						|
    scopes = None
 | 
						|
    futures = ()
 | 
						|
 | 
						|
    def __init__(self, tree):
 | 
						|
        self.graph = pyassem.PyFlowGraph("<expression>", tree.filename)
 | 
						|
        self.__super_init()
 | 
						|
        walk(tree, self)
 | 
						|
 | 
						|
    def get_module(self):
 | 
						|
        return self
 | 
						|
 | 
						|
class InteractiveCodeGenerator(NestedScopeMixin, CodeGenerator):
 | 
						|
 | 
						|
    __super_init = CodeGenerator.__init__
 | 
						|
 | 
						|
    scopes = None
 | 
						|
    futures = ()
 | 
						|
 | 
						|
    def __init__(self, tree):
 | 
						|
        self.graph = pyassem.PyFlowGraph("<interactive>", tree.filename)
 | 
						|
        self.__super_init()
 | 
						|
        self.set_lineno(tree)
 | 
						|
        walk(tree, self)
 | 
						|
        self.emit('RETURN_VALUE')
 | 
						|
 | 
						|
    def get_module(self):
 | 
						|
        return self
 | 
						|
 | 
						|
    def visitDiscard(self, node):
 | 
						|
        # XXX Discard means it's an expression.  Perhaps this is a bad
 | 
						|
        # name.
 | 
						|
        self.visit(node.expr)
 | 
						|
        self.emit('PRINT_EXPR')
 | 
						|
 | 
						|
class AbstractFunctionCode:
 | 
						|
    optimized = 1
 | 
						|
    lambdaCount = 0
 | 
						|
 | 
						|
    def __init__(self, func, scopes, isLambda, class_name, mod):
 | 
						|
        self.class_name = class_name
 | 
						|
        self.module = mod
 | 
						|
        if isLambda:
 | 
						|
            klass = FunctionCodeGenerator
 | 
						|
            name = "<lambda.%d>" % klass.lambdaCount
 | 
						|
            klass.lambdaCount = klass.lambdaCount + 1
 | 
						|
        else:
 | 
						|
            name = func.name
 | 
						|
 | 
						|
        args, hasTupleArg = generateArgList(func.argnames)
 | 
						|
        self.graph = pyassem.PyFlowGraph(name, func.filename, args,
 | 
						|
                                         optimized=1)
 | 
						|
        self.isLambda = isLambda
 | 
						|
        self.super_init()
 | 
						|
 | 
						|
        if not isLambda and func.doc:
 | 
						|
            self.setDocstring(func.doc)
 | 
						|
 | 
						|
        lnf = walk(func.code, self.NameFinder(args), verbose=0)
 | 
						|
        self.locals.push(lnf.getLocals())
 | 
						|
        if func.varargs:
 | 
						|
            self.graph.setFlag(CO_VARARGS)
 | 
						|
        if func.kwargs:
 | 
						|
            self.graph.setFlag(CO_VARKEYWORDS)
 | 
						|
        self.set_lineno(func)
 | 
						|
        if hasTupleArg:
 | 
						|
            self.generateArgUnpack(func.argnames)
 | 
						|
 | 
						|
    def get_module(self):
 | 
						|
        return self.module
 | 
						|
 | 
						|
    def finish(self):
 | 
						|
        self.graph.startExitBlock()
 | 
						|
        if not self.isLambda:
 | 
						|
            self.emit('LOAD_CONST', None)
 | 
						|
        self.emit('RETURN_VALUE')
 | 
						|
 | 
						|
    def generateArgUnpack(self, args):
 | 
						|
        for i in range(len(args)):
 | 
						|
            arg = args[i]
 | 
						|
            if isinstance(arg, tuple):
 | 
						|
                self.emit('LOAD_FAST', '.%d' % (i * 2))
 | 
						|
                self.unpackSequence(arg)
 | 
						|
 | 
						|
    def unpackSequence(self, tup):
 | 
						|
        if VERSION > 1:
 | 
						|
            self.emit('UNPACK_SEQUENCE', len(tup))
 | 
						|
        else:
 | 
						|
            self.emit('UNPACK_TUPLE', len(tup))
 | 
						|
        for elt in tup:
 | 
						|
            if isinstance(elt, tuple):
 | 
						|
                self.unpackSequence(elt)
 | 
						|
            else:
 | 
						|
                self._nameOp('STORE', elt)
 | 
						|
 | 
						|
    unpackTuple = unpackSequence
 | 
						|
 | 
						|
class FunctionCodeGenerator(NestedScopeMixin, AbstractFunctionCode,
 | 
						|
                            CodeGenerator):
 | 
						|
    super_init = CodeGenerator.__init__ # call be other init
 | 
						|
    scopes = None
 | 
						|
 | 
						|
    __super_init = AbstractFunctionCode.__init__
 | 
						|
 | 
						|
    def __init__(self, func, scopes, isLambda, class_name, mod):
 | 
						|
        self.scopes = scopes
 | 
						|
        self.scope = scopes[func]
 | 
						|
        self.__super_init(func, scopes, isLambda, class_name, mod)
 | 
						|
        self.graph.setFreeVars(self.scope.get_free_vars())
 | 
						|
        self.graph.setCellVars(self.scope.get_cell_vars())
 | 
						|
        if self.scope.generator is not None:
 | 
						|
            self.graph.setFlag(CO_GENERATOR)
 | 
						|
 | 
						|
class GenExprCodeGenerator(NestedScopeMixin, AbstractFunctionCode,
 | 
						|
                           CodeGenerator):
 | 
						|
    super_init = CodeGenerator.__init__ # call be other init
 | 
						|
    scopes = None
 | 
						|
 | 
						|
    __super_init = AbstractFunctionCode.__init__
 | 
						|
 | 
						|
    def __init__(self, gexp, scopes, class_name, mod):
 | 
						|
        self.scopes = scopes
 | 
						|
        self.scope = scopes[gexp]
 | 
						|
        self.__super_init(gexp, scopes, 1, class_name, mod)
 | 
						|
        self.graph.setFreeVars(self.scope.get_free_vars())
 | 
						|
        self.graph.setCellVars(self.scope.get_cell_vars())
 | 
						|
        self.graph.setFlag(CO_GENERATOR)
 | 
						|
 | 
						|
class AbstractClassCode:
 | 
						|
 | 
						|
    def __init__(self, klass, scopes, module):
 | 
						|
        self.class_name = klass.name
 | 
						|
        self.module = module
 | 
						|
        self.graph = pyassem.PyFlowGraph(klass.name, klass.filename,
 | 
						|
                                           optimized=0, klass=1)
 | 
						|
        self.super_init()
 | 
						|
        lnf = walk(klass.code, self.NameFinder(), verbose=0)
 | 
						|
        self.locals.push(lnf.getLocals())
 | 
						|
        self.graph.setFlag(CO_NEWLOCALS)
 | 
						|
        if klass.doc:
 | 
						|
            self.setDocstring(klass.doc)
 | 
						|
 | 
						|
    def get_module(self):
 | 
						|
        return self.module
 | 
						|
 | 
						|
    def finish(self):
 | 
						|
        self.graph.startExitBlock()
 | 
						|
        self.emit('LOAD_LOCALS')
 | 
						|
        self.emit('RETURN_VALUE')
 | 
						|
 | 
						|
class ClassCodeGenerator(NestedScopeMixin, AbstractClassCode, CodeGenerator):
 | 
						|
    super_init = CodeGenerator.__init__
 | 
						|
    scopes = None
 | 
						|
 | 
						|
    __super_init = AbstractClassCode.__init__
 | 
						|
 | 
						|
    def __init__(self, klass, scopes, module):
 | 
						|
        self.scopes = scopes
 | 
						|
        self.scope = scopes[klass]
 | 
						|
        self.__super_init(klass, scopes, module)
 | 
						|
        self.graph.setFreeVars(self.scope.get_free_vars())
 | 
						|
        self.graph.setCellVars(self.scope.get_cell_vars())
 | 
						|
        self.set_lineno(klass)
 | 
						|
        self.emit("LOAD_GLOBAL", "__name__")
 | 
						|
        self.storeName("__module__")
 | 
						|
        if klass.doc:
 | 
						|
            self.emit("LOAD_CONST", klass.doc)
 | 
						|
            self.storeName('__doc__')
 | 
						|
 | 
						|
def generateArgList(arglist):
 | 
						|
    """Generate an arg list marking TupleArgs"""
 | 
						|
    args = []
 | 
						|
    extra = []
 | 
						|
    count = 0
 | 
						|
    for i in range(len(arglist)):
 | 
						|
        elt = arglist[i]
 | 
						|
        if isinstance(elt, str):
 | 
						|
            args.append(elt)
 | 
						|
        elif isinstance(elt, tuple):
 | 
						|
            args.append(TupleArg(i * 2, elt))
 | 
						|
            extra.extend(misc.flatten(elt))
 | 
						|
            count = count + 1
 | 
						|
        else:
 | 
						|
            raise ValueError, "unexpect argument type:", elt
 | 
						|
    return args + extra, count
 | 
						|
 | 
						|
def findOp(node):
 | 
						|
    """Find the op (DELETE, LOAD, STORE) in an AssTuple tree"""
 | 
						|
    v = OpFinder()
 | 
						|
    walk(node, v, verbose=0)
 | 
						|
    return v.op
 | 
						|
 | 
						|
class OpFinder:
 | 
						|
    def __init__(self):
 | 
						|
        self.op = None
 | 
						|
    def visitAssName(self, node):
 | 
						|
        if self.op is None:
 | 
						|
            self.op = node.flags
 | 
						|
        elif self.op != node.flags:
 | 
						|
            raise ValueError, "mixed ops in stmt"
 | 
						|
    visitAssAttr = visitAssName
 | 
						|
    visitSubscript = visitAssName
 | 
						|
 | 
						|
class Delegator:
 | 
						|
    """Base class to support delegation for augmented assignment nodes
 | 
						|
 | 
						|
    To generator code for augmented assignments, we use the following
 | 
						|
    wrapper classes.  In visitAugAssign, the left-hand expression node
 | 
						|
    is visited twice.  The first time the visit uses the normal method
 | 
						|
    for that node .  The second time the visit uses a different method
 | 
						|
    that generates the appropriate code to perform the assignment.
 | 
						|
    These delegator classes wrap the original AST nodes in order to
 | 
						|
    support the variant visit methods.
 | 
						|
    """
 | 
						|
    def __init__(self, obj):
 | 
						|
        self.obj = obj
 | 
						|
 | 
						|
    def __getattr__(self, attr):
 | 
						|
        return getattr(self.obj, attr)
 | 
						|
 | 
						|
class AugGetattr(Delegator):
 | 
						|
    pass
 | 
						|
 | 
						|
class AugName(Delegator):
 | 
						|
    pass
 | 
						|
 | 
						|
class AugSlice(Delegator):
 | 
						|
    pass
 | 
						|
 | 
						|
class AugSubscript(Delegator):
 | 
						|
    pass
 | 
						|
 | 
						|
wrapper = {
 | 
						|
    ast.Getattr: AugGetattr,
 | 
						|
    ast.Name: AugName,
 | 
						|
    ast.Slice: AugSlice,
 | 
						|
    ast.Subscript: AugSubscript,
 | 
						|
    }
 | 
						|
 | 
						|
def wrap_aug(node):
 | 
						|
    return wrapper[node.__class__](node)
 | 
						|
 | 
						|
if __name__ == "__main__":
 | 
						|
    for file in sys.argv[1:]:
 | 
						|
        compileFile(file)
 |