diff options
author | Guido van Rossum <guido@python.org> | 2007-05-14 22:03:55 +0000 |
---|---|---|
committer | Guido van Rossum <guido@python.org> | 2007-05-14 22:03:55 +0000 |
commit | a8add0ec5ef05c26e1641b8310b65ddd75c0fec3 (patch) | |
tree | 1626110463ca617ab105990ee1923f6ee65c7476 /Lib/compiler/pycodegen.py | |
parent | 827b055ffe8060ac229cda8d75eb24176cc697c0 (diff) | |
download | cpython-git-a8add0ec5ef05c26e1641b8310b65ddd75c0fec3.tar.gz |
Merged revisions 55270-55324 via svnmerge from
svn+ssh://pythondev@svn.python.org/python/branches/p3yk
........
r55271 | fred.drake | 2007-05-11 10:14:47 -0700 (Fri, 11 May 2007) | 3 lines
remove jpeg, panel libraries for SGI; there is more IRIX stuff left over,
I guess that should be removed too, but will leave for someone who is sure
........
r55280 | fred.drake | 2007-05-11 19:11:37 -0700 (Fri, 11 May 2007) | 1 line
remove mention of file that has been removed
........
r55301 | brett.cannon | 2007-05-13 17:38:05 -0700 (Sun, 13 May 2007) | 4 lines
Remove rexec and Bastion from the stdlib. This also eliminates the need for
f_restricted on frames. This in turn negates the need for
PyEval_GetRestricted() and PyFrame_IsRestricted().
........
r55303 | brett.cannon | 2007-05-13 19:22:22 -0700 (Sun, 13 May 2007) | 2 lines
Remove the md5 and sha modules.
........
r55305 | george.yoshida | 2007-05-13 19:45:55 -0700 (Sun, 13 May 2007) | 2 lines
fix markup
........
r55306 | neal.norwitz | 2007-05-13 19:47:57 -0700 (Sun, 13 May 2007) | 1 line
Get the doc building again after some removals.
........
r55307 | neal.norwitz | 2007-05-13 19:50:45 -0700 (Sun, 13 May 2007) | 1 line
Get test_pyclbr passing again after getstatus was removed from commands. This "test case" was weird since it was just importing a seemingly random module. Remove the import
........
r55322 | brett.cannon | 2007-05-14 14:09:20 -0700 (Mon, 14 May 2007) | 3 lines
Remove the compiler package. Will eventually need a mechanism to byte compile
an AST.
........
Diffstat (limited to 'Lib/compiler/pycodegen.py')
-rw-r--r-- | Lib/compiler/pycodegen.py | 1580 |
1 files changed, 0 insertions, 1580 deletions
diff --git a/Lib/compiler/pycodegen.py b/Lib/compiler/pycodegen.py deleted file mode 100644 index cc24650cfd..0000000000 --- a/Lib/compiler/pycodegen.py +++ /dev/null @@ -1,1580 +0,0 @@ -import imp -import os -import marshal -import struct -import sys -from cStringIO import StringIO - -from compiler import ast, parse, walk, syntax -from compiler import pyassem, misc, future, symbols -from compiler.consts import SC_LOCAL, SC_GLOBAL, SC_FREE, SC_CELL -from compiler.consts import (CO_VARARGS, CO_VARKEYWORDS, CO_NEWLOCALS, - CO_NESTED, CO_GENERATOR, CO_FUTURE_DIVISION, - CO_FUTURE_ABSIMPORT, CO_FUTURE_WITH_STATEMENT) -from compiler.pyassem import TupleArg - -# XXX The version-specific code can go, since this code only works with 2.x. -# Do we have Python 1.x or Python 2.x? -try: - VERSION = sys.version_info[0] -except AttributeError: - VERSION = 1 - -callfunc_opcode_info = { - # (Have *args, Have **args) : opcode - (0,0) : "CALL_FUNCTION", - (1,0) : "CALL_FUNCTION_VAR", - (0,1) : "CALL_FUNCTION_KW", - (1,1) : "CALL_FUNCTION_VAR_KW", -} - -LOOP = 1 -EXCEPT = 2 -TRY_FINALLY = 3 -END_FINALLY = 4 - -def compileFile(filename, display=0): - f = open(filename, 'U') - buf = f.read() - f.close() - mod = Module(buf, filename) - try: - mod.compile(display) - except SyntaxError: - raise - else: - f = open(filename + "c", "wb") - mod.dump(f) - f.close() - -def compile(source, filename, mode, flags=None, dont_inherit=None): - """Replacement for builtin compile() function""" - if flags is not None or dont_inherit is not None: - raise RuntimeError, "not implemented yet" - - if mode == "single": - gen = Interactive(source, filename) - elif mode == "exec": - gen = Module(source, filename) - elif mode == "eval": - gen = Expression(source, filename) - else: - raise ValueError("compile() 3rd arg must be 'exec' or " - "'eval' or 'single'") - gen.compile() - return gen.code - -class AbstractCompileMode: - - mode = None # defined by subclass - - def __init__(self, source, filename): - self.source = source - self.filename = filename - self.code = None - - def _get_tree(self): - tree = parse(self.source, self.mode) - misc.set_filename(self.filename, tree) - syntax.check(tree) - return tree - - def compile(self): - pass # implemented by subclass - - def getCode(self): - return self.code - -class Expression(AbstractCompileMode): - - mode = "eval" - - def compile(self): - tree = self._get_tree() - gen = ExpressionCodeGenerator(tree) - self.code = gen.getCode() - -class Interactive(AbstractCompileMode): - - mode = "single" - - def compile(self): - tree = self._get_tree() - gen = InteractiveCodeGenerator(tree) - self.code = gen.getCode() - -class Module(AbstractCompileMode): - - mode = "exec" - - def compile(self, display=0): - tree = self._get_tree() - gen = ModuleCodeGenerator(tree) - if display: - import pprint - print(pprint.pprint(tree)) - self.code = gen.getCode() - - def dump(self, f): - f.write(self.getPycHeader()) - marshal.dump(self.code, f) - - MAGIC = imp.get_magic() - - 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. - mtime = os.path.getmtime(self.filename) - mtime = struct.pack('<i', mtime) - return self.MAGIC + mtime - -class LocalNameFinder: - """Find local names in scope""" - def __init__(self, names=()): - self.names = misc.Set() - self.globals = misc.Set() - for name in names: - self.names.add(name) - - # XXX list comprehensions and for loops - - def getLocals(self): - for elt in self.globals.elements(): - if self.names.has_elt(elt): - self.names.remove(elt) - return self.names - - def visitDict(self, node): - pass - - def visitGlobal(self, node): - for name in node.names: - self.globals.add(name) - - def visitFunction(self, node): - self.names.add(node.name) - - def visitLambda(self, node): - pass - - def visitImport(self, node): - for name, alias in node.names: - self.names.add(alias or name) - - def visitFrom(self, node): - for name, alias in node.names: - self.names.add(alias or name) - - def visitClass(self, node): - self.names.add(node.name) - - def visitAssName(self, node): - self.names.add(node.name) - -def is_constant_false(node): - if isinstance(node, ast.Const): - if not node.value: - return 1 - return 0 - -class CodeGenerator: - """Defines basic code generator for Python bytecode - - This class is an abstract base class. Concrete subclasses must - define an __init__() that defines self.graph and then calls the - __init__() defined in this class. - - The concrete class must also define the class attributes - NameFinder, FunctionGen, and ClassGen. These attributes can be - defined in the initClass() method, which is a hook for - initializing these methods after all the classes have been - defined. - """ - - optimized = 0 # is namespace access optimized? - __initialized = None - class_name = None # provide default for instance variable - - def __init__(self): - if self.__initialized is None: - self.initClass() - self.__class__.__initialized = 1 - self.checkClass() - self.locals = misc.Stack() - self.setups = misc.Stack() - self.last_lineno = None - self._setupGraphDelegation() - - # XXX set flags based on future features - futures = self.get_module().futures - for feature in futures: - if feature == "division": - self.graph.setFlag(CO_FUTURE_DIVISION) - elif feature == "absolute_import": - self.graph.setFlag(CO_FUTURE_ABSIMPORT) - elif feature == "with_statement": - self.graph.setFlag(CO_FUTURE_WITH_STATEMENT) - - def initClass(self): - """This method is called once for each class""" - - def checkClass(self): - """Verify that class is constructed correctly""" - try: - assert hasattr(self, 'graph') - assert getattr(self, 'NameFinder') - assert getattr(self, 'FunctionGen') - assert getattr(self, 'ClassGen') - except AssertionError as msg: - intro = "Bad class construction for %s" % self.__class__.__name__ - raise AssertionError, intro - - 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 - - def getCode(self): - """Return a code object""" - return self.graph.getCode() - - def mangle(self, name): - if self.class_name is not None: - return misc.mangle(name, self.class_name) - else: - return name - - def parseSymbols(self, tree): - s = symbols.SymbolVisitor() - walk(tree, s) - return s.scopes - - def get_module(self): - raise RuntimeError, "should be implemented by subclasses" - - # Next five methods handle name access - - def isLocalName(self, name): - return self.locals.top().has_elt(name) - - def storeName(self, name): - self._nameOp('STORE', name) - - def loadName(self, name): - self._nameOp('LOAD', name) - - def delName(self, name): - self._nameOp('DELETE', name) - - def _nameOp(self, prefix, name): - name = self.mangle(name) - scope = self.scope.check_name(name) - if scope == SC_LOCAL: - if not self.optimized: - self.emit(prefix + '_NAME', name) - else: - self.emit(prefix + '_FAST', name) - elif scope == SC_GLOBAL: - if not self.optimized: - self.emit(prefix + '_NAME', name) - else: - self.emit(prefix + '_GLOBAL', name) - elif scope == SC_FREE or scope == SC_CELL: - self.emit(prefix + '_DEREF', name) - else: - raise RuntimeError, "unsupported scope for var %s: %d" % \ - (name, scope) - - def _implicitNameOp(self, prefix, name): - """Emit name ops for names generated implicitly by for loops - - The interpreter generates names that start with a period or - dollar sign. The symbol table ignores these names because - they aren't present in the program text. - """ - if self.optimized: - self.emit(prefix + '_FAST', name) - else: - self.emit(prefix + '_NAME', name) - - # The set_lineno() function and the explicit emit() calls for - # SET_LINENO below are only used to generate the line number table. - # As of Python 2.3, the interpreter does not have a SET_LINENO - # instruction. pyassem treats SET_LINENO opcodes as a special case. - - def set_lineno(self, node, force=False): - """Emit SET_LINENO if necessary. - - The instruction is considered necessary if the node has a - lineno attribute and it is different than the last lineno - emitted. - - Returns true if SET_LINENO was emitted. - - There are no rules for when an AST node should have a lineno - attribute. The transformer and AST code need to be reviewed - and a consistent policy implemented and documented. Until - then, this method works around missing line numbers. - """ - lineno = getattr(node, 'lineno', None) - if lineno is not None and (lineno != self.last_lineno - or force): - self.emit('SET_LINENO', lineno) - self.last_lineno = lineno - return True - return False - - # The first few visitor methods handle nodes that generator new - # code objects. They use class attributes to determine what - # specialized code generators to use. - - NameFinder = LocalNameFinder - FunctionGen = None - ClassGen = None - - def visitModule(self, node): - self.scopes = self.parseSymbols(node) - self.scope = self.scopes[node] - self.emit('SET_LINENO', 0) - if node.doc: - self.emit('LOAD_CONST', node.doc) - self.storeName('__doc__') - lnf = walk(node.node, self.NameFinder(), verbose=0) - self.locals.push(lnf.getLocals()) - self.visit(node.node) - self.emit('LOAD_CONST', None) - self.emit('RETURN_VALUE') - - def visitExpression(self, node): - self.set_lineno(node) - self.scopes = self.parseSymbols(node) - self.scope = self.scopes[node] - self.visit(node.node) - self.emit('RETURN_VALUE') - - def visitFunction(self, node): - self._visitFuncOrLambda(node, isLambda=0) - if node.doc: - self.setDocstring(node.doc) - self.storeName(node.name) - - def visitLambda(self, node): - self._visitFuncOrLambda(node, isLambda=1) - - def _visitFuncOrLambda(self, node, isLambda=0): - if not isLambda and node.decorators: - for decorator in node.decorators.nodes: - self.visit(decorator) - ndecorators = len(node.decorators.nodes) - else: - ndecorators = 0 - - gen = self.FunctionGen(node, self.scopes, isLambda, - self.class_name, self.get_module()) - walk(node.code, gen) - gen.finish() - self.set_lineno(node) - num_kwargs = 0 - for keyword in node.kwonlyargs: - default = keyword.expr - if isinstance(default, ast.EmptyNode): - continue - self.emit('LOAD_CONST', keyword.arg.name) - self.visit(default) - num_kwargs += 1 - for default in node.defaults: - self.visit(default) - - num_annotations = self._visit_annotations(node) - - oparg = len(node.defaults) - oparg |= num_kwargs << 8 - oparg |= num_annotations << 16 - - self._makeClosure(gen, oparg) - for i in range(ndecorators): - self.emit('CALL_FUNCTION', 1) - - def _visit_annotations(self, node): - # emit code, return num_annotations - annotations = [] - annotations.extend(self._visit_argument_annotations(node.arguments)) - annotations.extend(self._visit_kwarg_annotations(node.kwonlyargs)) - if node.returns: - self.visit(node.returns) - annotations.append('return') - if not annotations: - return 0 - self.emit('LOAD_CONST', tuple(annotations)) - return len(annotations) + 1 - - def _visit_argument_annotations(self, arguments): - for arg in arguments: - if isinstance(arg, ast.SimpleArg): - if arg.annotation: - self.visit(arg.annotation) - yield arg.name - else: - for name in self._visit_argument_annotations(arg.args): - yield name - - def _visit_kwarg_annotations(self, kwargs): - for kwarg in kwargs: - arg = kwarg.arg - if arg.annotation: - self.visit(arg.annotation) - yield arg.name - - def visitClass(self, node): - gen = self.ClassGen(node, self.scopes, - self.get_module()) - walk(node.code, gen) - gen.finish() - self.set_lineno(node) - self.emit('LOAD_BUILD_CLASS') - self._makeClosure(gen, 0) - self.emit('LOAD_CONST', node.name) - self.finish_visit_call(node, 2) - self.storeName(node.name) - - # The rest are standard visitor methods - - # 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] - if is_constant_false(test): - # XXX will need to check generator stuff here - continue - self.set_lineno(test) - self.visit(test) - nextTest = self.newBlock() - self.emit('JUMP_IF_FALSE', nextTest) - self.nextBlock() - self.emit('POP_TOP') - self.visit(suite) - self.emit('JUMP_FORWARD', end) - self.startBlock(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() - - after = self.newBlock() - self.emit('SETUP_LOOP', after) - - self.nextBlock(loop) - self.setups.push((LOOP, loop)) - - self.set_lineno(node, force=True) - 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') - self.setups.pop() - if node.else_: - self.visit(node.else_) - self.nextBlock(after) - - def visitFor(self, node): - start = self.newBlock() - anchor = self.newBlock() - after = self.newBlock() - self.setups.push((LOOP, start)) - - self.set_lineno(node) - self.emit('SETUP_LOOP', after) - self.visit(node.list) - self.emit('GET_ITER') - - self.nextBlock(start) - self.set_lineno(node, force=1) - self.emit('FOR_ITER', anchor) - self.visit(node.assign) - self.visit(node.body) - self.emit('JUMP_ABSOLUTE', start) - self.nextBlock(anchor) - self.emit('POP_BLOCK') - self.setups.pop() - if node.else_: - self.visit(node.else_) - self.nextBlock(after) - - def visitBreak(self, node): - if not self.setups: - raise SyntaxError, "'break' outside loop (%s, %d)" % \ - (node.filename, node.lineno) - self.set_lineno(node) - self.emit('BREAK_LOOP') - - def visitContinue(self, node): - if not self.setups: - raise SyntaxError, "'continue' outside loop (%s, %d)" % \ - (node.filename, node.lineno) - kind, block = self.setups.top() - if kind == LOOP: - self.set_lineno(node) - self.emit('JUMP_ABSOLUTE', block) - self.nextBlock() - elif kind == EXCEPT or kind == TRY_FINALLY: - self.set_lineno(node) - # find the block that starts the loop - top = len(self.setups) - while top > 0: - top = top - 1 - kind, loop_block = self.setups[top] - if kind == LOOP: - break - if kind != LOOP: - raise SyntaxError, "'continue' outside loop (%s, %d)" % \ - (node.filename, node.lineno) - self.emit('CONTINUE_LOOP', loop_block) - self.nextBlock() - elif kind == END_FINALLY: - msg = "'continue' not allowed inside 'finally' clause (%s, %d)" - raise SyntaxError, msg % (node.filename, node.lineno) - - def visitTest(self, node, jump): - end = self.newBlock() - for child in node.nodes[:-1]: - self.visit(child) - self.emit(jump, end) - self.nextBlock() - self.emit('POP_TOP') - self.visit(node.nodes[-1]) - self.nextBlock(end) - - def visitAnd(self, node): - self.visitTest(node, 'JUMP_IF_FALSE') - - def visitOr(self, node): - self.visitTest(node, 'JUMP_IF_TRUE') - - def visitIfExp(self, node): - endblock = self.newBlock() - elseblock = self.newBlock() - self.visit(node.test) - self.emit('JUMP_IF_FALSE', elseblock) - self.emit('POP_TOP') - self.visit(node.then) - self.emit('JUMP_FORWARD', endblock) - self.nextBlock(elseblock) - self.emit('POP_TOP') - self.visit(node.else_) - self.nextBlock(endblock) - - 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.startBlock(cleanup) - self.emit('ROT_TWO') - self.emit('POP_TOP') - self.nextBlock(end) - - # list comprehensions - __list_count = 0 - - def visitListComp(self, node): - self.set_lineno(node) - # setup list - tmpname = "$list%d" % self.__list_count - self.__list_count = self.__list_count + 1 - self.emit('BUILD_LIST', 0) - self.emit('DUP_TOP') - self._implicitNameOp('STORE', tmpname) - - stack = [] - for i, for_ in zip(range(len(node.quals)), node.quals): - start, anchor = self.visit(for_) - cont = None - for if_ in for_.ifs: - if cont is None: - cont = self.newBlock() - self.visit(if_, cont) - stack.insert(0, (start, cont, anchor)) - - self._implicitNameOp('LOAD', tmpname) - self.visit(node.expr) - self.emit('LIST_APPEND') - - for start, cont, anchor in stack: - if cont: - skip_one = self.newBlock() - self.emit('JUMP_FORWARD', skip_one) - self.startBlock(cont) - self.emit('POP_TOP') - self.nextBlock(skip_one) - self.emit('JUMP_ABSOLUTE', start) - self.startBlock(anchor) - self._implicitNameOp('DELETE', tmpname) - - self.__list_count = self.__list_count - 1 - - def visitListCompFor(self, node): - start = self.newBlock() - anchor = self.newBlock() - - self.visit(node.list) - self.emit('GET_ITER') - self.nextBlock(start) - self.set_lineno(node, force=True) - self.emit('FOR_ITER', anchor) - self.nextBlock() - self.visit(node.assign) - return start, anchor - - def visitListCompIf(self, node, branch): - self.set_lineno(node, force=True) - self.visit(node.test) - self.emit('JUMP_IF_FALSE', branch) - self.newBlock() - self.emit('POP_TOP') - - def _makeClosure(self, gen, args): - frees = gen.scope.get_free_vars() - if frees: - for name in frees: - self.emit('LOAD_CLOSURE', name) - self.emit('BUILD_TUPLE', len(frees)) - self.emit('LOAD_CONST', gen) - self.emit('MAKE_CLOSURE', args) - else: - self.emit('LOAD_CONST', gen) - self.emit('MAKE_FUNCTION', args) - - def visitGenExpr(self, node): - gen = GenExprCodeGenerator(node, self.scopes, self.class_name, - self.get_module()) - walk(node.code, gen) - gen.finish() - self.set_lineno(node) - self._makeClosure(gen, 0) - # precomputation of outmost iterable - self.visit(node.code.quals[0].iter) - self.emit('GET_ITER') - self.emit('CALL_FUNCTION', 1) - - def visitGenExprInner(self, node): - self.set_lineno(node) - # setup list - - stack = [] - for i, for_ in zip(range(len(node.quals)), node.quals): - start, anchor, end = self.visit(for_) - cont = None - for if_ in for_.ifs: - if cont is None: - cont = self.newBlock() - self.visit(if_, cont) - stack.insert(0, (start, cont, anchor, end)) - - self.visit(node.expr) - self.emit('YIELD_VALUE') - self.emit('POP_TOP') - - for start, cont, anchor, end in stack: - if cont: - skip_one = self.newBlock() - self.emit('JUMP_FORWARD', skip_one) - self.startBlock(cont) - self.emit('POP_TOP') - self.nextBlock(skip_one) - self.emit('JUMP_ABSOLUTE', start) - self.startBlock(anchor) - self.emit('POP_BLOCK') - self.setups.pop() - self.startBlock(end) - - self.emit('LOAD_CONST', None) - - def visitGenExprFor(self, node): - start = self.newBlock() - anchor = self.newBlock() - end = self.newBlock() - - self.setups.push((LOOP, start)) - self.emit('SETUP_LOOP', end) - - if node.is_outmost: - self.loadName('.0') - else: - self.visit(node.iter) - self.emit('GET_ITER') - - self.nextBlock(start) - self.set_lineno(node, force=True) - self.emit('FOR_ITER', anchor) - self.nextBlock() - self.visit(node.assign) - return start, anchor, end - - def visitGenExprIf(self, node, branch): - self.set_lineno(node, force=True) - self.visit(node.test) - self.emit('JUMP_IF_FALSE', branch) - self.newBlock() - self.emit('POP_TOP') - - # exception related - - def visitAssert(self, node): - # XXX would be interesting to implement this via a - # transformation of the AST before this stage - if __debug__: - end = self.newBlock() - self.set_lineno(node) - # XXX AssertionError appears to be special case -- it is always - # loaded as a global even if there is a local name. I guess this - # is a sort of renaming op. - self.nextBlock() - self.visit(node.test) - self.emit('JUMP_IF_TRUE', end) - self.nextBlock() - self.emit('POP_TOP') - self.emit('LOAD_GLOBAL', 'AssertionError') - if node.fail: - self.visit(node.fail) - self.emit('RAISE_VARARGS', 2) - else: - self.emit('RAISE_VARARGS', 1) - self.nextBlock(end) - self.emit('POP_TOP') - - def visitRaise(self, node): - self.set_lineno(node) - n = 0 - if node.expr1: - self.visit(node.expr1) - n = n + 1 - if node.expr2: - self.visit(node.expr2) - n = n + 1 - if node.expr3: - self.visit(node.expr3) - n = n + 1 - self.emit('RAISE_VARARGS', n) - - def visitTryExcept(self, node): - body = self.newBlock() - handlers = self.newBlock() - end = self.newBlock() - if node.else_: - lElse = self.newBlock() - else: - lElse = end - self.set_lineno(node) - self.emit('SETUP_EXCEPT', handlers) - self.nextBlock(body) - self.setups.push((EXCEPT, body)) - self.visit(node.body) - self.emit('POP_BLOCK') - self.setups.pop() - self.emit('JUMP_FORWARD', lElse) - self.startBlock(handlers) - - last = len(node.handlers) - 1 - for i in range(len(node.handlers)): - expr, target, body = node.handlers[i] - self.set_lineno(expr) - if expr: - self.emit('DUP_TOP') - self.visit(expr) - self.emit('COMPARE_OP', 'exception match') - next = self.newBlock() - self.emit('JUMP_IF_FALSE', next) - self.nextBlock() - self.emit('POP_TOP') - self.emit('POP_TOP') - if target: - cleanup_body = self.newBlock() - cleanup_final = self.newBlock() - target_name = target[1] - - self.storeName(target_name) - self.emit('POP_TOP') - self.emit('SETUP_FINALLY', cleanup_final) - self.nextBlock(cleanup_body) - self.setups.push((TRY_FINALLY, cleanup_body)) - self.visit(body) - self.emit('POP_BLOCK') - self.setups.pop() - self.emit('LOAD_CONST', None) - self.nextBlock(cleanup_final) - self.setups.push((END_FINALLY, cleanup_final)) - - - self.emit('LOAD_CONST', None) - self.storeName(target_name) - self._implicitNameOp('DELETE', target_name) - - self.emit('END_FINALLY') - self.setups.pop() - else: - self.emit('POP_TOP') - self.emit('POP_TOP') - self.visit(body) - self.emit('JUMP_FORWARD', end) - if expr: - self.nextBlock(next) - else: - self.nextBlock() - if expr: # XXX - self.emit('POP_TOP') - self.emit('END_FINALLY') - if node.else_: - self.nextBlock(lElse) - self.visit(node.else_) - self.nextBlock(end) - - def visitTryFinally(self, node): - body = self.newBlock() - final = self.newBlock() - self.set_lineno(node) - self.emit('SETUP_FINALLY', final) - self.nextBlock(body) - self.setups.push((TRY_FINALLY, body)) - self.visit(node.body) - self.emit('POP_BLOCK') - self.setups.pop() - self.emit('LOAD_CONST', None) - self.nextBlock(final) - self.setups.push((END_FINALLY, final)) - self.visit(node.final) - self.emit('END_FINALLY') - self.setups.pop() - - __with_count = 0 - - def visitWith(self, node): - body = self.newBlock() - final = self.newBlock() - exitvar = "$exit%d" % self.__with_count - valuevar = "$value%d" % self.__with_count - self.__with_count += 1 - self.set_lineno(node) - self.visit(node.expr) - self.emit('DUP_TOP') - self.emit('LOAD_ATTR', '__exit__') - self._implicitNameOp('STORE', exitvar) - self.emit('LOAD_ATTR', '__enter__') - self.emit('CALL_FUNCTION', 0) - if node.vars is None: - self.emit('POP_TOP') - else: - self._implicitNameOp('STORE', valuevar) - self.emit('SETUP_FINALLY', final) - self.nextBlock(body) - self.setups.push((TRY_FINALLY, body)) - if node.vars is not None: - self._implicitNameOp('LOAD', valuevar) - self._implicitNameOp('DELETE', valuevar) - self.visit(node.vars) - self.visit(node.body) - self.emit('POP_BLOCK') - self.setups.pop() - self.emit('LOAD_CONST', None) - self.nextBlock(final) - self.setups.push((END_FINALLY, final)) - self._implicitNameOp('LOAD', exitvar) - self._implicitNameOp('DELETE', exitvar) - self.emit('WITH_CLEANUP') - self.emit('END_FINALLY') - self.setups.pop() - self.__with_count -= 1 - - # misc - - def visitDiscard(self, node): - self.set_lineno(node) - self.visit(node.expr) - self.emit('POP_TOP') - - def visitConst(self, node): - self.emit('LOAD_CONST', node.value) - - def visitBytes(self, node): - self.emit('LOAD_CONST', node.value) - self.emit('MAKE_BYTES') - - def visitKeyword(self, node): - self.emit('LOAD_CONST', node.name) - self.visit(node.expr) - - def visitGlobal(self, node): - # no code to generate - pass - - def visitName(self, node): - self.set_lineno(node) - self.loadName(node.name) - - def visitPass(self, node): - self.set_lineno(node) - - def visitImport(self, node): - self.set_lineno(node) - level = 0 if self.graph.checkFlag(CO_FUTURE_ABSIMPORT) else -1 - for name, alias in node.names: - if VERSION > 1: - self.emit('LOAD_CONST', level) - self.emit('LOAD_CONST', None) - self.emit('IMPORT_NAME', name) - mod = name.split(".")[0] - if alias: - self._resolveDots(name) - self.storeName(alias) - else: - self.storeName(mod) - - def visitFrom(self, node): - self.set_lineno(node) - level = node.level - if level == 0 and not self.graph.checkFlag(CO_FUTURE_ABSIMPORT): - level = -1 - fromlist = map(lambda (name, alias): name, node.names) - if VERSION > 1: - self.emit('LOAD_CONST', level) - self.emit('LOAD_CONST', tuple(fromlist)) - self.emit('IMPORT_NAME', node.modname) - for name, alias in node.names: - if VERSION > 1: - if name == '*': - self.namespace = 0 - self.emit('IMPORT_STAR') - # There can only be one name w/ from ... import * - assert len(node.names) == 1 - return - else: - self.emit('IMPORT_FROM', name) - self._resolveDots(name) - self.storeName(alias or name) - else: - self.emit('IMPORT_FROM', name) - self.emit('POP_TOP') - - def _resolveDots(self, name): - elts = name.split(".") - if len(elts) == 1: - return - for elt in elts[1:]: - self.emit('LOAD_ATTR', elt) - - def visitGetattr(self, node): - self.visit(node.expr) - self.emit('LOAD_ATTR', self.mangle(node.attrname)) - - # next five implement assignments - - def visitAssign(self, node): - self.set_lineno(node) - self.visit(node.expr) - dups = len(node.nodes) - 1 - for i in range(len(node.nodes)): - elt = node.nodes[i] - if i < dups: - self.emit('DUP_TOP') - if isinstance(elt, ast.Node): - self.visit(elt) - - def visitAssName(self, node): - if node.flags == 'OP_ASSIGN': - self.storeName(node.name) - elif node.flags == 'OP_DELETE': - self.set_lineno(node) - self.delName(node.name) - else: - print("oops", node.flags) - - def visitAssAttr(self, node): - self.visit(node.expr) - if node.flags == 'OP_ASSIGN': - self.emit('STORE_ATTR', self.mangle(node.attrname)) - elif node.flags == 'OP_DELETE': - self.emit('DELETE_ATTR', self.mangle(node.attrname)) - else: - print("warning: unexpected flags:", node.flags) - print(node) - - def _visitAssSequence(self, node, op='UNPACK_SEQUENCE'): - if findOp(node) != 'OP_DELETE': - self.emit(op, len(node.nodes)) - for child in node.nodes: - self.visit(child) - - if VERSION > 1: - visitAssTuple = _visitAssSequence - visitAssList = _visitAssSequence - else: - def visitAssTuple(self, node): - self._visitAssSequence(node, 'UNPACK_TUPLE') - - def visitAssList(self, node): - self._visitAssSequence(node, 'UNPACK_LIST') - - # augmented assignment - - def visitAugAssign(self, node): - self.set_lineno(node) - aug_node = wrap_aug(node.node) - self.visit(aug_node, "load") - self.visit(node.expr) - self.emit(self._augmented_opcode[node.op]) - self.visit(aug_node, "store") - - _augmented_opcode = { - '+=' : 'INPLACE_ADD', - '-=' : 'INPLACE_SUBTRACT', - '*=' : 'INPLACE_MULTIPLY', - '/=' : 'INPLACE_TRUE_DIVIDE', - '//=': 'INPLACE_FLOOR_DIVIDE', - '%=' : 'INPLACE_MODULO', - '**=': 'INPLACE_POWER', - '>>=': 'INPLACE_RSHIFT', - '<<=': 'INPLACE_LSHIFT', - '&=' : 'INPLACE_AND', - '^=' : 'INPLACE_XOR', - '|=' : 'INPLACE_OR', - } - - def visitAugName(self, node, mode): - if mode == "load": - self.loadName(node.name) - elif mode == "store": - self.storeName(node.name) - - def visitAugGetattr(self, node, mode): - if mode == "load": - self.visit(node.expr) - self.emit('DUP_TOP') - self.emit('LOAD_ATTR', self.mangle(node.attrname)) - elif mode == "store": - self.emit('ROT_TWO') - self.emit('STORE_ATTR', self.mangle(node.attrname)) - - def visitAugSlice(self, node, mode): - if mode == "load": - self.visitSlice(node, 1) - elif mode == "store": - slice = 0 - if node.lower: - slice = slice | 1 - if node.upper: - slice = slice | 2 - if slice == 0: - self.emit('ROT_TWO') - elif slice == 3: - self.emit('ROT_FOUR') - else: - self.emit('ROT_THREE') - self.emit('STORE_SLICE+%d' % slice) - - def visitAugSubscript(self, node, mode): - if mode == "load": - self.visitSubscript(node, 1) - elif mode == "store": - self.emit('ROT_THREE') - self.emit('STORE_SUBSCR') - - def visitCallFunc(self, node): - self.set_lineno(node) - self.visit(node.node) - self.finish_visit_call(node) - - def finish_visit_call(self, node, pos=0, kw=0): - for arg in node.args: - self.visit(arg) - if isinstance(arg, ast.Keyword): - kw = kw + 1 - else: - pos = pos + 1 - if node.star_args is not None: - self.visit(node.star_args) - if node.dstar_args is not None: - self.visit(node.dstar_args) - have_star = node.star_args is not None - have_dstar = node.dstar_args is not None - opcode = callfunc_opcode_info[have_star, have_dstar] - self.emit(opcode, kw << 8 | pos) - - def visitReturn(self, node): - self.set_lineno(node) - self.visit(node.value) - self.emit('RETURN_VALUE') - - def visitYield(self, node): - self.set_lineno(node) - self.visit(node.value) - self.emit('YIELD_VALUE') - - # slice and subscript stuff - - def visitSlice(self, node, aug_flag=None): - # aug_flag is used by visitAugSlice - self.visit(node.expr) - slice = 0 - if node.lower: - self.visit(node.lower) - slice = slice | 1 - if node.upper: - self.visit(node.upper) - slice = slice | 2 - if aug_flag: - if slice == 0: - self.emit('DUP_TOP') - elif slice == 3: - self.emit('DUP_TOPX', 3) - else: - self.emit('DUP_TOPX', 2) - if node.flags == 'OP_APPLY': - self.emit('SLICE+%d' % slice) - elif node.flags == 'OP_ASSIGN': - self.emit('STORE_SLICE+%d' % slice) - elif node.flags == 'OP_DELETE': - self.emit('DELETE_SLICE+%d' % slice) - else: - print("weird slice", node.flags) - raise - - def visitSubscript(self, node, aug_flag=None): - self.visit(node.expr) - for sub in node.subs: - self.visit(sub) - if len(node.subs) > 1: - self.emit('BUILD_TUPLE', len(node.subs)) - if aug_flag: - self.emit('DUP_TOPX', 2) - if node.flags == 'OP_APPLY': - self.emit('BINARY_SUBSCR') - elif node.flags == 'OP_ASSIGN': - self.emit('STORE_SUBSCR') - elif node.flags == 'OP_DELETE': - self.emit('DELETE_SUBSCR') - - # binary ops - - def binaryOp(self, node, op): - self.visit(node.left) - self.visit(node.right) - self.emit(op) - - def visitAdd(self, node): - return self.binaryOp(node, 'BINARY_ADD') - - def visitSub(self, node): - return self.binaryOp(node, 'BINARY_SUBTRACT') - - def visitMul(self, node): - return self.binaryOp(node, 'BINARY_MULTIPLY') - - def visitDiv(self, node): - return self.binaryOp(node, 'BINARY_TRUE_DIVIDE') - - def visitFloorDiv(self, node): - return self.binaryOp(node, 'BINARY_FLOOR_DIVIDE') - - def visitMod(self, node): - return self.binaryOp(node, 'BINARY_MODULO') - - def visitPower(self, node): - return self.binaryOp(node, 'BINARY_POWER') - - def visitLeftShift(self, node): - return self.binaryOp(node, 'BINARY_LSHIFT') - - def visitRightShift(self, node): - return self.binaryOp(node, 'BINARY_RSHIFT') - - # unary ops - - def unaryOp(self, node, op): - self.visit(node.expr) - self.emit(op) - - def visitInvert(self, node): - return self.unaryOp(node, 'UNARY_INVERT') - - 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 visitNot(self, node): - return self.unaryOp(node, 'UNARY_NOT') - - # bit ops - - def bitOp(self, nodes, op): - self.visit(nodes[0]) - for node in nodes[1:]: - self.visit(node) - self.emit(op) - - def visitBitand(self, node): - return self.bitOp(node.nodes, 'BINARY_AND') - - def visitBitor(self, node): - return self.bitOp(node.nodes, 'BINARY_OR') - - def visitBitxor(self, node): - return self.bitOp(node.nodes, 'BINARY_XOR') - - # object constructors - - def visitTuple(self, node): - self.set_lineno(node) - for elt in node.nodes: - self.visit(elt) - self.emit('BUILD_TUPLE', len(node.nodes)) - - def visitList(self, node): - self.set_lineno(node) - for elt in node.nodes: - self.visit(elt) - self.emit('BUILD_LIST', len(node.nodes)) - - def visitSet(self, node): - self.set_lineno(node) - for elt in node.items: - self.visit(elt) - self.emit('BUILD_SET', len(node.items)) - - def visitSliceobj(self, node): - for child in node.nodes: - self.visit(child) - self.emit('BUILD_SLICE', len(node.nodes)) - - def visitDict(self, node): - self.set_lineno(node) - self.emit('BUILD_MAP', 0) - for k, v in node.items: - self.emit('DUP_TOP') - self.visit(k) - self.visit(v) - self.emit('ROT_THREE') - self.emit('STORE_SUBSCR') - -class NestedScopeMixin: - """Defines initClass() for nested scoping (Python 2.2-compatible)""" - def initClass(self): - self.__class__.NameFinder = LocalNameFinder - self.__class__.FunctionGen = FunctionCodeGenerator - self.__class__.ClassGen = ClassCodeGenerator - -class ModuleCodeGenerator(NestedScopeMixin, CodeGenerator): - __super_init = CodeGenerator.__init__ - - scopes = None - - def __init__(self, tree): - self.graph = pyassem.PyFlowGraph("<module>", tree.filename) - self.futures = future.find_futures(tree) - self.__super_init() - walk(tree, self) - - def get_module(self): - return self - -class ExpressionCodeGenerator(NestedScopeMixin, CodeGenerator): - __super_init = CodeGenerator.__init__ - - scopes = None - futures = () - - def __init__(self, tree): - self.graph = pyassem.PyFlowGraph("<expression>", tree.filename) - self.__super_init() - walk(tree, self) - - def get_module(self): - return self - -class InteractiveCodeGenerator(NestedScopeMixin, CodeGenerator): - - __super_init = CodeGenerator.__init__ - - scopes = None - futures = () - - def __init__(self, tree): - self.graph = pyassem.PyFlowGraph("<interactive>", tree.filename) - self.__super_init() - self.set_lineno(tree) - walk(tree, self) - self.emit('RETURN_VALUE') - - def get_module(self): - return self - - def visitDiscard(self, node): - # XXX Discard means it's an expression. Perhaps this is a bad - # name. - self.visit(node.expr) - self.emit('PRINT_EXPR') - -class AbstractFunctionCode: - optimized = 1 - lambdaCount = 0 - - def __init__(self, func, scopes, isLambda, class_name, mod): - self.class_name = class_name - self.module = mod - if isLambda: - klass = FunctionCodeGenerator - name = "<lambda.%d>" % klass.lambdaCount - klass.lambdaCount = klass.lambdaCount + 1 - else: - name = func.name - - args, hasTupleArg = generateArgList(func.arguments) - kwonlyargs = generateKwonlyArgList(func.kwonlyargs) - self.graph = pyassem.PyFlowGraph(name, func.filename, args, - kwonlyargs=kwonlyargs, - optimized=1) - self.isLambda = isLambda - self.super_init() - - if not isLambda and func.doc: - self.setDocstring(func.doc) - - lnf = walk(func.code, self.NameFinder(args+kwonlyargs), verbose=0) - self.locals.push(lnf.getLocals()) - 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.arguments) - - def get_module(self): - return self.module - - def finish(self): - self.graph.startExitBlock() - if not self.isLambda: - self.emit('LOAD_CONST', None) - self.emit('RETURN_VALUE') - - def generateArgUnpack(self, args): - for i in range(len(args)): - arg = args[i] - if isinstance(arg, ast.NestedArgs): - self.emit('LOAD_FAST', '.%d' % (i * 2)) - self.unpackSequence(tuple(_nested_names(arg))) - - def unpackSequence(self, tup): - if VERSION > 1: - self.emit('UNPACK_SEQUENCE', len(tup)) - else: - self.emit('UNPACK_TUPLE', len(tup)) - for elt in tup: - if isinstance(elt, tuple): - self.unpackSequence(elt) - else: - self._nameOp('STORE', elt) - - unpackTuple = unpackSequence - -class FunctionCodeGenerator(NestedScopeMixin, AbstractFunctionCode, - CodeGenerator): - super_init = CodeGenerator.__init__ # call be other init - scopes = None - - __super_init = AbstractFunctionCode.__init__ - - def __init__(self, func, scopes, isLambda, class_name, mod): - self.scopes = scopes - self.scope = scopes[func] - self.__super_init(func, scopes, isLambda, class_name, mod) - self.graph.setFreeVars(self.scope.get_free_vars()) - self.graph.setCellVars(self.scope.get_cell_vars()) - if self.scope.generator is not None: - self.graph.setFlag(CO_GENERATOR) - -class GenExprCodeGenerator(NestedScopeMixin, AbstractFunctionCode, - CodeGenerator): - super_init = CodeGenerator.__init__ # call be other init - scopes = None - - __super_init = AbstractFunctionCode.__init__ - - def __init__(self, gexp, scopes, class_name, mod): - self.scopes = scopes - self.scope = scopes[gexp] - self.__super_init(gexp, scopes, 1, class_name, mod) - self.graph.setFreeVars(self.scope.get_free_vars()) - self.graph.setCellVars(self.scope.get_cell_vars()) - self.graph.setFlag(CO_GENERATOR) - -class AbstractClassCode: - - def __init__(self, klass, scopes, module): - self.class_name = klass.name - self.module = module - self.graph = pyassem.PyFlowGraph(klass.name, klass.filename, - optimized=0, klass=1) - self.super_init() - lnf = walk(klass.code, self.NameFinder(), verbose=0) - self.locals.push(lnf.getLocals()) - self.graph.setFlag(CO_NEWLOCALS) - if klass.doc: - self.setDocstring(klass.doc) - - def get_module(self): - return self.module - - def finish(self): - self.graph.startExitBlock() - self.emit('LOAD_CONST', None) - self.emit('RETURN_VALUE') - -class ClassCodeGenerator(NestedScopeMixin, AbstractClassCode, CodeGenerator): - super_init = CodeGenerator.__init__ - scopes = None - - __super_init = AbstractClassCode.__init__ - - def __init__(self, klass, scopes, module): - self.scopes = scopes - self.scope = scopes[klass] - self.__super_init(klass, scopes, module) - self.graph.setFreeVars(self.scope.get_free_vars()) - self.graph.setCellVars(self.scope.get_cell_vars()) - self.set_lineno(klass) - self.emit("LOAD_GLOBAL", "__name__") - self.storeName("__module__") - if klass.doc: - self.emit("LOAD_CONST", klass.doc) - self.storeName('__doc__') - -def generateArgList(arglist): - """Generate an arg list marking TupleArgs""" - args = [] - extra = [] - count = 0 - for i in range(len(arglist)): - elt = arglist[i] - if isinstance(elt, ast.SimpleArg): - args.append(elt.name) - elif isinstance(elt, ast.NestedArgs): - t = tuple(_nested_names(elt)) - args.append(TupleArg(i * 2, t)) - extra.extend(misc.flatten(t)) - count = count + 1 - else: - raise ValueError, "unexpect argument type:", elt - return args + extra, count - -def _nested_names(elt): - for arg in elt.args: - if isinstance(arg, ast.SimpleArg): - yield arg.name - elif isinstance(arg, ast.NestedArgs): - yield tuple(_nested_names(arg)) - -def generateKwonlyArgList(keywordOnlyArgs): - kwonlyargs = [] - for elt in keywordOnlyArgs: - assert isinstance(elt, ast.Kwarg) - kwonlyargs.append(elt.arg.name) - return kwonlyargs - -def findOp(node): - """Find the op (DELETE, LOAD, STORE) in an AssTuple tree""" - v = OpFinder() - walk(node, v, verbose=0) - return v.op - -class OpFinder: - def __init__(self): - self.op = None - def visitAssName(self, node): - if self.op is None: - self.op = node.flags - elif self.op != node.flags: - raise ValueError, "mixed ops in stmt" - visitAssAttr = visitAssName - visitSubscript = visitAssName - -class Delegator: - """Base class to support delegation for augmented assignment nodes - - To generator code for augmented assignments, we use the following - wrapper classes. In visitAugAssign, the left-hand expression node - is visited twice. The first time the visit uses the normal method - for that node . The second time the visit uses a different method - that generates the appropriate code to perform the assignment. - These delegator classes wrap the original AST nodes in order to - support the variant visit methods. - """ - def __init__(self, obj): - self.obj = obj - - def __getattr__(self, attr): - return getattr(self.obj, attr) - -class AugGetattr(Delegator): - pass - -class AugName(Delegator): - pass - -class AugSlice(Delegator): - pass - -class AugSubscript(Delegator): - pass - -wrapper = { - ast.Getattr: AugGetattr, - ast.Name: AugName, - ast.Slice: AugSlice, - ast.Subscript: AugSubscript, - } - -def wrap_aug(node): - return wrapper[node.__class__](node) - -if __name__ == "__main__": - for file in sys.argv[1:]: - compileFile(file) |