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 = \ yield [("RDE", [user_root, "frame"]), ("CNV", ["init"]), ("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]), ("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. Check if we've already returned. # # TODO: the statement below performs O(n) state reads whenever an instruction # finishes, where n is the number of 'interpret_function' stack frames. # I don't *think* that this is problematic (at least not in the short term), # but an O(1) solution would obviously be much better; that's the interpreter's # complexity. Perhaps we can annotate the stack frame we create here with a marker # that the kernel can pick up on? We could have the kernel throw an exception whenever # it encounters said marker. current_user_frame, = yield [("RD", [user_root, "frame"])] if current_user_frame == user_frame: # We're done here. Extract the return value and get out. returnvalue, = yield [("RD", [user_frame, "returnvalue"])] raise primitive_functions.PrimitiveFinished(returnvalue) else: yield None