new_primitives.py 15 KB

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