summaryrefslogtreecommitdiffstats
path: root/Lib/compiler/pycodegen.py
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/compiler/pycodegen.py')
-rw-r--r--Lib/compiler/pycodegen.py395
1 files changed, 199 insertions, 196 deletions
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)