|
@@ -0,0 +1,450 @@
|
|
|
+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):
|
|
|
+ if 'value' not in a:
|
|
|
+ a['value'], = yield [("RV", [a['id']])]
|
|
|
+ if 'value' not in b:
|
|
|
+ b['value'], = yield [("RV", [b['id']])]
|
|
|
+ result = {'value': a['value'] - b['value']}
|
|
|
+ raise PrimitiveFinished(result)
|
|
|
+
|
|
|
+def integer_addition(a, b, **remainder):
|
|
|
+ if 'value' not in a:
|
|
|
+ a['value'], = yield [("RV", [a['id']])]
|
|
|
+ if 'value' not in b:
|
|
|
+ b['value'], = yield [("RV", [b['id']])]
|
|
|
+ result = {'value': a['value'] + b['value']}
|
|
|
+ raise PrimitiveFinished(result)
|
|
|
+
|
|
|
+def integer_multiplication(a, b, **remainder):
|
|
|
+ if 'value' not in a:
|
|
|
+ a['value'], = yield [("RV", [a['id']])]
|
|
|
+ if 'value' not in b:
|
|
|
+ b['value'], = yield [("RV", [b['id']])]
|
|
|
+ result = {'value': a['value'] * b['value']}
|
|
|
+ raise PrimitiveFinished(result)
|
|
|
+
|
|
|
+def integer_division(a, b, **remainder):
|
|
|
+ if 'value' not in a:
|
|
|
+ a['value'], = yield [("RV", [a['id']])]
|
|
|
+ if 'value' not in b:
|
|
|
+ b['value'], = yield [("RV", [b['id']])]
|
|
|
+ result = {'value': int(a['value']) // b['value']}
|
|
|
+ raise PrimitiveFinished(result)
|
|
|
+
|
|
|
+def integer_lt(a, b, **remainder):
|
|
|
+ if 'value' not in a:
|
|
|
+ a['value'], = yield [("RV", [a['id']])]
|
|
|
+ if 'value' not in b:
|
|
|
+ b['value'], = yield [("RV", [b['id']])]
|
|
|
+ result = {'value': a['value'] < b['value']}
|
|
|
+ raise PrimitiveFinished(result)
|
|
|
+
|
|
|
+def bool_and(a, b, **remainder):
|
|
|
+ if 'value' not in a:
|
|
|
+ a['value'], = yield [("RV", [a['id']])]
|
|
|
+ if 'value' not in b:
|
|
|
+ b['value'], = yield [("RV", [b['id']])]
|
|
|
+ result = {'value': a['value'] and b['value']}
|
|
|
+ raise PrimitiveFinished(result)
|
|
|
+
|
|
|
+def bool_or(a, b, **remainder):
|
|
|
+ if 'value' not in a:
|
|
|
+ a['value'], = yield [("RV", [a['id']])]
|
|
|
+ if 'value' not in b:
|
|
|
+ b['value'], = yield [("RV", [b['id']])]
|
|
|
+ result = {'value': a['value'] or b['value']}
|
|
|
+ raise PrimitiveFinished(result)
|
|
|
+
|
|
|
+def bool_not(a, **remainder):
|
|
|
+ if 'value' not in a:
|
|
|
+ a['value'], = yield [("RV", [a['id']])]
|
|
|
+ result = {'value': not a['value']}
|
|
|
+ raise PrimitiveFinished(result)
|
|
|
+
|
|
|
+def float_subtraction(a, b, **remainder):
|
|
|
+ if 'value' not in a:
|
|
|
+ a['value'], = yield [("RV", [a['id']])]
|
|
|
+ if 'value' not in b:
|
|
|
+ b['value'], = yield [("RV", [b['id']])]
|
|
|
+ result = {'value': a['value'] - b['value']}
|
|
|
+ raise PrimitiveFinished(result)
|
|
|
+
|
|
|
+def float_addition(a, b, **remainder):
|
|
|
+ if 'value' not in a:
|
|
|
+ a['value'], = yield [("RV", [a['id']])]
|
|
|
+ if 'value' not in b:
|
|
|
+ b['value'], = yield [("RV", [b['id']])]
|
|
|
+ result = {'value': a['value'] + b['value']}
|
|
|
+ raise PrimitiveFinished(result)
|
|
|
+
|
|
|
+def float_multiplication(a, b, **remainder):
|
|
|
+ if 'value' not in a:
|
|
|
+ a['value'], = yield [("RV", [a['id']])]
|
|
|
+ if 'value' not in b:
|
|
|
+ b['value'], = yield [("RV", [b['id']])]
|
|
|
+ result = {'value': a['value'] * b['value']}
|
|
|
+ raise PrimitiveFinished(result)
|
|
|
+
|
|
|
+def float_division(a, b, **remainder):
|
|
|
+ if 'value' not in a:
|
|
|
+ a['value'], = yield [("RV", [a['id']])]
|
|
|
+ if 'value' not in b:
|
|
|
+ b['value'], = yield [("RV", [b['id']])]
|
|
|
+ result = {'value': float(a['value']) / float(b['value'])}
|
|
|
+ raise PrimitiveFinished(result)
|
|
|
+
|
|
|
+def float_lt(a, b, **remainder):
|
|
|
+ if 'value' not in a:
|
|
|
+ a['value'], = yield [("RV", [a['id']])]
|
|
|
+ if 'value' not in b:
|
|
|
+ b['value'], = yield [("RV", [b['id']])]
|
|
|
+ result = {'value': a['value'] < b['value']}
|
|
|
+ raise PrimitiveFinished(result)
|
|
|
+
|
|
|
+def string_join(a, b, **remainder):
|
|
|
+ if 'value' not in a:
|
|
|
+ a['value'], = yield [("RV", [a['id']])]
|
|
|
+ if 'value' not in b:
|
|
|
+ b['value'], = yield [("RV", [b['id']])]
|
|
|
+ result = {'value': 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)
|
|
|
+
|
|
|
+ if 'value' not in a:
|
|
|
+ a['value'], = yield [("RV", [a['id']])]
|
|
|
+ if 'value' not in b:
|
|
|
+ b['value'], = yield [("RV", [b['id']])]
|
|
|
+ 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({'id': 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])]
|
|
|
+ try:
|
|
|
+ result, = yield [("CNV", [isinstance(t, int) or isinstance(t, long)])]
|
|
|
+ except NameError:
|
|
|
+ result, = yield [("CNV", [isinstance(t, int)])]
|
|
|
+
|
|
|
+ raise PrimitiveFinished(result)
|
|
|
+
|
|
|
+def is_physical_string(a, **remainder):
|
|
|
+ t, = yield [("RV", [a])]
|
|
|
+ try:
|
|
|
+ result, = yield [("CNV", [isinstance(t, str) or isinstance(t, unicode)])]
|
|
|
+ except NameError:
|
|
|
+ result, = yield [("CNV", [isinstance(t, str)])]
|
|
|
+
|
|
|
+ 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
|
|
|
+ try:
|
|
|
+ b_value = hashlib.sha512(a_value).hexdigest()
|
|
|
+ except TypeError:
|
|
|
+ b_value = hashlib.sha512(a_value.encode()).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)
|