diff options
author | Thomas Wouters <thomas@python.org> | 2000-08-12 20:32:46 (GMT) |
---|---|---|
committer | Thomas Wouters <thomas@python.org> | 2000-08-12 20:32:46 (GMT) |
commit | 46cc7c0f7b7583927b8a253cdba87535c6522184 (patch) | |
tree | a2e781df185e6d78f53c3cf310f97ed5c8d8e6db /Lib/compiler | |
parent | 81f7eb6c6b50fb2d631c31a69fbbd4a68b9d2a50 (diff) | |
download | cpython-46cc7c0f7b7583927b8a253cdba87535c6522184.zip cpython-46cc7c0f7b7583927b8a253cdba87535c6522184.tar.gz cpython-46cc7c0f7b7583927b8a253cdba87535c6522184.tar.bz2 |
Bring Tools/compiler almost up to date. Specifically:
- 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.
Diffstat (limited to 'Lib/compiler')
-rw-r--r-- | Lib/compiler/pyassem.py | 258 | ||||
-rw-r--r-- | Lib/compiler/pycodegen.py | 395 | ||||
-rw-r--r-- | Lib/compiler/visitor.py | 44 |
3 files changed, 349 insertions, 348 deletions
diff --git a/Lib/compiler/pyassem.py b/Lib/compiler/pyassem.py index 6e07987..c8d9e90 100644 --- a/Lib/compiler/pyassem.py +++ b/Lib/compiler/pyassem.py @@ -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 diff --git a/Lib/compiler/pycodegen.py b/Lib/compiler/pycodegen.py index b3223c1..9d9b982 100644 --- a/Lib/compiler/pycodegen.py +++ b/Lib/compiler/pycodegen.py @@ -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) diff --git a/Lib/compiler/visitor.py b/Lib/compiler/visitor.py index d0e5223..32e72e1 100644 --- a/Lib/compiler/visitor.py +++ b/Lib/compiler/visitor.py @@ -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): |