primitives.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377
  1. import time as python_time
  2. import json
  3. class PrimitiveFinished(Exception):
  4. """Exception to indicate the result value of a primitive, as a return cannot be used."""
  5. def __init__(self, value):
  6. Exception.__init__(self)
  7. self.result = value
  8. class InterpretedFunctionFinished(Exception):
  9. """Exception to indicate the result value of an interpreted function, as a return
  10. cannot be used."""
  11. def __init__(self, value):
  12. Exception.__init__(self)
  13. self.result = value
  14. class SleepKernel(Exception):
  15. """Exception to indicate the kernel to sleep for some time."""
  16. def __init__(self, timeout, interruptable):
  17. Exception.__init__(self)
  18. self.timeout = timeout
  19. self.interruptable = interruptable
  20. # Functions annotated with __exception_return use the JIT's calling convention instead of
  21. # the kernel's: returns are handled by throwing a PrimitiveFinished exception; the caller's
  22. # returnvalue is not modified.
  23. #
  24. # ### Rationale for __exception_return
  25. #
  26. # __exception_return is a useful mechanism because it allows us to have a __call_function
  27. # implementation that has O(1) state read overhead. A previous implementation of
  28. # __call_function checked if the caller's frame had been popped whenever
  29. # ModelverseKernel.execute_yield threw a StopIteration exception. However, that incurs O(n) overhead
  30. # _per call,_ where n is the number of StopIteration exceptions that are thrown during the call.
  31. # O(n) is pretty bad, but this actually becomes O(n * m) when m calls to __call_function are
  32. # nested. And that's just not acceptable.
  33. # __exception_return requires kernel support, but I think the complexity gains are well worth it;
  34. # I reckon JIT-to-interpreter switches aren't going to get a whole lot cheaper than this.
  35. EXCEPTION_RETURN_KEY = "__exception_return"
  36. """A dictionary key for functions which request that the kernel throw a InterpretedFunctionFinished
  37. exception with the return value instead of injecting the return value in the caller's frame."""
  38. def integer_subtraction(a, b, **remainder):
  39. a_value, b_value = yield [("RV", [a]), ("RV", [b])]
  40. result, = yield [("CNV", [a_value - b_value])]
  41. raise PrimitiveFinished(result)
  42. def integer_addition(a, b, **remainder):
  43. a_value, b_value = yield [("RV", [a]), ("RV", [b])]
  44. result, = yield [("CNV", [a_value + b_value])]
  45. raise PrimitiveFinished(result)
  46. def integer_multiplication(a, b, **remainder):
  47. a_value, b_value = yield [("RV", [a]), ("RV", [b])]
  48. result, = yield [("CNV", [a_value * b_value])]
  49. raise PrimitiveFinished(result)
  50. def integer_division(a, b, **remainder):
  51. a_value, b_value = yield [("RV", [a]), ("RV", [b])]
  52. result, = yield [("CNV", [int(a_value) / b_value])]
  53. raise PrimitiveFinished(result)
  54. def integer_lt(a, b, **remainder):
  55. a_value, b_value = yield [("RV", [a]), ("RV", [b])]
  56. result, = yield [("CNV", [a_value < b_value])]
  57. raise PrimitiveFinished(result)
  58. def bool_and(a, b, **remainder):
  59. a_value, b_value = yield [("RV", [a]), ("RV", [b])]
  60. result, = yield [("CNV", [a_value and b_value])]
  61. raise PrimitiveFinished(result)
  62. def bool_or(a, b, **remainder):
  63. a_value, b_value = yield [("RV", [a]), ("RV", [b])]
  64. result, = yield [("CNV", [a_value or b_value])]
  65. raise PrimitiveFinished(result)
  66. def bool_not(a, **remainder):
  67. a_value, = yield [("RV", [a])]
  68. result, = yield [("CNV", [not a_value])]
  69. raise PrimitiveFinished(result)
  70. def float_subtraction(a, b, **remainder):
  71. a_value, b_value = yield [("RV", [a]), ("RV", [b])]
  72. result, = yield [("CNV", [a_value - b_value])]
  73. raise PrimitiveFinished(result)
  74. def float_addition(a, b, **remainder):
  75. a_value, b_value = yield [("RV", [a]), ("RV", [b])]
  76. result, = yield [("CNV", [a_value + b_value])]
  77. raise PrimitiveFinished(result)
  78. def float_multiplication(a, b, **remainder):
  79. a_value, b_value = yield [("RV", [a]), ("RV", [b])]
  80. result, = yield [("CNV", [a_value * b_value])]
  81. raise PrimitiveFinished(result)
  82. def float_division(a, b, **remainder):
  83. a_value, b_value = yield [("RV", [a]), ("RV", [b])]
  84. result, = yield [("CNV", [float(a_value) / float(b_value)])]
  85. raise PrimitiveFinished(result)
  86. def float_lt(a, b, **remainder):
  87. a_value, b_value = yield [("RV", [a]), ("RV", [b])]
  88. result, = yield [("CNV", [a_value < b_value])]
  89. raise PrimitiveFinished(result)
  90. def string_join(a, b, **remainder):
  91. a_value, b_value = yield [("RV", [a]), ("RV", [b])]
  92. result, = yield [("CNV", [str(a_value) + str(b_value)])]
  93. raise PrimitiveFinished(result)
  94. def string_split(a, b, **remainder):
  95. # TODO make non-primitive, though compiled
  96. a_value, b_value = yield [("RV", [a]), ("RV", [b])]
  97. result = a_value.split(b_value)
  98. elems = yield [("CN", [])] + [("CNV", [v]) for v in result]
  99. new_val = elems[0]
  100. yield [("CD", [new_val, i, v]) for i, v in enumerate(elems[1:])]
  101. raise PrimitiveFinished(new_val)
  102. def string_get(a, b, **remainder):
  103. a_value, b_value = yield [("RV", [a]), ("RV", [b])]
  104. result, = yield [("CNV", [a_value[b_value]])]
  105. raise PrimitiveFinished(result)
  106. def string_len(a, **remainder):
  107. a_value, = yield [("RV", [a])]
  108. result, = yield [("CNV", [len(a_value)])]
  109. raise PrimitiveFinished(result)
  110. def value_eq(a, b, **remainder):
  111. a_value, b_value = yield [("RV", [a]), ("RV", [b])]
  112. result, = yield [("CNV", [a_value == b_value])]
  113. raise PrimitiveFinished(result)
  114. def element_eq(a, b, **remainder):
  115. result, = yield [("CNV", [a == b])]
  116. raise PrimitiveFinished(result)
  117. def cast_string(a, **remainder):
  118. a_value, = yield [("RV", [a])]
  119. if isinstance(a_value, dict):
  120. result, = yield [("CNV", [str(a_value["value"])])]
  121. else:
  122. result, = yield [("CNV", [str(a_value)])]
  123. raise PrimitiveFinished(result)
  124. def cast_float(a, **remainder):
  125. a_value, = yield [("RV", [a])]
  126. result, = yield [("CNV", [float(a_value)])]
  127. raise PrimitiveFinished(result)
  128. def cast_boolean(a, **remainder):
  129. a_value, = yield [("RV", [a])]
  130. result, = yield [("CNV", [bool(a_value)])]
  131. raise PrimitiveFinished(result)
  132. def cast_integer(a, **remainder):
  133. a_value, = yield [("RV", [a])]
  134. result, = yield [("CNV", [int(a_value)])]
  135. raise PrimitiveFinished(result)
  136. def cast_value(a, **remainder):
  137. a_value, = yield [("RV", [a])]
  138. if isinstance(a_value, dict):
  139. # Action or type
  140. value = a_value["value"]
  141. else:
  142. value = json.dumps(a_value)
  143. result, = yield [("CNV", [value])]
  144. raise PrimitiveFinished(result)
  145. def cast_id(a, **remainder):
  146. result, = yield [("CNV", ["%s" % (a)])]
  147. raise PrimitiveFinished(result)
  148. def list_insert(a, b, c, **remainder):
  149. # TODO make non-primitive, though compiled
  150. a_outgoing, c_value = yield [("RO", [a]), ("RV", [c])]
  151. links = yield [("RD", [a, i]) for i in range(c_value, len(a_outgoing))] + \
  152. [("RDE", [a, i]) for i in range(c_value, len(a_outgoing))]
  153. values = links[:len(links)/2]
  154. edges = links[len(links)/2:]
  155. yield [("CD", [a, c_value, b])] + \
  156. [("CD", [a, c_value + 1 + index, value]) for index, value in enumerate(values)] + \
  157. [("DE", [i]) for i in edges]
  158. raise PrimitiveFinished(a)
  159. def list_delete(a, b, **remainder):
  160. # TODO make non-primitive, though compiled
  161. a_outgoing, b_value = yield [("RO", [a]), ("RV", [b])]
  162. links = yield [("RD", [a, i]) for i in range(b_value, len(a_outgoing))] + \
  163. [("RDE", [a, i]) for i in range(b_value, len(a_outgoing))]
  164. values = links[:len(links)/2]
  165. edges = links[len(links)/2:]
  166. yield [("CD", [a, b_value + index, value]) for index, value in enumerate(values[1:])] + \
  167. [("DE", [i]) for i in edges]
  168. raise PrimitiveFinished(a)
  169. def dict_add_fast(a, b, c, **remainder):
  170. # TODO deprecate, as dict_add is now also efficient
  171. v, = yield [("RV", [b])]
  172. yield [("CD", [a, v, c])]
  173. raise PrimitiveFinished(a)
  174. def dict_delete(a, b, **remainder):
  175. b_value, = yield [("RV", [b])]
  176. edge, = yield [("RDE", [a, b_value])]
  177. if edge is None:
  178. print("Failed dict_delete for value %s!" % b_value)
  179. keys, = yield [("RDK", [a])]
  180. keys = yield [("RV", [i]) for i in keys]
  181. print("Keys: " + str(keys))
  182. raise Exception()
  183. yield [("DE", [edge])]
  184. raise PrimitiveFinished(a)
  185. def dict_delete_node(a, b, **remainder):
  186. edge, = yield [("RDNE", [a, b])]
  187. if edge is None:
  188. print("Failed dict_delete_node!")
  189. yield [("DE", [edge])]
  190. raise PrimitiveFinished(a)
  191. def dict_read(a, b, **remainder):
  192. b_value, = yield [("RV", [b])]
  193. result, = yield [("RD", [a, b_value])]
  194. raise PrimitiveFinished(result)
  195. def dict_read_edge(a, b, **remainder):
  196. b_value, = yield [("RV", [b])]
  197. result, = yield [("RDE", [a, b_value])]
  198. raise PrimitiveFinished(result)
  199. def dict_read_node(a, b, **remainder):
  200. result, = yield [("RDN", [a, b])]
  201. raise PrimitiveFinished(result)
  202. def dict_in(a, b, **remainder):
  203. b_value, = yield [("RV", [b])]
  204. value, = yield [("RD", [a, b_value])]
  205. is_in = value is not None
  206. result, = yield [("CNV", [is_in])]
  207. raise PrimitiveFinished(result)
  208. def dict_in_node(a, b, **remainder):
  209. value, = yield [("RDN", [a, b])]
  210. result, = yield [("CNV", [value is not None])]
  211. raise PrimitiveFinished(result)
  212. def dict_keys(a, **remainder):
  213. keys, result = yield [("RDK", [a]), ("CN", [])]
  214. edges = yield [("CE", [result, result]) for _ in keys]
  215. _ = yield [("CE", [edge, key]) for edge, key in zip(edges, keys)]
  216. raise PrimitiveFinished(result)
  217. def is_physical_int(a, **remainder):
  218. t, = yield [("RV", [a])]
  219. result, = yield [("CNV", [isinstance(t, int) or isinstance(t, long)])]
  220. raise PrimitiveFinished(result)
  221. def is_physical_string(a, **remainder):
  222. t, = yield [("RV", [a])]
  223. result, = yield [("CNV", [isinstance(t, str) or isinstance(t, unicode)])]
  224. raise PrimitiveFinished(result)
  225. def is_physical_float(a, **remainder):
  226. t, = yield [("RV", [a])]
  227. result, = yield [("CNV", [isinstance(t, float)])]
  228. raise PrimitiveFinished(result)
  229. def is_physical_boolean(a, **remainder):
  230. t, = yield [("RV", [a])]
  231. result, = yield [("CNV", [isinstance(t, bool)])]
  232. raise PrimitiveFinished(result)
  233. def is_physical_action(a, **remainder):
  234. t, = yield [("RV", [a])]
  235. result, = yield [("CNV", [isinstance(t, dict) and t["value"] in ["if", "while", "assign", "call", "break", "continue", "return", "resolve", "access", "constant", "global", "declare"]])]
  236. raise PrimitiveFinished(result)
  237. def is_physical_none(a, **remainder):
  238. t, = yield [("RV", [a])]
  239. result, = yield [("CNV", [isinstance(t, dict) and t["value"] == "none"])]
  240. raise PrimitiveFinished(result)
  241. def create_node(**remainder):
  242. result, = yield [("CN", [])]
  243. raise PrimitiveFinished(result)
  244. def create_edge(a, b, **remainder):
  245. result, = yield [("CE", [a, b])]
  246. raise PrimitiveFinished(result)
  247. def create_value(a, **remainder):
  248. a_value, = yield [("RV", [a])]
  249. result, = yield [("CNV", [a_value])]
  250. raise PrimitiveFinished(result)
  251. def read_nr_out(a, **remainder):
  252. outgoing, = yield [("RO", [a])]
  253. result, = yield [("CNV", [len(outgoing)])]
  254. raise PrimitiveFinished(result)
  255. def read_out(a, b, root, **remainder):
  256. outgoing, b_value = yield [("RO", [a]), ("RV", [b])]
  257. raise PrimitiveFinished(sorted(outgoing)[b_value] if len(outgoing) > b_value else root)
  258. def read_nr_in(a, **remainder):
  259. incoming, = yield [("RI", [a])]
  260. result, = yield [("CNV", [len(incoming)])]
  261. raise PrimitiveFinished(result)
  262. def read_in(a, b, root, **remainder):
  263. incoming, b_value = yield [("RI", [a]), ("RV", [b])]
  264. raise PrimitiveFinished(sorted(incoming)[b_value] if len(incoming) > b_value else root)
  265. def read_edge_src(a, **remainder):
  266. result, = yield [("RE", [a])]
  267. raise PrimitiveFinished(result[0])
  268. def read_edge_dst(a, **remainder):
  269. result, = yield [("RE", [a])]
  270. raise PrimitiveFinished(result[1])
  271. def delete_element(a, **remainder):
  272. edge, = yield [("RE", [a])]
  273. if edge[0] is None:
  274. # Not an edge:
  275. yield [("DN", [a])]
  276. result, = yield [("CNV", [False])]
  277. raise PrimitiveFinished(result)
  278. else:
  279. yield [("DE", [a])]
  280. result, = yield [("CNV", [True])]
  281. raise PrimitiveFinished(result)
  282. def read_root(root, **remainder):
  283. raise PrimitiveFinished(root)
  284. def is_edge(a, **remainder):
  285. edge, = yield [("RE", [a])]
  286. result, = yield [("CNV", [edge[0] is not None])]
  287. raise PrimitiveFinished(result)
  288. def log(a, **remainder):
  289. a_value, = yield [("RV", [a])]
  290. print("== LOG == " + str(a_value))
  291. raise PrimitiveFinished(a)
  292. def read_taskroot(task_root, **remainder):
  293. raise PrimitiveFinished(task_root)
  294. def time(**remainder):
  295. a, = yield [("CNV", [python_time.time()])]
  296. raise PrimitiveFinished(a)
  297. def hash(a, **remainder):
  298. a_value, = yield [("RV", [a])]
  299. import hashlib
  300. b_value = hashlib.sha512(a_value).hexdigest()
  301. b, = yield [("CNV", [b_value])]
  302. raise PrimitiveFinished(b)
  303. def __sleep(a, b, **remainder):
  304. timeout, interruptable = yield [("RV", [a]), ("RV", [b])]
  305. yield [("SLEEP", [timeout, interruptable])]
  306. raise PrimitiveFinished(a)
  307. def is_error(a, **remainder):
  308. if a is None:
  309. result, = yield [("CNV", [True])]
  310. else:
  311. result, = yield [("CNV", [False])]
  312. raise PrimitiveFinished(result)