Ejemplo n.º 1
0
 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()
Ejemplo n.º 2
0
    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()
Ejemplo n.º 3
0
 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()
Ejemplo n.º 4
0
 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)
Ejemplo n.º 5
0
 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()
Ejemplo n.º 6
0
 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()
Ejemplo n.º 7
0
    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)
Ejemplo n.º 8
0
def build_phorth_ctx(stack_size, memory, word_impl):
    """Create a phorth context with the given stack size and memory.

    This context will have only the primitive words defined but is ready for
    bootstrapping.

    Parameters
    ----------
    word_impl : callable[str]
        A function which returns the next word to read. When there are no more
        words, this function should raise :class:`phorth.Done``.
    stack_size : int
        The size of the stack to build in the phorth frame.
    memory : int
        The size of the memory space for the phorth context. This translates
        to the size of the `co_code`.

    Returns
    -------
    here : int
        The first free memory address in ``ctx``.
    ctx : Context
        The phorth context object, this is a generator that must be consumed
        by `run_phorth` because the bytecode is non-standard.
    """
    word_instrs = {}
    order = []
    default_priority = 10
    is_immediate = {}

    def builtin(name=None, immediate=False, priority=None):
        def _(f):
            nonlocal name
            nonlocal priority
            nonlocal default_priority

            if name is None:
                name = f.__name__

            word_instrs[name] = tuple(f())
            is_immediate[name] = immediate

            if priority is None:
                priority = default_priority
                # leave 10 slots to weave new functions between functions that
                # don't really care about order
                default_priority += 10
            heappush(order, (priority, name))
            return f

        return _

    # build the vocab
    vocab = {}
    instrs = []
    here = 0

    def _compile_vocab():
        nonlocal here

        for _, name in order:
            vocab[name] = Word(
                name,
                len(list(_sparse_args(instrs))),
                is_immediate[name],
            )
            instrs.extend(word_instrs[name])

        order[:] = []
        here = len(list(_sparse_args(instrs)))

    @builtin()
    def __next():
        # pop the return address from the cstack and yield the new address
        # to jump to
        yield instructions.LOAD_CONST(pop_return_addr)
        yield instructions.CALL_FUNCTION(0)
        yield instructions.YIELD_VALUE()

    def next_instruction():
        """Create a new instruction that will exit using the control stack.
        """
        return instructions.JUMP_ABSOLUTE(word_instrs['__next'][0])

    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 _debug_print():
        """DUP_TOP() and PRINT_EXPR()

        Used for debugging the bytecode by providing a "print statment" like
        feature in the bytecode.
        """
        yield instructions.DUP_TOP()
        yield instructions.PRINT_EXPR()

    def _word():
        yield instructions.LOAD_CONST(word_impl)
        yield instructions.CALL_FUNCTION(0)

    @builtin()
    def word():
        yield from _word()
        yield next_instruction()

    @builtin()
    def find():
        yield instructions.LOAD_CONST(find_impl)
        yield instructions.ROT_TWO()
        yield instructions.CALL_FUNCTION(1)
        yield next_instruction()

    def _nip():
        yield instructions.ROT_TWO()
        yield instructions.POP_TOP()

    @builtin(name='>cfa')
    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)

    @builtin(name=',')
    def comma():
        yield instructions.LOAD_CONST(comma_impl)
        yield instructions.ROT_TWO()
        yield instructions.CALL_FUNCTION(1)
        yield instructions.STORE_FAST('here')
        yield next_instruction()

    @builtin(name='b,')
    def bcomma():
        yield instructions.LOAD_CONST(bcomma_impl)
        yield instructions.ROT_TWO()
        yield instructions.CALL_FUNCTION(1)
        yield instructions.STORE_FAST('here')
        yield next_instruction()

    def write_byte(b):
        yield instructions.LOAD_CONST(b)
        yield instructions.LOAD_CONST(push_return_addr)
        yield instructions.CALL_FUNCTION()
        yield instructions.POP_TOP()
        yield instructions.JUMP_ABSOLUTE(word_instrs['b,'][0])

    def write_short(s):
        yield instructions.LOAD_CONST(s)
        yield instructions.LOAD_CONST(push_return_addr)
        yield instructions.CALL_FUNCTION()
        yield instructions.POP_TOP()
        yield instructions.JUMP_ABSOLUTE(word_instrs[','][0])

    def inline_write_byte(b):
        yield instructions.LOAD_CONST(bcomma_impl)
        yield instructions.LOAD_CONST(b)
        yield instructions.CALL_FUNCTION(1)
        yield instructions.STORE_FAST('here')

    def inline_write_short_from_stack():
        yield instructions.LOAD_CONST(comma_impl)
        yield instructions.ROT_TWO()
        yield instructions.CALL_FUNCTION(1)
        yield instructions.STORE_FAST('here')

    def inline_write_short(s):
        yield instructions.LOAD_CONST(comma_impl)
        yield instructions.LOAD_CONST(s)
        yield instructions.CALL_FUNCTION(1)
        yield instructions.STORE_FAST('here')

    handle_exception_instr = instructions.POP_TOP()
    setup_except_instr = instructions.SETUP_EXCEPT(handle_exception_instr)

    def __start(*, counting_run=False):
        yield setup_except_instr
        first = instructions.LOAD_CONST(push_return_addr)
        yield first
        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 twice giving us 3 copies on the stack for:
        #   find
        #   literal lookup
        #   unknown word error
        yield instructions.DUP_TOP()
        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

        process_lit_instr = instructions.POP_TOP()
        yield instructions.POP_JUMP_IF_TRUE(process_lit_instr)

        # clear the word strings from the stack
        yield instructions.ROT_THREE()
        yield instructions.POP_TOP()
        yield instructions.POP_TOP()
        yield instructions.DUP_TOP()
        yield instructions.LOAD_ATTR('addr')
        yield instructions.LOAD_CONST(1)
        yield instructions.BINARY_SUBTRACT()
        yield instructions.LOAD_FAST('immediate')

        immediate_with_nip_instr = instructions.ROT_TWO()
        yield instructions.POP_JUMP_IF_TRUE(immediate_with_nip_instr)

        yield instructions.ROT_TWO()
        yield instructions.LOAD_ATTR('immediate')

        immediate_instr = instructions.LOAD_CONST(push_return_addr)
        yield instructions.POP_JUMP_IF_TRUE(immediate_instr)

        yield instructions.LOAD_CONST(push_return_addr)
        yield instructions.CALL_FUNCTION(0)
        yield instructions.POP_TOP()
        yield instructions.JUMP_ABSOLUTE(word_instrs[','][0])
        yield instructions.JUMP_ABSOLUTE(first)

        yield immediate_with_nip_instr
        yield instructions.POP_TOP()
        yield immediate_instr
        yield instructions.CALL_FUNCTION()
        yield instructions.POP_TOP()
        yield instructions.YIELD_VALUE()
        # We need to add some padding so that the return adress gets
        # computed correctly. Maybe we should have two functions like:
        # push_return_jmp_addr/push_return_yield_addr to handle this.
        yield instructions.NOP()
        yield instructions.NOP()
        yield instructions.JUMP_ABSOLUTE(first)

        yield process_lit_instr
        yield instructions.LOAD_CONST(process_lit)
        yield instructions.ROT_TWO()
        yield instructions.CALL_FUNCTION(1)
        yield instructions.DUP_TOP()
        yield instructions.LOAD_CONST(NotImplemented)
        yield instructions.COMPARE_OP.IS

        unknown_word_instr = instructions.POP_TOP()
        yield instructions.POP_JUMP_IF_TRUE(unknown_word_instr)
        # clear the word string left for the unknown word case
        yield from _nip()
        yield instructions.LOAD_FAST('immediate')
        yield instructions.POP_JUMP_IF_TRUE(first)

        yield instructions.LOAD_CONST(append_lit)
        yield instructions.ROT_TWO()
        yield instructions.CALL_FUNCTION(1)
        yield from inline_write_short(
            None if counting_run else
            len(list(_sparse_args(__start(counting_run=True)))) - 1, )
        yield from inline_write_short_from_stack()
        yield instructions.JUMP_ABSOLUTE(first)

        yield unknown_word_instr
        yield instructions.LOAD_CONST(UnknownWord)
        yield instructions.ROT_TWO()
        yield instructions.CALL_FUNCTION(1)
        yield instructions.RAISE_VARARGS(1)

        # this is the bytecode side of the literal implementation which
        # appears to be dead code but does get jumped to
        if counting_run:
            return

        yield instructions.LOAD_CONST(lit_impl)
        yield instructions.LOAD_CONST(pop_return_addr)
        yield instructions.CALL_FUNCTION(0)
        yield instructions.CALL_FUNCTION(1)
        yield instructions.UNPACK_SEQUENCE(2)
        yield instructions.YIELD_VALUE()

    # this segment goes first, it handles the input loop
    # this is not a decorator because it is recurisive to count the addr
    # of lit
    builtin(priority=0)(__start)

    @builtin()
    def __docol():
        yield instructions.LOAD_CONST(docol_impl)
        yield instructions.CALL_FUNCTION(0)
        yield instructions.YIELD_VALUE()

    @builtin()
    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()

    @builtin()
    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()

    @builtin()
    def create():
        yield instructions.LOAD_CONST(create_impl)
        yield instructions.ROT_TWO()
        yield instructions.CALL_FUNCTION(1)
        yield instructions.STORE_FAST('latest')
        yield next_instruction()

    @builtin(name='[')
    def lbracket():
        yield instructions.LOAD_CONST(False)
        yield instructions.STORE_FAST('immediate')
        yield next_instruction()

    @builtin(name=']', immediate=True)
    def rbracket():
        yield instructions.LOAD_CONST(True)
        yield instructions.STORE_FAST('immediate')
        yield next_instruction()

    @builtin(name="'")
    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)

    @builtin(name='@')
    def read():
        yield instructions.LOAD_CONST(read_impl)
        yield instructions.ROT_TWO()
        yield instructions.CALL_FUNCTION(1)
        yield next_instruction()

    @builtin(name='b@')
    def bread():
        yield instructions.LOAD_CONST(bread_impl)
        yield instructions.ROT_TWO()
        yield instructions.CALL_FUNCTION(1)
        yield next_instruction()

    @builtin(name='!')
    def write():
        yield instructions.LOAD_CONST(write_impl)
        yield instructions.ROT_THREE()
        yield instructions.CALL_FUNCTION(2)
        yield instructions.POP_TOP()
        yield next_instruction()

    @builtin(name='b!')
    def bwrite():
        yield instructions.LOAD_CONST(bwrite_impl)
        yield instructions.ROT_THREE()
        yield instructions.CALL_FUNCTION(2)
        yield instructions.POP_TOP()
        yield next_instruction()

    @builtin()
    def over():
        yield instructions.ROT_TWO()
        yield instructions.DUP_TOP()
        yield instructions.ROT_THREE()
        yield next_instruction()

    @builtin(immediate=True)
    def branch():
        yield instructions.LOAD_CONST(branch_impl)
        yield instructions.ROT_TWO()
        yield instructions.CALL_FUNCTION()
        yield instructions.YIELD_VALUE()

    @builtin(name='0branch', immediate=True)
    def zerobranch():
        yield instructions.LOAD_CONST(0)
        yield instructions.COMPARE_OP.EQ
        yield instructions.POP_JUMP_IF_TRUE(word_instrs['branch'][0])
        yield instructions.YIELD_VALUE()

    @builtin(name='.s')
    def print_stack():
        yield from sync_frame()  # syncing because we want the stacksize
        yield instructions.LOAD_CONST(print_stack_impl)
        yield instructions.CALL_FUNCTION(0)
        yield instructions.POP_TOP()
        yield next_instruction()

    @builtin('/mod')
    def _divmod():
        yield instructions.LOAD_CONST(divmod)
        yield instructions.ROT_THREE()
        yield instructions.CALL_FUNCTION()
        yield next_instruction()

    @builtin()
    def bye():
        yield instructions.LOAD_CONST(Done())
        yield instructions.RAISE_VARARGS(1)

    @builtin()
    def nip():
        yield from _nip()
        yield next_instruction()

    for name, instr in _single_instr_words.items():
        # build all the words that are one CPython instruction
        @builtin(name=name)
        def _(instr=instr):
            yield instr()
            yield next_instruction()

    _compile_vocab()

    @builtin(name=':')
    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()

    @builtin()
    def _license():
        yield instructions.LOAD_CONST(license_impl)
        yield instructions.CALL_FUNCTION(0)
        yield instructions.POP_TOP()
        yield next_instruction()

    @builtin()
    def exit():
        yield instructions.LOAD_CONST(pop_return_addr)
        yield instructions.CALL_FUNCTION(0)
        yield instructions.POP_TOP()
        yield next_instruction()

    _compile_vocab()

    @builtin(name=';', immediate=True)
    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()

    @builtin()
    def immediate():
        yield instructions.LOAD_CONST(True)
        yield instructions.LOAD_FAST('latest')
        yield instructions.STORE_ATTR('immediate')
        yield next_instruction()

    @builtin(name='(', immediate=True)
    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()

    @builtin(name='py::import')
    def py_import():
        yield instructions.LOAD_CONST(__import__)
        yield instructions.ROT_TWO()
        yield instructions.CALL_FUNCTION(1)
        yield next_instruction()

    @builtin(name='py::getattr')
    def py_getattr():
        yield instructions.LOAD_CONST(getattr)
        yield instructions.ROT_THREE()
        yield instructions.CALL_FUNCTION(2)
        yield next_instruction()

    def _nrot():
        yield instructions.ROT_THREE()
        yield instructions.ROT_THREE()

    @builtin(name='py::call')
    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()

    _compile_vocab()

    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)

    instrs.extend(_tail())

    code = Code(
        instrs,
        argnames=argnames,
        flags={
            'CO_NEWLOCALS': True
        },
    ).to_pycode()
    return here, FunctionType(
        CodeType(
            len(argnames),
            0,
            len(argnames),
            stack_size,
            code.co_flags,
            code.co_code,
            tuple(map(_coerce_false_and_true, code.co_consts)),
            code.co_names,
            code.co_varnames,
            '<phorth>',
            '<phorth>',
            1,
            b'',
            (),
            (),
        ),
        {k: v
         for k, v in vocab.items() if not k.startswith('__')},
    )
Ejemplo n.º 9
0
 def exit():
     yield instructions.LOAD_CONST(pop_return_addr)
     yield instructions.CALL_FUNCTION(0)
     yield instructions.POP_TOP()
     yield next_instruction()
Ejemplo n.º 10
0
 def _license():
     yield instructions.LOAD_CONST(license_impl)
     yield instructions.CALL_FUNCTION(0)
     yield instructions.POP_TOP()
     yield next_instruction()
Ejemplo n.º 11
0
 def write_short(s):
     yield instructions.LOAD_CONST(s)
     yield instructions.LOAD_CONST(push_return_addr)
     yield instructions.CALL_FUNCTION()
     yield instructions.POP_TOP()
     yield instructions.JUMP_ABSOLUTE(word_instrs[','][0])
Ejemplo n.º 12
0
 def print_stack():
     yield from sync_frame()  # syncing because we want the stacksize
     yield instructions.LOAD_CONST(print_stack_impl)
     yield instructions.CALL_FUNCTION(0)
     yield instructions.POP_TOP()
     yield next_instruction()
Ejemplo n.º 13
0
 def bwrite():
     yield instructions.LOAD_CONST(bwrite_impl)
     yield instructions.ROT_THREE()
     yield instructions.CALL_FUNCTION(2)
     yield instructions.POP_TOP()
     yield next_instruction()
Ejemplo n.º 14
0
    def __start(*, counting_run=False):
        yield setup_except_instr
        first = instructions.LOAD_CONST(push_return_addr)
        yield first
        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 twice giving us 3 copies on the stack for:
        #   find
        #   literal lookup
        #   unknown word error
        yield instructions.DUP_TOP()
        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

        process_lit_instr = instructions.POP_TOP()
        yield instructions.POP_JUMP_IF_TRUE(process_lit_instr)

        # clear the word strings from the stack
        yield instructions.ROT_THREE()
        yield instructions.POP_TOP()
        yield instructions.POP_TOP()
        yield instructions.DUP_TOP()
        yield instructions.LOAD_ATTR('addr')
        yield instructions.LOAD_CONST(1)
        yield instructions.BINARY_SUBTRACT()
        yield instructions.LOAD_FAST('immediate')

        immediate_with_nip_instr = instructions.ROT_TWO()
        yield instructions.POP_JUMP_IF_TRUE(immediate_with_nip_instr)

        yield instructions.ROT_TWO()
        yield instructions.LOAD_ATTR('immediate')

        immediate_instr = instructions.LOAD_CONST(push_return_addr)
        yield instructions.POP_JUMP_IF_TRUE(immediate_instr)

        yield instructions.LOAD_CONST(push_return_addr)
        yield instructions.CALL_FUNCTION(0)
        yield instructions.POP_TOP()
        yield instructions.JUMP_ABSOLUTE(word_instrs[','][0])
        yield instructions.JUMP_ABSOLUTE(first)

        yield immediate_with_nip_instr
        yield instructions.POP_TOP()
        yield immediate_instr
        yield instructions.CALL_FUNCTION()
        yield instructions.POP_TOP()
        yield instructions.YIELD_VALUE()
        # We need to add some padding so that the return adress gets
        # computed correctly. Maybe we should have two functions like:
        # push_return_jmp_addr/push_return_yield_addr to handle this.
        yield instructions.NOP()
        yield instructions.NOP()
        yield instructions.JUMP_ABSOLUTE(first)

        yield process_lit_instr
        yield instructions.LOAD_CONST(process_lit)
        yield instructions.ROT_TWO()
        yield instructions.CALL_FUNCTION(1)
        yield instructions.DUP_TOP()
        yield instructions.LOAD_CONST(NotImplemented)
        yield instructions.COMPARE_OP.IS

        unknown_word_instr = instructions.POP_TOP()
        yield instructions.POP_JUMP_IF_TRUE(unknown_word_instr)
        # clear the word string left for the unknown word case
        yield from _nip()
        yield instructions.LOAD_FAST('immediate')
        yield instructions.POP_JUMP_IF_TRUE(first)

        yield instructions.LOAD_CONST(append_lit)
        yield instructions.ROT_TWO()
        yield instructions.CALL_FUNCTION(1)
        yield from inline_write_short(
            None if counting_run else
            len(list(_sparse_args(__start(counting_run=True)))) - 1, )
        yield from inline_write_short_from_stack()
        yield instructions.JUMP_ABSOLUTE(first)

        yield unknown_word_instr
        yield instructions.LOAD_CONST(UnknownWord)
        yield instructions.ROT_TWO()
        yield instructions.CALL_FUNCTION(1)
        yield instructions.RAISE_VARARGS(1)

        # this is the bytecode side of the literal implementation which
        # appears to be dead code but does get jumped to
        if counting_run:
            return

        yield instructions.LOAD_CONST(lit_impl)
        yield instructions.LOAD_CONST(pop_return_addr)
        yield instructions.CALL_FUNCTION(0)
        yield instructions.CALL_FUNCTION(1)
        yield instructions.UNPACK_SEQUENCE(2)
        yield instructions.YIELD_VALUE()
Ejemplo n.º 15
0
 def _list_append_in_comprehension(self, instr):
     yield instrs.LOAD_FAST('.append').steal(instr)
     yield instrs.ROT_TWO()
     yield instrs.CALL_FUNCTION(1)
     yield instrs.POP_TOP()
Ejemplo n.º 16
0
        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()
Ejemplo n.º 17
0
 def _nip():
     yield instructions.ROT_TWO()
     yield instructions.POP_TOP()