import modelverse_kernel.primitives as primitive_functions

def interpret_function(function_id, named_arguments, **kwargs):
    """Makes the interpreter run the function with the given id with the specified
       argument dictionary."""

    user_root = kwargs['user_root']
    user_frame, = yield [("RD", [user_root, "frame"])]
    inst, = yield [("RD", [user_frame, "IP"])]

    body_id, = yield [("RD", [function_id, "body"])]

    # Create a new stack frame.
    frame_link, new_phase, new_frame, new_evalstack, new_symbols, \
        new_returnvalue, intrinsic_return = \
                    yield [("RDE", [user_root, "frame"]),
                           ("CNV", ["init"]),
                           ("CN", []),
                           ("CN", []),
                           ("CN", []),
                           ("CN", []),
                           ("CN", [])
                          ]

    _, _, _, _, _, _, _, _, _, _ = \
                    yield [("CD", [user_root, "frame", new_frame]),
                           ("CD", [new_frame, "evalstack", new_evalstack]),
                           ("CD", [new_frame, "symbols", new_symbols]),
                           ("CD", [new_frame, "returnvalue", new_returnvalue]),
                           ("CD", [new_frame, "caller", inst]),
                           ("CD", [new_frame, "phase", new_phase]),
                           ("CD", [new_frame, "IP", body_id]),
                           ("CD", [new_frame, "prev", user_frame]),
                           ("CD", [
                               new_frame,
                               primitive_functions.PRIMITIVE_RETURN_KEY,
                               intrinsic_return]),
                           ("DE", [frame_link])
                          ]

    # Put the parameters in the new stack frame's symbol table.
    kernel = kwargs['mvk']
    try:
        gen = kernel.jit.jit_parameters(body_id)
        inp = None
        while 1:
            inp = yield gen.send(inp)
    except primitive_functions.PrimitiveFinished as ex:
        parameter_vars, parameter_names = ex.result
        parameter_dict = dict(zip(parameter_names, parameter_vars))

    for (key, value) in named_arguments.items():
        param_var = parameter_dict[key]
        variable, = yield [("CN", [])]
        yield [("CD", [variable, "value", value])]
        symbol_edge, = yield [("CE", [new_symbols, variable])]
        yield [("CE", [symbol_edge, param_var])]

    username = kwargs['username']
    while 1:
        try:
            gen = kernel.execute_rule(username)
            inp = None
            while 1:
                inp = yield gen.send(inp)
        except StopIteration:
            # An instruction has been completed. Forward it.
            yield None
        # Let primitive_functions.PrimitiveFinished bubble up.
