From ad9a86fb1c8cca20c10beb35fdd54eab44c0f479 Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Wed, 16 Feb 2000 00:55:44 +0000 Subject: [PATCH] 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) --- Lib/compiler/pycodegen.py | 76 ++++++++++++++++++++++------ Tools/compiler/compiler/pycodegen.py | 76 ++++++++++++++++++++++------ 2 files changed, 122 insertions(+), 30 deletions(-) diff --git a/Lib/compiler/pycodegen.py b/Lib/compiler/pycodegen.py index 5313191b146..9b9ee16dffa 100644 --- a/Lib/compiler/pycodegen.py +++ b/Lib/compiler/pycodegen.py @@ -16,6 +16,7 @@ import sys import os import stat import struct +import types def parse(path): f = open(path) @@ -177,7 +178,10 @@ class CodeGenerator: def _generateFunctionOrLambdaCode(self, func): self.name = func.name 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, filename=filename) self.namespace = self.OPTIMIZED @@ -188,8 +192,41 @@ class CodeGenerator: lnf = walk(func.code, LocalNameFinder(args), 0) self.locals.push(lnf.getLocals()) self.emit('SET_LINENO', func.lineno) + if hasTupleArg: + self.generateArgUnpack(func.argnames) 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): """Generate code for a function body""" self._generateFunctionOrLambdaCode(func) @@ -391,7 +428,8 @@ class CodeGenerator: else: lElse = l.breakAnchor 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.emit('JUMP_IF_FALSE', lElse) self.emit('POP_TOP') @@ -455,7 +493,8 @@ class CodeGenerator: self.emit('POP_TOP') self.visit(body) self.emit('JUMP_FORWARD', end) - next.bind(self.code.getCurInst()) + if expr: + next.bind(self.code.getCurInst()) self.emit('POP_TOP') self.emit('END_FINALLY') if node.else_: @@ -553,7 +592,6 @@ class CodeGenerator: self.emit('STORE_SUBSCR') elif node.flags == 'OP_DELETE': self.emit('DELETE_SUBSCR') - print return 1 def visitSlice(self, node): @@ -596,16 +634,20 @@ class CodeGenerator: return 1 def visitAssName(self, node): + # XXX handle OP_DELETE if node.flags != 'OP_ASSIGN': print "oops", node.flags self.storeName(node.name) 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 node - self.visit(node.expr) - self.emit('STORE_ATTR', node.attrname) return 1 def visitAssTuple(self, node): @@ -865,7 +907,7 @@ class CompiledModule: t = transformer.Transformer() self.ast = t.parsesuite(self.source) cg = CodeGenerator(self.filename) - walk(self.ast, cg, walker=ExampleASTVisitor) + walk(self.ast, cg) self.code = cg.asConst() def dump(self, path): @@ -888,18 +930,22 @@ class CompiledModule: if __name__ == "__main__": import getopt + VERBOSE = 0 opts, args = getopt.getopt(sys.argv[1:], 'vq') for k, v in opts: if k == '-v': + VERBOSE = 1 ASTVisitor.VERBOSE = ASTVisitor.VERBOSE + 1 if k == '-q': f = open('/dev/null', 'wb') sys.stdout = f - if args: - filename = args[0] + if not args: + print "no files to compile" else: - filename = 'test.py' - buf = open(filename).read() - mod = CompiledModule(buf, filename) - mod.compile() - mod.dump(filename + 'c') + for filename in args: + if VERBOSE: + print filename + buf = open(filename).read() + mod = CompiledModule(buf, filename) + mod.compile() + mod.dump(filename + 'c') diff --git a/Tools/compiler/compiler/pycodegen.py b/Tools/compiler/compiler/pycodegen.py index 5313191b146..9b9ee16dffa 100644 --- a/Tools/compiler/compiler/pycodegen.py +++ b/Tools/compiler/compiler/pycodegen.py @@ -16,6 +16,7 @@ import sys import os import stat import struct +import types def parse(path): f = open(path) @@ -177,7 +178,10 @@ class CodeGenerator: def _generateFunctionOrLambdaCode(self, func): self.name = func.name 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, filename=filename) self.namespace = self.OPTIMIZED @@ -188,8 +192,41 @@ class CodeGenerator: lnf = walk(func.code, LocalNameFinder(args), 0) self.locals.push(lnf.getLocals()) self.emit('SET_LINENO', func.lineno) + if hasTupleArg: + self.generateArgUnpack(func.argnames) 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): """Generate code for a function body""" self._generateFunctionOrLambdaCode(func) @@ -391,7 +428,8 @@ class CodeGenerator: else: lElse = l.breakAnchor 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.emit('JUMP_IF_FALSE', lElse) self.emit('POP_TOP') @@ -455,7 +493,8 @@ class CodeGenerator: self.emit('POP_TOP') self.visit(body) self.emit('JUMP_FORWARD', end) - next.bind(self.code.getCurInst()) + if expr: + next.bind(self.code.getCurInst()) self.emit('POP_TOP') self.emit('END_FINALLY') if node.else_: @@ -553,7 +592,6 @@ class CodeGenerator: self.emit('STORE_SUBSCR') elif node.flags == 'OP_DELETE': self.emit('DELETE_SUBSCR') - print return 1 def visitSlice(self, node): @@ -596,16 +634,20 @@ class CodeGenerator: return 1 def visitAssName(self, node): + # XXX handle OP_DELETE if node.flags != 'OP_ASSIGN': print "oops", node.flags self.storeName(node.name) 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 node - self.visit(node.expr) - self.emit('STORE_ATTR', node.attrname) return 1 def visitAssTuple(self, node): @@ -865,7 +907,7 @@ class CompiledModule: t = transformer.Transformer() self.ast = t.parsesuite(self.source) cg = CodeGenerator(self.filename) - walk(self.ast, cg, walker=ExampleASTVisitor) + walk(self.ast, cg) self.code = cg.asConst() def dump(self, path): @@ -888,18 +930,22 @@ class CompiledModule: if __name__ == "__main__": import getopt + VERBOSE = 0 opts, args = getopt.getopt(sys.argv[1:], 'vq') for k, v in opts: if k == '-v': + VERBOSE = 1 ASTVisitor.VERBOSE = ASTVisitor.VERBOSE + 1 if k == '-q': f = open('/dev/null', 'wb') sys.stdout = f - if args: - filename = args[0] + if not args: + print "no files to compile" else: - filename = 'test.py' - buf = open(filename).read() - mod = CompiledModule(buf, filename) - mod.compile() - mod.dump(filename + 'c') + for filename in args: + if VERBOSE: + print filename + buf = open(filename).read() + mod = CompiledModule(buf, filename) + mod.compile() + mod.dump(filename + 'c')