def _make_function(self, instr): """ Functions should have strict names. """ yield instructions.LOAD_CONST(strict).steal(instr) # TOS = strict # TOS1 = func_name yield instructions.ROT_TWO() # TOS = func_name # TOS1 = strict yield instructions.CALL_FUNCTION(1) # TOS = strict(func_name) yield instr # TOS = new_function yield instructions.LOAD_CONST(thunk_type.fromexpr) # TOS thunk_type.fromexpr # TOS1 new_function yield instructions.ROT_TWO() # TOS new_function # TOS1 thunk_type.fromexpr yield instructions.CALL_FUNCTION(1)
def _dis(): yield instructions.LOAD_CONST(dis) yield instructions.LOAD_CONST(sys._getframe) yield instructions.CALL_FUNCTION(0) yield instructions.LOAD_ATTR('f_code') yield instructions.CALL_FUNCTION(1) yield instructions.POP_TOP() yield next_instruction()
def words(): yield instructions.LOAD_CONST( compose( pprint, partial(sorted, key=op.attrgetter('name')), dict.values, )) yield instructions.LOAD_CONST(globals) yield instructions.CALL_FUNCTION(0) yield instructions.CALL_FUNCTION(1) yield instructions.POP_TOP() yield next_instruction()
def lparen(): loop = instructions.LOAD_CONST(')') yield loop yield from _word() yield instructions.COMPARE_OP.EQ yield instructions.POP_JUMP_IF_FALSE(loop) yield next_instruction()
def semicolon(): yield from write_short(vocab['exit'].addr - 1) yield instructions.LOAD_CONST(push_return_addr) yield instructions.CALL_FUNCTION() yield instructions.POP_TOP() yield instructions.JUMP_ABSOLUTE(word_instrs[']'][0]) yield next_instruction()
def _start_comprehension(self, instr, *instrs): yield instructions.LOAD_CONST(self.astype).steal(instr) # TOS = self.astype yield instructions.CALL_FUNCTION(0) # TOS = m = self.astype() yield instructions.STORE_FAST('__map__') body, map_add = instrs[:-1], instrs[-1] for item in self.patterndispatcher(body): yield item # TOS = k # TOS1 = v yield instructions.LOAD_FAST('__map__').steal(map_add) # TOS = __map__ # TOS1 = k # TOS2 = v yield instructions.ROT_TWO() # TOS = k # TOS1 = __map__ # TOS2 = v yield instructions.STORE_SUBSCR() self.begin(IN_COMPREHENSION)
def pushcfa(): yield instructions.DUP_TOP() yield instructions.LOAD_CONST(Word) yield instructions.LOAD_CONST(isinstance) yield instructions.ROT_THREE() yield instructions.CALL_FUNCTION(2) not_word_instr = instructions.LOAD_CONST(NotAWord) yield instructions.POP_JUMP_IF_FALSE(not_word_instr) yield instructions.LOAD_ATTR('addr') yield next_instruction() yield not_word_instr yield instructions.ROT_TWO() yield instructions.CALL_FUNCTION(1) yield instructions.RAISE_VARARGS(1)
def _build_list(self, instr): if instr.arg == 0: yield instrs.LOAD_CONST(jl.jlist).steal(instr) yield instrs.CALL_FUNCTION(0) elif instr.arg == 1: yield instrs.LOAD_CONST(jl.jlist._from_starargs).steal(instr) yield instrs.ROT_TWO() yield instrs.CALL_FUNCTION(1) elif instr.arg == 2: yield instrs.LOAD_CONST(jl.jlist._from_starargs).steal(instr) yield instrs.ROT_THREE() yield instrs.CALL_FUNCTION(2) else: yield instr yield instrs.LOAD_CONST(jl.jlist) yield instrs.ROT_TWO() yield instrs.CALL_FUNCTION(1)
def make_noop_func(n): """Create a function with N noops""" co = Code( [inst.NOP() for _ in range(n)] + [inst.LOAD_CONST(None), inst.RETURN_VALUE()]) f = lambda: None f.__code__ = co.to_pycode() return f
def _build_map(self, instr): yield instructions.LOAD_CONST(self.astype).steal(instr) # TOS = self.astype yield instructions.CALL_FUNCTION(0) # TOS = m = self.astype() for item in (instructions.DUP_TOP(), ) * instr.arg: yield item
def sync_frame(): """Sync the frame object with some local variables in PyEval_EvalFrameEx. This needs to happend before using any primitive function that cares about the instruction pointer or the stacksize. """ # our custom runner understands that `yield None` means 'do not jump # anywhere, just sync the frame and continue yield instructions.LOAD_CONST(None) yield instructions.YIELD_VALUE()
def _load_name(self, instr): yield instructions.LOAD_CONST(thunk_type.fromexpr).steal(instr) # TOS thunk_type.fromexpr yield instr # TOS v # TOS1 thunk_type.fromexpr yield instructions.CALL_FUNCTION(1)
def _tail(): for _ in range(memory - len(list(_sparse_args(instrs))) - 15): yield instructions.NOP() yield handle_exception_instr yield from _nip() yield instructions.LOAD_CONST(handle_exception) yield instructions.ROT_TWO() yield instructions.CALL_FUNCTION(1) yield instructions.POP_TOP() yield instructions.POP_EXCEPT() yield instructions.JUMP_ABSOLUTE(setup_except_instr)
def colon(): yield instructions.LOAD_CONST(push_return_addr) yield instructions.CALL_FUNCTION() yield instructions.POP_TOP() yield instructions.JUMP_ABSOLUTE(word_instrs['word'][0]) yield instructions.LOAD_CONST(push_return_addr) yield instructions.CALL_FUNCTION() yield instructions.POP_TOP() yield instructions.JUMP_ABSOLUTE(word_instrs['create'][0]) yield from write_byte(instructions.LOAD_CONST.opcode) yield from write_short(0) # push_return_addr yield from write_byte(instructions.CALL_FUNCTION.opcode) yield from write_short(0) yield from write_byte(instructions.POP_TOP.opcode) yield from write_byte(instructions.JUMP_ABSOLUTE.opcode) yield from write_short(vocab['__docol'].addr) yield instructions.LOAD_CONST(push_return_addr) yield instructions.CALL_FUNCTION() yield instructions.POP_TOP() yield instructions.JUMP_ABSOLUTE(word_instrs['['][0]) yield next_instruction()
def _binary_subscr(self, instr): yield instructions.LOAD_CONST(self._islicer).steal(instr) # TOS = self._islicer # TOS1 = k # TOS2 = m yield instructions.ROT_THREE() # TOS = k # TOS1 = m # TOS2 = self._islicer yield instructions.CALL_FUNCTION(2)
def _import_name(self, instr): # TOS fromlist # TOS1 level yield instructions.LOAD_CONST(self._import_wrapper).steal(instr) # TOS self._import_wrapper # TOS1 fromlist # TOS2 level yield instructions.ROT_THREE() # TOS fromlist # TOS1 level # TOS2 self._import_wrapper yield instructions.LOAD_CONST(instr.arg) # TOS name # TOS1 fromlist # TOS2 level # TOS3 self._import_wrapper yield instructions.CALL_FUNCTION(3)
def quote(): yield instructions.LOAD_CONST(push_return_addr) yield instructions.CALL_FUNCTION() yield instructions.POP_TOP() yield instructions.JUMP_ABSOLUTE(word_instrs['word'][0]) # We need to duplicate the word on the stack for proper error handling # later. # We dup it once giving us 2 copies on the stack for: # find # unknown word error yield instructions.DUP_TOP() yield instructions.LOAD_CONST(push_return_addr) yield instructions.CALL_FUNCTION(0) yield instructions.POP_TOP() yield instructions.JUMP_ABSOLUTE(word_instrs['find'][0]) yield instructions.DUP_TOP() yield instructions.LOAD_CONST(None) yield instructions.COMPARE_OP.IS unknown_word_instr = instructions.POP_TOP() yield instructions.POP_JUMP_IF_TRUE(unknown_word_instr) # clear the word strings from the stack yield from _nip() yield instructions.LOAD_CONST(push_return_addr) yield instructions.CALL_FUNCTION(0) yield instructions.POP_TOP() yield instructions.JUMP_ABSOLUTE(word_instrs['>cfa'][0]) yield next_instruction() yield instructions.POP_JUMP_IF_TRUE(unknown_word_instr) # clear the word string left for the unknown word case yield from _nip() yield next_instruction() yield unknown_word_instr yield instructions.LOAD_CONST(UnknownWord) yield instructions.ROT_TWO() yield instructions.CALL_FUNCTION(1) yield instructions.RAISE_VARARGS(1)
def _build(self, instr): yield instr # TOS = new_list yield instructions.LOAD_CONST(self.xform) # TOS = astype # TOS1 = new_list yield instructions.ROT_TWO() # TOS = new_list # TOS1 = astype yield instructions.CALL_FUNCTION(1)
def _start_dict_comprehension(self, instr, *instrs): # put thunk call on the stack for right befor the return. yield instructions.LOAD_CONST(partial(thunk_type, dict)) # TOS partial(thunk_type, dict) yield instr # TOS dict_ # TOS1 partial(thunk_type, dict) *body, ret = instrs yield from self.patterndispatcher(body) # TOS dict_ # TOS1 partial(thunk_type, dict) yield ret
def _unary_not(self, instr): """ Replace the `not` operator to act on the values that the thunks. represent. This makes `not` lazy. """ yield instructions.LOAD_CONST(_lazy_not).steal(instr) # TOS = _lazy_not # TOS1 = arg yield instructions.ROT_TWO() # TOS = arg # TOS1 = _lazy_not yield instructions.CALL_FUNCTION(1)
def _return_value(self, instr): # TOS = collection yield instructions.LOAD_CONST(self.xform).steal(instr) # TOS = self.xform # TOS1 = collection yield instructions.ROT_TWO() # TOS = collection # TOS1 = self.xform yield instructions.CALL_FUNCTION(1) # TOS = self.xform(collection) yield instr
def build_seq(self, instr): # TOS v_0 # ... # TOSn v_n yield instructions.BUILD_TUPLE(instr.arg).steal(instr) # TOS (v_0, ..., v_n) yield instructions.LOAD_CONST(partial(thunk_type, type_)) # TOS partial(thunk_type, type_) # TOS1 (v_0, ..., v_n) yield instructions.ROT_TWO() # TOS (v_0, ..., v_n) # TOS1 partial(thunk_type, type_) yield instructions.CALL_FUNCTION(1)
def _load_fast(self, instr): name = instr.arg if name in self.code.argnames: # perf note: we only need to wrap lookups to arguments as # thunks To assign to a name, it must have been a value already # so it is a thunk_type unless it was passed into the function. yield instructions.LOAD_CONST(thunk_type.fromexpr).steal(instr) # TOS thunk_type.fromexpr yield instr # TOS v # TOS thunk_type.fromexpr yield instructions.CALL_FUNCTION(1) # TOS thunk_type.fromexpr(v) else: yield instr
def comprehension(self, *instrs): first, *body, ret = instrs yield instructions.LOAD_CONST(partial(thunk_type, type_)) # TOS partial(thunk_type, type_) yield first # TOS strict_seq # TOS1 partial(thunk_type, type_) yield from self.patterndispatcher(body) # TOS strict_seq # TOS1 partial(thunk_type, type_) yield instructions.CALL_FUNCTION(1) # TOS partial(thunk_type, type_)(*strict_seq) yield ret
def _build_map(self, instr): # TOS k_0 # TOS1 v_0 # ... # TOSn k_n # TOSm v_n yield instr # TOS dict_ yield instructions.LOAD_CONST(partial(thunk_type, dict), ) # TOS partial(thunk_type, dict) # TOS1 dict_ yield instructions.ROT_TWO() # TOS dict_ # TOS1 partial(thunk_type, dict) yield instructions.CALL_FUNCTION_KW(0)
def _build_const_map(self, keys, instr): yield instructions.BUILD_TUPLE(len(keys.arg)).steal(keys) # TOS = (v0, v1, ..., vn) yield keys # TOS = (k0, k1, ..., kn) # TOS1 = (v0, v1, ..., vn) yield instructions.LOAD_CONST(self._construct_const_map) # TOS = self._construct_const_map # TOS1 = (k0, k1, ..., kn) # TOS2 = (v0, v1, ..., vn) yield instructions.ROT_THREE() # TOS = (k0, k1, ..., kn) # TOS1 = (v0, v1, ..., vn) # TOS2 = self._construct_const_map yield instructions.CALL_FUNCTION(2)
def _build_map(self, instr): # TOS = vn # TOS1 = kn # ... # TOSN = v0 # TOSN + 1 = k0 # Construct a tuple of (k0, v0, k1, v1, ..., kn, vn) for # each of the key: value pairs in the dictionary. yield instructions.BUILD_TUPLE(instr.arg * 2).steal(instr) # TOS = (k0, v0, k1, v1, ..., kn, vn) yield instructions.LOAD_CONST(self._construct_map) # TOS = self._construct_map # TOS1 = (k0, v0, k1, v1, ..., kn, vn) yield instructions.ROT_TWO() # TOS = (k0, v0, k1, v1, ..., kn, vn) # TOS1 = self._construct_map yield instructions.CALL_FUNCTION(1)
def py_call(): start = instructions.BUILD_LIST(0) # validate that nargs is >= 0 to avoid infinite loop yield instructions.DUP_TOP() yield instructions.LOAD_CONST(0) yield instructions.COMPARE_OP.LT yield instructions.POP_JUMP_IF_FALSE(start) yield instructions.LOAD_CONST('nargs must be >= 0; got %s') yield instructions.ROT_TWO() yield instructions.BINARY_MODULO() yield instructions.LOAD_CONST(ValueError) yield instructions.ROT_TWO() yield instructions.CALL_FUNCTION(1) yield instructions.RAISE_VARARGS(1) # create a list to hold the function and arguments; append the function # first yield start yield from _nrot() yield instructions.LIST_APPEND(1) yield instructions.STORE_FAST('tmp') # use the nargs as a counter; append elements until nargs == 0 loop = instructions.DUP_TOP() yield loop yield instructions.LOAD_CONST(0) yield instructions.COMPARE_OP.EQ call_impl = instructions.POP_TOP() yield instructions.POP_JUMP_IF_TRUE(call_impl) yield instructions.LOAD_CONST(1) yield instructions.BINARY_SUBTRACT() yield instructions.LOAD_FAST('tmp') yield from _nrot() yield instructions.LIST_APPEND(1) yield instructions.POP_TOP() yield instructions.JUMP_ABSOLUTE(loop) # *unpack the argument list into `py_call_impl` yield call_impl yield instructions.LOAD_CONST(py_call_impl) yield instructions.LOAD_FAST('tmp') yield instructions.CALL_FUNCTION_VAR(0) yield next_instruction()
def _compare_op(self, instr): """ Replace the `is` operator to act on the values the thunk represent. This makes `is` lazy. """ if instr.arg != 8: # is yield instr return yield instructions.LOAD_CONST(_lazy_is).steal(instr) # TOS = _lazy_is # TOS1 = a # TOS2 = b # This safe to do because `is` is commutative 100% of the time. # We are doing a pointer compare so we can move the operands # around. This saves us from doing an extra ROT_TWO to preserve the # order. yield instructions.ROT_THREE() # TOS = a # TOS1 = b # TOS2 = _lazy_is yield instructions.CALL_FUNCTION(2)
def _build_list_in_comprehension(self, build_instr, load_instr): yield instrs.LOAD_CONST(jl.jlist).steal(build_instr) yield instrs.CALL_FUNCTION(0) yield instrs.DUP_TOP() yield instrs.DUP_TOP() # TOS = <jlist> # TOS1 = <jlist> # TOS2 = <jlist> yield instrs.LOAD_ATTR('append') yield instrs.STORE_FAST('.append') # TOS = <jlist> # TOS1 = <jlist> yield load_instr # TOS = .0 # TOS1 = <jlist> # TOS2 = <jlist> yield instrs.DUP_TOP() # TOS = .0 # TOS1 = .0 # TOS2 = <jlist> # TOS3 = <jlist> yield instrs.ROT_THREE() # TOS = .0 # TOS1 = <jlist> # TOS2 = .0 # TOS3 = <jlist> yield instrs.LOAD_CONST(operator.length_hint) yield instrs.ROT_TWO() yield instrs.CALL_FUNCTION(1) # TOS = <length_hint> # TOS1 = <jlist> # TOS2 = .0 # TOS3 = <jlist> yield instrs.ROT_TWO() # TOS = <jlist> # TOS1 = <length_hint> # TOS2 = .0 # TOS3 = <jlist> if sys.version_info >= (3, 7): yield instrs.LOAD_METHOD('_reserve') # TOS = <jlist._reserve> # TOS1 = <length_hint> # TOS2 = .0 # TOS3 = <jlist> yield instrs.ROT_TWO() # TOS = <length_hint> # TOS1 = <jlist._reserve> # TOS2 = .0 # TOS3 = <jlist> yield instrs.CALL_METHOD(1) # TOS = None # TOS1 = .0 # TOS3 = <jlist> else: yield instrs.LOAD_ATTR('_reserve') # TOS = <jlist._reserve> # TOS1 = <length_hint> # TOS2 = .0 # TOS3 = <jlist> yield instrs.ROT_TWO() # TOS = <length_hint> # TOS1 = <jlist._reserve> # TOS2 = .0 # TOS3 = <jlist> yield instrs.CALL_FUNCTION(1) # TOS = None # TOS1 = .0 # TOS3 = <jlist> yield instrs.POP_TOP()