diff options
-rw-r--r-- | Lib/compiler/pycodegen.py | 201 | ||||
-rw-r--r-- | Tools/compiler/compiler/pycodegen.py | 201 |
2 files changed, 50 insertions, 352 deletions
diff --git a/Lib/compiler/pycodegen.py b/Lib/compiler/pycodegen.py index 4587bf4..b2d55d9 100644 --- a/Lib/compiler/pycodegen.py +++ b/Lib/compiler/pycodegen.py @@ -5,7 +5,7 @@ CodeGenerator. Eventually, this will get split into the ASTVisitor as a generic tool and CodeGenerator as a specific tool. """ -from p2c import transformer, ast +from compiler import parseFile, ast, visitor, walk, parse from pyassem import StackRef, PyAssembler, TupleArg import dis import misc @@ -18,143 +18,6 @@ import stat import struct import types -def parse(path): - f = open(path) - src = f.read() - f.close() - t = transformer.Transformer() - return t.parsesuite(src) - -def walk(tree, visitor, verbose=None, walker=None): - if walker: - w = walker() - else: - w = ASTVisitor() - if verbose is not None: - w.VERBOSE = verbose - w.preorder(tree, visitor) - return w.visitor - -def dumpNode(node): - print node.__class__ - for attr in dir(node): - if attr[0] != '_': - print "\t", "%-10.10s" % attr, getattr(node, attr) - -class ASTVisitor: - """Performs a depth-first walk of the AST - - The ASTVisitor will walk the AST, performing either a preorder or - postorder traversal depending on which method is called. - - methods: - preorder(tree, visitor) - postorder(tree, visitor) - tree: an instance of ast.Node - visitor: an instance with visitXXX methods - - The ASTVisitor is responsible for walking over the tree in the - correct order. For each node, it checks the visitor argument for - a method named 'visitNodeType' where NodeType is the name of the - node's class, e.g. Classdef. If the method exists, it is called - with the node as its sole argument. - - The visitor method for a particular node type can control how - child nodes are visited during a preorder walk. (It can't control - the order during a postorder walk, because it is called _after_ - the walk has occurred.) The ASTVisitor modifies the visitor - argument by adding a visit method to the visitor; this method can - be used to visit a particular child node. If the visitor method - returns a true value, the ASTVisitor will not traverse the child - nodes. - - XXX The interface for controlling the preorder walk needs to be - re-considered. The current interface is convenient for visitors - that mostly let the ASTVisitor do everything. For something like - a code generator, where you want to walk to occur in a specific - order, it's a pain to add "return 1" to the end of each method. - - XXX Perhaps I can use a postorder walk for the code generator? - """ - - VERBOSE = 0 - - def __init__(self): - self.node = None - - def preorder(self, tree, visitor): - """Do preorder walk of tree using visitor""" - self.visitor = visitor - visitor.visit = self._preorder - self._preorder(tree) - - def _preorder(self, node): - stop = self.dispatch(node) - if stop: - return - for child in node.getChildren(): - if isinstance(child, ast.Node): - self._preorder(child) - - def postorder(self, tree, visitor): - """Do preorder walk of tree using visitor""" - self.visitor = visitor - visitor.visit = self._postorder - self._postorder(tree) - - def _postorder(self, tree): - for child in node.getChildren(): - if isinstance(child, ast.Node): - self._preorder(child) - self.dispatch(node) - - def dispatch(self, node): - self.node = node - className = node.__class__.__name__ - meth = getattr(self.visitor, 'visit' + className, None) - 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) - -class ExampleASTVisitor(ASTVisitor): - """Prints examples of the nodes that aren't visited - - This visitor-driver is only useful for development, when it's - helpful to develop a visitor incremently, and get feedback on what - you still have to do. - """ - examples = {} - - def dispatch(self, node): - self.node = node - className = node.__class__.__name__ - meth = getattr(self.visitor, 'visit' + className, None) - 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) - else: - klass = node.__class__ - if self.VERBOSE < 2: - if self.examples.has_key(klass): - return - self.examples[klass] = klass - print - print klass - for attr in dir(node): - if attr[0] != '_': - print "\t", "%-12.12s" % attr, getattr(node, attr) - print - class CodeGenerator: """Generate bytecode for the Python VM""" @@ -311,7 +174,7 @@ class CodeGenerator: self.emit('IMPORT_FROM', name) self.emit('POP_TOP') - def visitClassdef(self, node): + def visitClass(self, node): self.emit('SET_LINENO', node.lineno) self.emit('LOAD_CONST', node.name) for base in node.bases: @@ -660,17 +523,14 @@ class CodeGenerator: visitAssList = visitAssTuple + # binary ops + def binaryOp(self, node, op): self.visit(node.left) self.visit(node.right) self.emit(op) return 1 - def unaryOp(self, node, op): - self.visit(node.expr) - self.emit(op) - return 1 - def visitAdd(self, node): return self.binaryOp(node, 'BINARY_ADD') @@ -695,6 +555,13 @@ class CodeGenerator: def visitRightShift(self, node): return self.binaryOp(node, 'BINARY_RSHIFT') + # unary ops + + def unaryOp(self, node, op): + self.visit(node.expr) + self.emit(op) + return 1 + def visitInvert(self, node): return self.unaryOp(node, 'UNARY_INVERT') @@ -713,6 +580,8 @@ class CodeGenerator: def visitBackquote(self, node): return self.unaryOp(node, 'UNARY_CONVERT') + # bit ops + def bitOp(self, nodes, op): self.visit(nodes[0]) for node in nodes[1:]: @@ -729,16 +598,6 @@ class CodeGenerator: def visitBitxor(self, node): return self.bitOp(node.nodes, 'BINARY_XOR') - def visitTest(self, node, jump): - end = StackRef() - for child in node.nodes[:-1]: - self.visit(child) - self.emit(jump, end) - self.emit('POP_TOP') - self.visit(node.nodes[-1]) - end.bind(self.code.getCurInst()) - return 1 - def visitAssert(self, node): # XXX __debug__ and AssertionError appear to be special cases # -- they are always loaded as globals even if there are local @@ -757,6 +616,16 @@ class CodeGenerator: self.emit('POP_TOP') return 1 + def visitTest(self, node, jump): + end = StackRef() + for child in node.nodes[:-1]: + self.visit(child) + self.emit(jump, end) + self.emit('POP_TOP') + self.visit(node.nodes[-1]) + end.bind(self.code.getCurInst()) + return 1 + def visitAnd(self, node): return self.visitTest(node, 'JUMP_IF_FALSE') @@ -879,7 +748,7 @@ class LocalNameFinder: for name in node.names: self.names.add(name) - def visitClassdef(self, node): + def visitClass(self, node): self.names.add(node.name) return 1 @@ -921,8 +790,7 @@ class CompiledModule: self.filename = filename def compile(self): - t = transformer.Transformer() - self.ast = t.parsesuite(self.source) + self.ast = parse(self.source) cg = CodeGenerator(self.filename) walk(self.ast, cg) self.code = cg.asConst() @@ -950,22 +818,3 @@ def compile(filename): mod.compile() mod.dump(filename + 'c') -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 not args: - print "no files to compile" - else: - for filename in args: - if VERBOSE: - print filename - compile(filename) diff --git a/Tools/compiler/compiler/pycodegen.py b/Tools/compiler/compiler/pycodegen.py index 4587bf4..b2d55d9 100644 --- a/Tools/compiler/compiler/pycodegen.py +++ b/Tools/compiler/compiler/pycodegen.py @@ -5,7 +5,7 @@ CodeGenerator. Eventually, this will get split into the ASTVisitor as a generic tool and CodeGenerator as a specific tool. """ -from p2c import transformer, ast +from compiler import parseFile, ast, visitor, walk, parse from pyassem import StackRef, PyAssembler, TupleArg import dis import misc @@ -18,143 +18,6 @@ import stat import struct import types -def parse(path): - f = open(path) - src = f.read() - f.close() - t = transformer.Transformer() - return t.parsesuite(src) - -def walk(tree, visitor, verbose=None, walker=None): - if walker: - w = walker() - else: - w = ASTVisitor() - if verbose is not None: - w.VERBOSE = verbose - w.preorder(tree, visitor) - return w.visitor - -def dumpNode(node): - print node.__class__ - for attr in dir(node): - if attr[0] != '_': - print "\t", "%-10.10s" % attr, getattr(node, attr) - -class ASTVisitor: - """Performs a depth-first walk of the AST - - The ASTVisitor will walk the AST, performing either a preorder or - postorder traversal depending on which method is called. - - methods: - preorder(tree, visitor) - postorder(tree, visitor) - tree: an instance of ast.Node - visitor: an instance with visitXXX methods - - The ASTVisitor is responsible for walking over the tree in the - correct order. For each node, it checks the visitor argument for - a method named 'visitNodeType' where NodeType is the name of the - node's class, e.g. Classdef. If the method exists, it is called - with the node as its sole argument. - - The visitor method for a particular node type can control how - child nodes are visited during a preorder walk. (It can't control - the order during a postorder walk, because it is called _after_ - the walk has occurred.) The ASTVisitor modifies the visitor - argument by adding a visit method to the visitor; this method can - be used to visit a particular child node. If the visitor method - returns a true value, the ASTVisitor will not traverse the child - nodes. - - XXX The interface for controlling the preorder walk needs to be - re-considered. The current interface is convenient for visitors - that mostly let the ASTVisitor do everything. For something like - a code generator, where you want to walk to occur in a specific - order, it's a pain to add "return 1" to the end of each method. - - XXX Perhaps I can use a postorder walk for the code generator? - """ - - VERBOSE = 0 - - def __init__(self): - self.node = None - - def preorder(self, tree, visitor): - """Do preorder walk of tree using visitor""" - self.visitor = visitor - visitor.visit = self._preorder - self._preorder(tree) - - def _preorder(self, node): - stop = self.dispatch(node) - if stop: - return - for child in node.getChildren(): - if isinstance(child, ast.Node): - self._preorder(child) - - def postorder(self, tree, visitor): - """Do preorder walk of tree using visitor""" - self.visitor = visitor - visitor.visit = self._postorder - self._postorder(tree) - - def _postorder(self, tree): - for child in node.getChildren(): - if isinstance(child, ast.Node): - self._preorder(child) - self.dispatch(node) - - def dispatch(self, node): - self.node = node - className = node.__class__.__name__ - meth = getattr(self.visitor, 'visit' + className, None) - 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) - -class ExampleASTVisitor(ASTVisitor): - """Prints examples of the nodes that aren't visited - - This visitor-driver is only useful for development, when it's - helpful to develop a visitor incremently, and get feedback on what - you still have to do. - """ - examples = {} - - def dispatch(self, node): - self.node = node - className = node.__class__.__name__ - meth = getattr(self.visitor, 'visit' + className, None) - 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) - else: - klass = node.__class__ - if self.VERBOSE < 2: - if self.examples.has_key(klass): - return - self.examples[klass] = klass - print - print klass - for attr in dir(node): - if attr[0] != '_': - print "\t", "%-12.12s" % attr, getattr(node, attr) - print - class CodeGenerator: """Generate bytecode for the Python VM""" @@ -311,7 +174,7 @@ class CodeGenerator: self.emit('IMPORT_FROM', name) self.emit('POP_TOP') - def visitClassdef(self, node): + def visitClass(self, node): self.emit('SET_LINENO', node.lineno) self.emit('LOAD_CONST', node.name) for base in node.bases: @@ -660,17 +523,14 @@ class CodeGenerator: visitAssList = visitAssTuple + # binary ops + def binaryOp(self, node, op): self.visit(node.left) self.visit(node.right) self.emit(op) return 1 - def unaryOp(self, node, op): - self.visit(node.expr) - self.emit(op) - return 1 - def visitAdd(self, node): return self.binaryOp(node, 'BINARY_ADD') @@ -695,6 +555,13 @@ class CodeGenerator: def visitRightShift(self, node): return self.binaryOp(node, 'BINARY_RSHIFT') + # unary ops + + def unaryOp(self, node, op): + self.visit(node.expr) + self.emit(op) + return 1 + def visitInvert(self, node): return self.unaryOp(node, 'UNARY_INVERT') @@ -713,6 +580,8 @@ class CodeGenerator: def visitBackquote(self, node): return self.unaryOp(node, 'UNARY_CONVERT') + # bit ops + def bitOp(self, nodes, op): self.visit(nodes[0]) for node in nodes[1:]: @@ -729,16 +598,6 @@ class CodeGenerator: def visitBitxor(self, node): return self.bitOp(node.nodes, 'BINARY_XOR') - def visitTest(self, node, jump): - end = StackRef() - for child in node.nodes[:-1]: - self.visit(child) - self.emit(jump, end) - self.emit('POP_TOP') - self.visit(node.nodes[-1]) - end.bind(self.code.getCurInst()) - return 1 - def visitAssert(self, node): # XXX __debug__ and AssertionError appear to be special cases # -- they are always loaded as globals even if there are local @@ -757,6 +616,16 @@ class CodeGenerator: self.emit('POP_TOP') return 1 + def visitTest(self, node, jump): + end = StackRef() + for child in node.nodes[:-1]: + self.visit(child) + self.emit(jump, end) + self.emit('POP_TOP') + self.visit(node.nodes[-1]) + end.bind(self.code.getCurInst()) + return 1 + def visitAnd(self, node): return self.visitTest(node, 'JUMP_IF_FALSE') @@ -879,7 +748,7 @@ class LocalNameFinder: for name in node.names: self.names.add(name) - def visitClassdef(self, node): + def visitClass(self, node): self.names.add(node.name) return 1 @@ -921,8 +790,7 @@ class CompiledModule: self.filename = filename def compile(self): - t = transformer.Transformer() - self.ast = t.parsesuite(self.source) + self.ast = parse(self.source) cg = CodeGenerator(self.filename) walk(self.ast, cg) self.code = cg.asConst() @@ -950,22 +818,3 @@ def compile(filename): mod.compile() mod.dump(filename + 'c') -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 not args: - print "no files to compile" - else: - for filename in args: - if VERBOSE: - print filename - compile(filename) |