new_primitives.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414
  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. if 'value' not in a:
  41. a['value'], = yield [("RV", [a['id']])]
  42. if 'value' not in b:
  43. b['value'], = yield [("RV", [b['id']])]
  44. raise PrimitiveFinished({'value': a['value'] - b['value']})
  45. def integer_addition(a, b, **remainder):
  46. if 'value' not in a:
  47. a['value'], = yield [("RV", [a['id']])]
  48. if 'value' not in b:
  49. b['value'], = yield [("RV", [b['id']])]
  50. raise PrimitiveFinished({'value': a['value'] + b['value']})
  51. def integer_multiplication(a, b, **remainder):
  52. if 'value' not in a:
  53. a['value'], = yield [("RV", [a['id']])]
  54. if 'value' not in b:
  55. b['value'], = yield [("RV", [b['id']])]
  56. raise PrimitiveFinished({'value': a['value'] * b['value']})
  57. def integer_division(a, b, **remainder):
  58. if 'value' not in a:
  59. a['value'], = yield [("RV", [a['id']])]
  60. if 'value' not in b:
  61. b['value'], = yield [("RV", [b['id']])]
  62. raise PrimitiveFinished({'value': int(a['value']) // b['value']})
  63. def integer_lt(a, b, **remainder):
  64. if 'value' not in a:
  65. a['value'], = yield [("RV", [a['id']])]
  66. if 'value' not in b:
  67. b['value'], = yield [("RV", [b['id']])]
  68. raise PrimitiveFinished({'value': a['value'] < b['value']})
  69. def bool_and(a, b, **remainder):
  70. if 'value' not in a:
  71. a['value'], = yield [("RV", [a['id']])]
  72. if 'value' not in b:
  73. b['value'], = yield [("RV", [b['id']])]
  74. raise PrimitiveFinished({'value': a['value'] and b['value']})
  75. def bool_or(a, b, **remainder):
  76. if 'value' not in a:
  77. a['value'], = yield [("RV", [a['id']])]
  78. if 'value' not in b:
  79. b['value'], = yield [("RV", [b['id']])]
  80. raise PrimitiveFinished({'value': a['value'] or b['value']})
  81. def bool_not(a, **remainder):
  82. if 'value' not in a:
  83. a['value'], = yield [("RV", [a['id']])]
  84. raise PrimitiveFinished({'value': not a['value']})
  85. def float_subtraction(a, b, **remainder):
  86. if 'value' not in a:
  87. a['value'], = yield [("RV", [a['id']])]
  88. if 'value' not in b:
  89. b['value'], = yield [("RV", [b['id']])]
  90. raise PrimitiveFinished({'value': a['value'] - b['value']})
  91. def float_addition(a, b, **remainder):
  92. if 'value' not in a:
  93. a['value'], = yield [("RV", [a['id']])]
  94. if 'value' not in b:
  95. b['value'], = yield [("RV", [b['id']])]
  96. raise PrimitiveFinished({'value': a['value'] + b['value']})
  97. def float_multiplication(a, b, **remainder):
  98. if 'value' not in a:
  99. a['value'], = yield [("RV", [a['id']])]
  100. if 'value' not in b:
  101. b['value'], = yield [("RV", [b['id']])]
  102. raise PrimitiveFinished({'value': a['value'] * b['value']})
  103. def float_division(a, b, **remainder):
  104. if 'value' not in a:
  105. a['value'], = yield [("RV", [a['id']])]
  106. if 'value' not in b:
  107. b['value'], = yield [("RV", [b['id']])]
  108. raise PrimitiveFinished({'value': float(a['value']) / float(b['value'])})
  109. def float_lt(a, b, **remainder):
  110. if 'value' not in a:
  111. a['value'], = yield [("RV", [a['id']])]
  112. if 'value' not in b:
  113. b['value'], = yield [("RV", [b['id']])]
  114. raise PrimitiveFinished({'value': a['value'] < b['value']})
  115. def string_join(a, b, **remainder):
  116. if 'value' not in a:
  117. a['value'], = yield [("RV", [a['id']])]
  118. if 'value' not in b:
  119. b['value'], = yield [("RV", [b['id']])]
  120. raise PrimitiveFinished({'value': str(a['value']) + str(b['value'])})
  121. def string_split(a, b, **remainder):
  122. # TODO make non-primitive, though compiled
  123. if 'value' not in a:
  124. a['value'], = yield [("RV", [a['id']])]
  125. if 'value' not in b:
  126. b['value'], = yield [("RV", [b['id']])]
  127. result = a['value'].split(b['value'])
  128. elems = yield [("CN", [])] + [("CNV", [v]) for v in result]
  129. new_val = elems[0]
  130. yield [("CD", [new_val, i, v]) for i, v in enumerate(elems[1:])]
  131. raise PrimitiveFinished({'id': new_val})
  132. def string_get(a, b, **remainder):
  133. if 'value' not in a:
  134. a['value'], = yield [("RV", [a['id']])]
  135. if 'value' not in b:
  136. b['value'], = yield [("RV", [b['id']])]
  137. raise PrimitiveFinished({'value': a['value'][b['value']]})
  138. def string_len(a, **remainder):
  139. if 'value' not in a:
  140. a['value'], = yield [("RV", [a['id']])]
  141. raise PrimitiveFinished({'value': len(a['value'])})
  142. def value_eq(a, b, **remainder):
  143. if 'value' not in a:
  144. a['value'], = yield [("RV", [a['id']])]
  145. if 'value' not in b:
  146. b['value'], = yield [("RV", [b['id']])]
  147. raise PrimitiveFinished({'value': a['value'] == b['value']})
  148. def element_eq(a, b, **remainder):
  149. if "id" not in a:
  150. print("MATERIALIZING A element_eq")
  151. if "value" in a:
  152. a['id'], = yield [("CNV", [a['value']])]
  153. if "id" not in b:
  154. print("MATERIALIZING B element_eq")
  155. if "value" in b:
  156. b['id'], = yield [("CNV", [b['value']])]
  157. raise PrimitiveFinished({'value': a['id'] == b['id']})
  158. def cast_string(a, **remainder):
  159. if 'value' not in a:
  160. a['value'], = yield [("RV", [a['id']])]
  161. if isinstance(a_value, dict):
  162. raise PrimitiveFinished({'value': str(a['value']['value'])})
  163. else:
  164. raise PrimitiveFinished({'value': str(a['value'])})
  165. def cast_float(a, **remainder):
  166. if 'value' not in a:
  167. a['value'], = yield [("RV", [a['id']])]
  168. raise PrimitiveFinished({'value': float(a['value'])})
  169. def cast_boolean(a, **remainder):
  170. if 'value' not in a:
  171. a['value'], = yield [("RV", [a['id']])]
  172. raise PrimitiveFinished({'value': bool(a['value'])})
  173. def cast_integer(a, **remainder):
  174. if 'value' not in a:
  175. a['value'], = yield [("RV", [a['id']])]
  176. raise PrimitiveFinished({'value': int(a['value'])})
  177. def cast_value(a, **remainder):
  178. if 'value' not in a:
  179. a['value'], = yield [("RV", [a['id']])]
  180. if isinstance(a_value, dict):
  181. raise PrimitiveFinished({'value': str(a['value']['value'])})
  182. else:
  183. raise PrimitiveFinished({'value': json.dumps(a['value'])})
  184. def cast_id(a, **remainder):
  185. if "id" not in a:
  186. print("MATERIALIZING A cast_id")
  187. if "value" in a:
  188. a['id'], = yield [("CNV", [a['value']])]
  189. else:
  190. a['id'], = yield [("CN", [])]
  191. raise PrimitiveFinished({'value': str(a['id'])})
  192. def dict_add_fast(a, b, c, **remainder):
  193. # TODO deprecate, as dict_add is now also efficient
  194. if "value" not in b:
  195. b['value'], = yield [("RV", [b['id']])]
  196. if "id" not in c:
  197. print("MATERIALIZING C dict_add_fast")
  198. c['id'], = yield [("CNV", [c['value']])]
  199. yield [("CD", [a['id'], b['value'], c['id']])]
  200. raise PrimitiveFinished(a)
  201. def dict_delete(a, b, **remainder):
  202. b_value, = yield [("RV", [b])]
  203. edge, = yield [("RDE", [a, b_value])]
  204. if edge is None:
  205. print("Failed dict_delete for value '%s'!" % b_value)
  206. keys, = yield [("RDK", [a])]
  207. keys = yield [("RV", [i]) for i in keys]
  208. print("Keys: " + str(keys))
  209. raise Exception()
  210. yield [("DE", [edge])]
  211. raise PrimitiveFinished(a)
  212. def dict_delete_node(a, b, **remainder):
  213. edge, = yield [("RDNE", [a, b])]
  214. if edge is None:
  215. print("Failed dict_delete_node!")
  216. yield [("DE", [edge])]
  217. raise PrimitiveFinished(a)
  218. def dict_read(a, b, **remainder):
  219. b_value, = yield [("RV", [b])]
  220. result, = yield [("RD", [a, b_value])]
  221. raise PrimitiveFinished(result)
  222. def dict_read_edge(a, b, **remainder):
  223. b_value, = yield [("RV", [b])]
  224. result, = yield [("RDE", [a, b_value])]
  225. raise PrimitiveFinished(result)
  226. def dict_read_node(a, b, **remainder):
  227. result, = yield [("RDN", [a, b])]
  228. raise PrimitiveFinished(result)
  229. def dict_in(a, b, **remainder):
  230. b_value, = yield [("RV", [b])]
  231. value, = yield [("RD", [a, b_value])]
  232. is_in = value is not None
  233. result, = yield [("CNV", [is_in])]
  234. raise PrimitiveFinished(result)
  235. def dict_in_node(a, b, **remainder):
  236. value, = yield [("RDN", [a, b])]
  237. result, = yield [("CNV", [value is not None])]
  238. raise PrimitiveFinished(result)
  239. def dict_keys(a, **remainder):
  240. keys, result = yield [("RDK", [a]), ("CN", [])]
  241. edges = yield [("CE", [result, result]) for _ in keys]
  242. _ = yield [("CE", [edge, key]) for edge, key in zip(edges, keys)]
  243. raise PrimitiveFinished(result)
  244. def is_physical_int(a, **remainder):
  245. t, = yield [("RV", [a])]
  246. try:
  247. result, = yield [("CNV", [isinstance(t, int) or isinstance(t, long)])]
  248. except NameError:
  249. result, = yield [("CNV", [isinstance(t, int)])]
  250. raise PrimitiveFinished(result)
  251. def is_physical_string(a, **remainder):
  252. t, = yield [("RV", [a])]
  253. try:
  254. result, = yield [("CNV", [isinstance(t, str) or isinstance(t, unicode)])]
  255. except NameError:
  256. result, = yield [("CNV", [isinstance(t, str)])]
  257. raise PrimitiveFinished(result)
  258. def is_physical_float(a, **remainder):
  259. t, = yield [("RV", [a])]
  260. result, = yield [("CNV", [isinstance(t, float)])]
  261. raise PrimitiveFinished(result)
  262. def is_physical_boolean(a, **remainder):
  263. t, = yield [("RV", [a])]
  264. result, = yield [("CNV", [isinstance(t, bool)])]
  265. raise PrimitiveFinished(result)
  266. def is_physical_action(a, **remainder):
  267. t, = yield [("RV", [a])]
  268. result, = yield [("CNV", [isinstance(t, dict) and t["value"] in ["if", "while", "assign", "call", "break", "continue", "return", "resolve", "access", "constant", "global", "declare"]])]
  269. raise PrimitiveFinished(result)
  270. def is_physical_none(a, **remainder):
  271. t, = yield [("RV", [a])]
  272. result, = yield [("CNV", [isinstance(t, dict) and t["value"] == "none"])]
  273. raise PrimitiveFinished(result)
  274. def create_node(**remainder):
  275. result, = yield [("CN", [])]
  276. raise PrimitiveFinished(result)
  277. def create_edge(a, b, **remainder):
  278. result, = yield [("CE", [a, b])]
  279. raise PrimitiveFinished(result)
  280. def create_value(a, **remainder):
  281. a_value, = yield [("RV", [a])]
  282. result, = yield [("CNV", [a_value])]
  283. raise PrimitiveFinished(result)
  284. def read_nr_out(a, **remainder):
  285. outgoing, = yield [("RO", [a])]
  286. result, = yield [("CNV", [len(outgoing)])]
  287. raise PrimitiveFinished(result)
  288. def read_out(a, b, root, **remainder):
  289. outgoing, b_value = yield [("RO", [a]), ("RV", [b])]
  290. raise PrimitiveFinished(sorted(outgoing)[b_value] if len(outgoing) > b_value else root)
  291. def read_nr_in(a, **remainder):
  292. incoming, = yield [("RI", [a])]
  293. result, = yield [("CNV", [len(incoming)])]
  294. raise PrimitiveFinished(result)
  295. def read_in(a, b, root, **remainder):
  296. incoming, b_value = yield [("RI", [a]), ("RV", [b])]
  297. raise PrimitiveFinished(sorted(incoming)[b_value] if len(incoming) > b_value else root)
  298. def read_edge_src(a, **remainder):
  299. result, = yield [("RE", [a])]
  300. raise PrimitiveFinished(result[0])
  301. def read_edge_dst(a, **remainder):
  302. result, = yield [("RE", [a])]
  303. raise PrimitiveFinished(result[1])
  304. def delete_element(a, **remainder):
  305. edge, = yield [("RE", [a])]
  306. if edge[0] is None:
  307. # Not an edge:
  308. yield [("DN", [a])]
  309. result, = yield [("CNV", [False])]
  310. raise PrimitiveFinished(result)
  311. else:
  312. yield [("DE", [a])]
  313. result, = yield [("CNV", [True])]
  314. raise PrimitiveFinished(result)
  315. def read_root(root, **remainder):
  316. raise PrimitiveFinished(root)
  317. def is_edge(a, **remainder):
  318. edge, = yield [("RE", [a])]
  319. result, = yield [("CNV", [edge[0] is not None])]
  320. raise PrimitiveFinished(result)
  321. def log(a, **remainder):
  322. a_value, = yield [("RV", [a])]
  323. print("== LOG == " + str(a_value))
  324. raise PrimitiveFinished(a)
  325. def read_taskroot(task_root, **remainder):
  326. raise PrimitiveFinished(task_root)
  327. def time(**remainder):
  328. a, = yield [("CNV", [python_time.time()])]
  329. raise PrimitiveFinished(a)
  330. def hash(a, **remainder):
  331. a_value, = yield [("RV", [a])]
  332. import hashlib
  333. try:
  334. b_value = hashlib.sha512(a_value).hexdigest()
  335. except TypeError:
  336. b_value = hashlib.sha512(a_value.encode()).hexdigest()
  337. b, = yield [("CNV", [b_value])]
  338. raise PrimitiveFinished(b)
  339. def __sleep(a, b, **remainder):
  340. timeout, interruptable = yield [("RV", [a]), ("RV", [b])]
  341. yield [("SLEEP", [timeout, interruptable])]
  342. raise PrimitiveFinished(a)
  343. def is_error(a, **remainder):
  344. if a is None:
  345. result, = yield [("CNV", [True])]
  346. else:
  347. result, = yield [("CNV", [False])]
  348. raise PrimitiveFinished(result)