include "primitives.alh" include "modelling.alh" include "object_operations.alh" include "random.alh" include "utils.alh" Boolean function main(model : Element): String result Element nodes String node Element edges String edge String new_node String source String destination String name Element to_explore Element rules String rule String value Element explored Element remainder_to_explore String attr explored = set_create() result = "" // Add the preamble... result = result + "import modelverse_kernel.primitives as primitive_functions\n" result = result + "import modelverse_kernel.compiled as compiled_functions\n" result = result + "from collections import defaultdict\n" result = result + "import sys\n" result = result + "import time\n" result = result + "\n" result = result + "class ModelverseKernel(object):\n" result = result + " def __init__(self, root):\n" result = result + " self.root = root\n" result = result + " self.primitives = {}\n" result = result + " self.returnvalue = None\n" result = result + " self.success = True\n" result = result + " self.generators = {}\n" result = result + "\n" result = result + " def execute_yields(self, taskname, operation, params, reply):\n" result = result + " try:\n" result = result + " self.success = True\n" result = result + " self.taskname = taskname\n" result = result + " if taskname not in self.generators:\n" result = result + " self.generators[taskname] = {}\n" result = result + " if operation not in self.generators[taskname]:\n" result = result + " # Create the generator for the function to execute\n" result = result + " self.generators[taskname][operation] = getattr(self, operation)(taskname, *params)\n" result = result + "\n" result = result + " if reply is not None:\n" result = result + " return self.generators[taskname][operation].send(reply)\n" result = result + " else:\n" result = result + " return self.generators[taskname][operation].next()\n" result = result + " except StopIteration:\n" result = result + " # Done, so remove the generator\n" result = result + " del self.generators[taskname][operation]\n" result = result + " return None\n" result = result + " except:\n" result = result + " raise\n" result = result + "\n" result = result + " ##########################\n" result = result + " ### Process primitives ###\n" result = result + " ##########################\n" result = result + " def load_primitives(self, taskname):\n" result = result + " hierarchy, = yield [('RD', [self.root, '__hierarchy'])]\n" result = result + " primitives, = yield [('RD', [hierarchy, 'primitives'])]\n" result = result + " keys, = yield [('RDK', [primitives])]\n" result = result + " function_names = yield [('RV', [f]) for f in keys]\n" result = result + " signatures = yield [('RDN', [primitives, f]) for f in keys]\n" result = result + " bodies = yield [('RD', [f, 'body']) for f in signatures]\n" result = result + " for i in range(len(keys)):\n" result = result + " self.primitives[bodies[i]] = getattr(primitive_functions, function_names[i])\n" result = result + "\n" result = result + " def execute_primitive(self, task_root, inst, taskname):\n" result = result + " # execute_primitive\n" result = result + " task_frame, = yield [('RD', [task_root, 'frame'])]\n" result = result + " symbols, = yield [('RD', [task_frame, 'symbols'])]\n" result = result + " all_links, = yield [('RO', [symbols])]\n" result = result + " containers = yield [('RE', [v]) for v in all_links]\n" result = result + " outgoings = yield [('RO', [v]) for v in all_links]\n" result = result + " dict_values = yield [('RD', [v[1], 'value']) for v in containers]\n" result = result + " formals_1 = yield [('RE', [v[0]]) for v in outgoings]\n" result = result + " dict_keys_ref = yield [('RD', [v[1], 'name']) for v in formals_1]\n" result = result + " dict_keys = yield [('RV', [v]) for v in dict_keys_ref]\n" result = result + " parameters = dict(zip(dict_keys, dict_values))\n" result = result + "\n" result = result + " parameters['root'] = self.root\n" result = result + " parameters['task_root'] = task_root\n" result = result + " parameters['taskname'] = taskname\n" result = result + " parameters['mvk'] = self\n" result = result + "\n" result = result + " # prim is a generator itself!\n" result = result + " try:\n" result = result + " # Forward the message we get to this generator\n" 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" result = result + " prim = self.primitives[inst](**parameters)\n" result = result + " inp = None\n" result = result + " while 1:\n" result = result + " inp = yield prim.send(inp)\n" result = result + " except StopIteration:\n" result = result + " # Execution has ended without return value, so we have no idea what to do\n" result = result + " raise Exception('Primitive finished without returning a value!')\n" result = result + " except primitive_functions.PrimitiveFinished as e:\n" result = result + " # Execution has ended with a returnvalue, so read it out from the exception being thrown\n" result = result + " result = e.result\n" result = result + "\n" result = result + " #if result is None:\n" result = result + " # raise Exception('Primitive raised exception: value of None for operation %s with parameters %s' % (self.compiled[inst], str(parameters)))\n" result = result + "\n" result = result + " # Clean up the current stack, as if a return happened\n" result = result + " old_frame, = yield [('RD', [task_frame, 'prev'])]\n" result = result + " lnk, = yield [('RDE', [old_frame, 'returnvalue'])]\n" result = result + " _, _, _, _ = yield [('CD', [old_frame, 'returnvalue', result]),\n" result = result + " ('CD', [task_root, 'frame', old_frame]),\n" result = result + " ('DE', [lnk]),\n" result = result + " ('DN', [task_frame]),\n" result = result + " ]\n" result = result + "\n" result = result + " ########################################\n" result = result + " ### Execute input and output methods ###\n" result = result + " ########################################\n" result = result + " def get_output(self, taskname):\n" result = result + " task_root, = yield [('RD', [self.root, taskname])]\n" result = result + " first_output, = yield [('RD', [task_root, 'output'])]\n" result = result + " next_output, rv = yield [('RD', [first_output, 'next']),\n" result = result + " ('RD', [first_output, 'value']),\n" result = result + " ]\n" result = result + " if next_output is None:\n" result = result + " self.success = False\n" result = result + " self.returnvalue = None\n" result = result + " else:\n" result = result + " rv_value, = yield [('RV', [rv])]\n" result = result + " _, _ = yield [('CD', [task_root, 'output', next_output]),\n" result = result + " ('DN', [first_output]),\n" result = result + " ]\n" result = result + " self.returnvalue = rv_value\n" result = result + "\n" result = result + " def set_input(self, taskname, value):\n" result = result + " task_root, = yield [('RD', [self.root, taskname])]\n" result = result + " old_input, link = yield [('RD', [task_root, 'last_input']),\n" result = result + " ('RDE', [task_root, 'last_input']),\n" result = result + " ]\n" result = result + " new_input, = yield [('CN', [])]\n" result = result + " _, _ = yield [('CD', [task_root, 'last_input', new_input]),\n" result = result + " ('CD', [old_input, 'next', new_input]),\n" result = result + " ]\n" result = result + "\n" result = result + " new_value, = yield [('CNV', [value])]\n" result = result + " _, _ = yield [('CD', [old_input, 'value', new_value]),\n" result = result + " ('DE', [link])\n" result = result + " ]\n" result = result + " self.returnvalue = {'id': 100, 'value': 'success'}\n" result = result + "\n" result = result + " ### Generated rules\n" result = result + " def execute_rule(self, taskname):\n" // Done, now generate the variable code result = result + " root, = yield [('RR', [])]\n" nodes = allInstances(model, "Rules/Root") while (set_len(nodes) > 0): node = set_pop(nodes) source = string_replace(node, "/", "_") result = result + " " + source + " = root\n" // Keep following outgoing edges to find matching nodes to_explore = set_create() remainder_to_explore = set_create() set_add(to_explore, node) while (set_len(to_explore) > 0): // Still explore more! node = set_pop(to_explore) source = string_replace(node, "/", "_") edges = allOutgoingAssociationInstances(model, node, "Rules/MatchEdge") while (set_len(edges) > 0): edge = set_pop(edges) new_node = readAssociationDestination(model, edge) if (value_eq(read_attribute(model, new_node, "match"), True)): // Is a match node, so fetch the value on the edge name = read_attribute(model, edge, "value") destination = string_replace(new_node, "/", "_") if (element_eq(name, read_root())): String node_key node_key = string_replace(set_pop(allAssociationDestinations(model, edge, "")), "/", "_") if (set_in(explored, node_key)): result = result + " " + destination + ", = yield [('RDN', [" + source + ", " + node_key + "])]\n" else: set_add(remainder_to_explore, node) else: if (set_in(explored, destination)): // Already visited this one in another way, so try to merge! String rand rand = random_string(10) result = result + " " + rand + ", = yield [('RD', [" + source + ", " + name + "])]\n" result = result + " " + "if " + rand + " != " + destination + ":\n" // If the values don't agree, this is not a correct match, and we say that this element remains unmatched (i.e., assign None) result = result + " " + destination + " = None\n" else: // First visit to this element, so just assign result = result + " " + destination + ", = yield [('RD', [" + source + ", " + name + "])]\n" set_add(explored, destination) String value value = read_attribute(model, new_node, "value") if (element_neq(value, read_root())): // Match node has a value we should compare as well! // Read out the value from the Modelverse result = result + " " + destination + "_V, = yield [('RV', [" + destination + "])]\n" set_add(to_explore, new_node) if (bool_and(set_len(to_explore) == 0, set_len(remainder_to_explore) > 0)): to_explore = remainder_to_explore remainder_to_explore = set_create() rules = allInstances(model, "Rules/Rule") result = result + " " + "if (False):\n" result = result + " # here to make code generation nicer...\n" result = result + " pass\n" while (set_len(rules) > 0): // Check if this rule is applicable rule = set_pop(rules) // Fetch all elements with a "match" label and check that they are not None (and possibly, that they have a value) result = result + " elif (True " nodes = allAssociationDestinations(model, rule, "Rules/contains") while (set_len(nodes) > 0): node = set_pop(nodes) if (value_eq(read_attribute(model, node, "match"), True)): // We should match on this node: value = read_attribute(model, node, "value") if (bool_and(read_type(model, node) == "Rules/NAC", element_eq(value, read_root()))): result = result + " and " + string_replace(node, "/", "_") + " is None " else: result = result + " and " + string_replace(node, "/", "_") + " is not None " if (element_neq(value, read_root())): // Got a value, so match that as well // But check if it is an action element (first character == !) String sign if (read_type(model, node) == "Rules/NAC"): sign = "!=" else: sign = "==" if (string_get(value, 0) == "!"): result = result + " and " + string_replace(node, "/", "_") + "_V['value'] " + sign + " '" + string_replace(value, "!", "") + "'" else: result = result + " and " + string_replace(node, "/", "_") + "_V " + sign + " " + value result = result + "):\n" result = result + " # Execute rule " + rule + "\n" result = result + " print('Execute rule " + rule + "')\n" // We know all the nodes that we already have (in variable "explored") // Still have to match all "match" and "delete" elements // For "match", it is exactly the same as before // For "delete" edges, we match using "RD" and "RDE" Element create_edges Element all_nodes Element assigned log("Matching rule " + rule) nodes = allAssociationDestinations(model, rule, "Rules/contains") all_nodes = set_overlap(nodes, allInstances(model, "Rules/Match")) create_edges = set_create() explored = set_create() assigned = set_create() // Username is always assigned in the beginning set_add(assigned, "username") set_add(assigned, "taskname") while (set_len(nodes) > 0): node = set_pop(nodes) if (read_type(model, node) == "Rules/Root"): // Found the root of this rule, so start exploring again // Keep following outgoing edges to find matching nodes to_explore = set_create() remainder_to_explore = set_create() set_add(to_explore, node) while (set_len(explored) < set_len(all_nodes)): while (set_len(to_explore) > 0): // Still explore more! node = set_pop(to_explore) log("Explore " + node) set_add(explored, node) source = string_replace(node, "/", "_") edges = allOutgoingAssociationInstances(model, node, "Rules/MatchEdge") while (set_len(edges) > 0): edge = set_pop(edges) new_node = readAssociationDestination(model, edge) name = read_attribute(model, edge, "value") destination = string_replace(new_node, "/", "_") if (element_eq(name, read_root())): String node_key node_key = set_pop(allAssociationDestinations(model, edge, "")) if (set_in(explored, node_key)): result = result + " " + destination + ", = yield [('RDN', [" + source + ", " + string_replace(node_key, "/", "_") + "])]\n" set_add(to_explore, new_node) else: set_add(remainder_to_explore, node) continue! if (read_type(model, edge) == "Rules/DeleteEdge"): // Delete edge result = result + " " + destination + "_DEL, = yield [('RDNE', [" + source + ", " + string_replace(node_key, "/", "_") + "])]\n" result = result + " yield [('DE', [" + destination + "_DEL])]\n" else: if (bool_or(string_get(name, 0) == "'", set_in(assigned, name))): if (set_in(explored, new_node)): // Already visited this one in another way, so try to merge! String rand rand = random_string(10) result = result + " " + rand + ", = yield [('RD', [" + source + ", " + name + "])]\n" result = result + " " + "if " + rand + " != " + destination + ":\n" // If the values don't agree, this is not a correct match, and we say that this element remains unmatched (i.e., assign None) result = result + " " + " " + destination + " = None\n" else: // First visit to this element, so just assign result = result + " " + destination + ", = yield [('RD', [" + source + ", " + name + "])]\n" String value value = read_attribute(model, new_node, "value") if (element_neq(value, read_root())): // Read out the value from the Modelverse, as this can be used later for reading edges if (bool_and(bool_and(string_get(value, 0) != "!", string_get(value, 0) != "'"), bool_and(value != "False", value != "True"))): result = result + " " + value + ", = yield [('RV', [" + destination + "])]\n" set_add(assigned, value) set_add(to_explore, new_node) if (read_type(model, edge) == "Rules/DeleteEdge"): // Delete edge result = result + " " + destination + "_DEL, = yield [('RDE', [" + source + ", " + name + "])]\n" result = result + " yield [('DE', [" + destination + "_DEL])]\n" else: set_add(remainder_to_explore, node) if (bool_and(set_len(to_explore) == 0, set_len(remainder_to_explore) > 0)): to_explore = remainder_to_explore remainder_to_explore = set_create() // Check for nodes that are unexplored, but seemingly not reachable from the root directly Element remaining String check_elem Boolean found if (set_len(explored) < set_len(all_nodes)): // Find all unexplored nodes remaining = set_difference(all_nodes, explored) log("Remain unexplored: " + set_to_string(remaining)) while (set_len(remaining) > 0): check_elem = set_pop(remaining) edges = allOutgoingAssociationInstances(model, check_elem, "Rules/MatchEdge") found = False while (set_len(edges) > 0): edge = set_pop(edges) new_node = readAssociationDestination(model, edge) name = read_attribute(model, edge, "value") if (set_in(explored, new_node)): // We already explored this node, so we can do a RRD from this place! if (bool_not(found)): result = result + " matched = []\n" result = result + " _tmp, = yield [('RRD', [" + string_replace(new_node, "/", "_") + ", " + name + "])]\n" result = result + " matched.append(_tmp)\n" found = True // Iterated over all edges, so check how many we found! if (found): set_add(to_explore, check_elem) set_add(explored, check_elem) // Found at least one match, so we try to find an overlapping node result = result + " " + string_replace(check_elem, "/", "_") + " = self.set_overlap(matched)[0]\n" elif (read_type(model, node) == "Rules/Create"): // A node that we should create: do that already, as otherwise we might have to sort String attr attr = read_attribute(model, node, "value") if (element_eq(attr, read_root())): // Create empty node result = result + " " + string_replace(node, "/", "_") + ", = yield [('CN', [])]\n" else: // Create value node result = result + " " + string_replace(node, "/", "_") + ", = yield [('CNV', [" + cast_string(attr) + "])]\n" elif (read_type(model, node) == "Rules/CreateEdge"): // Encounter a create edge, so alreade record this for later set_add(create_edges, node) // Now create all the new edges, given that we assume everything to be matched while (set_len(create_edges) > 0): edge = set_pop(create_edges) attr = read_attribute(model, edge, "value") if (element_neq(attr, read_root())): result = result + " " + string_replace(edge, "/", "_") + ", = yield [('CD', [" + string_replace(readAssociationSource(model, edge), "/", "_") + ", " + attr + ", " + string_replace(readAssociationDestination(model, edge), "/", "_") + "])]\n" else: result = result + " " + string_replace(edge, "/", "_") + ", = yield [('CE', [" + string_replace(readAssociationSource(model, edge), "/", "_") + ", " + string_replace(readAssociationDestination(model, edge), "/", "_") + "])]\n" result = result + " else:\n" result = result + " # no rules were applicable, so idle for some time\n" result = result + " pass #TODO\n" result = result + " raise Exception(str(locals()))\n" log("Got result:") log(result) String file file = instantiate_node(model, "Files/File", "") instantiate_attribute(model, file, "name", "generated_kernel.py") instantiate_attribute(model, file, "content", result) return True!