import time as python_time import json import sys class PrimitiveFinished(Exception): """Exception to indicate the result value of a primitive, as a return cannot be used.""" def __init__(self, value): Exception.__init__(self) self.result = value class InterpretedFunctionFinished(Exception): """Exception to indicate the result value of an interpreted function, as a return cannot be used.""" def __init__(self, value): Exception.__init__(self) self.result = value class SleepKernel(Exception): """Exception to indicate the kernel to sleep for some time.""" def __init__(self, timeout, interruptable): Exception.__init__(self) self.timeout = timeout self.interruptable = interruptable # Functions annotated with __exception_return use the JIT's calling convention instead of # the kernel's: returns are handled by throwing a PrimitiveFinished exception; the caller's # returnvalue is not modified. # # ### Rationale for __exception_return # # __exception_return is a useful mechanism because it allows us to have a __call_function # implementation that has O(1) state read overhead. A previous implementation of # __call_function checked if the caller's frame had been popped whenever # ModelverseKernel.execute_yield threw a StopIteration exception. However, that incurs O(n) overhead # _per call,_ where n is the number of StopIteration exceptions that are thrown during the call. # O(n) is pretty bad, but this actually becomes O(n * m) when m calls to __call_function are # nested. And that's just not acceptable. # __exception_return requires kernel support, but I think the complexity gains are well worth it; # I reckon JIT-to-interpreter switches aren't going to get a whole lot cheaper than this. EXCEPTION_RETURN_KEY = "__exception_return" """A dictionary key for functions which request that the kernel throw a InterpretedFunctionFinished exception with the return value instead of injecting the return value in the caller's frame.""" def integer_subtraction(a, b, **remainder): a_value, b_value = yield [("RV", [a]), ("RV", [b])] result, = yield [("CNV", [a_value - b_value])] raise PrimitiveFinished(result) def integer_addition(a, b, **remainder): a_value, b_value = yield [("RV", [a]), ("RV", [b])] result, = yield [("CNV", [a_value + b_value])] raise PrimitiveFinished(result) def integer_multiplication(a, b, **remainder): a_value, b_value = yield [("RV", [a]), ("RV", [b])] result, = yield [("CNV", [a_value * b_value])] raise PrimitiveFinished(result) def integer_division(a, b, **remainder): a_value, b_value = yield [("RV", [a]), ("RV", [b])] result, = yield [("CNV", [int(a_value) / b_value])] raise PrimitiveFinished(result) def integer_lt(a, b, **remainder): a_value, b_value = yield [("RV", [a]), ("RV", [b])] result, = yield [("CNV", [a_value < b_value])] raise PrimitiveFinished(result) def bool_and(a, b, **remainder): a_value, b_value = yield [("RV", [a]), ("RV", [b])] result, = yield [("CNV", [a_value and b_value])] raise PrimitiveFinished(result) def bool_or(a, b, **remainder): a_value, b_value = yield [("RV", [a]), ("RV", [b])] result, = yield [("CNV", [a_value or b_value])] raise PrimitiveFinished(result) def bool_not(a, **remainder): a_value, = yield [("RV", [a])] result, = yield [("CNV", [not a_value])] raise PrimitiveFinished(result) def float_subtraction(a, b, **remainder): a_value, b_value = yield [("RV", [a]), ("RV", [b])] result, = yield [("CNV", [a_value - b_value])] raise PrimitiveFinished(result) def float_addition(a, b, **remainder): a_value, b_value = yield [("RV", [a]), ("RV", [b])] result, = yield [("CNV", [a_value + b_value])] raise PrimitiveFinished(result) def float_multiplication(a, b, **remainder): a_value, b_value = yield [("RV", [a]), ("RV", [b])] result, = yield [("CNV", [a_value * b_value])] raise PrimitiveFinished(result) def float_division(a, b, **remainder): a_value, b_value = yield [("RV", [a]), ("RV", [b])] result, = yield [("CNV", [float(a_value) / float(b_value)])] raise PrimitiveFinished(result) def float_lt(a, b, **remainder): a_value, b_value = yield [("RV", [a]), ("RV", [b])] result, = yield [("CNV", [a_value < b_value])] raise PrimitiveFinished(result) def string_join(a, b, **remainder): a_value, b_value = yield [("RV", [a]), ("RV", [b])] result, = yield [("CNV", [str(a_value) + str(b_value)])] raise PrimitiveFinished(result) def string_split(a, b, **remainder): # TODO make non-primitive, though compiled a_value, b_value = yield [("RV", [a]), ("RV", [b])] result = a_value.split(b_value) elems = yield [("CN", [])] + [("CNV", [v]) for v in result] new_val = elems[0] yield [("CD", [new_val, i, v]) for i, v in enumerate(elems[1:])] raise PrimitiveFinished(new_val) def string_get(a, b, **remainder): a_value, b_value = yield [("RV", [a]), ("RV", [b])] result, = yield [("CNV", [a_value[b_value]])] raise PrimitiveFinished(result) def string_len(a, **remainder): a_value, = yield [("RV", [a])] result, = yield [("CNV", [len(a_value)])] raise PrimitiveFinished(result) def value_eq(a, b, **remainder): a_value, b_value = yield [("RV", [a]), ("RV", [b])] result, = yield [("CNV", [a_value == b_value])] raise PrimitiveFinished(result) def element_eq(a, b, **remainder): result, = yield [("CNV", [a == b])] raise PrimitiveFinished(result) def cast_string(a, **remainder): a_value, = yield [("RV", [a])] if isinstance(a_value, dict): result, = yield [("CNV", [str(a_value["value"])])] else: result, = yield [("CNV", [str(a_value)])] raise PrimitiveFinished(result) def cast_float(a, **remainder): a_value, = yield [("RV", [a])] result, = yield [("CNV", [float(a_value)])] raise PrimitiveFinished(result) def cast_boolean(a, **remainder): a_value, = yield [("RV", [a])] result, = yield [("CNV", [bool(a_value)])] raise PrimitiveFinished(result) def cast_integer(a, **remainder): a_value, = yield [("RV", [a])] result, = yield [("CNV", [int(a_value)])] raise PrimitiveFinished(result) def cast_value(a, **remainder): a_value, = yield [("RV", [a])] if isinstance(a_value, dict): # Action or type value = a_value["value"] else: value = json.dumps(a_value) result, = yield [("CNV", [value])] raise PrimitiveFinished(result) def cast_id(a, **remainder): result, = yield [("CNV", ["%s" % (a)])] raise PrimitiveFinished(result) def list_insert(a, b, c, **remainder): # TODO make non-primitive, though compiled a_outgoing, c_value = yield [("RO", [a]), ("RV", [c])] links = yield [("RD", [a, i]) for i in range(c_value, len(a_outgoing))] + \ [("RDE", [a, i]) for i in range(c_value, len(a_outgoing))] if sys.version_info[0] < 3: values = links[:len(links)/2] edges = links[len(links)/2:] else: values = links[:len(links) // 2] edges = links[len(links) // 2:] yield [("CD", [a, c_value, b])] + \ [("CD", [a, c_value + 1 + index, value]) for index, value in enumerate(values)] + \ [("DE", [i]) for i in edges] raise PrimitiveFinished(a) def list_delete(a, b, **remainder): # TODO make non-primitive, though compiled a_outgoing, b_value = yield [("RO", [a]), ("RV", [b])] links = yield [("RD", [a, i]) for i in range(b_value, len(a_outgoing))] + \ [("RDE", [a, i]) for i in range(b_value, len(a_outgoing))] if sys.version_info[0] < 3: values = links[:len(links) / 2] edges = links[len(links) / 2:] else: values = links[:len(links) // 2] edges = links[len(links) // 2:] yield [("CD", [a, b_value + index, value]) for index, value in enumerate(values[1:])] + \ [("DE", [i]) for i in edges] raise PrimitiveFinished(a) def dict_add_fast(a, b, c, **remainder): # TODO deprecate, as dict_add is now also efficient v, = yield [("RV", [b])] yield [("CD", [a, v, c])] raise PrimitiveFinished(a) def dict_delete(a, b, **remainder): b_value, = yield [("RV", [b])] edge, = yield [("RDE", [a, b_value])] if edge is None: print("Failed dict_delete for value '%s'!" % b_value) keys, = yield [("RDK", [a])] keys = yield [("RV", [i]) for i in keys] print("Keys: " + str(keys)) raise Exception() yield [("DE", [edge])] raise PrimitiveFinished(a) def dict_delete_node(a, b, **remainder): edge, = yield [("RDNE", [a, b])] if edge is None: print("Failed dict_delete_node!") yield [("DE", [edge])] raise PrimitiveFinished(a) def dict_read(a, b, **remainder): b_value, = yield [("RV", [b])] result, = yield [("RD", [a, b_value])] raise PrimitiveFinished(result) def dict_read_edge(a, b, **remainder): b_value, = yield [("RV", [b])] result, = yield [("RDE", [a, b_value])] raise PrimitiveFinished(result) def dict_read_node(a, b, **remainder): result, = yield [("RDN", [a, b])] raise PrimitiveFinished(result) def dict_in(a, b, **remainder): b_value, = yield [("RV", [b])] value, = yield [("RD", [a, b_value])] is_in = value is not None result, = yield [("CNV", [is_in])] raise PrimitiveFinished(result) def dict_in_node(a, b, **remainder): value, = yield [("RDN", [a, b])] result, = yield [("CNV", [value is not None])] raise PrimitiveFinished(result) def dict_keys(a, **remainder): keys, result = yield [("RDK", [a]), ("CN", [])] edges = yield [("CE", [result, result]) for _ in keys] _ = yield [("CE", [edge, key]) for edge, key in zip(edges, keys)] raise PrimitiveFinished(result) def is_physical_int(a, **remainder): t, = yield [("RV", [a])] result, = yield [("CNV", [isinstance(t, int) or isinstance(t, long)])] raise PrimitiveFinished(result) def is_physical_string(a, **remainder): t, = yield [("RV", [a])] result, = yield [("CNV", [isinstance(t, str) or isinstance(t, unicode)])] raise PrimitiveFinished(result) def is_physical_float(a, **remainder): t, = yield [("RV", [a])] result, = yield [("CNV", [isinstance(t, float)])] raise PrimitiveFinished(result) def is_physical_boolean(a, **remainder): t, = yield [("RV", [a])] result, = yield [("CNV", [isinstance(t, bool)])] raise PrimitiveFinished(result) def is_physical_action(a, **remainder): t, = yield [("RV", [a])] result, = yield [("CNV", [isinstance(t, dict) and t["value"] in ["if", "while", "assign", "call", "break", "continue", "return", "resolve", "access", "constant", "global", "declare"]])] raise PrimitiveFinished(result) def is_physical_none(a, **remainder): t, = yield [("RV", [a])] result, = yield [("CNV", [isinstance(t, dict) and t["value"] == "none"])] raise PrimitiveFinished(result) def create_node(**remainder): result, = yield [("CN", [])] raise PrimitiveFinished(result) def create_edge(a, b, **remainder): result, = yield [("CE", [a, b])] raise PrimitiveFinished(result) def create_value(a, **remainder): a_value, = yield [("RV", [a])] result, = yield [("CNV", [a_value])] raise PrimitiveFinished(result) def read_nr_out(a, **remainder): outgoing, = yield [("RO", [a])] result, = yield [("CNV", [len(outgoing)])] raise PrimitiveFinished(result) def read_out(a, b, root, **remainder): outgoing, b_value = yield [("RO", [a]), ("RV", [b])] raise PrimitiveFinished(sorted(outgoing)[b_value] if len(outgoing) > b_value else root) def read_nr_in(a, **remainder): incoming, = yield [("RI", [a])] result, = yield [("CNV", [len(incoming)])] raise PrimitiveFinished(result) def read_in(a, b, root, **remainder): incoming, b_value = yield [("RI", [a]), ("RV", [b])] raise PrimitiveFinished(sorted(incoming)[b_value] if len(incoming) > b_value else root) def read_edge_src(a, **remainder): result, = yield [("RE", [a])] raise PrimitiveFinished(result[0]) def read_edge_dst(a, **remainder): result, = yield [("RE", [a])] raise PrimitiveFinished(result[1]) def delete_element(a, **remainder): edge, = yield [("RE", [a])] if edge[0] is None: # Not an edge: yield [("DN", [a])] result, = yield [("CNV", [False])] raise PrimitiveFinished(result) else: yield [("DE", [a])] result, = yield [("CNV", [True])] raise PrimitiveFinished(result) def read_root(root, **remainder): raise PrimitiveFinished(root) def is_edge(a, **remainder): edge, = yield [("RE", [a])] result, = yield [("CNV", [edge[0] is not None])] raise PrimitiveFinished(result) def log(a, **remainder): a_value, = yield [("RV", [a])] print("== LOG == " + str(a_value)) raise PrimitiveFinished(a) def read_taskroot(task_root, **remainder): raise PrimitiveFinished(task_root) def time(**remainder): a, = yield [("CNV", [python_time.time()])] raise PrimitiveFinished(a) def hash(a, **remainder): a_value, = yield [("RV", [a])] import hashlib b_value = hashlib.sha512(a_value).hexdigest() b, = yield [("CNV", [b_value])] raise PrimitiveFinished(b) def __sleep(a, b, **remainder): timeout, interruptable = yield [("RV", [a]), ("RV", [b])] yield [("SLEEP", [timeout, interruptable])] raise PrimitiveFinished(a) def is_error(a, **remainder): if a is None: result, = yield [("CNV", [True])] else: result, = yield [("CNV", [False])] raise PrimitiveFinished(result)