]> granicus.if.org Git - python/commitdiff
Bring Tools/compiler almost up to date. Specifically:
authorThomas Wouters <thomas@python.org>
Sat, 12 Aug 2000 20:32:46 +0000 (20:32 +0000)
committerThomas Wouters <thomas@python.org>
Sat, 12 Aug 2000 20:32:46 +0000 (20:32 +0000)
- fix tab space issues (SF patch #101167 by Neil Schemenauer)
- fix co_flags for classes to include CO_NEWLOCALS (SF patch #101145 by Neil)
- fix for merger of UNPACK_LIST and UNPACK_TUPLE into UNPACK_SEQUENCE,
  (SF patch #101168 by, well, Neil :)
- Adjust bytecode MAGIC to current bytecode.

TODO: teach compile.py about list comprehensions.

Lib/compiler/pyassem.py
Lib/compiler/pycodegen.py
Lib/compiler/visitor.py
Tools/compiler/compiler/pyassem.py
Tools/compiler/compiler/pycodegen.py
Tools/compiler/compiler/visitor.py

index 6e079870a2fb96114c98cc4cc029dc29c752564f..c8d9e90cc87d5a639fd948065157dd55804f3661 100644 (file)
@@ -9,71 +9,71 @@ from compiler import misc
 
 class FlowGraph:
     def __init__(self):
-       self.current = self.entry = Block()
-       self.exit = Block("exit")
-       self.blocks = misc.Set()
-       self.blocks.add(self.entry)
-       self.blocks.add(self.exit)
+        self.current = self.entry = Block()
+        self.exit = Block("exit")
+        self.blocks = misc.Set()
+        self.blocks.add(self.entry)
+        self.blocks.add(self.exit)
 
     def startBlock(self, block):
-       self.current = block
+        self.current = block
 
     def nextBlock(self, block=None):
-       if block is None:
-           block = self.newBlock()
-       # XXX think we need to specify when there is implicit transfer
-       # from one block to the next
-       #
-       # I think this strategy works: each block has a child
-       # designated as "next" which is returned as the last of the
-       # children.  because the nodes in a graph are emitted in
-       # reverse post order, the "next" block will always be emitted
-       # immediately after its parent.
-       # Worry: maintaining this invariant could be tricky
-       self.current.addNext(block)
-       self.startBlock(block)
+        if block is None:
+            block = self.newBlock()
+        # XXX think we need to specify when there is implicit transfer
+        # from one block to the next
+        #
+        # I think this strategy works: each block has a child
+        # designated as "next" which is returned as the last of the
+        # children.  because the nodes in a graph are emitted in
+        # reverse post order, the "next" block will always be emitted
+        # immediately after its parent.
+        # Worry: maintaining this invariant could be tricky
+        self.current.addNext(block)
+        self.startBlock(block)
 
     def newBlock(self):
-       b = Block()
-       self.blocks.add(b)
-       return b
+        b = Block()
+        self.blocks.add(b)
+        return b
 
     def startExitBlock(self):
-       self.startBlock(self.exit)
+        self.startBlock(self.exit)
 
     def emit(self, *inst):
-       # XXX should jump instructions implicitly call nextBlock?
-       if inst[0] == 'RETURN_VALUE':
-           self.current.addOutEdge(self.exit)
-       self.current.emit(inst)
+        # XXX should jump instructions implicitly call nextBlock?
+        if inst[0] == 'RETURN_VALUE':
+            self.current.addOutEdge(self.exit)
+        self.current.emit(inst)
 
     def getBlocks(self):
-       """Return the blocks in reverse postorder
-
-       i.e. each node appears before all of its successors
-       """
-       # XXX make sure every node that doesn't have an explicit next
-       # is set so that next points to exit
-       for b in self.blocks.elements():
-           if b is self.exit:
-               continue
-           if not b.next:
-               b.addNext(self.exit)
-       order = dfs_postorder(self.entry, {})
-       order.reverse()
-       # hack alert
-       if not self.exit in order:
-           order.append(self.exit)
-       return order
+        """Return the blocks in reverse postorder
+
+        i.e. each node appears before all of its successors
+        """
+        # XXX make sure every node that doesn't have an explicit next
+        # is set so that next points to exit
+        for b in self.blocks.elements():
+            if b is self.exit:
+                continue
+            if not b.next:
+                b.addNext(self.exit)
+        order = dfs_postorder(self.entry, {})
+        order.reverse()
+        # hack alert
+        if not self.exit in order:
+            order.append(self.exit)
+        return order
 
 def dfs_postorder(b, seen):
     """Depth-first search of tree rooted at b, return in postorder"""
     order = []
     seen[b] = b
     for c in b.children():
-       if seen.has_key(c):
-           continue
-       order = order + dfs_postorder(c, seen)
+        if seen.has_key(c):
+            continue
+        order = order + dfs_postorder(c, seen)
     order.append(b)
     return order
 
@@ -81,47 +81,47 @@ class Block:
     _count = 0
 
     def __init__(self, label=''):
-       self.insts = []
-       self.inEdges = misc.Set()
-       self.outEdges = misc.Set()
-       self.label = label
-       self.bid = Block._count
-       self.next = []
-       Block._count = Block._count + 1
+        self.insts = []
+        self.inEdges = misc.Set()
+        self.outEdges = misc.Set()
+        self.label = label
+        self.bid = Block._count
+        self.next = []
+        Block._count = Block._count + 1
 
     def __repr__(self):
-       if self.label:
-           return "<block %s id=%d len=%d>" % (self.label, self.bid,
-                                               len(self.insts)) 
-       else:
-           return "<block id=%d len=%d>" % (self.bid, len(self.insts))
+        if self.label:
+            return "<block %s id=%d len=%d>" % (self.label, self.bid,
+                                                len(self.insts)) 
+        else:
+            return "<block id=%d len=%d>" % (self.bid, len(self.insts))
 
     def __str__(self):
-       insts = map(str, self.insts)
-       return "<block %s %d:\n%s>" % (self.label, self.bid,
-                                      string.join(insts, '\n')) 
+        insts = map(str, self.insts)
+        return "<block %s %d:\n%s>" % (self.label, self.bid,
+                                       string.join(insts, '\n')) 
 
     def emit(self, inst):
-       op = inst[0]
-       if op[:4] == 'JUMP':
-           self.outEdges.add(inst[1])
-       self.insts.append(inst)
+        op = inst[0]
+        if op[:4] == 'JUMP':
+            self.outEdges.add(inst[1])
+        self.insts.append(inst)
 
     def getInstructions(self):
-       return self.insts
+        return self.insts
 
     def addInEdge(self, block):
-       self.inEdges.add(block)
+        self.inEdges.add(block)
 
     def addOutEdge(self, block):
-       self.outEdges.add(block)
+        self.outEdges.add(block)
 
     def addNext(self, block):
-       self.next.append(block)
-       assert len(self.next) == 1, map(str, self.next)
+        self.next.append(block)
+        assert len(self.next) == 1, map(str, self.next)
 
     def children(self):
-       return self.outEdges.elements() + self.next
+        return self.outEdges.elements() + self.next
 
 # flags for code objects
 CO_OPTIMIZED = 0x0001
@@ -139,18 +139,18 @@ class PyFlowGraph(FlowGraph):
     super_init = FlowGraph.__init__
 
     def __init__(self, name, filename, args=(), optimized=0):
-       self.super_init()
-       self.name = name
-       self.filename = filename
-       self.docstring = None
-       self.args = args # XXX
-       self.argcount = getArgCount(args)
-       if optimized:
-           self.flags = CO_OPTIMIZED | CO_NEWLOCALS 
-       else:
-           self.flags = 0
-       self.consts = []
-       self.names = []
+        self.super_init()
+        self.name = name
+        self.filename = filename
+        self.docstring = None
+        self.args = args # XXX
+        self.argcount = getArgCount(args)
+        if optimized:
+            self.flags = CO_OPTIMIZED | CO_NEWLOCALS 
+        else:
+            self.flags = 0
+        self.consts = []
+        self.names = []
         self.varnames = list(args) or []
         for i in range(len(self.varnames)):
             var = self.varnames[i]
@@ -163,13 +163,13 @@ class PyFlowGraph(FlowGraph):
         self.consts.insert(0, doc)
 
     def setFlag(self, flag):
-       self.flags = self.flags | flag
-       if flag == CO_VARARGS:
-           self.argcount = self.argcount - 1
+        self.flags = self.flags | flag
+        if flag == CO_VARARGS:
+            self.argcount = self.argcount - 1
 
     def getCode(self):
-       """Get a Python code object"""
-       if self.stage == RAW:
+        """Get a Python code object"""
+        if self.stage == RAW:
             self.flattenGraph()
         if self.stage == FLAT:
             self.convertArgs()
@@ -198,38 +198,38 @@ class PyFlowGraph(FlowGraph):
             sys.stdout = save
 
     def flattenGraph(self):
-       """Arrange the blocks in order and resolve jumps"""
-       assert self.stage == RAW
-       self.insts = insts = []
-       pc = 0
-       begin = {}
-       end = {}
-       for b in self.getBlocks():
-           begin[b] = pc
-           for inst in b.getInstructions():
-               insts.append(inst)
-               if len(inst) == 1:
-                   pc = pc + 1
-               else:
-                   # arg takes 2 bytes
-                   pc = pc + 3
-           end[b] = pc
-       pc = 0
-       for i in range(len(insts)):
-           inst = insts[i]
-           if len(inst) == 1:
+        """Arrange the blocks in order and resolve jumps"""
+        assert self.stage == RAW
+        self.insts = insts = []
+        pc = 0
+        begin = {}
+        end = {}
+        for b in self.getBlocks():
+            begin[b] = pc
+            for inst in b.getInstructions():
+                insts.append(inst)
+                if len(inst) == 1:
+                    pc = pc + 1
+                else:
+                    # arg takes 2 bytes
+                    pc = pc + 3
+            end[b] = pc
+        pc = 0
+        for i in range(len(insts)):
+            inst = insts[i]
+            if len(inst) == 1:
                 pc = pc + 1
             else:
                 pc = pc + 3
-           opname = inst[0]
-           if self.hasjrel.has_elt(opname):
+            opname = inst[0]
+            if self.hasjrel.has_elt(opname):
                 oparg = inst[1]
                 offset = begin[oparg] - pc
                 insts[i] = opname, offset
             elif self.hasjabs.has_elt(opname):
                 insts[i] = opname, begin[inst[1]]
-       self.stacksize = findDepth(self.insts)
-       self.stage = FLAT
+        self.stacksize = findDepth(self.insts)
+        self.stage = FLAT
 
     hasjrel = misc.Set()
     for i in dis.hasjrel:
@@ -292,7 +292,7 @@ class PyFlowGraph(FlowGraph):
 
     _cmp = list(dis.cmp_op)
     def _convert_COMPARE_OP(self, arg):
-       return self._cmp.index(arg)
+        return self._cmp.index(arg)
 
     # similarly for other opcodes...
 
@@ -314,12 +314,12 @@ class PyFlowGraph(FlowGraph):
                 if opname == "SET_LINENO":
                     lnotab.nextLine(oparg)
                 hi, lo = twobyte(oparg)
-               try:
-                   lnotab.addCode(self.opnum[opname], lo, hi)
-               except ValueError:
-                   print opname, oparg
-                   print self.opnum[opname], lo, hi
-                   raise
+                try:
+                    lnotab.addCode(self.opnum[opname], lo, hi)
+                except ValueError:
+                    print opname, oparg
+                    print self.opnum[opname], lo, hi
+                    raise
         self.stage = DONE
 
     opnum = {}
@@ -354,10 +354,10 @@ class PyFlowGraph(FlowGraph):
                 elt = elt.getCode()
             l.append(elt)
         return tuple(l)
-           
+            
 def isJump(opname):
     if opname[:4] == 'JUMP':
-       return 1
+        return 1
 
 class TupleArg:
     """Helper for marking func defs with nested tuples in arglist"""
@@ -372,10 +372,10 @@ class TupleArg:
 def getArgCount(args):
     argcount = len(args)
     if args:
-       for arg in args:
-           if isinstance(arg, TupleArg):
-               numNames = len(misc.flatten(arg.names))
-               argcount = argcount - numNames
+        for arg in args:
+            if isinstance(arg, TupleArg):
+                numNames = len(misc.flatten(arg.names))
+                argcount = argcount - numNames
     return argcount
 
 def twobyte(val):
@@ -513,11 +513,9 @@ class StackDepthTracker:
         ]
     
     # special cases:
-    # UNPACK_TUPLE, UNPACK_LIST, BUILD_TUPLE,
+    # UNPACK_SEQUENCE, BUILD_TUPLE,
     # BUILD_LIST, CALL_FUNCTION, MAKE_FUNCTION, BUILD_SLICE
-    def UNPACK_TUPLE(self, count):
-        return count
-    def UNPACK_LIST(self, count):
+    def UNPACK_SEQUENCE(self, count):
         return count
     def BUILD_TUPLE(self, count):
         return -count
index b3223c1c4a7735fea0a10e3303c28d5d90fa0609..9d9b9825518250faa126b365b95735f893401c1c 100644 (file)
@@ -7,7 +7,7 @@ from cStringIO import StringIO
 
 from compiler import ast, parse, walk
 from compiler import pyassem, misc
-from compiler.pyassem import CO_VARARGS, CO_VARKEYWORDS, TupleArg
+from compiler.pyassem import CO_VARARGS, CO_VARKEYWORDS, CO_NEWLOCALS, TupleArg
 
 callfunc_opcode_info = {
     # (Have *args, Have **args) : opcode
@@ -29,32 +29,32 @@ def compile(filename):
 
 class Module:
     def __init__(self, source, filename):
-       self.filename = filename
-       self.source = source
-       self.code = None
+        self.filename = filename
+        self.source = source
+        self.code = None
 
     def compile(self):
-       ast = parse(self.source)
+        ast = parse(self.source)
         root, filename = os.path.split(self.filename)
-       gen = ModuleCodeGenerator(filename)
-       walk(ast, gen, 1)
-       self.code = gen.getCode()
+        gen = ModuleCodeGenerator(filename)
+        walk(ast, gen, 1)
+        self.code = gen.getCode()
 
     def dump(self, f):
-       f.write(self.getPycHeader())
-       marshal.dump(self.code, f)
+        f.write(self.getPycHeader())
+        marshal.dump(self.code, f)
 
-    MAGIC = (20121 | (ord('\r')<<16) | (ord('\n')<<24))
+    MAGIC = (50811 | (ord('\r')<<16) | (ord('\n')<<24))
 
     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.
-       magic = marshal.dumps(self.MAGIC)[1:]
-       mtime = os.stat(self.filename)[stat.ST_MTIME]
-       mtime = struct.pack('i', mtime)
-       return magic + mtime
+        # 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.
+        magic = marshal.dumps(self.MAGIC)[1:]
+        mtime = os.stat(self.filename)[stat.ST_MTIME]
+        mtime = struct.pack('i', mtime)
+        return magic + mtime
 
 class CodeGenerator:
 
@@ -63,24 +63,24 @@ class CodeGenerator:
     def __init__(self, filename):
 ## Subclasses must define a constructor that intializes self.graph
 ## before calling this init function
-##     self.graph = pyassem.PyFlowGraph()
-       self.filename = filename
-       self.locals = misc.Stack()
-       self.loops = misc.Stack()
-       self.curStack = 0
-       self.maxStack = 0
-       self._setupGraphDelegation()
+##         self.graph = pyassem.PyFlowGraph()
+        self.filename = filename
+        self.locals = misc.Stack()
+        self.loops = misc.Stack()
+        self.curStack = 0
+        self.maxStack = 0
+        self._setupGraphDelegation()
 
     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
+        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()
+        """Return a code object"""
+        return self.graph.getCode()
 
     # Next five methods handle name access
 
@@ -97,11 +97,11 @@ class CodeGenerator:
         self._nameOp('DELETE', name)
 
     def _nameOp(self, prefix, name):
-       if not self.optimized:
-           self.emit(prefix + '_NAME', name)
-           return
+        if not self.optimized:
+            self.emit(prefix + '_NAME', name)
+            return
         if self.isLocalName(name):
-           self.emit(prefix + '_FAST', name)
+            self.emit(prefix + '_FAST', name)
         else:
             self.emit(prefix + '_GLOBAL', name)
 
@@ -125,25 +125,25 @@ class CodeGenerator:
     # code objects 
 
     def visitModule(self, node):
-       lnf = walk(node.node, LocalNameFinder(), 0)
-       self.locals.push(lnf.getLocals())
-       self.setDocstring(node.doc)
-       self.visit(node.node)
-       self.emit('LOAD_CONST', None)
-       self.emit('RETURN_VALUE')
+        lnf = walk(node.node, LocalNameFinder(), 0)
+        self.locals.push(lnf.getLocals())
+        self.setDocstring(node.doc)
+        self.visit(node.node)
+        self.emit('LOAD_CONST', None)
+        self.emit('RETURN_VALUE')
 
     def visitFunction(self, node):
-       self._visitFuncOrLambda(node, isLambda=0)
-       self.storeName(node.name)
+        self._visitFuncOrLambda(node, isLambda=0)
+        self.storeName(node.name)
 
     def visitLambda(self, node):
-       self._visitFuncOrLambda(node, isLambda=1)
-##     self.storeName("<lambda>")
+        self._visitFuncOrLambda(node, isLambda=1)
+##        self.storeName("<lambda>")
 
     def _visitFuncOrLambda(self, node, isLambda):
-       gen = FunctionCodeGenerator(node, self.filename, isLambda)
-       walk(node.code, gen)
-       gen.finish()
+        gen = FunctionCodeGenerator(node, self.filename, isLambda)
+        walk(node.code, gen)
+        gen.finish()
         self.set_lineno(node)
         for default in node.defaults:
             self.visit(default)
@@ -170,158 +170,158 @@ class CodeGenerator:
     # 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]
+        end = self.newBlock()
+        numtests = len(node.tests)
+        for i in range(numtests):
+            test, suite = node.tests[i]
             self.set_lineno(test)
-           self.visit(test)
-##         if i == numtests - 1 and not node.else_:
-##             nextTest = end
-##         else:
-##             nextTest = self.newBlock()
-           nextTest = self.newBlock()
-           self.emit('JUMP_IF_FALSE', nextTest)
-           self.nextBlock()
-           self.emit('POP_TOP')
-           self.visit(suite)
-           self.emit('JUMP_FORWARD', end)
-           self.nextBlock(nextTest)
-           self.emit('POP_TOP')
-       if node.else_:
-           self.visit(node.else_)
-       self.nextBlock(end)
+            self.visit(test)
+##            if i == numtests - 1 and not node.else_:
+##                nextTest = end
+##            else:
+##                nextTest = self.newBlock()
+            nextTest = self.newBlock()
+            self.emit('JUMP_IF_FALSE', nextTest)
+            self.nextBlock()
+            self.emit('POP_TOP')
+            self.visit(suite)
+            self.emit('JUMP_FORWARD', end)
+            self.nextBlock(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()
+        loop = self.newBlock()
+        else_ = self.newBlock()
 
-       after = self.newBlock()
-       self.emit('SETUP_LOOP', after)
+        after = self.newBlock()
+        self.emit('SETUP_LOOP', after)
 
-       self.nextBlock(loop)
-       self.loops.push(loop)
+        self.nextBlock(loop)
+        self.loops.push(loop)
 
         self.set_lineno(node)
-       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')
-       if node.else_:
-           self.visit(node.else_)
-       self.loops.pop()
-       self.nextBlock(after)
+        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')
+        if node.else_:
+            self.visit(node.else_)
+        self.loops.pop()
+        self.nextBlock(after)
 
     def visitFor(self, node):
-       start = self.newBlock()
+        start = self.newBlock()
         anchor = self.newBlock()
-       after = self.newBlock()
+        after = self.newBlock()
         self.loops.push(start)
 
         self.set_lineno(node)
-       self.emit('SETUP_LOOP', after)
+        self.emit('SETUP_LOOP', after)
         self.visit(node.list)
         self.visit(ast.Const(0))
-       self.nextBlock(start)
+        self.nextBlock(start)
         self.set_lineno(node)
         self.emit('FOR_LOOP', anchor)
         self.visit(node.assign)
         self.visit(node.body)
         self.emit('JUMP_ABSOLUTE', start)
-       self.nextBlock(anchor)
+        self.nextBlock(anchor)
         self.emit('POP_BLOCK')
         if node.else_:
             self.visit(node.else_)
-       self.loops.pop()
-       self.nextBlock(after)
+        self.loops.pop()
+        self.nextBlock(after)
 
     def visitBreak(self, node):
-       if not self.loops:
-           raise SyntaxError, "'break' outside loop (%s, %d)" % \
-                 (self.filename, node.lineno)
+        if not self.loops:
+            raise SyntaxError, "'break' outside loop (%s, %d)" % \
+                  (self.filename, node.lineno)
         self.set_lineno(node)
-       self.emit('BREAK_LOOP')
+        self.emit('BREAK_LOOP')
 
     def visitContinue(self, node):
         if not self.loops:
             raise SyntaxError, "'continue' outside loop (%s, %d)" % \
-                 (self.filename, node.lineno)
+                  (self.filename, node.lineno)
         l = self.loops.top()
         self.set_lineno(node)
         self.emit('JUMP_ABSOLUTE', l)
-       self.nextBlock()
+        self.nextBlock()
 
     def visitTest(self, node, jump):
-       end = self.newBlock()
+        end = self.newBlock()
         for child in node.nodes[:-1]:
             self.visit(child)
             self.emit(jump, end)
-           self.nextBlock()
+            self.nextBlock()
             self.emit('POP_TOP')
         self.visit(node.nodes[-1])
-       self.nextBlock(end)
+        self.nextBlock(end)
 
     def visitAnd(self, node):
-       self.visitTest(node, 'JUMP_IF_FALSE')
+        self.visitTest(node, 'JUMP_IF_FALSE')
 
     def visitOr(self, node):
-       self.visitTest(node, 'JUMP_IF_TRUE')
+        self.visitTest(node, 'JUMP_IF_TRUE')
 
     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.nextBlock(cleanup)
-           self.emit('ROT_TWO')
-           self.emit('POP_TOP')
-           self.nextBlock(end)
+        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.nextBlock(cleanup)
+            self.emit('ROT_TWO')
+            self.emit('POP_TOP')
+            self.nextBlock(end)
 
     # exception related
 
     def visitAssert(self, node):
-       # XXX would be interesting to implement this via a
-       # transformation of the AST before this stage
-       end = self.newBlock()
+        # XXX would be interesting to implement this via a
+        # transformation of the AST before this stage
+        end = self.newBlock()
         self.set_lineno(node)
         # XXX __debug__ and AssertionError appear to be special cases
         # -- they are always loaded as globals even if there are local
         # names.  I guess this is a sort of renaming op.
-       self.emit('LOAD_GLOBAL', '__debug__')
-       self.emit('JUMP_IF_FALSE', end)
-       self.nextBlock()
-       self.emit('POP_TOP')
-       self.visit(node.test)
-       self.emit('JUMP_IF_TRUE', end)
-       self.nextBlock()
-       self.emit('LOAD_GLOBAL', 'AssertionError')
-       self.visit(node.fail)
-       self.emit('RAISE_VARARGS', 2)
-       self.nextBlock(end)
-       self.emit('POP_TOP')
+        self.emit('LOAD_GLOBAL', '__debug__')
+        self.emit('JUMP_IF_FALSE', end)
+        self.nextBlock()
+        self.emit('POP_TOP')
+        self.visit(node.test)
+        self.emit('JUMP_IF_TRUE', end)
+        self.nextBlock()
+        self.emit('LOAD_GLOBAL', 'AssertionError')
+        self.visit(node.fail)
+        self.emit('RAISE_VARARGS', 2)
+        self.nextBlock(end)
+        self.emit('POP_TOP')
 
     def visitRaise(self, node):
         self.set_lineno(node)
@@ -349,7 +349,7 @@ class CodeGenerator:
         self.visit(node.body)
         self.emit('POP_BLOCK')
         self.emit('JUMP_FORWARD', lElse)
-       self.nextBlock(handlers)
+        self.nextBlock(handlers)
         
         last = len(node.handlers) - 1
         for i in range(len(node.handlers)):
@@ -361,7 +361,7 @@ class CodeGenerator:
                 self.emit('COMPARE_OP', 'exception match')
                 next = self.newBlock()
                 self.emit('JUMP_IF_FALSE', next)
-               self.nextBlock()
+                self.nextBlock()
                 self.emit('POP_TOP')
             self.emit('POP_TOP')
             if target:
@@ -372,13 +372,13 @@ class CodeGenerator:
             self.visit(body)
             self.emit('JUMP_FORWARD', end)
             if expr:
-               self.nextBlock(next)
+                self.nextBlock(next)
             self.emit('POP_TOP')
         self.emit('END_FINALLY')
         if node.else_:
-           self.nextBlock(lElse)
+            self.nextBlock(lElse)
             self.visit(node.else_)
-       self.nextBlock(end)
+        self.nextBlock(end)
     
     def visitTryFinally(self, node):
         final = self.newBlock()
@@ -387,15 +387,15 @@ class CodeGenerator:
         self.visit(node.body)
         self.emit('POP_BLOCK')
         self.emit('LOAD_CONST', None)
-       self.nextBlock(final)
+        self.nextBlock(final)
         self.visit(node.final)
         self.emit('END_FINALLY')
 
     # misc
 
 ##     def visitStmt(self, node):
-##     # nothing to do except walk the children
-##     pass
+##         # nothing to do except walk the children
+##         pass
 
     def visitDiscard(self, node):
         self.visit(node.expr)
@@ -405,12 +405,12 @@ class CodeGenerator:
         self.emit('LOAD_CONST', node.value)
 
     def visitKeyword(self, node):
-       self.emit('LOAD_CONST', node.name)
-       self.visit(node.expr)
+        self.emit('LOAD_CONST', node.name)
+        self.visit(node.expr)
 
     def visitGlobal(self, node):
         # no code to generate
-       pass
+        pass
 
     def visitName(self, node):
         self.loadName(node.name)
@@ -470,7 +470,7 @@ class CodeGenerator:
 
     def visitAssTuple(self, node):
         if findOp(node) != 'OP_DELETE':
-            self.emit('UNPACK_TUPLE', len(node.nodes))
+            self.emit('UNPACK_SEQUENCE', len(node.nodes))
         for child in node.nodes:
             self.visit(child)
 
@@ -655,10 +655,10 @@ class CodeGenerator:
             set.emit('SET_LINENO', lineno)
         self.emit('BUILD_MAP', 0)
         for k, v in node.items:
-           lineno2 = getattr(node, 'lineno', None)
+            lineno2 = getattr(node, 'lineno', None)
             if lineno2 is not None and lineno != lineno2:
-               self.emit('SET_LINENO', lineno2)
-               lineno = lineno2
+                self.emit('SET_LINENO', lineno2)
+                lineno = lineno2
             self.emit('DUP_TOP')
             self.visit(v)
             self.emit('ROT_TWO')
@@ -669,9 +669,9 @@ class ModuleCodeGenerator(CodeGenerator):
     super_init = CodeGenerator.__init__
     
     def __init__(self, filename):
-       # XXX <module> is ? in compile.c
-       self.graph = pyassem.PyFlowGraph("<module>", filename)
-       self.super_init(filename)
+        # XXX <module> is ? in compile.c
+        self.graph = pyassem.PyFlowGraph("<module>", filename)
+        self.super_init(filename)
 
 class FunctionCodeGenerator(CodeGenerator):
     super_init = CodeGenerator.__init__
@@ -686,27 +686,27 @@ class FunctionCodeGenerator(CodeGenerator):
             klass.lambdaCount = klass.lambdaCount + 1
         else:
             name = func.name
-       args, hasTupleArg = generateArgList(func.argnames)
-       self.graph = pyassem.PyFlowGraph(name, filename, args, 
-                                          optimized=1) 
-       self.isLambda = isLambda
-       self.super_init(filename)
+        args, hasTupleArg = generateArgList(func.argnames)
+        self.graph = pyassem.PyFlowGraph(name, filename, args, 
+                                           optimized=1) 
+        self.isLambda = isLambda
+        self.super_init(filename)
 
         lnf = walk(func.code, LocalNameFinder(args), 0)
         self.locals.push(lnf.getLocals())
-       if func.varargs:
-           self.graph.setFlag(CO_VARARGS)
-       if func.kwargs:
-           self.graph.setFlag(CO_VARKEYWORDS)
+        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 finish(self):
-       self.graph.startExitBlock()
-       if not self.isLambda:
-           self.emit('LOAD_CONST', None)
-       self.emit('RETURN_VALUE')
+        self.graph.startExitBlock()
+        if not self.isLambda:
+            self.emit('LOAD_CONST', None)
+        self.emit('RETURN_VALUE')
 
     def generateArgUnpack(self, args):
         count = 0
@@ -714,30 +714,33 @@ class FunctionCodeGenerator(CodeGenerator):
             if type(arg) == types.TupleType:
                 self.emit('LOAD_FAST', '.nested%d' % count)
                 count = count + 1
-                self.unpackTuple(arg)
+                self.unpackSequence(arg)
                         
-    def unpackTuple(self, tup):
-        self.emit('UNPACK_TUPLE', len(tup))
+    def unpackSequence(self, tup):
+        self.emit('UNPACK_SEQUENCE', len(tup))
         for elt in tup:
             if type(elt) == types.TupleType:
-                self.unpackTuple(elt)
+                self.unpackSequence(elt)
             else:
                 self.emit('STORE_FAST', elt)
 
+    unpackTuple = unpackSequence
+
 class ClassCodeGenerator(CodeGenerator):
     super_init = CodeGenerator.__init__
 
     def __init__(self, klass, filename):
-       self.graph = pyassem.PyFlowGraph(klass.name, filename,
-                                          optimized=0)
+        self.graph = pyassem.PyFlowGraph(klass.name, filename,
+                                           optimized=0)
         self.super_init(filename)
         lnf = walk(klass.code, LocalNameFinder(), 0)
         self.locals.push(lnf.getLocals())
+        self.graph.setFlag(CO_NEWLOCALS)
 
     def finish(self):
-       self.graph.startExitBlock()
+        self.graph.startExitBlock()
         self.emit('LOAD_LOCALS')
-       self.emit('RETURN_VALUE')
+        self.emit('RETURN_VALUE')
 
 
 def generateArgList(arglist):
@@ -746,14 +749,14 @@ def generateArgList(arglist):
     extra = []
     count = 0
     for elt in arglist:
-       if type(elt) == types.StringType:
-           args.append(elt)
-       elif type(elt) == types.TupleType:
-           args.append(TupleArg(count, elt))
-           count = count + 1
-           extra.extend(misc.flatten(elt))
-       else:
-           raise ValueError, "unexpect argument type:", elt
+        if type(elt) == types.StringType:
+            args.append(elt)
+        elif type(elt) == types.TupleType:
+            args.append(TupleArg(count, elt))
+            count = count + 1
+            extra.extend(misc.flatten(elt))
+        else:
+            raise ValueError, "unexpect argument type:", elt
     return args + extra, count
 
 class LocalNameFinder:
@@ -771,7 +774,7 @@ class LocalNameFinder:
         return self.names
 
     def visitDict(self, node):
-       pass
+        pass
 
     def visitGlobal(self, node):
         for name in node.names:
@@ -781,7 +784,7 @@ class LocalNameFinder:
         self.names.add(node.name)
 
     def visitLambda(self, node):
-       pass
+        pass
 
     def visitImport(self, node):
         for name in node.names:
@@ -816,4 +819,4 @@ if __name__ == "__main__":
     import sys
 
     for file in sys.argv[1:]:
-       compile(file)
+        compile(file)
index d0e522375c10de81336230896d4ada6248ebb48a..32e72e1bd9ba3151eb9a07d58408a4b215810df4 100644 (file)
@@ -38,7 +38,7 @@ class ASTVisitor:
 
     def __init__(self):
         self.node = None
-       self._cache = {}
+        self._cache = {}
 
     def preorder(self, tree, visitor):
         """Do preorder walk of tree using visitor"""
@@ -47,7 +47,7 @@ class ASTVisitor:
         self._preorder(tree)
 
     def _preorder(self, node, *args):
-       return apply(self.dispatch, (node,) + args)
+        return apply(self.dispatch, (node,) + args)
 
     def default(self, node, *args):
         for child in node.getChildren():
@@ -56,18 +56,18 @@ class ASTVisitor:
 
     def dispatch(self, node, *args):
         self.node = node
-       meth = self._cache.get(node.__class__, None)
-       className = node.__class__.__name__
-       if meth is None:
-           meth = getattr(self.visitor, 'visit' + className, self.default)
-           self._cache[node.__class__] = meth
+        meth = self._cache.get(node.__class__, None)
+        className = node.__class__.__name__
+        if meth is None:
+            meth = getattr(self.visitor, 'visit' + className, self.default)
+            self._cache[node.__class__] = meth
         if self.VERBOSE > 0:
             if self.VERBOSE == 1:
                 if meth == 0:
                     print "dispatch", className
             else:
                 print "dispatch", className, (meth and meth.__name__ or '')
-       return apply(meth, (node,) + args)
+        return apply(meth, (node,) + args)
 
 class ExampleASTVisitor(ASTVisitor):
     """Prints examples of the nodes that aren't visited
@@ -80,11 +80,11 @@ class ExampleASTVisitor(ASTVisitor):
     
     def dispatch(self, node, *args):
         self.node = node
-       meth = self._cache.get(node.__class__, None)
-       className = node.__class__.__name__
-       if meth is None:
-           meth = getattr(self.visitor, 'visit' + className, 0)
-           self._cache[node.__class__] = meth
+        meth = self._cache.get(node.__class__, None)
+        className = node.__class__.__name__
+        if meth is None:
+            meth = getattr(self.visitor, 'visit' + className, 0)
+            self._cache[node.__class__] = meth
         if self.VERBOSE > 1:
             print "dispatch", className, (meth and meth.__name__ or '')
         if meth:
@@ -92,15 +92,15 @@ class ExampleASTVisitor(ASTVisitor):
         elif self.VERBOSE > 0:
             klass = node.__class__
             if not self.examples.has_key(klass):
-               self.examples[klass] = klass
-               print
-               print self.visitor
-               print klass
-               for attr in dir(node):
-                   if attr[0] != '_':
-                       print "\t", "%-12.12s" % attr, getattr(node, attr)
-               print
-           return apply(self.default, (node,) + args)
+                self.examples[klass] = klass
+                print
+                print self.visitor
+                print klass
+                for attr in dir(node):
+                    if attr[0] != '_':
+                        print "\t", "%-12.12s" % attr, getattr(node, attr)
+                print
+            return apply(self.default, (node,) + args)
 
 _walker = ASTVisitor
 def walk(tree, visitor, verbose=None):
index 6e079870a2fb96114c98cc4cc029dc29c752564f..c8d9e90cc87d5a639fd948065157dd55804f3661 100644 (file)
@@ -9,71 +9,71 @@ from compiler import misc
 
 class FlowGraph:
     def __init__(self):
-       self.current = self.entry = Block()
-       self.exit = Block("exit")
-       self.blocks = misc.Set()
-       self.blocks.add(self.entry)
-       self.blocks.add(self.exit)
+        self.current = self.entry = Block()
+        self.exit = Block("exit")
+        self.blocks = misc.Set()
+        self.blocks.add(self.entry)
+        self.blocks.add(self.exit)
 
     def startBlock(self, block):
-       self.current = block
+        self.current = block
 
     def nextBlock(self, block=None):
-       if block is None:
-           block = self.newBlock()
-       # XXX think we need to specify when there is implicit transfer
-       # from one block to the next
-       #
-       # I think this strategy works: each block has a child
-       # designated as "next" which is returned as the last of the
-       # children.  because the nodes in a graph are emitted in
-       # reverse post order, the "next" block will always be emitted
-       # immediately after its parent.
-       # Worry: maintaining this invariant could be tricky
-       self.current.addNext(block)
-       self.startBlock(block)
+        if block is None:
+            block = self.newBlock()
+        # XXX think we need to specify when there is implicit transfer
+        # from one block to the next
+        #
+        # I think this strategy works: each block has a child
+        # designated as "next" which is returned as the last of the
+        # children.  because the nodes in a graph are emitted in
+        # reverse post order, the "next" block will always be emitted
+        # immediately after its parent.
+        # Worry: maintaining this invariant could be tricky
+        self.current.addNext(block)
+        self.startBlock(block)
 
     def newBlock(self):
-       b = Block()
-       self.blocks.add(b)
-       return b
+        b = Block()
+        self.blocks.add(b)
+        return b
 
     def startExitBlock(self):
-       self.startBlock(self.exit)
+        self.startBlock(self.exit)
 
     def emit(self, *inst):
-       # XXX should jump instructions implicitly call nextBlock?
-       if inst[0] == 'RETURN_VALUE':
-           self.current.addOutEdge(self.exit)
-       self.current.emit(inst)
+        # XXX should jump instructions implicitly call nextBlock?
+        if inst[0] == 'RETURN_VALUE':
+            self.current.addOutEdge(self.exit)
+        self.current.emit(inst)
 
     def getBlocks(self):
-       """Return the blocks in reverse postorder
-
-       i.e. each node appears before all of its successors
-       """
-       # XXX make sure every node that doesn't have an explicit next
-       # is set so that next points to exit
-       for b in self.blocks.elements():
-           if b is self.exit:
-               continue
-           if not b.next:
-               b.addNext(self.exit)
-       order = dfs_postorder(self.entry, {})
-       order.reverse()
-       # hack alert
-       if not self.exit in order:
-           order.append(self.exit)
-       return order
+        """Return the blocks in reverse postorder
+
+        i.e. each node appears before all of its successors
+        """
+        # XXX make sure every node that doesn't have an explicit next
+        # is set so that next points to exit
+        for b in self.blocks.elements():
+            if b is self.exit:
+                continue
+            if not b.next:
+                b.addNext(self.exit)
+        order = dfs_postorder(self.entry, {})
+        order.reverse()
+        # hack alert
+        if not self.exit in order:
+            order.append(self.exit)
+        return order
 
 def dfs_postorder(b, seen):
     """Depth-first search of tree rooted at b, return in postorder"""
     order = []
     seen[b] = b
     for c in b.children():
-       if seen.has_key(c):
-           continue
-       order = order + dfs_postorder(c, seen)
+        if seen.has_key(c):
+            continue
+        order = order + dfs_postorder(c, seen)
     order.append(b)
     return order
 
@@ -81,47 +81,47 @@ class Block:
     _count = 0
 
     def __init__(self, label=''):
-       self.insts = []
-       self.inEdges = misc.Set()
-       self.outEdges = misc.Set()
-       self.label = label
-       self.bid = Block._count
-       self.next = []
-       Block._count = Block._count + 1
+        self.insts = []
+        self.inEdges = misc.Set()
+        self.outEdges = misc.Set()
+        self.label = label
+        self.bid = Block._count
+        self.next = []
+        Block._count = Block._count + 1
 
     def __repr__(self):
-       if self.label:
-           return "<block %s id=%d len=%d>" % (self.label, self.bid,
-                                               len(self.insts)) 
-       else:
-           return "<block id=%d len=%d>" % (self.bid, len(self.insts))
+        if self.label:
+            return "<block %s id=%d len=%d>" % (self.label, self.bid,
+                                                len(self.insts)) 
+        else:
+            return "<block id=%d len=%d>" % (self.bid, len(self.insts))
 
     def __str__(self):
-       insts = map(str, self.insts)
-       return "<block %s %d:\n%s>" % (self.label, self.bid,
-                                      string.join(insts, '\n')) 
+        insts = map(str, self.insts)
+        return "<block %s %d:\n%s>" % (self.label, self.bid,
+                                       string.join(insts, '\n')) 
 
     def emit(self, inst):
-       op = inst[0]
-       if op[:4] == 'JUMP':
-           self.outEdges.add(inst[1])
-       self.insts.append(inst)
+        op = inst[0]
+        if op[:4] == 'JUMP':
+            self.outEdges.add(inst[1])
+        self.insts.append(inst)
 
     def getInstructions(self):
-       return self.insts
+        return self.insts
 
     def addInEdge(self, block):
-       self.inEdges.add(block)
+        self.inEdges.add(block)
 
     def addOutEdge(self, block):
-       self.outEdges.add(block)
+        self.outEdges.add(block)
 
     def addNext(self, block):
-       self.next.append(block)
-       assert len(self.next) == 1, map(str, self.next)
+        self.next.append(block)
+        assert len(self.next) == 1, map(str, self.next)
 
     def children(self):
-       return self.outEdges.elements() + self.next
+        return self.outEdges.elements() + self.next
 
 # flags for code objects
 CO_OPTIMIZED = 0x0001
@@ -139,18 +139,18 @@ class PyFlowGraph(FlowGraph):
     super_init = FlowGraph.__init__
 
     def __init__(self, name, filename, args=(), optimized=0):
-       self.super_init()
-       self.name = name
-       self.filename = filename
-       self.docstring = None
-       self.args = args # XXX
-       self.argcount = getArgCount(args)
-       if optimized:
-           self.flags = CO_OPTIMIZED | CO_NEWLOCALS 
-       else:
-           self.flags = 0
-       self.consts = []
-       self.names = []
+        self.super_init()
+        self.name = name
+        self.filename = filename
+        self.docstring = None
+        self.args = args # XXX
+        self.argcount = getArgCount(args)
+        if optimized:
+            self.flags = CO_OPTIMIZED | CO_NEWLOCALS 
+        else:
+            self.flags = 0
+        self.consts = []
+        self.names = []
         self.varnames = list(args) or []
         for i in range(len(self.varnames)):
             var = self.varnames[i]
@@ -163,13 +163,13 @@ class PyFlowGraph(FlowGraph):
         self.consts.insert(0, doc)
 
     def setFlag(self, flag):
-       self.flags = self.flags | flag
-       if flag == CO_VARARGS:
-           self.argcount = self.argcount - 1
+        self.flags = self.flags | flag
+        if flag == CO_VARARGS:
+            self.argcount = self.argcount - 1
 
     def getCode(self):
-       """Get a Python code object"""
-       if self.stage == RAW:
+        """Get a Python code object"""
+        if self.stage == RAW:
             self.flattenGraph()
         if self.stage == FLAT:
             self.convertArgs()
@@ -198,38 +198,38 @@ class PyFlowGraph(FlowGraph):
             sys.stdout = save
 
     def flattenGraph(self):
-       """Arrange the blocks in order and resolve jumps"""
-       assert self.stage == RAW
-       self.insts = insts = []
-       pc = 0
-       begin = {}
-       end = {}
-       for b in self.getBlocks():
-           begin[b] = pc
-           for inst in b.getInstructions():
-               insts.append(inst)
-               if len(inst) == 1:
-                   pc = pc + 1
-               else:
-                   # arg takes 2 bytes
-                   pc = pc + 3
-           end[b] = pc
-       pc = 0
-       for i in range(len(insts)):
-           inst = insts[i]
-           if len(inst) == 1:
+        """Arrange the blocks in order and resolve jumps"""
+        assert self.stage == RAW
+        self.insts = insts = []
+        pc = 0
+        begin = {}
+        end = {}
+        for b in self.getBlocks():
+            begin[b] = pc
+            for inst in b.getInstructions():
+                insts.append(inst)
+                if len(inst) == 1:
+                    pc = pc + 1
+                else:
+                    # arg takes 2 bytes
+                    pc = pc + 3
+            end[b] = pc
+        pc = 0
+        for i in range(len(insts)):
+            inst = insts[i]
+            if len(inst) == 1:
                 pc = pc + 1
             else:
                 pc = pc + 3
-           opname = inst[0]
-           if self.hasjrel.has_elt(opname):
+            opname = inst[0]
+            if self.hasjrel.has_elt(opname):
                 oparg = inst[1]
                 offset = begin[oparg] - pc
                 insts[i] = opname, offset
             elif self.hasjabs.has_elt(opname):
                 insts[i] = opname, begin[inst[1]]
-       self.stacksize = findDepth(self.insts)
-       self.stage = FLAT
+        self.stacksize = findDepth(self.insts)
+        self.stage = FLAT
 
     hasjrel = misc.Set()
     for i in dis.hasjrel:
@@ -292,7 +292,7 @@ class PyFlowGraph(FlowGraph):
 
     _cmp = list(dis.cmp_op)
     def _convert_COMPARE_OP(self, arg):
-       return self._cmp.index(arg)
+        return self._cmp.index(arg)
 
     # similarly for other opcodes...
 
@@ -314,12 +314,12 @@ class PyFlowGraph(FlowGraph):
                 if opname == "SET_LINENO":
                     lnotab.nextLine(oparg)
                 hi, lo = twobyte(oparg)
-               try:
-                   lnotab.addCode(self.opnum[opname], lo, hi)
-               except ValueError:
-                   print opname, oparg
-                   print self.opnum[opname], lo, hi
-                   raise
+                try:
+                    lnotab.addCode(self.opnum[opname], lo, hi)
+                except ValueError:
+                    print opname, oparg
+                    print self.opnum[opname], lo, hi
+                    raise
         self.stage = DONE
 
     opnum = {}
@@ -354,10 +354,10 @@ class PyFlowGraph(FlowGraph):
                 elt = elt.getCode()
             l.append(elt)
         return tuple(l)
-           
+            
 def isJump(opname):
     if opname[:4] == 'JUMP':
-       return 1
+        return 1
 
 class TupleArg:
     """Helper for marking func defs with nested tuples in arglist"""
@@ -372,10 +372,10 @@ class TupleArg:
 def getArgCount(args):
     argcount = len(args)
     if args:
-       for arg in args:
-           if isinstance(arg, TupleArg):
-               numNames = len(misc.flatten(arg.names))
-               argcount = argcount - numNames
+        for arg in args:
+            if isinstance(arg, TupleArg):
+                numNames = len(misc.flatten(arg.names))
+                argcount = argcount - numNames
     return argcount
 
 def twobyte(val):
@@ -513,11 +513,9 @@ class StackDepthTracker:
         ]
     
     # special cases:
-    # UNPACK_TUPLE, UNPACK_LIST, BUILD_TUPLE,
+    # UNPACK_SEQUENCE, BUILD_TUPLE,
     # BUILD_LIST, CALL_FUNCTION, MAKE_FUNCTION, BUILD_SLICE
-    def UNPACK_TUPLE(self, count):
-        return count
-    def UNPACK_LIST(self, count):
+    def UNPACK_SEQUENCE(self, count):
         return count
     def BUILD_TUPLE(self, count):
         return -count
index b3223c1c4a7735fea0a10e3303c28d5d90fa0609..9d9b9825518250faa126b365b95735f893401c1c 100644 (file)
@@ -7,7 +7,7 @@ from cStringIO import StringIO
 
 from compiler import ast, parse, walk
 from compiler import pyassem, misc
-from compiler.pyassem import CO_VARARGS, CO_VARKEYWORDS, TupleArg
+from compiler.pyassem import CO_VARARGS, CO_VARKEYWORDS, CO_NEWLOCALS, TupleArg
 
 callfunc_opcode_info = {
     # (Have *args, Have **args) : opcode
@@ -29,32 +29,32 @@ def compile(filename):
 
 class Module:
     def __init__(self, source, filename):
-       self.filename = filename
-       self.source = source
-       self.code = None
+        self.filename = filename
+        self.source = source
+        self.code = None
 
     def compile(self):
-       ast = parse(self.source)
+        ast = parse(self.source)
         root, filename = os.path.split(self.filename)
-       gen = ModuleCodeGenerator(filename)
-       walk(ast, gen, 1)
-       self.code = gen.getCode()
+        gen = ModuleCodeGenerator(filename)
+        walk(ast, gen, 1)
+        self.code = gen.getCode()
 
     def dump(self, f):
-       f.write(self.getPycHeader())
-       marshal.dump(self.code, f)
+        f.write(self.getPycHeader())
+        marshal.dump(self.code, f)
 
-    MAGIC = (20121 | (ord('\r')<<16) | (ord('\n')<<24))
+    MAGIC = (50811 | (ord('\r')<<16) | (ord('\n')<<24))
 
     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.
-       magic = marshal.dumps(self.MAGIC)[1:]
-       mtime = os.stat(self.filename)[stat.ST_MTIME]
-       mtime = struct.pack('i', mtime)
-       return magic + mtime
+        # 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.
+        magic = marshal.dumps(self.MAGIC)[1:]
+        mtime = os.stat(self.filename)[stat.ST_MTIME]
+        mtime = struct.pack('i', mtime)
+        return magic + mtime
 
 class CodeGenerator:
 
@@ -63,24 +63,24 @@ class CodeGenerator:
     def __init__(self, filename):
 ## Subclasses must define a constructor that intializes self.graph
 ## before calling this init function
-##     self.graph = pyassem.PyFlowGraph()
-       self.filename = filename
-       self.locals = misc.Stack()
-       self.loops = misc.Stack()
-       self.curStack = 0
-       self.maxStack = 0
-       self._setupGraphDelegation()
+##         self.graph = pyassem.PyFlowGraph()
+        self.filename = filename
+        self.locals = misc.Stack()
+        self.loops = misc.Stack()
+        self.curStack = 0
+        self.maxStack = 0
+        self._setupGraphDelegation()
 
     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
+        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()
+        """Return a code object"""
+        return self.graph.getCode()
 
     # Next five methods handle name access
 
@@ -97,11 +97,11 @@ class CodeGenerator:
         self._nameOp('DELETE', name)
 
     def _nameOp(self, prefix, name):
-       if not self.optimized:
-           self.emit(prefix + '_NAME', name)
-           return
+        if not self.optimized:
+            self.emit(prefix + '_NAME', name)
+            return
         if self.isLocalName(name):
-           self.emit(prefix + '_FAST', name)
+            self.emit(prefix + '_FAST', name)
         else:
             self.emit(prefix + '_GLOBAL', name)
 
@@ -125,25 +125,25 @@ class CodeGenerator:
     # code objects 
 
     def visitModule(self, node):
-       lnf = walk(node.node, LocalNameFinder(), 0)
-       self.locals.push(lnf.getLocals())
-       self.setDocstring(node.doc)
-       self.visit(node.node)
-       self.emit('LOAD_CONST', None)
-       self.emit('RETURN_VALUE')
+        lnf = walk(node.node, LocalNameFinder(), 0)
+        self.locals.push(lnf.getLocals())
+        self.setDocstring(node.doc)
+        self.visit(node.node)
+        self.emit('LOAD_CONST', None)
+        self.emit('RETURN_VALUE')
 
     def visitFunction(self, node):
-       self._visitFuncOrLambda(node, isLambda=0)
-       self.storeName(node.name)
+        self._visitFuncOrLambda(node, isLambda=0)
+        self.storeName(node.name)
 
     def visitLambda(self, node):
-       self._visitFuncOrLambda(node, isLambda=1)
-##     self.storeName("<lambda>")
+        self._visitFuncOrLambda(node, isLambda=1)
+##        self.storeName("<lambda>")
 
     def _visitFuncOrLambda(self, node, isLambda):
-       gen = FunctionCodeGenerator(node, self.filename, isLambda)
-       walk(node.code, gen)
-       gen.finish()
+        gen = FunctionCodeGenerator(node, self.filename, isLambda)
+        walk(node.code, gen)
+        gen.finish()
         self.set_lineno(node)
         for default in node.defaults:
             self.visit(default)
@@ -170,158 +170,158 @@ class CodeGenerator:
     # 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]
+        end = self.newBlock()
+        numtests = len(node.tests)
+        for i in range(numtests):
+            test, suite = node.tests[i]
             self.set_lineno(test)
-           self.visit(test)
-##         if i == numtests - 1 and not node.else_:
-##             nextTest = end
-##         else:
-##             nextTest = self.newBlock()
-           nextTest = self.newBlock()
-           self.emit('JUMP_IF_FALSE', nextTest)
-           self.nextBlock()
-           self.emit('POP_TOP')
-           self.visit(suite)
-           self.emit('JUMP_FORWARD', end)
-           self.nextBlock(nextTest)
-           self.emit('POP_TOP')
-       if node.else_:
-           self.visit(node.else_)
-       self.nextBlock(end)
+            self.visit(test)
+##            if i == numtests - 1 and not node.else_:
+##                nextTest = end
+##            else:
+##                nextTest = self.newBlock()
+            nextTest = self.newBlock()
+            self.emit('JUMP_IF_FALSE', nextTest)
+            self.nextBlock()
+            self.emit('POP_TOP')
+            self.visit(suite)
+            self.emit('JUMP_FORWARD', end)
+            self.nextBlock(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()
+        loop = self.newBlock()
+        else_ = self.newBlock()
 
-       after = self.newBlock()
-       self.emit('SETUP_LOOP', after)
+        after = self.newBlock()
+        self.emit('SETUP_LOOP', after)
 
-       self.nextBlock(loop)
-       self.loops.push(loop)
+        self.nextBlock(loop)
+        self.loops.push(loop)
 
         self.set_lineno(node)
-       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')
-       if node.else_:
-           self.visit(node.else_)
-       self.loops.pop()
-       self.nextBlock(after)
+        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')
+        if node.else_:
+            self.visit(node.else_)
+        self.loops.pop()
+        self.nextBlock(after)
 
     def visitFor(self, node):
-       start = self.newBlock()
+        start = self.newBlock()
         anchor = self.newBlock()
-       after = self.newBlock()
+        after = self.newBlock()
         self.loops.push(start)
 
         self.set_lineno(node)
-       self.emit('SETUP_LOOP', after)
+        self.emit('SETUP_LOOP', after)
         self.visit(node.list)
         self.visit(ast.Const(0))
-       self.nextBlock(start)
+        self.nextBlock(start)
         self.set_lineno(node)
         self.emit('FOR_LOOP', anchor)
         self.visit(node.assign)
         self.visit(node.body)
         self.emit('JUMP_ABSOLUTE', start)
-       self.nextBlock(anchor)
+        self.nextBlock(anchor)
         self.emit('POP_BLOCK')
         if node.else_:
             self.visit(node.else_)
-       self.loops.pop()
-       self.nextBlock(after)
+        self.loops.pop()
+        self.nextBlock(after)
 
     def visitBreak(self, node):
-       if not self.loops:
-           raise SyntaxError, "'break' outside loop (%s, %d)" % \
-                 (self.filename, node.lineno)
+        if not self.loops:
+            raise SyntaxError, "'break' outside loop (%s, %d)" % \
+                  (self.filename, node.lineno)
         self.set_lineno(node)
-       self.emit('BREAK_LOOP')
+        self.emit('BREAK_LOOP')
 
     def visitContinue(self, node):
         if not self.loops:
             raise SyntaxError, "'continue' outside loop (%s, %d)" % \
-                 (self.filename, node.lineno)
+                  (self.filename, node.lineno)
         l = self.loops.top()
         self.set_lineno(node)
         self.emit('JUMP_ABSOLUTE', l)
-       self.nextBlock()
+        self.nextBlock()
 
     def visitTest(self, node, jump):
-       end = self.newBlock()
+        end = self.newBlock()
         for child in node.nodes[:-1]:
             self.visit(child)
             self.emit(jump, end)
-           self.nextBlock()
+            self.nextBlock()
             self.emit('POP_TOP')
         self.visit(node.nodes[-1])
-       self.nextBlock(end)
+        self.nextBlock(end)
 
     def visitAnd(self, node):
-       self.visitTest(node, 'JUMP_IF_FALSE')
+        self.visitTest(node, 'JUMP_IF_FALSE')
 
     def visitOr(self, node):
-       self.visitTest(node, 'JUMP_IF_TRUE')
+        self.visitTest(node, 'JUMP_IF_TRUE')
 
     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.nextBlock(cleanup)
-           self.emit('ROT_TWO')
-           self.emit('POP_TOP')
-           self.nextBlock(end)
+        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.nextBlock(cleanup)
+            self.emit('ROT_TWO')
+            self.emit('POP_TOP')
+            self.nextBlock(end)
 
     # exception related
 
     def visitAssert(self, node):
-       # XXX would be interesting to implement this via a
-       # transformation of the AST before this stage
-       end = self.newBlock()
+        # XXX would be interesting to implement this via a
+        # transformation of the AST before this stage
+        end = self.newBlock()
         self.set_lineno(node)
         # XXX __debug__ and AssertionError appear to be special cases
         # -- they are always loaded as globals even if there are local
         # names.  I guess this is a sort of renaming op.
-       self.emit('LOAD_GLOBAL', '__debug__')
-       self.emit('JUMP_IF_FALSE', end)
-       self.nextBlock()
-       self.emit('POP_TOP')
-       self.visit(node.test)
-       self.emit('JUMP_IF_TRUE', end)
-       self.nextBlock()
-       self.emit('LOAD_GLOBAL', 'AssertionError')
-       self.visit(node.fail)
-       self.emit('RAISE_VARARGS', 2)
-       self.nextBlock(end)
-       self.emit('POP_TOP')
+        self.emit('LOAD_GLOBAL', '__debug__')
+        self.emit('JUMP_IF_FALSE', end)
+        self.nextBlock()
+        self.emit('POP_TOP')
+        self.visit(node.test)
+        self.emit('JUMP_IF_TRUE', end)
+        self.nextBlock()
+        self.emit('LOAD_GLOBAL', 'AssertionError')
+        self.visit(node.fail)
+        self.emit('RAISE_VARARGS', 2)
+        self.nextBlock(end)
+        self.emit('POP_TOP')
 
     def visitRaise(self, node):
         self.set_lineno(node)
@@ -349,7 +349,7 @@ class CodeGenerator:
         self.visit(node.body)
         self.emit('POP_BLOCK')
         self.emit('JUMP_FORWARD', lElse)
-       self.nextBlock(handlers)
+        self.nextBlock(handlers)
         
         last = len(node.handlers) - 1
         for i in range(len(node.handlers)):
@@ -361,7 +361,7 @@ class CodeGenerator:
                 self.emit('COMPARE_OP', 'exception match')
                 next = self.newBlock()
                 self.emit('JUMP_IF_FALSE', next)
-               self.nextBlock()
+                self.nextBlock()
                 self.emit('POP_TOP')
             self.emit('POP_TOP')
             if target:
@@ -372,13 +372,13 @@ class CodeGenerator:
             self.visit(body)
             self.emit('JUMP_FORWARD', end)
             if expr:
-               self.nextBlock(next)
+                self.nextBlock(next)
             self.emit('POP_TOP')
         self.emit('END_FINALLY')
         if node.else_:
-           self.nextBlock(lElse)
+            self.nextBlock(lElse)
             self.visit(node.else_)
-       self.nextBlock(end)
+        self.nextBlock(end)
     
     def visitTryFinally(self, node):
         final = self.newBlock()
@@ -387,15 +387,15 @@ class CodeGenerator:
         self.visit(node.body)
         self.emit('POP_BLOCK')
         self.emit('LOAD_CONST', None)
-       self.nextBlock(final)
+        self.nextBlock(final)
         self.visit(node.final)
         self.emit('END_FINALLY')
 
     # misc
 
 ##     def visitStmt(self, node):
-##     # nothing to do except walk the children
-##     pass
+##         # nothing to do except walk the children
+##         pass
 
     def visitDiscard(self, node):
         self.visit(node.expr)
@@ -405,12 +405,12 @@ class CodeGenerator:
         self.emit('LOAD_CONST', node.value)
 
     def visitKeyword(self, node):
-       self.emit('LOAD_CONST', node.name)
-       self.visit(node.expr)
+        self.emit('LOAD_CONST', node.name)
+        self.visit(node.expr)
 
     def visitGlobal(self, node):
         # no code to generate
-       pass
+        pass
 
     def visitName(self, node):
         self.loadName(node.name)
@@ -470,7 +470,7 @@ class CodeGenerator:
 
     def visitAssTuple(self, node):
         if findOp(node) != 'OP_DELETE':
-            self.emit('UNPACK_TUPLE', len(node.nodes))
+            self.emit('UNPACK_SEQUENCE', len(node.nodes))
         for child in node.nodes:
             self.visit(child)
 
@@ -655,10 +655,10 @@ class CodeGenerator:
             set.emit('SET_LINENO', lineno)
         self.emit('BUILD_MAP', 0)
         for k, v in node.items:
-           lineno2 = getattr(node, 'lineno', None)
+            lineno2 = getattr(node, 'lineno', None)
             if lineno2 is not None and lineno != lineno2:
-               self.emit('SET_LINENO', lineno2)
-               lineno = lineno2
+                self.emit('SET_LINENO', lineno2)
+                lineno = lineno2
             self.emit('DUP_TOP')
             self.visit(v)
             self.emit('ROT_TWO')
@@ -669,9 +669,9 @@ class ModuleCodeGenerator(CodeGenerator):
     super_init = CodeGenerator.__init__
     
     def __init__(self, filename):
-       # XXX <module> is ? in compile.c
-       self.graph = pyassem.PyFlowGraph("<module>", filename)
-       self.super_init(filename)
+        # XXX <module> is ? in compile.c
+        self.graph = pyassem.PyFlowGraph("<module>", filename)
+        self.super_init(filename)
 
 class FunctionCodeGenerator(CodeGenerator):
     super_init = CodeGenerator.__init__
@@ -686,27 +686,27 @@ class FunctionCodeGenerator(CodeGenerator):
             klass.lambdaCount = klass.lambdaCount + 1
         else:
             name = func.name
-       args, hasTupleArg = generateArgList(func.argnames)
-       self.graph = pyassem.PyFlowGraph(name, filename, args, 
-                                          optimized=1) 
-       self.isLambda = isLambda
-       self.super_init(filename)
+        args, hasTupleArg = generateArgList(func.argnames)
+        self.graph = pyassem.PyFlowGraph(name, filename, args, 
+                                           optimized=1) 
+        self.isLambda = isLambda
+        self.super_init(filename)
 
         lnf = walk(func.code, LocalNameFinder(args), 0)
         self.locals.push(lnf.getLocals())
-       if func.varargs:
-           self.graph.setFlag(CO_VARARGS)
-       if func.kwargs:
-           self.graph.setFlag(CO_VARKEYWORDS)
+        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 finish(self):
-       self.graph.startExitBlock()
-       if not self.isLambda:
-           self.emit('LOAD_CONST', None)
-       self.emit('RETURN_VALUE')
+        self.graph.startExitBlock()
+        if not self.isLambda:
+            self.emit('LOAD_CONST', None)
+        self.emit('RETURN_VALUE')
 
     def generateArgUnpack(self, args):
         count = 0
@@ -714,30 +714,33 @@ class FunctionCodeGenerator(CodeGenerator):
             if type(arg) == types.TupleType:
                 self.emit('LOAD_FAST', '.nested%d' % count)
                 count = count + 1
-                self.unpackTuple(arg)
+                self.unpackSequence(arg)
                         
-    def unpackTuple(self, tup):
-        self.emit('UNPACK_TUPLE', len(tup))
+    def unpackSequence(self, tup):
+        self.emit('UNPACK_SEQUENCE', len(tup))
         for elt in tup:
             if type(elt) == types.TupleType:
-                self.unpackTuple(elt)
+                self.unpackSequence(elt)
             else:
                 self.emit('STORE_FAST', elt)
 
+    unpackTuple = unpackSequence
+
 class ClassCodeGenerator(CodeGenerator):
     super_init = CodeGenerator.__init__
 
     def __init__(self, klass, filename):
-       self.graph = pyassem.PyFlowGraph(klass.name, filename,
-                                          optimized=0)
+        self.graph = pyassem.PyFlowGraph(klass.name, filename,
+                                           optimized=0)
         self.super_init(filename)
         lnf = walk(klass.code, LocalNameFinder(), 0)
         self.locals.push(lnf.getLocals())
+        self.graph.setFlag(CO_NEWLOCALS)
 
     def finish(self):
-       self.graph.startExitBlock()
+        self.graph.startExitBlock()
         self.emit('LOAD_LOCALS')
-       self.emit('RETURN_VALUE')
+        self.emit('RETURN_VALUE')
 
 
 def generateArgList(arglist):
@@ -746,14 +749,14 @@ def generateArgList(arglist):
     extra = []
     count = 0
     for elt in arglist:
-       if type(elt) == types.StringType:
-           args.append(elt)
-       elif type(elt) == types.TupleType:
-           args.append(TupleArg(count, elt))
-           count = count + 1
-           extra.extend(misc.flatten(elt))
-       else:
-           raise ValueError, "unexpect argument type:", elt
+        if type(elt) == types.StringType:
+            args.append(elt)
+        elif type(elt) == types.TupleType:
+            args.append(TupleArg(count, elt))
+            count = count + 1
+            extra.extend(misc.flatten(elt))
+        else:
+            raise ValueError, "unexpect argument type:", elt
     return args + extra, count
 
 class LocalNameFinder:
@@ -771,7 +774,7 @@ class LocalNameFinder:
         return self.names
 
     def visitDict(self, node):
-       pass
+        pass
 
     def visitGlobal(self, node):
         for name in node.names:
@@ -781,7 +784,7 @@ class LocalNameFinder:
         self.names.add(node.name)
 
     def visitLambda(self, node):
-       pass
+        pass
 
     def visitImport(self, node):
         for name in node.names:
@@ -816,4 +819,4 @@ if __name__ == "__main__":
     import sys
 
     for file in sys.argv[1:]:
-       compile(file)
+        compile(file)
index d0e522375c10de81336230896d4ada6248ebb48a..32e72e1bd9ba3151eb9a07d58408a4b215810df4 100644 (file)
@@ -38,7 +38,7 @@ class ASTVisitor:
 
     def __init__(self):
         self.node = None
-       self._cache = {}
+        self._cache = {}
 
     def preorder(self, tree, visitor):
         """Do preorder walk of tree using visitor"""
@@ -47,7 +47,7 @@ class ASTVisitor:
         self._preorder(tree)
 
     def _preorder(self, node, *args):
-       return apply(self.dispatch, (node,) + args)
+        return apply(self.dispatch, (node,) + args)
 
     def default(self, node, *args):
         for child in node.getChildren():
@@ -56,18 +56,18 @@ class ASTVisitor:
 
     def dispatch(self, node, *args):
         self.node = node
-       meth = self._cache.get(node.__class__, None)
-       className = node.__class__.__name__
-       if meth is None:
-           meth = getattr(self.visitor, 'visit' + className, self.default)
-           self._cache[node.__class__] = meth
+        meth = self._cache.get(node.__class__, None)
+        className = node.__class__.__name__
+        if meth is None:
+            meth = getattr(self.visitor, 'visit' + className, self.default)
+            self._cache[node.__class__] = meth
         if self.VERBOSE > 0:
             if self.VERBOSE == 1:
                 if meth == 0:
                     print "dispatch", className
             else:
                 print "dispatch", className, (meth and meth.__name__ or '')
-       return apply(meth, (node,) + args)
+        return apply(meth, (node,) + args)
 
 class ExampleASTVisitor(ASTVisitor):
     """Prints examples of the nodes that aren't visited
@@ -80,11 +80,11 @@ class ExampleASTVisitor(ASTVisitor):
     
     def dispatch(self, node, *args):
         self.node = node
-       meth = self._cache.get(node.__class__, None)
-       className = node.__class__.__name__
-       if meth is None:
-           meth = getattr(self.visitor, 'visit' + className, 0)
-           self._cache[node.__class__] = meth
+        meth = self._cache.get(node.__class__, None)
+        className = node.__class__.__name__
+        if meth is None:
+            meth = getattr(self.visitor, 'visit' + className, 0)
+            self._cache[node.__class__] = meth
         if self.VERBOSE > 1:
             print "dispatch", className, (meth and meth.__name__ or '')
         if meth:
@@ -92,15 +92,15 @@ class ExampleASTVisitor(ASTVisitor):
         elif self.VERBOSE > 0:
             klass = node.__class__
             if not self.examples.has_key(klass):
-               self.examples[klass] = klass
-               print
-               print self.visitor
-               print klass
-               for attr in dir(node):
-                   if attr[0] != '_':
-                       print "\t", "%-12.12s" % attr, getattr(node, attr)
-               print
-           return apply(self.default, (node,) + args)
+                self.examples[klass] = klass
+                print
+                print self.visitor
+                print klass
+                for attr in dir(node):
+                    if attr[0] != '_':
+                        print "\t", "%-12.12s" % attr, getattr(node, attr)
+                print
+            return apply(self.default, (node,) + args)
 
 _walker = ASTVisitor
 def walk(tree, visitor, verbose=None):