diff options
author | Jeremy Hylton <jeremy@alum.mit.edu> | 2000-02-08 21:15:48 (GMT) |
---|---|---|
committer | Jeremy Hylton <jeremy@alum.mit.edu> | 2000-02-08 21:15:48 (GMT) |
commit | 402456020ba862c4641853fe7f3c87a5c5b37cf0 (patch) | |
tree | ac8ddbe9a64b6b26d5712cc7ed6ed111475fb38a | |
parent | 156a9754760585bbf7fbe1d124300bdc1c2b019d (diff) | |
download | cpython-402456020ba862c4641853fe7f3c87a5c5b37cf0.zip cpython-402456020ba862c4641853fe7f3c87a5c5b37cf0.tar.gz cpython-402456020ba862c4641853fe7f3c87a5c5b37cf0.tar.bz2 |
add optional verbose arg to walk function. it overrides the global
VERBOSE setting for the ASTVisitor
add getopt handling for one or more -v args
rename ForwardRef to StackRef, because it isn't necessarily directional
CodeGenerator:
* add assertStackEmpty method. prints warning if stack is not empty
when it should be
* define methods for AssName, UNARY_*, For
PythonVMCode:
* fix mix up between hasjrel and hasjabs for address calculation
-rw-r--r-- | Lib/compiler/pycodegen.py | 166 | ||||
-rw-r--r-- | Tools/compiler/compiler/pycodegen.py | 166 |
2 files changed, 274 insertions, 58 deletions
diff --git a/Lib/compiler/pycodegen.py b/Lib/compiler/pycodegen.py index ee0c75b..fd2c454 100644 --- a/Lib/compiler/pycodegen.py +++ b/Lib/compiler/pycodegen.py @@ -23,9 +23,13 @@ def parse(path): t = transformer.Transformer() return t.parsesuite(src) -def walk(tree, visitor): +def walk(tree, visitor, verbose=None): + print visitor, "start" w = ASTVisitor() + if verbose is not None: + w.VERBOSE = verbose w.preorder(tree, visitor) + print visitor, "finish" return w.visitor class ASTVisitor: @@ -64,7 +68,7 @@ class ASTVisitor: XXX Perhaps I can use a postorder walk for the code generator? """ - VERBOSE = 1 + VERBOSE = 0 def __init__(self): self.node = None @@ -99,8 +103,12 @@ class ASTVisitor: self.node = node className = node.__class__.__name__ meth = getattr(self.visitor, 'visit' + className, None) - if self.VERBOSE: - print "dispatch", className, (meth and meth.__name__ or '') + if self.VERBOSE > 0: + if self.VERBOSE == 1: + if meth is None: + print "dispatch", className + else: + print "dispatch", className, (meth and meth.__name__ or '') if meth: return meth(node) @@ -123,6 +131,9 @@ class CodeGenerator: """ return self.code.makeCodeObject(self.maxStack) + def isLocalName(self, name): + return self.locals.top().has_elt(name) + def push(self, n): self.curStack = self.curStack + n if self.curStack > self.maxStack: @@ -134,11 +145,15 @@ class CodeGenerator: else: self.curStack = 0 + def assertStackEmpty(self): + if self.curStack != 0: + print "warning: stack should be empty" + def visitDiscard(self, node): return 1 def visitModule(self, node): - lnf = walk(node.node, LocalNameFinder()) + lnf = walk(node.node, LocalNameFinder(), 0) self.locals.push(lnf.getLocals()) self.visit(node.node) self.code.emit('LOAD_CONST', None) @@ -162,11 +177,14 @@ class CodeGenerator: return 1 def visitIf(self, node): - after = ForwardRef() + after = StackRef() for test, suite in node.tests: - self.code.setLineNo(test.lineno) + if hasattr(test, 'lineno'): + self.code.setLineNo(test.lineno) + else: + print "warning", "no line number" self.visit(test) - dest = ForwardRef() + dest = StackRef() self.code.jumpIfFalse(dest) self.code.popTop() self.visit(suite) @@ -178,6 +196,30 @@ class CodeGenerator: after.bind(self.code.getCurInst()) return 1 + def visitFor(self, node): + # three refs needed + start = StackRef() + anchor = StackRef() + breakAnchor = StackRef() + + self.code.emit('SET_LINENO', node.lineno) + self.code.emit('SETUP_LOOP', breakAnchor) + self.visit(node.list) + self.visit(ast.Const(0)) + start.bind(self.code.getCurInst()) + self.code.setLineNo(node.lineno) + self.code.emit('FOR_LOOP', anchor) + self.push(1) + self.visit(node.assign) + self.visit(node.body) + self.code.emit('JUMP_ABSOLUTE', start) + anchor.bind(self.code.getCurInst()) + self.code.emit('POP_BLOCK') + if node.else_: + self.visit(node.else_) + breakAnchor.bind(self.code.getCurInst()) + return 1 + def visitCompare(self, node): """Comment from compile.c follows: @@ -214,29 +256,54 @@ class CodeGenerator: """ self.visit(node.expr) # if refs are never emitted, subsequent bind call has no effect - l1 = ForwardRef() - l2 = ForwardRef() + l1 = StackRef() + l2 = StackRef() for op, code in node.ops[:-1]: # emit every comparison except the last self.visit(code) self.code.dupTop() self.code.rotThree() self.code.compareOp(op) + # dupTop and compareOp cancel stack effect self.code.jumpIfFalse(l1) self.code.popTop() + self.pop(1) if node.ops: # emit the last comparison op, code = node.ops[-1] self.visit(code) self.code.compareOp(op) + self.pop(1) if len(node.ops) > 1: self.code.jumpForward(l2) l1.bind(self.code.getCurInst()) self.code.rotTwo() self.code.popTop() + self.pop(1) l2.bind(self.code.getCurInst()) return 1 + def visitAssign(self, node): + self.code.setLineNo(node.lineno) + print "Assign" + print node.nodes + print node.expr + print + self.visit(node.expr) + for elt in node.nodes: + if isinstance(elt, ast.Node): + self.visit(elt) + return 1 + + def visitAssName(self, node): + if node.flags != 'OP_ASSIGN': + print "oops", node.flags + if self.isLocalName(node.name): + self.code.emit('STORE_FAST', node.name) + else: + self.code.emit('STORE_GLOBAL', node.name) + self.pop(1) + def binaryOp(self, node, op): self.visit(node.left) self.visit(node.right) @@ -244,6 +311,11 @@ class CodeGenerator: self.pop(1) return 1 + def unaryOp(self, node, op): + self.visit(node.expr) + self.code.emit(op) + return 1 + def visitAdd(self, node): return self.binaryOp(node, 'BINARY_ADD') @@ -256,9 +328,20 @@ class CodeGenerator: def visitDiv(self, node): return self.binaryOp(node, 'BINARY_DIVIDE') + def visitUnarySub(self, node): + return self.unaryOp(node, 'UNARY_NEGATIVE') + + def visitUnaryAdd(self, node): + return self.unaryOp(node, 'UNARY_POSITIVE') + + def visitUnaryInvert(self, node): + return self.unaryOp(node, 'UNARY_INVERT') + + def visitBackquote(self, node): + return self.unaryOp(node, 'UNARY_CONVERT') + def visitName(self, node): - locals = self.locals.top() - if locals.has_elt(node.name): + if self.isLocalName(node.name): self.code.loadFast(node.name) else: self.code.loadGlobal(node.name) @@ -267,11 +350,21 @@ class CodeGenerator: def visitConst(self, node): self.code.loadConst(node.value) self.push(1) + return 1 + + def visitTuple(self, node): + for elt in node.nodes: + self.visit(elt) + self.code.emit('BUILD_TUPLE', len(node.nodes)) + self.pop(len(node.nodes)) + return 1 def visitReturn(self, node): self.code.setLineNo(node.lineno) self.visit(node.value) self.code.returnValue() + self.pop(1) + self.assertStackEmpty() return 1 def visitRaise(self, node): @@ -326,14 +419,14 @@ class NestedCodeGenerator(CodeGenerator): self.code.setVarArgs() if func.kwargs: self.code.setKWArgs() - lnf = walk(func.code, LocalNameFinder(args)) + lnf = walk(func.code, LocalNameFinder(args), 0) self.locals.push(lnf.getLocals()) def __repr__(self): return "<NestedCodeGenerator: %s>" % self.name def visitFunction(self, node): - lnf = walk(node.code, LocalNameFinder(node.argnames)) + lnf = walk(node.code, LocalNameFinder(node.argnames), 0) self.locals.push(lnf.getLocals()) # XXX need to handle def foo((a, b)): self.code.setLineNo(node.lineno) @@ -376,21 +469,22 @@ class Label: def __repr__(self): return "Label(%d)" % self.num -class ForwardRef: +class StackRef: + """Manage stack locations for jumps, loops, etc.""" count = 0 def __init__(self, id=None, val=None): if id is None: - id = ForwardRef.count - ForwardRef.count = ForwardRef.count + 1 + id = StackRef.count + StackRef.count = StackRef.count + 1 self.id = id self.val = val def __repr__(self): if self.val: - return "ForwardRef(val=%d)" % self.val + return "StackRef(val=%d)" % self.val else: - return "ForwardRef(id=%d)" % self.id + return "StackRef(id=%d)" % self.id def bind(self, inst): self.val = inst @@ -522,7 +616,11 @@ class PythonVMCode: oparg = self._convertArg(opname, t[1]) if opname == 'SET_LINENO': lnotab.nextLine(oparg) - hi, lo = divmod(oparg, 256) + try: + hi, lo = divmod(oparg, 256) + except TypeError: + print opname, oparg + raise lnotab.addCode(chr(self.opnum[opname]) + chr(lo) + chr(hi)) # why is a module a special case? @@ -551,7 +649,7 @@ class PythonVMCode: return tuple(l) def _findOffsets(self): - """Find offsets for use in resolving ForwardRefs""" + """Find offsets for use in resolving StackRefs""" self.offsets = [] cur = 0 for t in self.insts: @@ -560,10 +658,10 @@ class PythonVMCode: if l == 1: cur = cur + 1 elif l == 2: + cur = cur + 3 arg = t[1] - if isinstance(arg, ForwardRef): + if isinstance(arg, StackRef): arg.__offset = cur - cur = cur + 3 def _convertArg(self, op, arg): """Convert the string representation of an arg to a number @@ -577,23 +675,26 @@ class PythonVMCode: return arg if op == 'LOAD_CONST': return self._lookupName(arg, self.consts) - if op == 'LOAD_FAST': + if op in self.localOps: if arg in self.names: return self._lookupName(arg, self.varnames) else: return self._lookupName(arg, self.varnames, self.names) - if op == 'LOAD_GLOBAL': + if op in self.globalOps: return self._lookupName(arg, self.names) if op == 'STORE_NAME': return self._lookupName(arg, self.names) if op == 'COMPARE_OP': return self.cmp_op.index(arg) if self.hasjrel.has_elt(op): - return self.offsets[arg.resolve()] - if self.hasjabs.has_elt(op): return self.offsets[arg.resolve()] - arg.__offset + if self.hasjabs.has_elt(op): + return self.offsets[arg.resolve()] return arg + localOps = ('LOAD_FAST', 'STORE_FAST') + globalOps = ('LOAD_GLOBAL', 'STORE_GLOBAL') + def _lookupName(self, name, list, list2=None): """Return index of name in list, appending if necessary @@ -787,8 +888,15 @@ class CompiledModule: return magic + mtime if __name__ == "__main__": - if len(sys.argv) > 1: - filename = sys.argv[1] + import getopt + + opts, args = getopt.getopt(sys.argv[1:], 'v') + for k, v in opts: + if k == '-v': + ASTVisitor.VERBOSE = ASTVisitor.VERBOSE + 1 + print k + if args: + filename = args[0] else: filename = 'test.py' buf = open(filename).read() diff --git a/Tools/compiler/compiler/pycodegen.py b/Tools/compiler/compiler/pycodegen.py index ee0c75b..fd2c454 100644 --- a/Tools/compiler/compiler/pycodegen.py +++ b/Tools/compiler/compiler/pycodegen.py @@ -23,9 +23,13 @@ def parse(path): t = transformer.Transformer() return t.parsesuite(src) -def walk(tree, visitor): +def walk(tree, visitor, verbose=None): + print visitor, "start" w = ASTVisitor() + if verbose is not None: + w.VERBOSE = verbose w.preorder(tree, visitor) + print visitor, "finish" return w.visitor class ASTVisitor: @@ -64,7 +68,7 @@ class ASTVisitor: XXX Perhaps I can use a postorder walk for the code generator? """ - VERBOSE = 1 + VERBOSE = 0 def __init__(self): self.node = None @@ -99,8 +103,12 @@ class ASTVisitor: self.node = node className = node.__class__.__name__ meth = getattr(self.visitor, 'visit' + className, None) - if self.VERBOSE: - print "dispatch", className, (meth and meth.__name__ or '') + if self.VERBOSE > 0: + if self.VERBOSE == 1: + if meth is None: + print "dispatch", className + else: + print "dispatch", className, (meth and meth.__name__ or '') if meth: return meth(node) @@ -123,6 +131,9 @@ class CodeGenerator: """ return self.code.makeCodeObject(self.maxStack) + def isLocalName(self, name): + return self.locals.top().has_elt(name) + def push(self, n): self.curStack = self.curStack + n if self.curStack > self.maxStack: @@ -134,11 +145,15 @@ class CodeGenerator: else: self.curStack = 0 + def assertStackEmpty(self): + if self.curStack != 0: + print "warning: stack should be empty" + def visitDiscard(self, node): return 1 def visitModule(self, node): - lnf = walk(node.node, LocalNameFinder()) + lnf = walk(node.node, LocalNameFinder(), 0) self.locals.push(lnf.getLocals()) self.visit(node.node) self.code.emit('LOAD_CONST', None) @@ -162,11 +177,14 @@ class CodeGenerator: return 1 def visitIf(self, node): - after = ForwardRef() + after = StackRef() for test, suite in node.tests: - self.code.setLineNo(test.lineno) + if hasattr(test, 'lineno'): + self.code.setLineNo(test.lineno) + else: + print "warning", "no line number" self.visit(test) - dest = ForwardRef() + dest = StackRef() self.code.jumpIfFalse(dest) self.code.popTop() self.visit(suite) @@ -178,6 +196,30 @@ class CodeGenerator: after.bind(self.code.getCurInst()) return 1 + def visitFor(self, node): + # three refs needed + start = StackRef() + anchor = StackRef() + breakAnchor = StackRef() + + self.code.emit('SET_LINENO', node.lineno) + self.code.emit('SETUP_LOOP', breakAnchor) + self.visit(node.list) + self.visit(ast.Const(0)) + start.bind(self.code.getCurInst()) + self.code.setLineNo(node.lineno) + self.code.emit('FOR_LOOP', anchor) + self.push(1) + self.visit(node.assign) + self.visit(node.body) + self.code.emit('JUMP_ABSOLUTE', start) + anchor.bind(self.code.getCurInst()) + self.code.emit('POP_BLOCK') + if node.else_: + self.visit(node.else_) + breakAnchor.bind(self.code.getCurInst()) + return 1 + def visitCompare(self, node): """Comment from compile.c follows: @@ -214,29 +256,54 @@ class CodeGenerator: """ self.visit(node.expr) # if refs are never emitted, subsequent bind call has no effect - l1 = ForwardRef() - l2 = ForwardRef() + l1 = StackRef() + l2 = StackRef() for op, code in node.ops[:-1]: # emit every comparison except the last self.visit(code) self.code.dupTop() self.code.rotThree() self.code.compareOp(op) + # dupTop and compareOp cancel stack effect self.code.jumpIfFalse(l1) self.code.popTop() + self.pop(1) if node.ops: # emit the last comparison op, code = node.ops[-1] self.visit(code) self.code.compareOp(op) + self.pop(1) if len(node.ops) > 1: self.code.jumpForward(l2) l1.bind(self.code.getCurInst()) self.code.rotTwo() self.code.popTop() + self.pop(1) l2.bind(self.code.getCurInst()) return 1 + def visitAssign(self, node): + self.code.setLineNo(node.lineno) + print "Assign" + print node.nodes + print node.expr + print + self.visit(node.expr) + for elt in node.nodes: + if isinstance(elt, ast.Node): + self.visit(elt) + return 1 + + def visitAssName(self, node): + if node.flags != 'OP_ASSIGN': + print "oops", node.flags + if self.isLocalName(node.name): + self.code.emit('STORE_FAST', node.name) + else: + self.code.emit('STORE_GLOBAL', node.name) + self.pop(1) + def binaryOp(self, node, op): self.visit(node.left) self.visit(node.right) @@ -244,6 +311,11 @@ class CodeGenerator: self.pop(1) return 1 + def unaryOp(self, node, op): + self.visit(node.expr) + self.code.emit(op) + return 1 + def visitAdd(self, node): return self.binaryOp(node, 'BINARY_ADD') @@ -256,9 +328,20 @@ class CodeGenerator: def visitDiv(self, node): return self.binaryOp(node, 'BINARY_DIVIDE') + def visitUnarySub(self, node): + return self.unaryOp(node, 'UNARY_NEGATIVE') + + def visitUnaryAdd(self, node): + return self.unaryOp(node, 'UNARY_POSITIVE') + + def visitUnaryInvert(self, node): + return self.unaryOp(node, 'UNARY_INVERT') + + def visitBackquote(self, node): + return self.unaryOp(node, 'UNARY_CONVERT') + def visitName(self, node): - locals = self.locals.top() - if locals.has_elt(node.name): + if self.isLocalName(node.name): self.code.loadFast(node.name) else: self.code.loadGlobal(node.name) @@ -267,11 +350,21 @@ class CodeGenerator: def visitConst(self, node): self.code.loadConst(node.value) self.push(1) + return 1 + + def visitTuple(self, node): + for elt in node.nodes: + self.visit(elt) + self.code.emit('BUILD_TUPLE', len(node.nodes)) + self.pop(len(node.nodes)) + return 1 def visitReturn(self, node): self.code.setLineNo(node.lineno) self.visit(node.value) self.code.returnValue() + self.pop(1) + self.assertStackEmpty() return 1 def visitRaise(self, node): @@ -326,14 +419,14 @@ class NestedCodeGenerator(CodeGenerator): self.code.setVarArgs() if func.kwargs: self.code.setKWArgs() - lnf = walk(func.code, LocalNameFinder(args)) + lnf = walk(func.code, LocalNameFinder(args), 0) self.locals.push(lnf.getLocals()) def __repr__(self): return "<NestedCodeGenerator: %s>" % self.name def visitFunction(self, node): - lnf = walk(node.code, LocalNameFinder(node.argnames)) + lnf = walk(node.code, LocalNameFinder(node.argnames), 0) self.locals.push(lnf.getLocals()) # XXX need to handle def foo((a, b)): self.code.setLineNo(node.lineno) @@ -376,21 +469,22 @@ class Label: def __repr__(self): return "Label(%d)" % self.num -class ForwardRef: +class StackRef: + """Manage stack locations for jumps, loops, etc.""" count = 0 def __init__(self, id=None, val=None): if id is None: - id = ForwardRef.count - ForwardRef.count = ForwardRef.count + 1 + id = StackRef.count + StackRef.count = StackRef.count + 1 self.id = id self.val = val def __repr__(self): if self.val: - return "ForwardRef(val=%d)" % self.val + return "StackRef(val=%d)" % self.val else: - return "ForwardRef(id=%d)" % self.id + return "StackRef(id=%d)" % self.id def bind(self, inst): self.val = inst @@ -522,7 +616,11 @@ class PythonVMCode: oparg = self._convertArg(opname, t[1]) if opname == 'SET_LINENO': lnotab.nextLine(oparg) - hi, lo = divmod(oparg, 256) + try: + hi, lo = divmod(oparg, 256) + except TypeError: + print opname, oparg + raise lnotab.addCode(chr(self.opnum[opname]) + chr(lo) + chr(hi)) # why is a module a special case? @@ -551,7 +649,7 @@ class PythonVMCode: return tuple(l) def _findOffsets(self): - """Find offsets for use in resolving ForwardRefs""" + """Find offsets for use in resolving StackRefs""" self.offsets = [] cur = 0 for t in self.insts: @@ -560,10 +658,10 @@ class PythonVMCode: if l == 1: cur = cur + 1 elif l == 2: + cur = cur + 3 arg = t[1] - if isinstance(arg, ForwardRef): + if isinstance(arg, StackRef): arg.__offset = cur - cur = cur + 3 def _convertArg(self, op, arg): """Convert the string representation of an arg to a number @@ -577,23 +675,26 @@ class PythonVMCode: return arg if op == 'LOAD_CONST': return self._lookupName(arg, self.consts) - if op == 'LOAD_FAST': + if op in self.localOps: if arg in self.names: return self._lookupName(arg, self.varnames) else: return self._lookupName(arg, self.varnames, self.names) - if op == 'LOAD_GLOBAL': + if op in self.globalOps: return self._lookupName(arg, self.names) if op == 'STORE_NAME': return self._lookupName(arg, self.names) if op == 'COMPARE_OP': return self.cmp_op.index(arg) if self.hasjrel.has_elt(op): - return self.offsets[arg.resolve()] - if self.hasjabs.has_elt(op): return self.offsets[arg.resolve()] - arg.__offset + if self.hasjabs.has_elt(op): + return self.offsets[arg.resolve()] return arg + localOps = ('LOAD_FAST', 'STORE_FAST') + globalOps = ('LOAD_GLOBAL', 'STORE_GLOBAL') + def _lookupName(self, name, list, list2=None): """Return index of name in list, appending if necessary @@ -787,8 +888,15 @@ class CompiledModule: return magic + mtime if __name__ == "__main__": - if len(sys.argv) > 1: - filename = sys.argv[1] + import getopt + + opts, args = getopt.getopt(sys.argv[1:], 'v') + for k, v in opts: + if k == '-v': + ASTVisitor.VERBOSE = ASTVisitor.VERBOSE + 1 + print k + if args: + filename = args[0] else: filename = 'test.py' buf = open(filename).read() |