to_python.alc 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423
  1. include "primitives.alh"
  2. include "modelling.alh"
  3. include "object_operations.alh"
  4. include "random.alh"
  5. include "utils.alh"
  6. Boolean function main(model : Element):
  7. String result
  8. Element nodes
  9. String node
  10. Element edges
  11. String edge
  12. String new_node
  13. String source
  14. String destination
  15. String name
  16. Element to_explore
  17. Element rules
  18. String rule
  19. String value
  20. Element explored
  21. Element remainder_to_explore
  22. String attr
  23. explored = set_create()
  24. result = ""
  25. // Add the preamble...
  26. result = result + "import modelverse_kernel.primitives as primitive_functions\n"
  27. result = result + "import modelverse_kernel.compiled as compiled_functions\n"
  28. result = result + "from collections import defaultdict\n"
  29. result = result + "import sys\n"
  30. result = result + "import time\n"
  31. result = result + "\n"
  32. result = result + "class ModelverseKernel(object):\n"
  33. result = result + " def __init__(self, root):\n"
  34. result = result + " self.root = root\n"
  35. result = result + " self.primitives = {}\n"
  36. result = result + " self.returnvalue = None\n"
  37. result = result + " self.success = True\n"
  38. result = result + " self.generators = {}\n"
  39. result = result + "\n"
  40. result = result + " def execute_yields(self, taskname, operation, params, reply):\n"
  41. result = result + " try:\n"
  42. result = result + " self.success = True\n"
  43. result = result + " self.taskname = taskname\n"
  44. result = result + " if taskname not in self.generators:\n"
  45. result = result + " self.generators[taskname] = {}\n"
  46. result = result + " if operation not in self.generators[taskname]:\n"
  47. result = result + " # Create the generator for the function to execute\n"
  48. result = result + " self.generators[taskname][operation] = getattr(self, operation)(taskname, *params)\n"
  49. result = result + "\n"
  50. result = result + " if reply is not None:\n"
  51. result = result + " return self.generators[taskname][operation].send(reply)\n"
  52. result = result + " else:\n"
  53. result = result + " return self.generators[taskname][operation].next()\n"
  54. result = result + " except StopIteration:\n"
  55. result = result + " # Done, so remove the generator\n"
  56. result = result + " del self.generators[taskname][operation]\n"
  57. result = result + " return None\n"
  58. result = result + " except:\n"
  59. result = result + " raise\n"
  60. result = result + "\n"
  61. result = result + " ##########################\n"
  62. result = result + " ### Process primitives ###\n"
  63. result = result + " ##########################\n"
  64. result = result + " def load_primitives(self, taskname):\n"
  65. result = result + " hierarchy, = yield [('RD', [self.root, '__hierarchy'])]\n"
  66. result = result + " primitives, = yield [('RD', [hierarchy, 'primitives'])]\n"
  67. result = result + " keys, = yield [('RDK', [primitives])]\n"
  68. result = result + " function_names = yield [('RV', [f]) for f in keys]\n"
  69. result = result + " signatures = yield [('RDN', [primitives, f]) for f in keys]\n"
  70. result = result + " bodies = yield [('RD', [f, 'body']) for f in signatures]\n"
  71. result = result + " for i in range(len(keys)):\n"
  72. result = result + " self.primitives[bodies[i]] = getattr(primitive_functions, function_names[i])\n"
  73. result = result + "\n"
  74. result = result + " def execute_primitive(self, task_root, inst, taskname):\n"
  75. result = result + " # execute_primitive\n"
  76. result = result + " task_frame, = yield [('RD', [task_root, 'frame'])]\n"
  77. result = result + " symbols, = yield [('RD', [task_frame, 'symbols'])]\n"
  78. result = result + " all_links, = yield [('RO', [symbols])]\n"
  79. result = result + " containers = yield [('RE', [v]) for v in all_links]\n"
  80. result = result + " outgoings = yield [('RO', [v]) for v in all_links]\n"
  81. result = result + " dict_values = yield [('RD', [v[1], 'value']) for v in containers]\n"
  82. result = result + " formals_1 = yield [('RE', [v[0]]) for v in outgoings]\n"
  83. result = result + " dict_keys_ref = yield [('RD', [v[1], 'name']) for v in formals_1]\n"
  84. result = result + " dict_keys = yield [('RV', [v]) for v in dict_keys_ref]\n"
  85. result = result + " parameters = dict(zip(dict_keys, dict_values))\n"
  86. result = result + "\n"
  87. result = result + " parameters['root'] = self.root\n"
  88. result = result + " parameters['task_root'] = task_root\n"
  89. result = result + " parameters['taskname'] = taskname\n"
  90. result = result + " parameters['mvk'] = self\n"
  91. result = result + "\n"
  92. result = result + " # prim is a generator itself!\n"
  93. result = result + " try:\n"
  94. result = result + " # Forward the message we get to this generator\n"
  95. result = result + " # Sometimes it might not even be a generator, in which case this should already be in the except block (i.e., for the Read Root operation)\n"
  96. result = result + " prim = self.primitives[inst](**parameters)\n"
  97. result = result + " inp = None\n"
  98. result = result + " while 1:\n"
  99. result = result + " inp = yield prim.send(inp)\n"
  100. result = result + " except StopIteration:\n"
  101. result = result + " # Execution has ended without return value, so we have no idea what to do\n"
  102. result = result + " raise Exception('Primitive finished without returning a value!')\n"
  103. result = result + " except primitive_functions.PrimitiveFinished as e:\n"
  104. result = result + " # Execution has ended with a returnvalue, so read it out from the exception being thrown\n"
  105. result = result + " result = e.result\n"
  106. result = result + "\n"
  107. result = result + " #if result is None:\n"
  108. result = result + " # raise Exception('Primitive raised exception: value of None for operation %s with parameters %s' % (self.compiled[inst], str(parameters)))\n"
  109. result = result + "\n"
  110. result = result + " # Clean up the current stack, as if a return happened\n"
  111. result = result + " old_frame, = yield [('RD', [task_frame, 'prev'])]\n"
  112. result = result + " lnk, = yield [('RDE', [old_frame, 'returnvalue'])]\n"
  113. result = result + " _, _, _, _ = yield [('CD', [old_frame, 'returnvalue', result]),\n"
  114. result = result + " ('CD', [task_root, 'frame', old_frame]),\n"
  115. result = result + " ('DE', [lnk]),\n"
  116. result = result + " ('DN', [task_frame]),\n"
  117. result = result + " ]\n"
  118. result = result + "\n"
  119. result = result + " ########################################\n"
  120. result = result + " ### Execute input and output methods ###\n"
  121. result = result + " ########################################\n"
  122. result = result + " def get_output(self, taskname):\n"
  123. result = result + " task_root, = yield [('RD', [self.root, taskname])]\n"
  124. result = result + " first_output, = yield [('RD', [task_root, 'output'])]\n"
  125. result = result + " next_output, rv = yield [('RD', [first_output, 'next']),\n"
  126. result = result + " ('RD', [first_output, 'value']),\n"
  127. result = result + " ]\n"
  128. result = result + " if next_output is None:\n"
  129. result = result + " self.success = False\n"
  130. result = result + " self.returnvalue = None\n"
  131. result = result + " else:\n"
  132. result = result + " rv_value, = yield [('RV', [rv])]\n"
  133. result = result + " _, _ = yield [('CD', [task_root, 'output', next_output]),\n"
  134. result = result + " ('DN', [first_output]),\n"
  135. result = result + " ]\n"
  136. result = result + " self.returnvalue = rv_value\n"
  137. result = result + "\n"
  138. result = result + " def set_input(self, taskname, value):\n"
  139. result = result + " task_root, = yield [('RD', [self.root, taskname])]\n"
  140. result = result + " old_input, link = yield [('RD', [task_root, 'last_input']),\n"
  141. result = result + " ('RDE', [task_root, 'last_input']),\n"
  142. result = result + " ]\n"
  143. result = result + " new_input, = yield [('CN', [])]\n"
  144. result = result + " _, _ = yield [('CD', [task_root, 'last_input', new_input]),\n"
  145. result = result + " ('CD', [old_input, 'next', new_input]),\n"
  146. result = result + " ]\n"
  147. result = result + "\n"
  148. result = result + " new_value, = yield [('CNV', [value])]\n"
  149. result = result + " _, _ = yield [('CD', [old_input, 'value', new_value]),\n"
  150. result = result + " ('DE', [link])\n"
  151. result = result + " ]\n"
  152. result = result + " self.returnvalue = {'id': 100, 'value': 'success'}\n"
  153. result = result + "\n"
  154. result = result + " ### Generated rules\n"
  155. result = result + " def execute_rule(self, taskname):\n"
  156. // Done, now generate the variable code
  157. result = result + " root, = yield [('RR', [])]\n"
  158. nodes = allInstances(model, "Rules/Root")
  159. while (set_len(nodes) > 0):
  160. node = set_pop(nodes)
  161. source = string_replace(node, "/", "_")
  162. result = result + " " + source + " = root\n"
  163. // Keep following outgoing edges to find matching nodes
  164. to_explore = set_create()
  165. remainder_to_explore = set_create()
  166. set_add(to_explore, node)
  167. while (set_len(to_explore) > 0):
  168. // Still explore more!
  169. node = set_pop(to_explore)
  170. source = string_replace(node, "/", "_")
  171. edges = allOutgoingAssociationInstances(model, node, "Rules/MatchEdge")
  172. while (set_len(edges) > 0):
  173. edge = set_pop(edges)
  174. new_node = readAssociationDestination(model, edge)
  175. if (value_eq(read_attribute(model, new_node, "match"), True)):
  176. // Is a match node, so fetch the value on the edge
  177. name = read_attribute(model, edge, "value")
  178. destination = string_replace(new_node, "/", "_")
  179. if (element_eq(name, read_root())):
  180. String node_key
  181. node_key = string_replace(set_pop(allAssociationDestinations(model, edge, "")), "/", "_")
  182. if (set_in(explored, node_key)):
  183. result = result + " " + destination + ", = yield [('RDN', [" + source + ", " + node_key + "])]\n"
  184. else:
  185. set_add(remainder_to_explore, node)
  186. else:
  187. if (set_in(explored, destination)):
  188. // Already visited this one in another way, so try to merge!
  189. String rand
  190. rand = random_string(10)
  191. result = result + " " + rand + ", = yield [('RD', [" + source + ", " + name + "])]\n"
  192. result = result + " " + "if " + rand + " != " + destination + ":\n"
  193. // If the values don't agree, this is not a correct match, and we say that this element remains unmatched (i.e., assign None)
  194. result = result + " " + destination + " = None\n"
  195. else:
  196. // First visit to this element, so just assign
  197. result = result + " " + destination + ", = yield [('RD', [" + source + ", " + name + "])]\n"
  198. set_add(explored, destination)
  199. String value
  200. value = read_attribute(model, new_node, "value")
  201. if (element_neq(value, read_root())):
  202. // Match node has a value we should compare as well!
  203. // Read out the value from the Modelverse
  204. result = result + " " + destination + "_V, = yield [('RV', [" + destination + "])]\n"
  205. set_add(to_explore, new_node)
  206. if (bool_and(set_len(to_explore) == 0, set_len(remainder_to_explore) > 0)):
  207. to_explore = remainder_to_explore
  208. remainder_to_explore = set_create()
  209. rules = allInstances(model, "Rules/Rule")
  210. result = result + " " + "if (False):\n"
  211. result = result + " # here to make code generation nicer...\n"
  212. result = result + " pass\n"
  213. while (set_len(rules) > 0):
  214. // Check if this rule is applicable
  215. rule = set_pop(rules)
  216. // Fetch all elements with a "match" label and check that they are not None (and possibly, that they have a value)
  217. result = result + " elif (True "
  218. nodes = allAssociationDestinations(model, rule, "Rules/contains")
  219. while (set_len(nodes) > 0):
  220. node = set_pop(nodes)
  221. if (value_eq(read_attribute(model, node, "match"), True)):
  222. // We should match on this node:
  223. value = read_attribute(model, node, "value")
  224. if (bool_and(read_type(model, node) == "Rules/NAC", element_eq(value, read_root()))):
  225. result = result + " and " + string_replace(node, "/", "_") + " is None "
  226. else:
  227. result = result + " and " + string_replace(node, "/", "_") + " is not None "
  228. if (element_neq(value, read_root())):
  229. // Got a value, so match that as well
  230. // But check if it is an action element (first character == !)
  231. String sign
  232. if (read_type(model, node) == "Rules/NAC"):
  233. sign = "!="
  234. else:
  235. sign = "=="
  236. if (string_get(value, 0) == "!"):
  237. result = result + " and " + string_replace(node, "/", "_") + "_V['value'] " + sign + " '" + string_replace(value, "!", "") + "'"
  238. else:
  239. result = result + " and " + string_replace(node, "/", "_") + "_V " + sign + " " + value
  240. result = result + "):\n"
  241. result = result + " # Execute rule " + rule + "\n"
  242. result = result + " print('Execute rule " + rule + "')\n"
  243. // We know all the nodes that we already have (in variable "explored")
  244. // Still have to match all "match" and "delete" elements
  245. // For "match", it is exactly the same as before
  246. // For "delete" edges, we match using "RD" and "RDE"
  247. Element create_edges
  248. Element all_nodes
  249. Element assigned
  250. log("Matching rule " + rule)
  251. nodes = allAssociationDestinations(model, rule, "Rules/contains")
  252. all_nodes = set_overlap(nodes, allInstances(model, "Rules/Match"))
  253. create_edges = set_create()
  254. explored = set_create()
  255. assigned = set_create()
  256. // Username is always assigned in the beginning
  257. set_add(assigned, "username")
  258. set_add(assigned, "taskname")
  259. while (set_len(nodes) > 0):
  260. node = set_pop(nodes)
  261. if (read_type(model, node) == "Rules/Root"):
  262. // Found the root of this rule, so start exploring again
  263. // Keep following outgoing edges to find matching nodes
  264. to_explore = set_create()
  265. remainder_to_explore = set_create()
  266. set_add(to_explore, node)
  267. while (set_len(explored) < set_len(all_nodes)):
  268. while (set_len(to_explore) > 0):
  269. // Still explore more!
  270. node = set_pop(to_explore)
  271. log("Explore " + node)
  272. set_add(explored, node)
  273. source = string_replace(node, "/", "_")
  274. edges = allOutgoingAssociationInstances(model, node, "Rules/MatchEdge")
  275. while (set_len(edges) > 0):
  276. edge = set_pop(edges)
  277. new_node = readAssociationDestination(model, edge)
  278. name = read_attribute(model, edge, "value")
  279. destination = string_replace(new_node, "/", "_")
  280. if (element_eq(name, read_root())):
  281. String node_key
  282. node_key = set_pop(allAssociationDestinations(model, edge, ""))
  283. if (set_in(explored, node_key)):
  284. result = result + " " + destination + ", = yield [('RDN', [" + source + ", " + string_replace(node_key, "/", "_") + "])]\n"
  285. set_add(to_explore, new_node)
  286. else:
  287. set_add(remainder_to_explore, node)
  288. continue!
  289. if (read_type(model, edge) == "Rules/DeleteEdge"):
  290. // Delete edge
  291. result = result + " " + destination + "_DEL, = yield [('RDNE', [" + source + ", " + string_replace(node_key, "/", "_") + "])]\n"
  292. result = result + " yield [('DE', [" + destination + "_DEL])]\n"
  293. else:
  294. if (bool_or(string_get(name, 0) == "'", set_in(assigned, name))):
  295. if (set_in(explored, new_node)):
  296. // Already visited this one in another way, so try to merge!
  297. String rand
  298. rand = random_string(10)
  299. result = result + " " + rand + ", = yield [('RD', [" + source + ", " + name + "])]\n"
  300. result = result + " " + "if " + rand + " != " + destination + ":\n"
  301. // If the values don't agree, this is not a correct match, and we say that this element remains unmatched (i.e., assign None)
  302. result = result + " " + " " + destination + " = None\n"
  303. else:
  304. // First visit to this element, so just assign
  305. result = result + " " + destination + ", = yield [('RD', [" + source + ", " + name + "])]\n"
  306. String value
  307. value = read_attribute(model, new_node, "value")
  308. if (element_neq(value, read_root())):
  309. // Read out the value from the Modelverse, as this can be used later for reading edges
  310. if (bool_and(bool_and(string_get(value, 0) != "!", string_get(value, 0) != "'"), bool_and(value != "False", value != "True"))):
  311. result = result + " " + value + ", = yield [('RV', [" + destination + "])]\n"
  312. set_add(assigned, value)
  313. set_add(to_explore, new_node)
  314. if (read_type(model, edge) == "Rules/DeleteEdge"):
  315. // Delete edge
  316. result = result + " " + destination + "_DEL, = yield [('RDE', [" + source + ", " + name + "])]\n"
  317. result = result + " yield [('DE', [" + destination + "_DEL])]\n"
  318. else:
  319. set_add(remainder_to_explore, node)
  320. if (bool_and(set_len(to_explore) == 0, set_len(remainder_to_explore) > 0)):
  321. to_explore = remainder_to_explore
  322. remainder_to_explore = set_create()
  323. // Check for nodes that are unexplored, but seemingly not reachable from the root directly
  324. Element remaining
  325. String check_elem
  326. Boolean found
  327. if (set_len(explored) < set_len(all_nodes)):
  328. // Find all unexplored nodes
  329. remaining = set_difference(all_nodes, explored)
  330. log("Remain unexplored: " + set_to_string(remaining))
  331. while (set_len(remaining) > 0):
  332. check_elem = set_pop(remaining)
  333. edges = allOutgoingAssociationInstances(model, check_elem, "Rules/MatchEdge")
  334. found = False
  335. while (set_len(edges) > 0):
  336. edge = set_pop(edges)
  337. new_node = readAssociationDestination(model, edge)
  338. name = read_attribute(model, edge, "value")
  339. if (set_in(explored, new_node)):
  340. // We already explored this node, so we can do a RRD from this place!
  341. if (bool_not(found)):
  342. result = result + " matched = []\n"
  343. result = result + " _tmp, = yield [('RRD', [" + string_replace(new_node, "/", "_") + ", " + name + "])]\n"
  344. result = result + " matched.append(_tmp)\n"
  345. found = True
  346. // Iterated over all edges, so check how many we found!
  347. if (found):
  348. set_add(to_explore, check_elem)
  349. set_add(explored, check_elem)
  350. // Found at least one match, so we try to find an overlapping node
  351. result = result + " " + string_replace(check_elem, "/", "_") + " = self.set_overlap(matched)[0]\n"
  352. elif (read_type(model, node) == "Rules/Create"):
  353. // A node that we should create: do that already, as otherwise we might have to sort
  354. String attr
  355. attr = read_attribute(model, node, "value")
  356. if (element_eq(attr, read_root())):
  357. // Create empty node
  358. result = result + " " + string_replace(node, "/", "_") + ", = yield [('CN', [])]\n"
  359. else:
  360. // Create value node
  361. result = result + " " + string_replace(node, "/", "_") + ", = yield [('CNV', [" + cast_string(attr) + "])]\n"
  362. elif (read_type(model, node) == "Rules/CreateEdge"):
  363. // Encounter a create edge, so alreade record this for later
  364. set_add(create_edges, node)
  365. // Now create all the new edges, given that we assume everything to be matched
  366. while (set_len(create_edges) > 0):
  367. edge = set_pop(create_edges)
  368. attr = read_attribute(model, edge, "value")
  369. if (element_neq(attr, read_root())):
  370. result = result + " " + string_replace(edge, "/", "_") + ", = yield [('CD', [" + string_replace(readAssociationSource(model, edge), "/", "_") + ", " + attr + ", " + string_replace(readAssociationDestination(model, edge), "/", "_") + "])]\n"
  371. else:
  372. result = result + " " + string_replace(edge, "/", "_") + ", = yield [('CE', [" + string_replace(readAssociationSource(model, edge), "/", "_") + ", " + string_replace(readAssociationDestination(model, edge), "/", "_") + "])]\n"
  373. result = result + " else:\n"
  374. result = result + " # no rules were applicable, so idle for some time\n"
  375. result = result + " pass #TODO\n"
  376. result = result + " raise Exception(str(locals()))\n"
  377. log("Got result:")
  378. log(result)
  379. String file
  380. file = instantiate_node(model, "Files/File", "")
  381. instantiate_attribute(model, file, "name", "generated_kernel.py")
  382. instantiate_attribute(model, file, "content", result)
  383. return True!