primitives.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456
  1. import time as python_time
  2. import json
  3. import sys
  4. class InterpretedFunctionFinished(Exception):
  5. """Exception to indicate the result value of an interpreted function, as a return
  6. cannot be used."""
  7. def __init__(self, value):
  8. Exception.__init__(self)
  9. self.result = value
  10. class SleepKernel(Exception):
  11. """Exception to indicate the kernel to sleep for some time."""
  12. def __init__(self, timeout, interruptable):
  13. Exception.__init__(self)
  14. self.timeout = timeout
  15. self.interruptable = interruptable
  16. # Functions annotated with __exception_return use the JIT's calling convention instead of
  17. # returnvalue is not modified.
  18. #
  19. # ### Rationale for __exception_return
  20. #
  21. # __exception_return is a useful mechanism because it allows us to have a __call_function
  22. # implementation that has O(1) state read overhead. A previous implementation of
  23. # __call_function checked if the caller's frame had been popped whenever
  24. # ModelverseKernel.execute_yield threw a StopIteration exception. However, that incurs O(n) overhead
  25. # _per call,_ where n is the number of StopIteration exceptions that are thrown during the call.
  26. # O(n) is pretty bad, but this actually becomes O(n * m) when m calls to __call_function are
  27. # nested. And that's just not acceptable.
  28. # __exception_return requires kernel support, but I think the complexity gains are well worth it;
  29. # I reckon JIT-to-interpreter switches aren't going to get a whole lot cheaper than this.
  30. EXCEPTION_RETURN_KEY = "__exception_return"
  31. """A dictionary key for functions which request that the kernel throw a InterpretedFunctionFinished
  32. exception with the return value instead of injecting the return value in the caller's frame."""
  33. def integer_subtraction(a, b, **remainder):
  34. if 'value' not in a:
  35. a['value'], = yield [("RV", [a['id']])]
  36. if 'value' not in b:
  37. b['value'], = yield [("RV", [b['id']])]
  38. yield [("RETURN", [{'value': a['value'] - b['value']}])]
  39. def integer_addition(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. yield [("RETURN", [{'value': a['value'] + b['value']}])]
  45. def integer_multiplication(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. yield [("RETURN", [{'value': a['value'] * b['value']}])]
  51. def integer_division(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. yield [("RETURN", [{'value': int(a['value']) // b['value']}])]
  57. def integer_lt(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. yield [("RETURN", [{'value': a['value'] < b['value']}])]
  63. def bool_and(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. yield [("RETURN", [{'value': a['value'] and b['value']}])]
  69. def bool_or(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. yield [("RETURN", [{'value': a['value'] or b['value']}])]
  75. def bool_not(a, **remainder):
  76. if 'value' not in a:
  77. a['value'], = yield [("RV", [a['id']])]
  78. yield [("RETURN", [{'value': not a['value']}])]
  79. def float_subtraction(a, b, **remainder):
  80. if 'value' not in a:
  81. a['value'], = yield [("RV", [a['id']])]
  82. if 'value' not in b:
  83. b['value'], = yield [("RV", [b['id']])]
  84. yield [("RETURN", [{'value': a['value'] - b['value']}])]
  85. def float_addition(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. yield [("RETURN", [{'value': a['value'] + b['value']}])]
  91. def float_multiplication(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. yield [("RETURN", [{'value': a['value'] * b['value']}])]
  97. def float_division(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. yield [("RETURN", [{'value': float(a['value']) / b['value']}])]
  103. def float_lt(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. yield [("RETURN", [{'value': a['value'] < b['value']}])]
  109. def string_join(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. yield [("RETURN", [{'value': str(a['value']) + str(b['value'])}])]
  115. def string_split(a, b, **remainder):
  116. # TODO make non-primitive, though compiled
  117. if 'value' not in a:
  118. a['value'], = yield [("RV", [a['id']])]
  119. if 'value' not in b:
  120. b['value'], = yield [("RV", [b['id']])]
  121. result = a['value'].split(b['value'])
  122. elems = yield [("CN", [])] + [("CNV", [v]) for v in result]
  123. new_val = elems[0]
  124. yield [("CD", [new_val, i, v]) for i, v in enumerate(elems[1:])]
  125. yield [("RETURN", [{'id': new_val}])]
  126. def string_get(a, b, **remainder):
  127. if 'value' not in a:
  128. a['value'], = yield [("RV", [a['id']])]
  129. if 'value' not in b:
  130. b['value'], = yield [("RV", [b['id']])]
  131. yield [("RETURN", [{'value': a['value'][b['value']]}])]
  132. def string_len(a, **remainder):
  133. if 'value' not in a:
  134. a['value'], = yield [("RV", [a['id']])]
  135. yield [("RETURN", [{'value': len(a['value'])}])]
  136. def value_eq(a, b, **remainder):
  137. if 'value' not in a:
  138. a['value'], = yield [("RV", [a['id']])]
  139. if 'value' not in b:
  140. b['value'], = yield [("RV", [b['id']])]
  141. yield [("RETURN", [{'value': a['value'] == b['value']}])]
  142. def element_eq(a, b, **remainder):
  143. if "id" not in a:
  144. #print("MATERIALIZING A element_eq")
  145. a['id'], = yield [("CNV", [a['value']])]
  146. if "id" not in b:
  147. #print("MATERIALIZING B element_eq")
  148. b['id'], = yield [("CNV", [b['value']])]
  149. yield [("RETURN", [{'value': a['id'] == b['id']}])]
  150. def cast_string(a, **remainder):
  151. if 'value' not in a:
  152. a['value'], = yield [("RV", [a['id']])]
  153. if isinstance(a['value'], dict):
  154. yield [("RETURN", [{'value': str(a['value']['value'])}])]
  155. else:
  156. yield [("RETURN", [{'value': str(a['value'])}])]
  157. def cast_float(a, **remainder):
  158. if 'value' not in a:
  159. a['value'], = yield [("RV", [a['id']])]
  160. try:
  161. yield [("RETURN", [{'value': float(a['value'])}])]
  162. except ValueError:
  163. yield [("RETURN", [{'value': -1.0}])]
  164. def cast_boolean(a, **remainder):
  165. if 'value' not in a:
  166. a['value'], = yield [("RV", [a['id']])]
  167. try:
  168. yield [("RETURN", [{'value': bool(a['value'])}])]
  169. except ValueError:
  170. yield [("RETURN", [{'value': False}])]
  171. def cast_integer(a, **remainder):
  172. if 'value' not in a:
  173. a['value'], = yield [("RV", [a['id']])]
  174. try:
  175. yield [("RETURN", [{'value': int(a['value'])}])]
  176. except ValueError:
  177. yield [("RETURN", [{'value': -1}])]
  178. def cast_value(a, **remainder):
  179. if 'value' not in a:
  180. a['value'], = yield [("RV", [a['id']])]
  181. if isinstance(a['value'], dict):
  182. yield [("RETURN", [{'value': str(a['value']['value'])}])]
  183. else:
  184. yield [("RETURN", [{'value': json.dumps(a['value'])}])]
  185. def cast_id(a, **remainder):
  186. if "id" not in a:
  187. #print("MATERIALIZING A cast_id")
  188. a['id'], = yield [("CNV", [a['value']])]
  189. yield [("RETURN", [{'value': str(a['id'])}])]
  190. def dict_add_fast(a, b, c, **remainder):
  191. # TODO deprecate, as dict_add is now also efficient
  192. if "value" not in b:
  193. b['value'], = yield [("RV", [b['id']])]
  194. if "id" not in c:
  195. #print("MATERIALIZING C dict_add_fast")
  196. c['id'], = yield [("CNV", [c['value']])]
  197. yield [("CD", [a['id'], b['value'], c['id']])]
  198. yield [("RETURN", [a])]
  199. def dict_delete(a, b, **remainder):
  200. if "value" not in b:
  201. b['value'], = yield [("RV", [b['id']])]
  202. edge, = yield [("RDE", [a['id'], b['value']])]
  203. if edge is None:
  204. print("Failed dict_delete for value '%s'!" % b['value'])
  205. keys, = yield [("RDK", [a['id']])]
  206. keys = yield [("RV", [i]) for i in keys]
  207. print("Keys: " + str(keys))
  208. raise Exception()
  209. yield [("DE", [edge])]
  210. yield [("RETURN", [a])]
  211. def dict_delete_node(a, b, **remainder):
  212. edge, = yield [("RDNE", [a['id'], b['id']])]
  213. if edge is None:
  214. print("Failed dict_delete_node!")
  215. yield [("DE", [edge])]
  216. yield [("RETURN", [a])]
  217. def dict_read(a, b, **remainder):
  218. if "value" not in b:
  219. b['value'], = yield [("RV", [b['id']])]
  220. result, = yield [("RD", [a['id'], b['value']])]
  221. yield [("RETURN", [{'id': result}])]
  222. def dict_read_edge(a, b, **remainder):
  223. if "value" not in b:
  224. b['value'], = yield [("RV", [b['id']])]
  225. result, = yield [("RDE", [a['id'], b['value']])]
  226. yield [("RETURN", [{'id': result}])]
  227. def dict_read_node(a, b, **remainder):
  228. result, = yield [("RDN", [a['id'], b['id']])]
  229. yield [("RETURN", [{'id': result}])]
  230. def dict_in(a, b, **remainder):
  231. if "value" not in b:
  232. b['value'], = yield [("RV", [b['id']])]
  233. value, = yield [("RD", [a['id'], b['value']])]
  234. yield [("RETURN", [{'value': value is not None}])]
  235. def dict_in_node(a, b, **remainder):
  236. if "id" not in b:
  237. # Not even allocated the node, so it is certain not to be in the dictionary
  238. yield [("RETURN", [{'value': False}])]
  239. value, = yield [("RDN", [a['id'], b['id']])]
  240. yield [("RETURN", [{'value': value is not None}])]
  241. def dict_keys(a, **remainder):
  242. keys, result = yield [("RDK", [a['id']]), ("CN", [])]
  243. edges = yield [("CE", [result, result]) for _ in keys]
  244. _ = yield [("CE", [edge, key]) for edge, key in zip(edges, keys)]
  245. yield [("RETURN", [{'id': result}])]
  246. def is_physical_int(a, **remainder):
  247. if "value" not in a:
  248. a['value'], = yield [("RV", [a['id']])]
  249. try:
  250. yield [("RETURN", [{'value': isinstance(a['value'], int) or isinstance(a['value'], long)}])]
  251. except NameError:
  252. yield [("RETURN", [{'value': isinstance(a['value'], int)}])]
  253. def is_physical_string(a, **remainder):
  254. if "value" not in a:
  255. a['value'], = yield [("RV", [a['id']])]
  256. try:
  257. yield [("RETURN", [{'value': isinstance(a['value'], str) or isinstance(a['value'], unicode)}])]
  258. except NameError:
  259. yield [("RETURN", [{'value': isinstance(a['value'], str)}])]
  260. def is_physical_float(a, **remainder):
  261. if "value" not in a:
  262. a['value'], = yield [("RV", [a['id']])]
  263. yield [("RETURN", [{'value': isinstance(a['value'], float)}])]
  264. def is_physical_boolean(a, **remainder):
  265. if "value" not in a:
  266. a['value'], = yield [("RV", [a['id']])]
  267. yield [("RETURN", [{'value': isinstance(a['value'], bool)}])]
  268. def is_physical_action(a, **remainder):
  269. if "value" not in a:
  270. a['value'], = yield [("RV", [a['id']])]
  271. yield [("RETURN", [{'value': isinstance(a['value'], dict) and a['value']["value"] in ["if", "while", "assign", "call", "break", "continue", "return", "resolve", "access", "constant", "global", "declare"]}])]
  272. def is_physical_none(a, **remainder):
  273. if a['id'] is None:
  274. yield [("RETURN", [{"value": True}])]
  275. if "value" not in a:
  276. a['value'], = yield [("RV", [a['id']])]
  277. elif a['value'] is None:
  278. yield [("RETURN", [{"value": True}])]
  279. yield [("RETURN", [{'value': isinstance(a['value'], dict) and a['value']["value"] == "none"}])]
  280. def create_node(**remainder):
  281. result, = yield [("CN", [])]
  282. yield [("RETURN", [{'id': result}])]
  283. def create_edge(a, b, **remainder):
  284. if "id" not in a:
  285. #print("MATERIALIZING A create_edge")
  286. a['id'], = yield [("CNV", [a['value']])]
  287. if "id" not in b:
  288. #print("MATERIALIZING B create_edge")
  289. b['id'], = yield [("CNV", [b['value']])]
  290. result, = yield [("CE", [a['id'], b['id']])]
  291. yield [("RETURN", [{'id': result}])]
  292. def create_value(a, **remainder):
  293. if "value" not in a:
  294. a['value'], = yield [("RV", [a['id']])]
  295. yield [("RETURN", [{'value': a['value']}])]
  296. def read_nr_out(a, **remainder):
  297. if "id" not in a:
  298. yield [("RETURN", [{'value': 0}])]
  299. else:
  300. outgoing, = yield [("RO", [a['id']])]
  301. yield [("RETURN", [{'value': len(outgoing)}])]
  302. def read_out(a, b, root, **remainder):
  303. if "id" not in a:
  304. a['id'], = yield [("CNV", [a['value']])]
  305. if "value" not in b:
  306. b['value'], = yield [("RV", [b['id']])]
  307. outgoing, = yield [("RO", [a['id']])]
  308. yield [("RETURN", [{'id': sorted(outgoing)[b['value']] if len(outgoing) > b['value'] else root}])]
  309. def read_nr_in(a, **remainder):
  310. if "id" not in a:
  311. yield [("RETURN", [{'value': 0}])]
  312. else:
  313. incoming, = yield [("RI", [a['id']])]
  314. yield [("RETURN", [{'value': len(incoming)}])]
  315. def read_in(a, b, root, **remainder):
  316. if "id" not in a:
  317. a['id'], = yield [("CNV", [a['value']])]
  318. if "value" not in b:
  319. b['value'], = yield [("RV", [b['id']])]
  320. incoming, = yield [("RI", [a['id']])]
  321. yield [("RETURN", [{'id': sorted(incoming)[b['value']] if len(incoming) > b['value'] else root}])]
  322. def read_edge_src(a, **remainder):
  323. result, = yield [("RE", [a['id']])]
  324. yield [("RETURN", [{'id': result[0]}])]
  325. def read_edge_dst(a, **remainder):
  326. result, = yield [("RE", [a['id']])]
  327. yield [("RETURN", [{'id': result[1]}])]
  328. def delete_element(a, **remainder):
  329. if "id" not in a:
  330. yield [("RETURN", [{'value': False}])]
  331. edge, = yield [("RE", [a['id']])]
  332. if edge[0] is None:
  333. # Not an edge:
  334. yield [("DN", [a['id']])]
  335. yield [("RETURN", [{'value': False}])]
  336. else:
  337. yield [("DE", [a['id']])]
  338. yield [("RETURN", [{'value': True}])]
  339. def read_root(root, **remainder):
  340. yield [("RETURN", [{'id': root}])]
  341. def is_edge(a, **remainder):
  342. if "id" not in a:
  343. yield [("RETURN", [{'value': False}])]
  344. edge, = yield [("RE", [a['id']])]
  345. yield [("RETURN", [{'value': edge[0] is not None}])]
  346. def log(a, **remainder):
  347. if "value" not in a:
  348. a['value'], = yield [("RV", [a['id']])]
  349. print("== LOG == " + str(a['value']))
  350. yield [("RETURN", [a])]
  351. def read_taskroot(task_root, **remainder):
  352. yield [("RETURN", [{'id': task_root}])]
  353. def time(**remainder):
  354. yield [("RETURN", [{'value': python_time.time()}])]
  355. def hash(a, **remainder):
  356. if "value" not in a:
  357. a['value'], = yield [("RV", [a['id']])]
  358. import hashlib
  359. try:
  360. value = hashlib.sha512(a['value']).hexdigest()
  361. except TypeError:
  362. value = hashlib.sha512(a['value'].encode()).hexdigest()
  363. yield [("RETURN", [{'value': value}])]
  364. def __sleep(a, b, **remainder):
  365. if "value" not in a:
  366. a['value'], = yield [("RV", [a['id']])]
  367. if "value" not in b:
  368. b['value'], = yield [("RV", [b['id']])]
  369. timeout = a['value']
  370. interruptable = b['value']
  371. yield [("SLEEP", [timeout, interruptable])]
  372. yield [("RETURN", [a])]
  373. def is_error(a, **remainder):
  374. if a['id'] is None:
  375. yield [("RETURN", [{'value': True}])]
  376. else:
  377. yield [("RETURN", [{'value': False}])]
  378. def list_sort(a, **remainder):
  379. elements, = yield [("RO", [a['id']])]
  380. values = yield [("RD", [a['id'], i]) for i in range(len(elements))]
  381. values = yield [("RV", [i]) for i in values]
  382. new_list, = yield [("CN", [])]
  383. values = yield [("CNV", [v]) for v in sorted(values)]
  384. yield [("CD", [new_list, i, v]) for i, v in enumerate(values)]
  385. yield [("RETURN", [{'id': new_list}])]