primitives.py 13 KB

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