primitives.py 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505
  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. yield [("RETURN", [{'value': a['value'] - b['value']}])]
  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. #raise PrimitiveFinished({'value': a['value'] + b['value']})
  52. yield [("RETURN", [{'value': a['value'] + b['value']}])]
  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. #raise PrimitiveFinished({'value': a['value'] * b['value']})
  59. yield [("RETURN", [{'value': a['value'] * b['value']}])]
  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. #raise PrimitiveFinished({'value': int(a['value']) // b['value']})
  66. yield [("RETURN", [{'value': int(a['value']) // b['value']}])]
  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. #raise PrimitiveFinished({'value': a['value'] < b['value']})
  73. yield [("RETURN", [{'value': a['value'] < b['value']}])]
  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. #raise PrimitiveFinished({'value': a['value'] and b['value']})
  80. yield [("RETURN", [{'value': a['value'] and b['value']}])]
  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. #raise PrimitiveFinished({'value': a['value'] or b['value']})
  87. yield [("RETURN", [{'value': a['value'] or b['value']}])]
  88. def bool_not(a, **remainder):
  89. if 'value' not in a:
  90. a['value'], = yield [("RV", [a['id']])]
  91. #raise PrimitiveFinished({'value': not a['value']})
  92. yield [("RETURN", [{'value': not a['value']}])]
  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. #raise PrimitiveFinished({'value': a['value'] - b['value']})
  99. yield [("RETURN", [{'value': a['value'] - b['value']}])]
  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. #raise PrimitiveFinished({'value': a['value'] + b['value']})
  106. yield [("RETURN", [{'value': a['value'] + b['value']}])]
  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. #raise PrimitiveFinished({'value': a['value'] * b['value']})
  113. yield [("RETURN", [{'value': a['value'] * b['value']}])]
  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. #raise PrimitiveFinished({'value': float(a['value']) / float(b['value'])})
  120. yield [("RETURN", [{'value': float(a['value']) / b['value']}])]
  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. #raise PrimitiveFinished({'value': a['value'] < b['value']})
  127. yield [("RETURN", [{'value': a['value'] < b['value']}])]
  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. #raise PrimitiveFinished({'value': str(a['value']) + str(b['value'])})
  134. yield [("RETURN", [{'value': str(a['value']) + str(b['value'])}])]
  135. def string_split(a, b, **remainder):
  136. # TODO make non-primitive, though compiled
  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. result = a['value'].split(b['value'])
  142. elems = yield [("CN", [])] + [("CNV", [v]) for v in result]
  143. new_val = elems[0]
  144. yield [("CD", [new_val, i, v]) for i, v in enumerate(elems[1:])]
  145. #raise PrimitiveFinished({'id': new_val})
  146. yield [("RETURN", [{'id': new_val}])]
  147. def string_get(a, b, **remainder):
  148. if 'value' not in a:
  149. a['value'], = yield [("RV", [a['id']])]
  150. if 'value' not in b:
  151. b['value'], = yield [("RV", [b['id']])]
  152. #raise PrimitiveFinished({'value': a['value'][b['value']]})
  153. yield [("RETURN", [{'value': a['value'][b['value']]}])]
  154. def string_len(a, **remainder):
  155. if 'value' not in a:
  156. a['value'], = yield [("RV", [a['id']])]
  157. #raise PrimitiveFinished({'value': len(a['value'])})
  158. yield [("RETURN", [{'value': len(a['value'])}])]
  159. def value_eq(a, b, **remainder):
  160. if 'value' not in a:
  161. a['value'], = yield [("RV", [a['id']])]
  162. if 'value' not in b:
  163. b['value'], = yield [("RV", [b['id']])]
  164. #raise PrimitiveFinished({'value': a['value'] == b['value']})
  165. yield [("RETURN", [{'value': a['value'] == b['value']}])]
  166. def element_eq(a, b, **remainder):
  167. if "id" not in a:
  168. #print("MATERIALIZING A element_eq")
  169. a['id'], = yield [("CNV", [a['value']])]
  170. if "id" not in b:
  171. #print("MATERIALIZING B element_eq")
  172. b['id'], = yield [("CNV", [b['value']])]
  173. #raise PrimitiveFinished({'value': a['id'] == b['id']})
  174. yield [("RETURN", [{'value': a['id'] == b['id']}])]
  175. def cast_string(a, **remainder):
  176. if 'value' not in a:
  177. a['value'], = yield [("RV", [a['id']])]
  178. if isinstance(a['value'], dict):
  179. #raise PrimitiveFinished({'value': str(a['value']['value'])})
  180. yield [("RETURN", [{'value': str(a['value']['value'])}])]
  181. else:
  182. #raise PrimitiveFinished({'value': str(a['value'])})
  183. yield [("RETURN", [{'value': str(a['value'])}])]
  184. def cast_float(a, **remainder):
  185. if 'value' not in a:
  186. a['value'], = yield [("RV", [a['id']])]
  187. #raise PrimitiveFinished({'value': float(a['value'])})
  188. yield [("RETURN", [{'value': float(a['value'])}])]
  189. def cast_boolean(a, **remainder):
  190. if 'value' not in a:
  191. a['value'], = yield [("RV", [a['id']])]
  192. #raise PrimitiveFinished({'value': bool(a['value'])})
  193. yield [("RETURN", [{'value': bool(a['value'])}])]
  194. def cast_integer(a, **remainder):
  195. if 'value' not in a:
  196. a['value'], = yield [("RV", [a['id']])]
  197. #raise PrimitiveFinished({'value': int(a['value'])})
  198. yield [("RETURN", [{'value': int(a['value'])}])]
  199. def cast_value(a, **remainder):
  200. if 'value' not in a:
  201. a['value'], = yield [("RV", [a['id']])]
  202. if isinstance(a['value'], dict):
  203. #raise PrimitiveFinished({'value': str(a['value']['value'])})
  204. yield [("RETURN", [{'value': str(a['value']['value'])}])]
  205. else:
  206. #raise PrimitiveFinished({'value': json.dumps(a['value'])})
  207. yield [("RETURN", [{'value': json.dumps(a['value'])}])]
  208. def cast_id(a, **remainder):
  209. if "id" not in a:
  210. #print("MATERIALIZING A cast_id")
  211. a['id'], = yield [("CNV", [a['value']])]
  212. #raise PrimitiveFinished({'value': str(a['id'])})
  213. yield [("RETURN", [{'value': str(a['id'])}])]
  214. def dict_add_fast(a, b, c, **remainder):
  215. # TODO deprecate, as dict_add is now also efficient
  216. if "value" not in b:
  217. b['value'], = yield [("RV", [b['id']])]
  218. if "id" not in c:
  219. #print("MATERIALIZING C dict_add_fast")
  220. c['id'], = yield [("CNV", [c['value']])]
  221. yield [("CD", [a['id'], b['value'], c['id']])]
  222. #raise PrimitiveFinished(a)
  223. yield [("RETURN", [a])]
  224. def dict_delete(a, b, **remainder):
  225. if "value" not in b:
  226. b['value'], = yield [("RV", [b['id']])]
  227. edge, = yield [("RDE", [a['id'], b['value']])]
  228. if edge is None:
  229. print("Failed dict_delete for value '%s'!" % b['value'])
  230. keys, = yield [("RDK", [a['id']])]
  231. keys = yield [("RV", [i]) for i in keys]
  232. print("Keys: " + str(keys))
  233. raise Exception()
  234. yield [("DE", [edge])]
  235. #raise PrimitiveFinished(a)
  236. yield [("RETURN", [a])]
  237. def dict_delete_node(a, b, **remainder):
  238. edge, = yield [("RDNE", [a['id'], b['id']])]
  239. if edge is None:
  240. print("Failed dict_delete_node!")
  241. yield [("DE", [edge])]
  242. #raise PrimitiveFinished(a)
  243. yield [("RETURN", [a])]
  244. def dict_read(a, b, **remainder):
  245. if "value" not in b:
  246. b['value'], = yield [("RV", [b['id']])]
  247. result, = yield [("RD", [a['id'], b['value']])]
  248. #raise PrimitiveFinished({'id': result})
  249. yield [("RETURN", [{'id': result}])]
  250. def dict_read_edge(a, b, **remainder):
  251. if "value" not in b:
  252. b['value'], = yield [("RV", [b['id']])]
  253. result, = yield [("RDE", [a['id'], b['value']])]
  254. #raise PrimitiveFinished({'id': result})
  255. yield [("RETURN", [{'id': result}])]
  256. def dict_read_node(a, b, **remainder):
  257. result, = yield [("RDN", [a['id'], b['id']])]
  258. #raise PrimitiveFinished({'id': result})
  259. yield [("RETURN", [{'id': result}])]
  260. def dict_in(a, b, **remainder):
  261. if "value" not in b:
  262. b['value'], = yield [("RV", [b['id']])]
  263. value, = yield [("RD", [a['id'], b['value']])]
  264. #raise PrimitiveFinished({'value': value is not None})
  265. yield [("RETURN", [{'value': value is not None}])]
  266. def dict_in_node(a, b, **remainder):
  267. if "id" not in b:
  268. # Not even allocated the node, so it is certain not to be in the dictionary
  269. #raise PrimitiveFinished({'value': False})
  270. yield [("RETURN", [{'value': False}])]
  271. value, = yield [("RDN", [a['id'], b['id']])]
  272. #raise PrimitiveFinished({'value': value is not None})
  273. yield [("RETURN", [{'value': value is not None}])]
  274. def dict_keys(a, **remainder):
  275. keys, result = yield [("RDK", [a['id']]), ("CN", [])]
  276. edges = yield [("CE", [result, result]) for _ in keys]
  277. _ = yield [("CE", [edge, key]) for edge, key in zip(edges, keys)]
  278. #raise PrimitiveFinished({'id': result})
  279. yield [("RETURN", [{'id': result}])]
  280. def is_physical_int(a, **remainder):
  281. if "value" not in a:
  282. a['value'], = yield [("RV", [a['id']])]
  283. try:
  284. #raise PrimitiveFinished({'value': isinstance(a['value'], int) or isinstance(a['value'], long)})
  285. yield [("RETURN", [{'value': isinstance(a['value'], int) or isinstance(a['value'], long)}])]
  286. except NameError:
  287. #raise PrimitiveFinished({'value': isinstance(a['value'], int)})
  288. yield [("RETURN", [{'value': isinstance(a['value'], int)}])]
  289. def is_physical_string(a, **remainder):
  290. if "value" not in a:
  291. a['value'], = yield [("RV", [a['id']])]
  292. try:
  293. #raise PrimitiveFinished({'value': isinstance(a['value'], str) or isinstance(a['value'], unicode)})
  294. yield [("RETURN", [{'value': isinstance(a['value'], str) or isinstance(a['value'], unicode)}])]
  295. except NameError:
  296. #raise PrimitiveFinished({'value': isinstance(a['value'], str)})
  297. yield [("RETURN", [{'value': isinstance(a['value'], str)}])]
  298. def is_physical_float(a, **remainder):
  299. if "value" not in a:
  300. a['value'], = yield [("RV", [a['id']])]
  301. #raise PrimitiveFinished({'value': isinstance(a['value'], float)})
  302. yield [("RETURN", [{'value': isinstance(a['value'], float)}])]
  303. def is_physical_boolean(a, **remainder):
  304. if "value" not in a:
  305. a['value'], = yield [("RV", [a['id']])]
  306. #raise PrimitiveFinished({'value': isinstance(a['value'], bool)})
  307. yield [("RETURN", [{'value': isinstance(a['value'], bool)}])]
  308. def is_physical_action(a, **remainder):
  309. if "value" not in a:
  310. a['value'], = yield [("RV", [a['id']])]
  311. #raise PrimitiveFinished({'value': isinstance(a['value'], dict) and a['value']["value"] in ["if", "while", "assign", "call", "break", "continue", "return", "resolve", "access", "constant", "global", "declare"]})
  312. yield [("RETURN", [{'value': isinstance(a['value'], dict) and a['value']["value"] in ["if", "while", "assign", "call", "break", "continue", "return", "resolve", "access", "constant", "global", "declare"]}])]
  313. def is_physical_none(a, **remainder):
  314. if "value" not in a:
  315. a['value'], = yield [("RV", [a['id']])]
  316. #raise PrimitiveFinished({'value': isinstance(a['value'], dict) and a['value']["value"] == "none"})
  317. yield [("RETURN", [{'value': isinstance(a['value'], dict) and a['value']["value"] == "none"}])]
  318. def create_node(**remainder):
  319. result, = yield [("CN", [])]
  320. #raise PrimitiveFinished({'id': result})
  321. yield [("RETURN", [{'id': result}])]
  322. def create_edge(a, b, **remainder):
  323. if "id" not in a:
  324. #print("MATERIALIZING A create_edge")
  325. a['id'], = yield [("CNV", [a['value']])]
  326. if "id" not in b:
  327. #print("MATERIALIZING B create_edge")
  328. b['id'], = yield [("CNV", [b['value']])]
  329. result, = yield [("CE", [a['id'], b['id']])]
  330. #raise PrimitiveFinished({'id': result})
  331. yield [("RETURN", [{'id': result}])]
  332. def create_value(a, **remainder):
  333. if "value" not in a:
  334. a['value'], = yield [("RV", [a['id']])]
  335. #raise PrimitiveFinished({'value': a['value']})
  336. yield [("RETURN", [{'value': a['value']}])]
  337. def read_nr_out(a, **remainder):
  338. if "id" not in a:
  339. a['id'], = yield [("CNV", [a['value']])]
  340. outgoing, = yield [("RO", [a['id']])]
  341. #raise PrimitiveFinished({'value': len(outgoing)})
  342. yield [("RETURN", [{'value': len(outgoing)}])]
  343. def read_out(a, b, root, **remainder):
  344. if "id" not in a:
  345. a['id'], = yield [("CNV", [a['value']])]
  346. if "value" not in b:
  347. b['value'], = yield [("RV", [b['id']])]
  348. outgoing, = yield [("RO", [a['id']])]
  349. #raise PrimitiveFinished({'id': sorted(outgoing)[b['value']] if len(outgoing) > b['value'] else root})
  350. yield [("RETURN", [{'id': sorted(outgoing)[b['value']] if len(outgoing) > b['value'] else root}])]
  351. def read_nr_in(a, **remainder):
  352. if "id" not in a:
  353. a['id'], = yield [("CNV", [a['value']])]
  354. incoming, = yield [("RI", [a['id']])]
  355. #raise PrimitiveFinished({'value': len(incoming)})
  356. yield [("RETURN", [{'value': len(incoming)}])]
  357. def read_in(a, b, root, **remainder):
  358. if "id" not in a:
  359. a['id'], = yield [("CNV", [a['value']])]
  360. if "value" not in b:
  361. b['value'], = yield [("RV", [b['id']])]
  362. incoming, = yield [("RI", [a['id']])]
  363. #raise PrimitiveFinished({'id': sorted(incoming)[b['value']] if len(incoming) > b['value'] else root})
  364. yield [("RETURN", [{'id': sorted(incoming)[b['value']] if len(incoming) > b['value'] else root}])]
  365. def read_edge_src(a, **remainder):
  366. result, = yield [("RE", [a['id']])]
  367. #raise PrimitiveFinished({'id': result[0]})
  368. yield [("RETURN", [{'id': result[0]}])]
  369. def read_edge_dst(a, **remainder):
  370. result, = yield [("RE", [a['id']])]
  371. #raise PrimitiveFinished({'id': result[1]})
  372. yield [("RETURN", [{'id': result[1]}])]
  373. def delete_element(a, **remainder):
  374. if "id" not in a:
  375. #raise PrimitiveFinished({'value': False})
  376. yield [("RETURN", [{'value': False}])]
  377. edge, = yield [("RE", [a['id']])]
  378. if edge[0] is None:
  379. # Not an edge:
  380. yield [("DN", [a['id']])]
  381. #raise PrimitiveFinished({'value': False})
  382. yield [("RETURN", [{'value': False}])]
  383. else:
  384. yield [("DE", [a['id']])]
  385. #raise PrimitiveFinished({'value': True})
  386. yield [("RETURN", [{'value': True}])]
  387. def read_root(root, **remainder):
  388. #raise PrimitiveFinished({'id': root})
  389. yield [("RETURN", [{'id': root}])]
  390. def is_edge(a, **remainder):
  391. if "id" not in a:
  392. #raise PrimitiveFinished({'value': False})
  393. yield [("RETURN", [{'value': False}])]
  394. edge, = yield [("RE", [a['id']])]
  395. #raise PrimitiveFinished({'value': edge[0] is not None})
  396. yield [("RETURN", [{'value': edge[0] is not None}])]
  397. def log(a, **remainder):
  398. if "value" not in a:
  399. a['value'], = yield [("RV", [a['id']])]
  400. print("== LOG == " + str(a['value']))
  401. #raise PrimitiveFinished(a)
  402. yield [("RETURN", [a])]
  403. def read_taskroot(task_root, **remainder):
  404. #raise PrimitiveFinished({'id': task_root})
  405. yield [("RETURN", [{'id': task_root}])]
  406. def time(**remainder):
  407. #raise PrimitiveFinished({'value': python_time.time()})
  408. yield [("RETURN", [{'value': python_time.time()}])]
  409. def hash(a, **remainder):
  410. if "value" not in a:
  411. a['value'], = yield [("RV", [a['id']])]
  412. import hashlib
  413. try:
  414. value = hashlib.sha512(a['value']).hexdigest()
  415. except TypeError:
  416. value = hashlib.sha512(a['value'].encode()).hexdigest()
  417. #raise PrimitiveFinished({'value': value})
  418. yield [("RETURN", [{'value': value}])]
  419. def __sleep(a, b, **remainder):
  420. if "value" not in a:
  421. a['value'], = yield [("RV", [a['id']])]
  422. if "value" not in b:
  423. b['value'], = yield [("RV", [b['id']])]
  424. timeout = a['value']
  425. interruptable = b['value']
  426. yield [("SLEEP", [timeout, interruptable])]
  427. #raise PrimitiveFinished(a)
  428. yield [("RETURN", [a])]
  429. def is_error(a, **remainder):
  430. if a['id'] is None:
  431. #raise PrimitiveFinished({'value': True})
  432. yield [("RETURN", [{'value': True}])]
  433. else:
  434. #raise PrimitiveFinished({'value': False})
  435. yield [("RETURN", [{'value': False}])]