Commit ad9a86fb authored by Jeremy Hylton's avatar Jeremy Hylton

support for arglists with implicit tuple unpacks

- added a number of support methods to generate code just before the
  body
- hack protocol for communicating number of args to PyAssembler

fix TryExcept generation for case where exception handler has no body
fix visitAssAttr
add comment about incomplete visitAssName

stop using the ExampleASTVisitor

change script invocation to accept a list of .py files (e.g. Lib/*.py)
parent 3d9f5e4d
...@@ -16,6 +16,7 @@ import sys ...@@ -16,6 +16,7 @@ import sys
import os import os
import stat import stat
import struct import struct
import types
def parse(path): def parse(path):
f = open(path) f = open(path)
...@@ -177,7 +178,10 @@ class CodeGenerator: ...@@ -177,7 +178,10 @@ class CodeGenerator:
def _generateFunctionOrLambdaCode(self, func): def _generateFunctionOrLambdaCode(self, func):
self.name = func.name self.name = func.name
self.filename = filename self.filename = filename
args = func.argnames
# keep a lookout for 'def foo((x,y)):'
args, hasTupleArg = self.generateArglist(func.argnames)
self.code = PyAssembler(args=args, name=func.name, self.code = PyAssembler(args=args, name=func.name,
filename=filename) filename=filename)
self.namespace = self.OPTIMIZED self.namespace = self.OPTIMIZED
...@@ -188,8 +192,41 @@ class CodeGenerator: ...@@ -188,8 +192,41 @@ class CodeGenerator:
lnf = walk(func.code, LocalNameFinder(args), 0) lnf = walk(func.code, LocalNameFinder(args), 0)
self.locals.push(lnf.getLocals()) self.locals.push(lnf.getLocals())
self.emit('SET_LINENO', func.lineno) self.emit('SET_LINENO', func.lineno)
if hasTupleArg:
self.generateArgUnpack(func.argnames)
walk(func.code, self) walk(func.code, self)
def generateArglist(self, arglist):
args = []
extra = []
count = 0
for elt in arglist:
if type(elt) == types.StringType:
args.append(elt)
elif type(elt) == types.TupleType:
args.append(".nested%d" % count)
count = count + 1
extra.extend(misc.flatten(elt))
else:
raise ValueError, "unexpect argument type:", elt
return args + extra, count
def generateArgUnpack(self, args):
count = 0
for arg in args:
if type(arg) == types.TupleType:
self.emit('LOAD_FAST', '.nested%d' % count)
count = count + 1
self.unpackTuple(arg)
def unpackTuple(self, tup):
self.emit('UNPACK_TUPLE', len(tup))
for elt in tup:
if type(elt) == types.TupleType:
self.unpackTuple(elt)
else:
self.emit('STORE_FAST', elt)
def generateFunctionCode(self, func): def generateFunctionCode(self, func):
"""Generate code for a function body""" """Generate code for a function body"""
self._generateFunctionOrLambdaCode(func) self._generateFunctionOrLambdaCode(func)
...@@ -391,7 +428,8 @@ class CodeGenerator: ...@@ -391,7 +428,8 @@ class CodeGenerator:
else: else:
lElse = l.breakAnchor lElse = l.breakAnchor
l.startAnchor.bind(self.code.getCurInst()) l.startAnchor.bind(self.code.getCurInst())
self.emit('SET_LINENO', node.test.lineno) if hasattr(node.test, 'lineno'):
self.emit('SET_LINENO', node.test.lineno)
self.visit(node.test) self.visit(node.test)
self.emit('JUMP_IF_FALSE', lElse) self.emit('JUMP_IF_FALSE', lElse)
self.emit('POP_TOP') self.emit('POP_TOP')
...@@ -455,7 +493,8 @@ class CodeGenerator: ...@@ -455,7 +493,8 @@ class CodeGenerator:
self.emit('POP_TOP') self.emit('POP_TOP')
self.visit(body) self.visit(body)
self.emit('JUMP_FORWARD', end) self.emit('JUMP_FORWARD', end)
next.bind(self.code.getCurInst()) if expr:
next.bind(self.code.getCurInst())
self.emit('POP_TOP') self.emit('POP_TOP')
self.emit('END_FINALLY') self.emit('END_FINALLY')
if node.else_: if node.else_:
...@@ -553,7 +592,6 @@ class CodeGenerator: ...@@ -553,7 +592,6 @@ class CodeGenerator:
self.emit('STORE_SUBSCR') self.emit('STORE_SUBSCR')
elif node.flags == 'OP_DELETE': elif node.flags == 'OP_DELETE':
self.emit('DELETE_SUBSCR') self.emit('DELETE_SUBSCR')
print
return 1 return 1
def visitSlice(self, node): def visitSlice(self, node):
...@@ -596,16 +634,20 @@ class CodeGenerator: ...@@ -596,16 +634,20 @@ class CodeGenerator:
return 1 return 1
def visitAssName(self, node): def visitAssName(self, node):
# XXX handle OP_DELETE
if node.flags != 'OP_ASSIGN': if node.flags != 'OP_ASSIGN':
print "oops", node.flags print "oops", node.flags
self.storeName(node.name) self.storeName(node.name)
def visitAssAttr(self, node): def visitAssAttr(self, node):
if node.flags != 'OP_ASSIGN': self.visit(node.expr)
if node.flags == 'OP_ASSIGN':
self.emit('STORE_ATTR', node.attrname)
elif node.flags == 'OP_DELETE':
self.emit('DELETE_ATTR', node.attrname)
else:
print "warning: unexpected flags:", node.flags print "warning: unexpected flags:", node.flags
print node print node
self.visit(node.expr)
self.emit('STORE_ATTR', node.attrname)
return 1 return 1
def visitAssTuple(self, node): def visitAssTuple(self, node):
...@@ -865,7 +907,7 @@ class CompiledModule: ...@@ -865,7 +907,7 @@ class CompiledModule:
t = transformer.Transformer() t = transformer.Transformer()
self.ast = t.parsesuite(self.source) self.ast = t.parsesuite(self.source)
cg = CodeGenerator(self.filename) cg = CodeGenerator(self.filename)
walk(self.ast, cg, walker=ExampleASTVisitor) walk(self.ast, cg)
self.code = cg.asConst() self.code = cg.asConst()
def dump(self, path): def dump(self, path):
...@@ -888,18 +930,22 @@ class CompiledModule: ...@@ -888,18 +930,22 @@ class CompiledModule:
if __name__ == "__main__": if __name__ == "__main__":
import getopt import getopt
VERBOSE = 0
opts, args = getopt.getopt(sys.argv[1:], 'vq') opts, args = getopt.getopt(sys.argv[1:], 'vq')
for k, v in opts: for k, v in opts:
if k == '-v': if k == '-v':
VERBOSE = 1
ASTVisitor.VERBOSE = ASTVisitor.VERBOSE + 1 ASTVisitor.VERBOSE = ASTVisitor.VERBOSE + 1
if k == '-q': if k == '-q':
f = open('/dev/null', 'wb') f = open('/dev/null', 'wb')
sys.stdout = f sys.stdout = f
if args: if not args:
filename = args[0] print "no files to compile"
else: else:
filename = 'test.py' for filename in args:
buf = open(filename).read() if VERBOSE:
mod = CompiledModule(buf, filename) print filename
mod.compile() buf = open(filename).read()
mod.dump(filename + 'c') mod = CompiledModule(buf, filename)
mod.compile()
mod.dump(filename + 'c')
...@@ -16,6 +16,7 @@ import sys ...@@ -16,6 +16,7 @@ import sys
import os import os
import stat import stat
import struct import struct
import types
def parse(path): def parse(path):
f = open(path) f = open(path)
...@@ -177,7 +178,10 @@ class CodeGenerator: ...@@ -177,7 +178,10 @@ class CodeGenerator:
def _generateFunctionOrLambdaCode(self, func): def _generateFunctionOrLambdaCode(self, func):
self.name = func.name self.name = func.name
self.filename = filename self.filename = filename
args = func.argnames
# keep a lookout for 'def foo((x,y)):'
args, hasTupleArg = self.generateArglist(func.argnames)
self.code = PyAssembler(args=args, name=func.name, self.code = PyAssembler(args=args, name=func.name,
filename=filename) filename=filename)
self.namespace = self.OPTIMIZED self.namespace = self.OPTIMIZED
...@@ -188,8 +192,41 @@ class CodeGenerator: ...@@ -188,8 +192,41 @@ class CodeGenerator:
lnf = walk(func.code, LocalNameFinder(args), 0) lnf = walk(func.code, LocalNameFinder(args), 0)
self.locals.push(lnf.getLocals()) self.locals.push(lnf.getLocals())
self.emit('SET_LINENO', func.lineno) self.emit('SET_LINENO', func.lineno)
if hasTupleArg:
self.generateArgUnpack(func.argnames)
walk(func.code, self) walk(func.code, self)
def generateArglist(self, arglist):
args = []
extra = []
count = 0
for elt in arglist:
if type(elt) == types.StringType:
args.append(elt)
elif type(elt) == types.TupleType:
args.append(".nested%d" % count)
count = count + 1
extra.extend(misc.flatten(elt))
else:
raise ValueError, "unexpect argument type:", elt
return args + extra, count
def generateArgUnpack(self, args):
count = 0
for arg in args:
if type(arg) == types.TupleType:
self.emit('LOAD_FAST', '.nested%d' % count)
count = count + 1
self.unpackTuple(arg)
def unpackTuple(self, tup):
self.emit('UNPACK_TUPLE', len(tup))
for elt in tup:
if type(elt) == types.TupleType:
self.unpackTuple(elt)
else:
self.emit('STORE_FAST', elt)
def generateFunctionCode(self, func): def generateFunctionCode(self, func):
"""Generate code for a function body""" """Generate code for a function body"""
self._generateFunctionOrLambdaCode(func) self._generateFunctionOrLambdaCode(func)
...@@ -391,7 +428,8 @@ class CodeGenerator: ...@@ -391,7 +428,8 @@ class CodeGenerator:
else: else:
lElse = l.breakAnchor lElse = l.breakAnchor
l.startAnchor.bind(self.code.getCurInst()) l.startAnchor.bind(self.code.getCurInst())
self.emit('SET_LINENO', node.test.lineno) if hasattr(node.test, 'lineno'):
self.emit('SET_LINENO', node.test.lineno)
self.visit(node.test) self.visit(node.test)
self.emit('JUMP_IF_FALSE', lElse) self.emit('JUMP_IF_FALSE', lElse)
self.emit('POP_TOP') self.emit('POP_TOP')
...@@ -455,7 +493,8 @@ class CodeGenerator: ...@@ -455,7 +493,8 @@ class CodeGenerator:
self.emit('POP_TOP') self.emit('POP_TOP')
self.visit(body) self.visit(body)
self.emit('JUMP_FORWARD', end) self.emit('JUMP_FORWARD', end)
next.bind(self.code.getCurInst()) if expr:
next.bind(self.code.getCurInst())
self.emit('POP_TOP') self.emit('POP_TOP')
self.emit('END_FINALLY') self.emit('END_FINALLY')
if node.else_: if node.else_:
...@@ -553,7 +592,6 @@ class CodeGenerator: ...@@ -553,7 +592,6 @@ class CodeGenerator:
self.emit('STORE_SUBSCR') self.emit('STORE_SUBSCR')
elif node.flags == 'OP_DELETE': elif node.flags == 'OP_DELETE':
self.emit('DELETE_SUBSCR') self.emit('DELETE_SUBSCR')
print
return 1 return 1
def visitSlice(self, node): def visitSlice(self, node):
...@@ -596,16 +634,20 @@ class CodeGenerator: ...@@ -596,16 +634,20 @@ class CodeGenerator:
return 1 return 1
def visitAssName(self, node): def visitAssName(self, node):
# XXX handle OP_DELETE
if node.flags != 'OP_ASSIGN': if node.flags != 'OP_ASSIGN':
print "oops", node.flags print "oops", node.flags
self.storeName(node.name) self.storeName(node.name)
def visitAssAttr(self, node): def visitAssAttr(self, node):
if node.flags != 'OP_ASSIGN': self.visit(node.expr)
if node.flags == 'OP_ASSIGN':
self.emit('STORE_ATTR', node.attrname)
elif node.flags == 'OP_DELETE':
self.emit('DELETE_ATTR', node.attrname)
else:
print "warning: unexpected flags:", node.flags print "warning: unexpected flags:", node.flags
print node print node
self.visit(node.expr)
self.emit('STORE_ATTR', node.attrname)
return 1 return 1
def visitAssTuple(self, node): def visitAssTuple(self, node):
...@@ -865,7 +907,7 @@ class CompiledModule: ...@@ -865,7 +907,7 @@ class CompiledModule:
t = transformer.Transformer() t = transformer.Transformer()
self.ast = t.parsesuite(self.source) self.ast = t.parsesuite(self.source)
cg = CodeGenerator(self.filename) cg = CodeGenerator(self.filename)
walk(self.ast, cg, walker=ExampleASTVisitor) walk(self.ast, cg)
self.code = cg.asConst() self.code = cg.asConst()
def dump(self, path): def dump(self, path):
...@@ -888,18 +930,22 @@ class CompiledModule: ...@@ -888,18 +930,22 @@ class CompiledModule:
if __name__ == "__main__": if __name__ == "__main__":
import getopt import getopt
VERBOSE = 0
opts, args = getopt.getopt(sys.argv[1:], 'vq') opts, args = getopt.getopt(sys.argv[1:], 'vq')
for k, v in opts: for k, v in opts:
if k == '-v': if k == '-v':
VERBOSE = 1
ASTVisitor.VERBOSE = ASTVisitor.VERBOSE + 1 ASTVisitor.VERBOSE = ASTVisitor.VERBOSE + 1
if k == '-q': if k == '-q':
f = open('/dev/null', 'wb') f = open('/dev/null', 'wb')
sys.stdout = f sys.stdout = f
if args: if not args:
filename = args[0] print "no files to compile"
else: else:
filename = 'test.py' for filename in args:
buf = open(filename).read() if VERBOSE:
mod = CompiledModule(buf, filename) print filename
mod.compile() buf = open(filename).read()
mod.dump(filename + 'c') mod = CompiledModule(buf, filename)
mod.compile()
mod.dump(filename + 'c')
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment