[pypy-svn] r66266 - pypy/branch/parser-compiler/pypy/interpreter/astcompiler
benjamin at codespeak.net
benjamin at codespeak.net
Thu Jul 16 01:05:48 CEST 2009
Author: benjamin
Date: Thu Jul 16 01:05:48 2009
New Revision: 66266
Added:
pypy/branch/parser-compiler/pypy/interpreter/astcompiler/assemble.py (contents, props changed)
pypy/branch/parser-compiler/pypy/interpreter/astcompiler/codegen.py (contents, props changed)
Log:
add new AST compiler
Added: pypy/branch/parser-compiler/pypy/interpreter/astcompiler/assemble.py
==============================================================================
--- (empty file)
+++ pypy/branch/parser-compiler/pypy/interpreter/astcompiler/assemble.py Thu Jul 16 01:05:48 2009
@@ -0,0 +1,547 @@
+"""
+Python control flow graph generation and bytecode assembly.
+"""
+
+from pypy.interpreter.astcompiler import ast2 as ast, symtable
+from pypy.interpreter import pycode
+from pypy.tool import stdlib_opcode as ops
+
+from pypy.interpreter.error import OperationError
+from pypy.rlib.objectmodel import we_are_translated
+
+
+class Instruction(object):
+
+ def __init__(self, opcode, lineno, arg=0):
+ assert lineno != -1
+ self.opcode = opcode
+ self.arg = arg
+ self.lineno = lineno
+ self.jump = None
+
+ def size(self):
+ if self.opcode >= ops.HAVE_ARGUMENT:
+ if self.arg > 0xFFFF:
+ return 6
+ else:
+ return 3
+ else:
+ return 1
+
+ def jump_to(self, target, absolute=False):
+ self.jump = (target, absolute)
+
+
+ def __repr__(self):
+ data = [ops.opname[self.opcode]]
+ template = "<%s"
+ if self.opcode >= ops.HAVE_ARGUMENT:
+ data.append(self.arg)
+ template += " %i"
+ if self.jump:
+ data.append(self.jump[0])
+ template += " %s"
+ template += ">"
+ return template % tuple(data)
+
+
+class Block(object):
+
+ def __init__(self):
+ self.instructions = []
+ self.next_block = None
+ self.marked = False
+ self.have_return = False
+
+ def _post_order(self, blocks):
+ if self.marked:
+ return
+ self.marked = True
+ if self.next_block is not None:
+ self.next_block._post_order(blocks)
+ for instr in self.instructions:
+ if instr.jump:
+ instr.jump[0]._post_order(blocks)
+ blocks.append(self)
+ self.marked = True
+
+ def post_order(self):
+ blocks = []
+ self._post_order(blocks)
+ blocks.reverse()
+ return blocks
+
+ def code_size(self):
+ i = 0
+ for instr in self.instructions:
+ i += instr.size()
+ return i
+
+ def get_code(self):
+ code = []
+ for instr in self.instructions:
+ opcode = instr.opcode
+ if opcode >= ops.HAVE_ARGUMENT:
+ arg = instr.arg
+ if instr.arg > 0xFFFF:
+ ext = arg >> 16
+ code.append(chr(ops.EXTENDED_ARG))
+ code.append(chr(ext & 0xFF))
+ code.append(chr(ext >> 8))
+ arg &= 0xFFFF
+ code.append(chr(opcode))
+ code.append(chr(arg & 0xFF))
+ code.append(chr(arg >> 8))
+ else:
+ code.append(chr(opcode))
+ return ''.join(code)
+
+
+def _make_index_dict_filter(syms, flag):
+ i = 0
+ result = {}
+ for name, scope in syms.iteritems():
+ if scope == flag:
+ result[name] = i
+ i += 1
+ return result
+
+def _list_to_dict(l, offset=0):
+ result = {}
+ index = offset
+ for i in range(len(l)):
+ result[l[i]] = index
+ index += 1
+ return result
+
+
+class PythonCodeMaker(ast.ASTVisitor):
+
+ def __init__(self, space, name, first_lineno, scope, compile_info):
+ self.space = space
+ self.name = name
+ self.first_lineno = first_lineno
+ self.compile_info = compile_info
+ self.lineno = -1
+ self.first_block = self.new_block()
+ self.use_block(self.first_block)
+ self.names = {}
+ self.var_names = _list_to_dict(scope.varnames)
+ self.cell_vars = _make_index_dict_filter(scope.symbols,
+ symtable.SCOPE_CELL)
+ self.free_vars = _list_to_dict(scope.free_vars, len(self.cell_vars))
+ self.w_consts = space.newdict()
+ self.argcount = 0
+ self.add_none_to_final_return = True
+
+ def new_block(self):
+ return Block()
+
+ def use_block(self, block):
+ self.current_block = block
+ self.instrs = block.instructions
+
+ def use_next_block(self, block=None):
+ if block is None:
+ block = self.new_block()
+ self.current_block.next_block = block
+ self.use_block(block)
+ return block
+
+ def emit_op(self, op):
+ instr = Instruction(op, self.lineno)
+ self.instrs.append(instr)
+ if op == ops.RETURN_VALUE:
+ self.current_block.have_return = True
+ return instr
+
+ def emit_op_arg(self, op, arg):
+ self.instrs.append(Instruction(op, self.lineno, arg))
+
+ def emit_op_name(self, op, container, name):
+ self.emit_op_arg(op, self.add_name(container, name))
+
+ def emit_jump(self, op, block_to, absolute=False):
+ self.emit_op(op).jump_to(block_to, absolute)
+
+ def add_name(self, container, name):
+ name = self.scope.mangle(name)
+ try:
+ index = container[name]
+ except KeyError:
+ index = len(container)
+ container[name] = index
+ return index
+
+ def add_const(self, obj):
+ space = self.space
+ w_key = space.newtuple([obj, space.type(obj)])
+ w_len = space.finditem(self.w_consts, w_key)
+ if w_len is None:
+ w_len = space.len(self.w_consts)
+ space.setitem(self.w_consts, w_key, w_len)
+ return space.int_w(w_len)
+
+ def load_const(self, obj):
+ index = self.add_const(obj)
+ self.emit_op_arg(ops.LOAD_CONST, index)
+
+ def update_position(self, node):
+ self.lineno = node.lineno
+ if self.first_lineno == -1:
+ self.first_lineno = node.lineno
+
+ def _resolve_block_targets(self, blocks):
+ last_extended_arg_count = 0
+ while True:
+ extended_arg_count = 0
+ offset = 0
+ for block in blocks:
+ block.offset = offset
+ offset += block.code_size()
+ for block in blocks:
+ offset = block.offset
+ for instr in block.instructions:
+ offset += instr.size()
+ if instr.jump:
+ target, absolute = instr.jump
+ if absolute:
+ jump_arg = target.offset
+ else:
+ jump_arg = target.offset - offset
+ instr.arg = jump_arg
+ if jump_arg > 0xFFFF:
+ extended_arg_count += 1
+ if extended_arg_count == last_extended_arg_count:
+ break
+ else:
+ last_extended_arg_count = extended_arg_count
+
+ def _build_consts_array(self):
+ w_consts = self.w_consts
+ space = self.space
+ consts_w = [space.w_None] * space.int_w(space.len(w_consts))
+ w_iter = space.iter(w_consts)
+ first = space.wrap(0)
+ while True:
+ try:
+ w_key = space.next(w_iter)
+ except OperationError, e:
+ if not e.match(space, space.w_StopIteration):
+ raise
+ break
+ w_index = space.getitem(w_consts, w_key)
+ consts_w[space.int_w(w_index)] = space.getitem(w_key, first)
+ return consts_w
+
+ def _get_code_flags(self):
+ raise NotImplementedError
+
+ def _stacksize(self, blocks):
+ for block in blocks:
+ block.marked = False
+ block.initial_depth = -1000
+ return self._recursive_stack_depth_walk(blocks[0], 0, 0)
+
+ def _recursive_stack_depth_walk(self, block, depth, max_depth):
+ if block.marked or block.initial_depth >= depth:
+ return max_depth
+ block.marked = True
+ block.initial_depth = depth
+ for instr in block.instructions:
+ depth += _opcode_stack_effect(instr.opcode, instr.arg)
+ if depth >= max_depth:
+ max_depth = depth
+ if instr.jump:
+ max_depth = self._recursive_stack_depth_walk(instr.jump[0],
+ depth, max_depth)
+ if instr.opcode == ops.JUMP_ABSOLUTE or \
+ instr.opcode == ops.JUMP_FORWARD:
+ break
+ if block.next_block:
+ max_depth = self._recursive_stack_depth_walk(block.next_block,
+ depth, max_depth)
+ block.marked = False
+ return max_depth
+
+ def _build_lnotab(self, blocks):
+ lineno_table_builder = LinenoTableBuilder(self.first_lineno)
+ for block in blocks:
+ offset = block.offset
+ for instr in block.instructions:
+ lineno_table_builder.note_lineno_here(offset, instr.lineno)
+ offset += instr.size()
+ return lineno_table_builder.get_table()
+
+ def assemble(self):
+ if self.lineno == -1:
+ self.lineno = self.first_lineno
+ if not self.current_block.have_return:
+ self.use_next_block()
+ if self.add_none_to_final_return:
+ self.load_const(self.space.w_None)
+ self.emit_op(ops.RETURN_VALUE)
+ blocks = self.first_block.post_order()
+ self._resolve_block_targets(blocks)
+ lnotab = self._build_lnotab(blocks)
+ stack_depth = self._stacksize(blocks)
+ consts_w = self._build_consts_array()
+ names = _list_from_dict(self.names)
+ var_names = _list_from_dict(self.var_names)
+ cell_names = _list_from_dict(self.cell_vars)
+ free_names = _list_from_dict(self.free_vars, len(cell_names))
+ flags = self._get_code_flags() | self.compile_info.flags
+ bytecode = ''.join([block.get_code() for block in blocks])
+ return pycode.PyCode(self.space,
+ self.argcount,
+ len(self.var_names),
+ stack_depth,
+ flags,
+ bytecode,
+ consts_w,
+ names,
+ var_names,
+ self.compile_info.filename,
+ self.name,
+ self.first_lineno,
+ lnotab,
+ free_names,
+ cell_names)
+
+
+def _list_from_dict(d, offset=0):
+ result = [None] * len(d)
+ for obj, index in d.iteritems():
+ result[index - offset] = obj
+ return result
+
+
+_static_opcode_stack_effects = {
+ ops.POP_TOP : -1,
+ ops.ROT_TWO : 0,
+ ops.ROT_THREE : 0,
+ ops.ROT_FOUR : 0,
+ ops.DUP_TOP : 1,
+
+ ops.UNARY_POSITIVE : 0,
+ ops.UNARY_NEGATIVE : 0,
+ ops.UNARY_NOT : 0,
+ ops.UNARY_CONVERT : 0,
+ ops.UNARY_INVERT : 0,
+
+ ops.LIST_APPEND : -1,
+
+ ops.BINARY_POWER : -1,
+ ops.BINARY_MULTIPLY : -1,
+ ops.BINARY_DIVIDE : -1,
+ ops.BINARY_MODULO : -1,
+ ops.BINARY_ADD : -1,
+ ops.BINARY_SUBTRACT : -1,
+ ops.BINARY_SUBSCR : -1,
+ ops.BINARY_FLOOR_DIVIDE : -1,
+ ops.BINARY_TRUE_DIVIDE : -1,
+ ops.BINARY_LSHIFT : -1,
+ ops.BINARY_RSHIFT : -1,
+ ops.BINARY_AND : -1,
+ ops.BINARY_OR : -1,
+ ops.BINARY_XOR : -1,
+
+ ops.INPLACE_FLOOR_DIVIDE : -1,
+ ops.INPLACE_TRUE_DIVIDE : -1,
+ ops.INPLACE_ADD : -1,
+ ops.INPLACE_SUBTRACT : -1,
+ ops.INPLACE_MULTIPLY : -1,
+ ops.INPLACE_DIVIDE : -1,
+ ops.INPLACE_MODULO : -1,
+ ops.INPLACE_POWER : -1,
+ ops.INPLACE_LSHIFT : -1,
+ ops.INPLACE_RSHIFT : -1,
+ ops.INPLACE_AND : -1,
+ ops.INPLACE_OR : -1,
+ ops.INPLACE_XOR : -1,
+
+ ops.SLICE+0 : 1,
+ ops.SLICE+1 : 0,
+ ops.SLICE+2 : 0,
+ ops.SLICE+3 : -1,
+ ops.STORE_SLICE+0 : -2,
+ ops.STORE_SLICE+1 : -3,
+ ops.STORE_SLICE+2 : -3,
+ ops.STORE_SLICE+3 : -4,
+ ops.DELETE_SLICE+0 : -1,
+ ops.DELETE_SLICE+1 : -2,
+ ops.DELETE_SLICE+2 : -2,
+ ops.DELETE_SLICE+3 : -3,
+
+ ops.STORE_SUBSCR : -2,
+ ops.DELETE_SUBSCR : -2,
+
+ ops.GET_ITER : 0,
+ ops.FOR_ITER : 1,
+ ops.BREAK_LOOP : 0,
+ ops.CONTINUE_LOOP : 0,
+ ops.SETUP_LOOP : 0,
+
+ ops.PRINT_EXPR : -1,
+ ops.PRINT_ITEM : -1,
+ ops.PRINT_NEWLINE : 0,
+ ops.PRINT_ITEM_TO : -2,
+ ops.PRINT_NEWLINE_TO : -1,
+
+ ops.WITH_CLEANUP : -1,
+ ops.POP_BLOCK : 0,
+ ops.END_FINALLY : -1,
+ ops.SETUP_FINALLY : 3,
+ ops.SETUP_EXCEPT : 3,
+
+ ops.LOAD_LOCALS : 1,
+ ops.RETURN_VALUE : -1,
+ ops.EXEC_STMT : -3,
+ ops.YIELD_VALUE : 0,
+ ops.BUILD_CLASS : -2,
+ ops.BUILD_MAP : 1,
+ ops.COMPARE_OP : -1,
+
+ ops.LOAD_NAME : 1,
+ ops.STORE_NAME : -1,
+ ops.DELETE_NAME : 0,
+
+ ops.LOAD_FAST : 1,
+ ops.STORE_FAST : -1,
+ ops.DELETE_FAST : 0,
+
+ ops.LOAD_ATTR : 0,
+ ops.STORE_ATTR : -2,
+ ops.DELETE_ATTR : -1,
+
+ ops.LOAD_GLOBAL : 1,
+ ops.STORE_GLOBAL : -1,
+ ops.DELETE_GLOBAL : 0,
+
+ ops.LOAD_CLOSURE : 1,
+ ops.LOAD_DEREF : 1,
+ ops.STORE_DEREF : -1,
+
+ ops.LOAD_CONST : 1,
+
+ ops.IMPORT_STAR : -1,
+ ops.IMPORT_NAME : 0,
+ ops.IMPORT_FROM : 1,
+
+ ops.JUMP_FORWARD : 0,
+ ops.JUMP_ABSOLUTE : 0,
+ ops.JUMP_IF_TRUE : 0,
+ ops.JUMP_IF_FALSE : 0,
+}
+
+
+def _compute_UNPACK_SEQUENCE(arg):
+ return arg + 1
+
+def _compute_DUP_TOPX(arg):
+ return arg
+
+def _compute_BUILD_TUPLE(arg):
+ return 1 - arg
+
+def _compute_BUILD_LIST(arg):
+ return 1 - arg
+
+def _compute_MAKE_CLOSURE(arg):
+ return -arg
+
+def _compute_MAKE_FUNCTION(arg):
+ return -arg
+
+def _compute_BUILD_SLICE(arg):
+ if arg == 3:
+ return -2
+ else:
+ return -1
+
+def _compute_RAISE_VARARGS(arg):
+ return -arg
+
+def _num_args(oparg):
+ return (oparg % 256) + 2 * (oparg / 256)
+
+def _compute_CALL_FUNCTION(arg):
+ return _num_args(arg)
+
+def _compute_CALL_FUNCTION_VAR(arg):
+ return _num_args(arg) - 1
+
+def _compute_CALL_FUNCTION_KW(arg):
+ return _num_args(arg) - 1
+
+def _compute_CALL_FUNCTION_VAR_KW(arg):
+ return _num_args(arg) - 2
+
+
+_stack_effect_computers = {}
+for name, func in globals().items():
+ if name.startswith("_compute_"):
+ _stack_effect_computers[getattr(ops, name[9:])] = func
+for op, value in _static_opcode_stack_effects.iteritems():
+ def func(arg, _value=value):
+ return value
+ _stack_effect_computers[op] = func
+del name, func, op, value
+
+
+def _opcode_stack_effect(op, arg):
+ if we_are_translated():
+ for possible_op in ops.unrolling_op_descs:
+ if op == possible_op.index:
+ return _stack_effect_computers[op](arg)
+ else:
+ raise AssertionError("unkown opcode: %s" % (op,))
+ else:
+ try:
+ return _static_opcode_stack_effects[op]
+ except KeyError:
+ return _stack_effect_computers[op](arg)
+
+
+class LinenoTableBuilder(object):
+
+ def __init__(self, first_lineno):
+ self.first = self.current_line = first_lineno
+ self.current_off = 0
+ self.table = []
+
+ def get_table(self):
+ return ''.join(self.table)
+
+ def note_lineno_here(self, offset, lineno):
+ # compute deltas
+ line = lineno - self.current_line
+ # Python assumes that lineno always increases with
+ # increasing bytecode address (lnotab is unsigned char).
+ # Depending on when SET_LINENO instructions are emitted
+ # this is not always true. Consider the code:
+ # a = (1,
+ # b)
+ # In the bytecode stream, the assignment to "a" occurs
+ # after the loading of "b". This works with the C Python
+ # compiler because it only generates a SET_LINENO instruction
+ # for the assignment.
+ if line >= 0:
+ addr = offset - self.current_off
+ if not addr and not line:
+ return
+ push = self.table.append
+ while addr > 255:
+ push(chr(255))
+ push(chr(0))
+ addr -= 255
+ while line > 255:
+ push(chr(addr))
+ push(chr(255))
+ line -= 255
+ addr = 0
+ if addr > 0 or line > 0:
+ push(chr(addr))
+ push(chr(line))
+ self.current_line = lineno
+ self.current_off = offset
Added: pypy/branch/parser-compiler/pypy/interpreter/astcompiler/codegen.py
==============================================================================
--- (empty file)
+++ pypy/branch/parser-compiler/pypy/interpreter/astcompiler/codegen.py Thu Jul 16 01:05:48 2009
@@ -0,0 +1,1133 @@
+"""
+Generate Python bytecode from a Abstract Syntax Tree.
+"""
+
+from pypy.interpreter.astcompiler import ast2 as ast, assemble, symtable, consts
+from pypy.interpreter.pyparser.error import SyntaxError
+from pypy.tool import stdlib_opcode as ops
+
+
+def compile_ast(space, module, info):
+ symbols = symtable.SymtableBuilder(space, module, info)
+ return TopLevelCodeGenerator(space, module, symbols, info).assemble()
+
+
+name_ops_default = {
+ ast.Load : ops.LOAD_NAME,
+ ast.Store : ops.STORE_NAME,
+ ast.Del : ops.DELETE_NAME
+}
+
+name_ops_fast = {
+ ast.Load : ops.LOAD_FAST,
+ ast.Store : ops.STORE_FAST,
+ ast.Del : ops.DELETE_FAST
+}
+
+name_ops_deref = {
+ ast.Load : ops.LOAD_DEREF,
+ ast.Store : ops.STORE_DEREF,
+}
+
+name_ops_global = {
+ ast.Load : ops.LOAD_GLOBAL,
+ ast.Store : ops.STORE_GLOBAL,
+ ast.Del : ops.DELETE_GLOBAL
+}
+
+
+unary_operations = {
+ ast.Invert : ops.UNARY_INVERT,
+ ast.Not : ops.UNARY_NOT,
+ ast.UAdd : ops.UNARY_POSITIVE,
+ ast.USub : ops.UNARY_NEGATIVE
+}
+
+binary_operations = {
+ ast.Add : ops.BINARY_ADD,
+ ast.Sub : ops.BINARY_SUBTRACT,
+ ast.Mult : ops.BINARY_MULTIPLY,
+ ast.Mod : ops.BINARY_MODULO,
+ ast.Pow : ops.BINARY_POWER,
+ ast.LShift : ops.BINARY_LSHIFT,
+ ast.RShift : ops.BINARY_RSHIFT,
+ ast.BitOr : ops.BINARY_OR,
+ ast.BitAnd : ops.BINARY_AND,
+ ast.BitXor : ops.BINARY_XOR,
+ ast.FloorDiv : ops.INPLACE_FLOOR_DIVIDE
+}
+
+inplace_operations = {
+ ast.Add : ops.INPLACE_ADD,
+ ast.Sub : ops.INPLACE_SUBTRACT,
+ ast.Mult : ops.INPLACE_MULTIPLY,
+ ast.Mod : ops.INPLACE_MODULO,
+ ast.Pow : ops.INPLACE_POWER,
+ ast.LShift : ops.INPLACE_LSHIFT,
+ ast.RShift : ops.INPLACE_RSHIFT,
+ ast.BitOr : ops.INPLACE_OR,
+ ast.BitAnd : ops.INPLACE_AND,
+ ast.BitXor : ops.INPLACE_XOR,
+ ast.FloorDiv : ops.INPLACE_FLOOR_DIVIDE
+}
+
+compare_operations = {
+ ast.Eq : 2,
+ ast.NotEq : 3,
+ ast.Lt : 0,
+ ast.LtE : 1,
+ ast.Gt : 4,
+ ast.GtE : 5,
+ ast.In : 6,
+ ast.NotIn : 7,
+ ast.Is : 8,
+ ast.IsNot : 9
+}
+
+subscr_operations = {
+ ast.AugLoad : ops.BINARY_SUBSCR,
+ ast.Load : ops.BINARY_SUBSCR,
+ ast.AugStore : ops.STORE_SUBSCR,
+ ast.Store : ops.STORE_SUBSCR,
+ ast.Del : ops.DELETE_SUBSCR
+}
+
+slice_operations = {
+ ast.AugLoad : ops.SLICE,
+ ast.Load : ops.SLICE,
+ ast.AugStore : ops.STORE_SLICE,
+ ast.Store : ops.STORE_SLICE,
+ ast.Del : ops.DELETE_SLICE
+}
+
+
+F_BLOCK_LOOP = 0
+F_BLOCK_EXCEPT = 1
+F_BLOCK_FINALLY = 2
+F_BLOCK_FINALLY_END = 3
+
+
+CONST_NOT_CONST = -1
+CONST_FALSE = 0
+CONST_TRUE = 1
+
+
+class PythonCodeGenerator(assemble.PythonCodeMaker):
+
+ def __init__(self, space, name, tree, lineno, symbols, compile_info):
+ self.scope = symbols.find_scope(tree)
+ assemble.PythonCodeMaker.__init__(self, space, name, lineno,
+ self.scope, compile_info)
+ self.symbols = symbols
+ self.frame_blocks = []
+ self.interactive = False
+ self.temporary_name_counter = 1
+ self.done_with_future = False
+ self._compile(tree)
+
+ def current_temporary_name(self):
+ name = "_[%i]" % (self.temporary_name_counter,)
+ self.temporary_name_counter += 1
+ return name
+
+ def sub_scope(self, kind, name, node):
+ generator = kind(self.space, name, node, node.lineno, self.symbols,
+ self.compile_info)
+ return generator.assemble()
+
+ def _expr_constant(self, expr):
+ if isinstance(expr, ast.Num):
+ return int(self.space.is_true(expr.n))
+ elif isinstance(expr, ast.Str):
+ return int(self.space.is_true(expr.s))
+ return CONST_NOT_CONST
+
+ def push_frame_block(self, kind, block):
+ self.frame_blocks.append((kind, block))
+
+ def pop_frame_block(self, kind, block):
+ actual_kind, old_block = self.frame_blocks.pop()
+ assert actual_kind == kind and old_block is block, \
+ "mismatched frame blocks"
+
+ def error(self, msg, node):
+ raise SyntaxError(msg, node.lineno, node.col_offset,
+ self.compile_info.filename)
+
+ def name_op(self, identifier, ctx):
+ scope = self.scope.lookup(identifier)
+ kind = name_ops_default
+ container = self.names
+ if scope == symtable.SCOPE_LOCAL:
+ if self.scope.optimized:
+ container = self.var_names
+ kind = name_ops_fast
+ elif scope == symtable.SCOPE_FREE:
+ kind = name_ops_deref
+ container = self.free_vars
+ elif scope == symtable.SCOPE_CELL:
+ kind = name_ops_deref
+ container = self.cell_vars
+ elif scope == symtable.SCOPE_GLOBAL_IMPLICIT:
+ if self.scope.optimized:
+ kind = name_ops_global
+ elif scope == symtable.SCOPE_GLOBAL_EXPLICIT:
+ kind = name_ops_global
+ try:
+ op = kind[ctx]
+ except KeyError:
+ if kind is names_ops_deref and ctx == ast.Del:
+ raise SyntaxError("Can't delete variable used in "
+ "nested scopes: %r" % (identifier,))
+ raise AssertionError("Unkown name operation")
+ self.emit_op_arg(op, self.add_name(container, identifier))
+
+ def is_docstring(self, node):
+ return isinstance(node, ast.Expr) and isinstance(node.value, ast.Str)
+
+ def _get_code_flags(self):
+ return consts.CO_NEWLOCALS
+
+ def _handle_body(self, body):
+ if body:
+ start = 0
+ if self.is_docstring(body[0]):
+ start = 1
+ body[0].value.walkabout(self)
+ self.name_op("__doc__", ast.Store)
+ for i in range(start, len(body)):
+ body[i].walkabout(self)
+ return True
+ else:
+ return False
+
+ def visit_Module(self, mod):
+ if not self._handle_body(mod.body):
+ self.first_lineno = self.lineno = 1
+
+ def visit_Interactive(self, mod):
+ self.interactive = True
+ self.visit_sequence(mod.body)
+
+ def visit_Expression(self, mod):
+ self.add_none_to_final_return = False
+ mod.body.walkabout(self)
+
+ def _make_function(self, code, num_defaults=0):
+ code_index = self.add_const(code)
+ if code.co_freevars:
+ for free in code.co_freevars:
+ free_scope = self.scope.lookup(free)
+ if free_scope == symtable.SCOPE_CELL:
+ index = self.cell_vars[free]
+ else:
+ index = self.free_vars[free]
+ self.emit_op_arg(ops.LOAD_CLOSURE, index)
+ self.emit_op_arg(ops.BUILD_TUPLE, len(code.co_freevars))
+ self.emit_op_arg(ops.LOAD_CONST, code_index)
+ self.emit_op_arg(ops.MAKE_CLOSURE, num_defaults)
+ else:
+ self.emit_op_arg(ops.LOAD_CONST, code_index)
+ self.emit_op_arg(ops.MAKE_FUNCTION, num_defaults)
+
+ def visit_FunctionDef(self, func):
+ if func.decorators:
+ self.visit_sequence(func.decorators)
+ if func.args.defaults:
+ self.visit_sequence(func.args.defaults)
+ num_defaults = len(func.args.defaults)
+ else:
+ num_defaults = 0
+ code = self.sub_scope(FunctionCodeGenerator, func.name, func)
+ self.update_position(func)
+ self._make_function(code, num_defaults)
+ self.name_op(func.name, ast.Store)
+
+ def visit_Lambda(self, lam):
+ if lam.args.defaults:
+ self.visit_sequence(lam.args.defaults)
+ default_count = len(lam.args.defaults)
+ else:
+ default_count = 0
+ code = self.sub_scope(LambdaCodeGenerator, "<lambda>", lam)
+ self.update_position(lam)
+ self._make_function(code, default_count)
+
+ def visit_ClassDef(self, cls):
+ self.update_position(cls)
+ self.load_const(self.space.wrap(cls.name))
+ if cls.bases:
+ bases_count = len(cls.bases)
+ self.visit_sequence(cls.bases)
+ else:
+ bases_count = 0
+ self.emit_op_arg(ops.BUILD_TUPLE, bases_count)
+ code = self.sub_scope(ClassCodeGenerator, cls.name, cls)
+ self.update_position(cls)
+ self._make_function(code, 0)
+ self.emit_op_arg(ops.CALL_FUNCTION, 0)
+ self.emit_op(ops.BUILD_CLASS)
+ self.name_op(cls.name, ast.Store)
+
+ def _op_for_augassign(self, op):
+ if op == ast.Div:
+ if self.compile_info.flags & consts.CO_FUTURE_DIVISION:
+ return ops.INPLACE_TRUE_DIVIDE
+ else:
+ return ops.INPLACE_FLOOR_DIVIDE
+ return inplace_operations[op]
+
+ def visit_AugAssign(self, assign):
+ self.update_position(assign)
+ target = assign.target
+ if isinstance(target, ast.Attribute):
+ attr = ast.Attribute(target.value, target.attr, ast.AugLoad,
+ target.lineno, target.col_offset)
+ attr.walkabout(self)
+ assign.value.walkabout(self)
+ self.emit_op(self._op_for_augassign(assign.op))
+ attr.ctx = ast.AugStore
+ attr.walkabout(self)
+ elif isinstance(target, ast.Subscript):
+ sub = ast.Subscript(target.value, target.slice, ast.AugLoad,
+ target.lineno, target.col_offset)
+ sub.walkabout(self)
+ assign.value.walkabout(self)
+ self.emit_op(self._op_for_augassign(assign.op))
+ sub.ctx = ast.AugStore
+ sub.walkabout(self)
+ elif isinstance(target, ast.Name):
+ self.name_op(target.id, ast.Load)
+ assign.value.walkabout(self)
+ self.emit_op(self._op_for_augassign(assign.op))
+ self.name_op(target.id, ast.Store)
+ else:
+ raise AssertionError("unkown augassign")
+
+ def visit_Assert(self, asrt):
+ self.update_position(asrt)
+ end = self.new_block()
+ asrt.test.walkabout(self)
+ self.emit_jump(ops.JUMP_IF_TRUE, end)
+ self.emit_op(ops.POP_TOP)
+ self.emit_op_name(ops.LOAD_GLOBAL, self.names, "AssertionError")
+ if asrt.msg:
+ asrt.msg.walkabout(self)
+ self.emit_op_arg(ops.RAISE_VARARGS, 2)
+ else:
+ self.emit_op_arg(ops.RAISE_VARARGS, 1)
+ self.use_next_block(end)
+ self.emit_op(ops.POP_TOP)
+
+ def _binop(self, op):
+ if op == ast.Div:
+ if self.compile_info.flags & consts.CO_FUTURE_DIVISION:
+ return ops.BINARY_TRUE_DIVIDE
+ else:
+ return ops.BINARY_FLOOR_DIVIDE
+ return binary_operations[op]
+
+ def visit_BinOp(self, binop):
+ self.update_position(binop)
+ binop.left.walkabout(self)
+ binop.right.walkabout(self)
+ self.emit_op(self._binop(binop.op))
+
+ def visit_Return(self, ret):
+ self.update_position(ret)
+ if ret.value:
+ ret.value.walkabout(self)
+ else:
+ self.load_const(self.space.w_None)
+ self.emit_op(ops.RETURN_VALUE)
+
+ def visit_Print(self, pr):
+ have_dest = bool(pr.dest)
+ if have_dest:
+ pr.dest.walkabout(self)
+ if pr.values:
+ for value in pr.values:
+ if have_dest:
+ self.emit_op(ops.DUP_TOP)
+ value.walkabout(self)
+ self.emit_op(ops.ROT_TWO)
+ self.emit_op(ops.PRINT_ITEM_TO)
+ else:
+ value.walkabout(self)
+ self.emit_op(ops.PRINT_ITEM)
+ if pr.nl:
+ if have_dest:
+ self.emit_op(ops.PRINT_NEWLINE_TO)
+ else:
+ self.emit_op(ops.PRINT_NEWLINE)
+ if have_dest:
+ self.emit_op(ops.POP_TOP)
+
+ def visit_Delete(self, delete):
+ self.update_position(delete)
+ self.visit_sequence(delete.targets)
+
+ def visit_If(self, if_):
+ self.update_position(if_)
+ end = self.new_block()
+ test_constant = self._expr_constant(if_.test)
+ if test_constant == CONST_FALSE:
+ if if_.orelse:
+ self.visit_sequence(if_.orelse)
+ elif test_constant == CONST_TRUE:
+ self.visit_sequence(if_.body)
+ else:
+ next = self.new_block()
+ if_.test.walkabout(self)
+ self.emit_jump(ops.JUMP_IF_FALSE, next)
+ self.emit_op(ops.POP_TOP)
+ self.visit_sequence(if_.body)
+ self.emit_jump(ops.JUMP_FORWARD, end)
+ self.use_next_block(next)
+ self.emit_op(ops.POP_TOP)
+ if if_.orelse:
+ self.visit_sequence(if_.orelse)
+ self.use_next_block(end)
+
+ def visit_Break(self, br):
+ self.update_position(br)
+ for f_block in self.frame_blocks:
+ if f_block[0] == F_BLOCK_LOOP:
+ break
+ else:
+ self.error("'break' outside loop", br)
+ self.emit_op(ops.BREAK_LOOP)
+
+ def visit_Continue(self, cont):
+ if not self.frame_blocks:
+ self.error("'continue' outside look", cont)
+ current_block, block = self.frame_blocks[-1]
+ if current_block == F_BLOCK_LOOP:
+ self.emit_jump(ops.JUMP_ABSOLUTE, block, True)
+ elif current_block == F_BLOCK_EXCEPT or \
+ current_block == F_BLOCK_FINALLY:
+ for i in range(len(self.frame_blocks) - 2, -1, -1):
+ f_type, block = self.frame_blocks[i]
+ if f_type == F_BLOCK_LOOP:
+ self.emit_jump(ops.CONTINUE_LOOP, block, True)
+ break
+ if self.frame_blocks[i][0] == F_BLOCK_FINALLY_END:
+ self.error("'continue' not allowed in 'finally' clause",
+ cont)
+ else:
+ self.error("'continue' outside loop", cont)
+ elif current_block == F_BLOCK_FINALLY_END:
+ self.error("'continue' not allowed in 'finally' clause", cont)
+
+ def visit_For(self, fr):
+ self.update_position(fr)
+ start = self.new_block()
+ cleanup = self.new_block()
+ end = self.new_block()
+ self.emit_jump(ops.SETUP_LOOP, end)
+ self.push_frame_block(F_BLOCK_LOOP, start)
+ fr.iter.walkabout(self)
+ self.emit_op(ops.GET_ITER)
+ self.use_next_block(start)
+ self.emit_jump(ops.FOR_ITER, cleanup)
+ fr.target.walkabout(self)
+ self.visit_sequence(fr.body)
+ self.emit_jump(ops.JUMP_ABSOLUTE, start, True)
+ self.use_next_block(cleanup)
+ self.emit_op(ops.POP_BLOCK)
+ self.pop_frame_block(F_BLOCK_LOOP, start)
+ if fr.orelse:
+ self.visit_sequence(fr.orelse)
+ self.use_next_block(end)
+
+ def visit_While(self, wh):
+ self.update_position(wh)
+ test_constant = self._expr_constant(wh.test)
+ if test_constant == CONST_FALSE:
+ if wh.orelse:
+ self.visit_sequence(orelse)
+ else:
+ end = self.new_block()
+ if test_constant == CONST_NOT_CONST:
+ anchor = self.new_block()
+ self.emit_jump(ops.SETUP_LOOP, end)
+ loop = self.new_block()
+ self.push_frame_block(F_BLOCK_LOOP, loop)
+ self.use_next_block(loop)
+ if test_constant == CONST_NOT_CONST:
+ wh.test.walkabout(self)
+ self.emit_jump(ops.JUMP_IF_FALSE, anchor)
+ self.emit_op(ops.POP_TOP)
+ self.visit_sequence(wh.body)
+ self.emit_jump(ops.JUMP_ABSOLUTE, loop, True)
+ if test_constant == CONST_NOT_CONST:
+ self.use_next_block(anchor)
+ self.emit_op(ops.POP_TOP)
+ self.emit_op(ops.POP_BLOCK)
+ self.pop_frame_block(F_BLOCK_LOOP, loop)
+ if wh.orelse:
+ self.visit_sequence(wh.orelse)
+ self.use_next_block(end)
+
+ def visit_TryExcept(self, te):
+ self.update_position(te)
+ exc = self.new_block()
+ otherwise = self.new_block()
+ end = self.new_block()
+ self.emit_jump(ops.SETUP_EXCEPT, exc)
+ body = self.use_next_block()
+ self.push_frame_block(F_BLOCK_EXCEPT, body)
+ self.visit_sequence(te.body)
+ self.emit_op(ops.POP_BLOCK)
+ self.pop_frame_block(F_BLOCK_EXCEPT, body)
+ self.emit_jump(ops.JUMP_FORWARD, otherwise)
+ self.use_next_block(exc)
+ for handler in te.handlers:
+ self.update_position(handler)
+ next_except = self.new_block()
+ if handler.type:
+ self.emit_op(ops.DUP_TOP)
+ handler.type.walkabout(self)
+ self.emit_op_arg(ops.COMPARE_OP, 10)
+ self.emit_jump(ops.JUMP_IF_FALSE, next_except)
+ self.emit_op(ops.POP_TOP)
+ self.emit_op(ops.POP_TOP)
+ if handler.name:
+ handler.name.walkabout(self)
+ else:
+ self.emit_op(ops.POP_TOP)
+ self.emit_op(ops.POP_TOP)
+ self.visit_sequence(handler.body)
+ self.emit_jump(ops.JUMP_FORWARD, end)
+ self.use_next_block(next_except)
+ if handler.type:
+ self.emit_op(ops.POP_TOP)
+ self.emit_op(ops.END_FINALLY)
+ self.use_next_block(otherwise)
+ if te.orelse:
+ self.visit_sequence(te.orelse)
+ self.use_next_block(end)
+
+ def visit_TryFinally(self, tf):
+ self.update_position(tf)
+ end = self.new_block()
+ self.emit_jump(ops.SETUP_FINALLY, end)
+ body = self.use_next_block()
+ self.push_frame_block(F_BLOCK_FINALLY, body)
+ self.visit_sequence(tf.body)
+ self.emit_op(ops.POP_BLOCK)
+ self.pop_frame_block(F_BLOCK_FINALLY, body)
+ self.load_const(self.space.w_None)
+ self.use_next_block(end)
+ self.push_frame_block(F_BLOCK_FINALLY_END, end)
+ self.visit_sequence(tf.finalbody)
+ self.emit_op(ops.END_FINALLY)
+ self.pop_frame_block(F_BLOCK_FINALLY_END, end)
+
+ def _import_as(self, alias):
+ source_name = alias.name
+ dot = source_name.find(".")
+ if dot != -1:
+ start = dot + 1
+ while True:
+ dot = source_name.find(".", start)
+ if dot == -1:
+ end = len(source_name)
+ else:
+ end = dot
+ attr = source_name[start:end]
+ self.emit_op_name(ops.LOAD_ATTR, self.names, attr)
+ if dot == -1:
+ break
+ start = dot + 1
+ self.name_op(alias.asname, ast.Store)
+
+ def visit_Import(self, imp):
+ self.update_position(imp)
+ for alias in imp.names:
+ if self.compile_info.flags & consts.CO_FUTURE_ABSOLUTE_IMPORT:
+ level = 0
+ else:
+ level = -1
+ self.load_const(self.space.wrap(level))
+ self.load_const(self.space.w_None)
+ self.emit_op_name(ops.IMPORT_NAME, self.names, alias.name)
+ if alias.asname:
+ self._import_as(alias)
+ else:
+ dot = alias.name.find(".")
+ if dot == -1:
+ store_name = alias.name
+ else:
+ store_name = alias.name[:dot]
+ self.name_op(store_name, ast.Store)
+
+ def visit_ImportFrom(self, imp):
+ self.update_position(imp)
+ space = self.space
+ if imp.module == "__future__":
+ if self.done_with_future:
+ self.error("__future__ statements must appear before other " \
+ "imports", imp)
+ else:
+ self.done_with_future = True
+ if imp.level == 0 and \
+ not self.compile_info.flags & consts.CO_FUTURE_ABSOLUTE_IMPORT:
+ level = -1
+ else:
+ level = imp.level
+ self.load_const(space.wrap(level))
+ names_w = [space.wrap(alias.name) for alias in imp.names]
+ self.load_const(space.newtuple(names_w))
+ self.emit_op_name(ops.IMPORT_NAME, self.names, imp.module)
+ if len(imp.names) == 1 and imp.names[0].name == "*":
+ self.emit_op(ops.IMPORT_STAR)
+ else:
+ for alias in imp.names:
+ self.emit_op_name(ops.IMPORT_FROM, self.names, alias.name)
+ if alias.asname:
+ store_name = alias.asname
+ else:
+ store_name = alias.name
+ self.name_op(store_name, ast.Store)
+ self.emit_op(ops.POP_TOP)
+
+ def visit_Assign(self, assign):
+ self.update_position(assign)
+ assign.value.walkabout(self)
+ duplications = len(assign.targets) - 1
+ for i in range(len(assign.targets)):
+ if i < duplications:
+ self.emit_op(ops.DUP_TOP)
+ assign.targets[i].walkabout(self)
+
+ def visit_With(self, wih):
+ self.update_position(wih)
+ body_block = self.new_block()
+ cleanup = self.new_block()
+ exit_storage = self.current_temporary_name()
+ if wih.optional_vars:
+ temp_result = self.current_temporary_name()
+ wih.context_expr.walkabout(self)
+ self.emit_op(ops.DUP_TOP)
+ self.emit_op_name(ops.LOAD_ATTR, self.names, "__exit__")
+ self.name_op(exit_storage, ast.Store)
+ self.emit_op_name(ops.LOAD_ATTR, self.names, "__enter__")
+ self.emit_op_arg(ops.CALL_FUNCTION, 0)
+ if wih.optional_vars:
+ self.name_op(temp_result, ast.Store)
+ else:
+ self.emit_op(ops.POP_TOP)
+ self.emit_jump(ops.SETUP_FINALLY, cleanup)
+ self.use_next_block(body_block)
+ self.push_frame_block(F_BLOCK_FINALLY, body_block)
+ if wih.optional_vars:
+ self.name_op(temp_result, ast.Load)
+ self.name_op(temp_result, ast.Del)
+ wih.optional_vars.walkabout(self)
+ self.visit_sequence(wih.body)
+ self.emit_op(ops.POP_BLOCK)
+ self.pop_frame_block(F_BLOCK_FINALLY, body_block)
+ self.load_const(self.space.w_None)
+ self.use_next_block(cleanup)
+ self.push_frame_block(F_BLOCK_FINALLY_END, cleanup)
+ self.name_op(exit_storage, ast.Load)
+ self.name_op(exit_storage, ast.Del)
+ self.emit_op(ops.WITH_CLEANUP)
+ self.emit_op(ops.END_FINALLY)
+ self.pop_frame_block(F_BLOCK_FINALLY_END, cleanup)
+
+ def visit_Raise(self, rais):
+ self.update_position(rais)
+ arg = 0
+ if rais.type:
+ rais.type.walkabout(self)
+ arg += 1
+ if rais.inst:
+ rais.inst.walkabout(self)
+ arg += 1
+ if rais.tback:
+ rais.tback.walkabout(self)
+ arg += 1
+ self.emit_op_arg(ops.RAISE_VARARGS, arg)
+
+ def visit_Exec(self, exc):
+ self.update_position(exc)
+ exc.body.walkabout(self)
+ if exc.globals:
+ exc.globals.walkabout(self)
+ if exc.locals:
+ exc.locals.walkabout(self)
+ else:
+ self.emit_op(ops.DUP_TOP)
+ else:
+ self.load_const(self.space.w_None)
+ self.emit_op(ops.DUP_TOP)
+ self.emit_op(ops.EXEC_STMT)
+
+ def visit_Global(self, glob):
+ # Handled in symbol table building.
+ pass
+
+ def visit_Pass(self, pas):
+ self.update_position(pas)
+
+ def visit_Expr(self, expr):
+ self.update_position(expr)
+ if self.interactive:
+ expr.value.walkabout(self)
+ self.emit_op(ops.PRINT_EXPR)
+ elif not isinstance(expr.value, ast.Num) and \
+ not isinstance(expr.value, ast.Str):
+ expr.value.walkabout(self)
+ self.emit_op(ops.POP_TOP)
+
+ def visit_Yield(self, yie):
+ self.update_position(yie)
+ if yie.value:
+ yie.value.walkabout(self)
+ else:
+ self.load_const(self.space.w_None)
+ self.emit_op(ops.YIELD_VALUE)
+
+ def visit_Num(self, num):
+ self.update_position(num)
+ self.load_const(num.n)
+
+ def visit_Str(self, string):
+ self.update_position(string)
+ self.load_const(string.s)
+
+ def visit_UnaryOp(self, op):
+ self.update_position(op)
+ op.operand.walkabout(self)
+ self.emit_op(unary_operations[op.op])
+
+ def visit_BoolOp(self, op):
+ self.update_position(op)
+ if op.op == ast.And:
+ instr = ops.JUMP_IF_FALSE
+ else:
+ instr = ops.JUMP_IF_TRUE
+ end = self.new_block()
+ for value in op.values[:-1]:
+ value.walkabout(self)
+ self.emit_jump(instr, end)
+ self.emit_op(ops.POP_TOP)
+ op.values[-1].walkabout(self)
+ self.use_next_block(end)
+
+ def visit_Compare(self, comp):
+ self.update_position(comp)
+ comp.left.walkabout(self)
+ ops_count = len(comp.ops)
+ if ops_count > 1:
+ cleanup = self.new_block()
+ comp.comparators[0].walkabout(self)
+ for i in range(1, ops_count):
+ self.emit_op(ops.DUP_TOP)
+ self.emit_op(ops.ROT_THREE)
+ op_kind = compare_operations[comp.ops[i - 1]]
+ self.emit_op_arg(ops.COMPARE_OP, op_kind)
+ self.emit_jump(ops.JUMP_IF_FALSE, cleanup)
+ self.emit_op(ops.POP_TOP)
+ if i < (ops_count - 1):
+ comp.comparators[i].walkabout(self)
+ comp.comparators[-1].walkabout(self)
+ last_kind = compare_operations[comp.ops[-1]]
+ self.emit_op_arg(ops.COMPARE_OP, last_kind)
+ if ops_count > 1:
+ end = self.new_block()
+ self.emit_jump(ops.JUMP_FORWARD, end)
+ self.use_next_block(cleanup)
+ self.emit_op(ops.ROT_TWO)
+ self.emit_op(ops.POP_TOP)
+ self.use_next_block(end)
+
+ def visit_IfExp(self, ifexp):
+ self.update_position(ifexp)
+ end = self.new_block()
+ otherwise = self.new_block()
+ ifexp.test.walkabout(self)
+ self.emit_jump(ops.JUMP_IF_FALSE, otherwise)
+ self.emit_op(ops.POP_TOP)
+ ifexp.body.walkabout(self)
+ self.emit_jump(ops.JUMP_FORWARD, end)
+ self.use_next_block(otherwise)
+ self.emit_op(ops.POP_TOP)
+ ifexp.orelse.walkabout(self)
+ self.use_next_block(end)
+
+ def visit_Tuple(self, tup):
+ self.update_position(tup)
+ if tup.elts:
+ elt_count = len(tup.elts)
+ else:
+ elt_count = 0
+ if tup.ctx == ast.Store:
+ self.emit_op_arg(ops.UNPACK_SEQUENCE, elt_count)
+ if elt_count:
+ self.visit_sequence(tup.elts)
+ if tup.ctx == ast.Load:
+ self.emit_op_arg(ops.BUILD_TUPLE, elt_count)
+
+ def visit_List(self, l):
+ self.update_position(l)
+ if l.elts:
+ elt_count = len(l.elts)
+ else:
+ elt_count = 0
+ if l.ctx == ast.Store:
+ self.emit_op_arg(ops.UNPACK_SEQUENCE, elt_count)
+ if elt_count:
+ self.visit_sequence(l.elts)
+ if l.ctx == ast.Load:
+ self.emit_op_arg(ops.BUILD_LIST, elt_count)
+
+ def visit_Dict(self, d):
+ self.update_position(d)
+ self.emit_op_arg(ops.BUILD_MAP, 0)
+ if d.values:
+ for i in range(len(d.values)):
+ self.emit_op(ops.DUP_TOP)
+ d.values[i].walkabout(self)
+ self.emit_op(ops.ROT_TWO)
+ d.keys[i].walkabout(self)
+ self.emit_op(ops.STORE_SUBSCR)
+
+ def visit_Name(self, name):
+ self.update_position(name)
+ self.name_op(name.id, name.ctx)
+
+ def visit_keyword(self, keyword):
+ self.load_const(self.space.wrap(keyword.arg))
+ keyword.value.walkabout(self)
+
+ def visit_Call(self, call):
+ self.update_position(call)
+ call.func.walkabout(self)
+ arg = 0
+ call_type = 0
+ if call.args:
+ arg = len(call.args)
+ self.visit_sequence(call.args)
+ if call.keywords:
+ self.visit_sequence(call.keywords)
+ arg |= len(call.keywords) << 8
+ if call.starargs:
+ call.starargs.walkabout(self)
+ call_type |= 1
+ if call.kwargs:
+ call.kwargs.walkabout(self)
+ call_type |= 2
+ if call_type == 0:
+ op = ops.CALL_FUNCTION
+ elif call_type == 1:
+ op = ops.CALL_FUNCTION_VAR
+ elif call_type == 2:
+ op = ops.CALL_FUNCTION_KW
+ elif call_type == 3:
+ op = ops.CALL_FUNCTION_VAR_KW
+ self.emit_op_arg(op, arg)
+
+ def _listcomp_generator(self, list_name, gens, gen_index, elt):
+ start = self.new_block()
+ skip = self.new_block()
+ if_cleanup = self.new_block()
+ anchor = self.new_block()
+ gen = gens[gen_index]
+ gen.iter.walkabout(self)
+ self.emit_op(ops.GET_ITER)
+ self.use_next_block(start)
+ self.emit_jump(ops.FOR_ITER, anchor)
+ self.use_next_block()
+ gen.target.walkabout(self)
+ if gen.ifs:
+ if_count = len(gen.ifs)
+ for if_ in gen.ifs:
+ if_.walkabout(self)
+ self.emit_jump(ops.JUMP_IF_FALSE, if_cleanup)
+ self.use_next_block()
+ self.emit_op(ops.POP_TOP)
+ else:
+ if_count = 0
+ gen_index += 1
+ if gen_index < len(gens):
+ self._listcomp_generator(list_name, gens, gen_index, elt)
+ else:
+ self.name_op(list_name, ast.Load)
+ elt.walkabout(self)
+ self.emit_op(ops.LIST_APPEND)
+ self.use_next_block(skip)
+ for i in range(if_count):
+ self.emit_op_arg(ops.JUMP_FORWARD, 1)
+ if i == 0:
+ self.use_next_block(if_cleanup)
+ self.emit_op(ops.POP_TOP)
+ self.emit_jump(ops.JUMP_ABSOLUTE, start, True)
+ self.use_next_block(anchor)
+ if gen_index == 1:
+ self.name_op(list_name, ast.Del)
+
+ def visit_ListComp(self, lc):
+ self.update_position(lc)
+ tmp_name = self.current_temporary_name()
+ self.emit_op_arg(ops.BUILD_LIST, 0)
+ self.emit_op(ops.DUP_TOP)
+ self.name_op(tmp_name, ast.Store)
+ self._listcomp_generator(tmp_name, lc.generators, 0, lc.elt)
+
+ def _genexp_generator(self, generators, gen_index, elt):
+ start = self.new_block()
+ skip = self.new_block()
+ if_cleanup = self.new_block()
+ anchor = self.new_block()
+ end = self.new_block()
+ gen = generators[gen_index]
+ self.emit_jump(ops.SETUP_LOOP, end)
+ self.push_frame_block(F_BLOCK_LOOP, start)
+ if gen_index == 0:
+ self.argcount = 1
+ self.name_op(".0", ast.Load)
+ else:
+ gen.iter.walkabout(self)
+ self.emit_op(ops.GET_ITER)
+ self.use_next_block(start)
+ self.emit_jump(ops.FOR_ITER, anchor)
+ self.use_next_block()
+ gen.target.walkabout(self)
+ if gen.ifs:
+ ifs_count = len(gen.ifs)
+ for if_ in gen.ifs:
+ if_.walkabout(self)
+ self.emit_jump(ops.JUMP_IF_FALSE, if_cleanup)
+ self.use_next_block()
+ self.emit_op(ops.POP_TOP)
+ else:
+ ifs_count = 0
+ gen_index += 1
+ if gen_index < len(generators):
+ self._genexp_generator(generators, gen_index, elt)
+ else:
+ elt.walkabout(self)
+ self.emit_op(ops.YIELD_VALUE)
+ self.emit_op(ops.POP_TOP)
+ self.use_next_block(skip)
+ for i in range(ifs_count):
+ self.emit_op_arg(ops.JUMP_FORWARD, 1)
+ if i == 0:
+ self.use_next_block(if_cleanup)
+ self.emit_op(ops.POP_TOP)
+ self.emit_jump(ops.JUMP_ABSOLUTE, start, True)
+ self.use_next_block(anchor)
+ self.emit_op(ops.POP_BLOCK)
+ self.pop_frame_block(F_BLOCK_LOOP, start)
+ self.use_next_block(end)
+
+ def visit_GeneratorExp(self, genexp):
+ code = self.sub_scope(GenExpCodeGenerator, "<genexp>", genexp)
+ self.update_position(genexp)
+ self._make_function(code)
+ genexp.generators[0].iter.walkabout(self)
+ self.emit_op(ops.GET_ITER)
+ self.emit_op_arg(ops.CALL_FUNCTION, 1)
+
+ def visit_Attribute(self, attr):
+ self.update_position(attr)
+ names = self.names
+ if attr.ctx != ast.AugStore:
+ attr.value.walkabout(self)
+ if attr.ctx == ast.AugLoad:
+ self.emit_op(ops.DUP_TOP)
+ self.emit_op_name(ops.LOAD_ATTR, names, attr.attr)
+ elif attr.ctx == ast.Load:
+ self.emit_op_name(ops.LOAD_ATTR, names, attr.attr)
+ elif attr.ctx == ast.AugStore:
+ self.emit_op(ops.ROT_TWO)
+ self.emit_op_name(ops.STORE_ATTR, names, attr.attr)
+ elif attr.ctx == ast.Store:
+ self.emit_op_name(ops.STORE_ATTR, names, attr.attr)
+
+ def _simple_slice(self, slc, ctx):
+ slice_offset = 0
+ stack_count = 0
+ if slc.lower:
+ slice_offset += 1
+ stack_count += 1
+ if ctx != ast.AugStore:
+ slc.lower.walkabout(self)
+ if slc.upper:
+ slice_offset += 2
+ stack_count += 1
+ if ctx != ast.AugStore:
+ slc.upper.walkabout(self)
+ if ctx == ast.AugLoad:
+ if stack_count == 0:
+ self.emit_op(ops.DUP_TOP)
+ elif stack_count == 1:
+ self.emit_op_arg(ops.DUP_TOPX, 2)
+ elif stack_count == 2:
+ self.emit_op-arg(ops.DUP_TOPX, 3)
+ elif ctx == ast.AugStore:
+ if stack_count == 0:
+ self.emit_op(ops.ROT_TWO)
+ elif stack_count == 1:
+ self.emit_op(ops.ROT_THREE)
+ elif stack_count == 2:
+ self.emit_op(ops.ROT_FOUR)
+ self.emit_op(slice_operations[ctx] + slice_offset)
+
+ def _complex_slice(self, slc, ctx):
+ if slc.lower:
+ slc.lower.walkabout(self)
+ else:
+ self.load_const(self.space.w_None)
+ if slc.upper:
+ slc.upper.walkabout(self)
+ else:
+ self.load_const(self.space.w_None)
+ arg = 2
+ if slc.step:
+ slc.step.walkabout(self)
+ arg += 1
+ self.emit_op_arg(ops.BUILD_SLICE, arg)
+
+ def _nested_slice(self, slc, ctx):
+ if isinstance(slc, ast.Ellipsis):
+ self.load_const(self.space.w_Ellipsis)
+ elif isinstance(slc, ast.Slice):
+ self._complex_slice(slc, ctx)
+ elif isinstance(slc, ast.Index):
+ slc.value.walkabout(self)
+ else:
+ raise AssertionError("unkown nested slice type")
+
+ def _compile_slice(self, slc, ctx):
+ if isinstance(slc, ast.Index):
+ kind = "index"
+ if ctx != ast.AugStore:
+ slc.value.walkabout(self)
+ elif isinstance(slc, ast.Ellipsis):
+ kind = "ellipsis"
+ if ctx != ast.AugStore:
+ self.load_const(self.space.w_Ellipsis)
+ elif isinstance(slc, ast.Slice):
+ kind = "slice"
+ if not slc.step:
+ self._simple_slice(slc, ctx)
+ return
+ elif ctx != ast.AugStore:
+ self._complex_slice(slc, ctx)
+ elif isinstance(slc, ast.ExtSlice):
+ kind = "extended slice"
+ if ctx != ast.AugStore:
+ for dim in slc.dims:
+ self._nested_slice(dim, ctx)
+ self.emit_op_arg(ops.BUILD_TUPLE, len(slc.dims))
+ else:
+ raise AssertionError("unkown slice type")
+ if ctx == ast.AugLoad:
+ self.emit_op_arg(ops.DUP_TOPX, 2)
+ elif ctx == ast.AugStore:
+ self.emit_op(ops.ROT_THREE)
+ self.emit_op(subscr_operations[ctx])
+
+ def visit_Subscript(self, sub):
+ self.update_position(sub)
+ if sub.ctx != ast.AugStore:
+ sub.value.walkabout(self)
+ self._compile_slice(sub.slice, sub.ctx)
+
+
+class TopLevelCodeGenerator(PythonCodeGenerator):
+
+ def __init__(self, space, tree, symbols, compile_info):
+ PythonCodeGenerator.__init__(self, space, "<module>", tree, -1,
+ symbols, compile_info)
+
+ def _compile(self, tree):
+ tree.walkabout(self)
+
+ def _get_code_flags(self):
+ return 0
+
+
+class AbstractFunctionCodeGenerator(PythonCodeGenerator):
+
+ def _compile(self, func):
+ assert isinstance(func, ast.FunctionDef)
+ if self.is_docstring(func.body[0]):
+ self.add_const(func.body[0].value.s)
+ start = 1
+ else:
+ self.add_const(self.space.w_None)
+ start = 0
+ if func.args.args:
+ self._handle_nested_args(func.args.args)
+ self.argcount = len(func.args.args)
+ for i in range(start, len(func.body)):
+ func.body[i].walkabout(self)
+
+ def _handle_nested_args(self, args):
+ for i in range(len(args)):
+ arg = args[i]
+ if isinstance(arg, ast.Tuple):
+ self.update_position(arg)
+ self.name_op(".%i" % (i,), ast.Load)
+ arg.walkabout(self)
+
+ def _get_code_flags(self):
+ scope = self.scope
+ assert isinstance(scope, symtable.FunctionScope)
+ flags = 0
+ if scope.optimized:
+ flags |= consts.CO_OPTIMIZED
+ if scope.nested:
+ flags |= consts.CO_NESTED
+ if scope.is_generator:
+ flags |= consts.CO_GENERATOR
+ if scope.has_variable_arg:
+ flags |= consts.CO_VARARGS
+ if scope.has_keywords_arg:
+ flags |= consts.CO_VARKEYWORDS
+ if not self.cell_vars and not self.free_vars:
+ flags |= consts.CO_NOFREE
+ return PythonCodeGenerator._get_code_flags(self) | flags
+
+
+class FunctionCodeGenerator(AbstractFunctionCodeGenerator):
+ pass
+
+
+class LambdaCodeGenerator(AbstractFunctionCodeGenerator):
+
+ def _compile(self, lam):
+ assert isinstance(lam, ast.Lambda)
+ if lam.args.args:
+ self._handle_nested_args(lam.args.args)
+ lam.body.walkabout(self)
+ self.emit_op(ops.RETURN_VALUE)
+
+
+class GenExpCodeGenerator(AbstractFunctionCodeGenerator):
+
+ def _compile(self, genexp):
+ assert isinstance(genexp, ast.GeneratorExp)
+ self.update_position(genexp)
+ self._genexp_generator(genexp.generators, 0, genexp.elt)
+
+ def _get_code_flags(self):
+ flags = AbstractFunctionCodeGenerator._get_code_flags(self)
+ return flags | consts.CO_GENERATOR
+
+
+class ClassCodeGenerator(PythonCodeGenerator):
+
+ def _compile(self, cls):
+ assert isinstance(cls, ast.ClassDef)
+ self.lineno = self.first_lineno
+ self.name_op("__name__", ast.Load)
+ self.name_op("__module__", ast.Store)
+ self._handle_body(cls.body)
+ self.emit_op(ops.LOAD_LOCALS)
+ self.emit_op(ops.RETURN_VALUE)
More information about the Pypy-commit
mailing list