Simon Van Mierlo 8 years ago
parent
commit
3b34e58195
66 changed files with 8366 additions and 2383 deletions
  1. 43 8
      bootstrap/90_core_formalism.mvc
  2. 6 2
      bootstrap/bottom.mvc
  3. 26 26
      bootstrap/conformance_finding.alc
  4. 5 5
      bootstrap/conformance_scd.alc
  5. 168 96
      bootstrap/core_algorithm.alc
  6. 13 0
      bootstrap/metamodels.alt
  7. 154 24
      bootstrap/mini_modify.alc
  8. 36 0
      bootstrap/modelling.alc
  9. 31 5
      bootstrap/object_operations.alc
  10. 29 7
      bootstrap/pm.mvc
  11. 22 4
      bootstrap/ramify.alc
  12. 9 1
      bootstrap/semi_primitives.alc
  13. 1 1
      bootstrap/services.alc
  14. 5 1
      bootstrap/tracability.mvc
  15. 12 4
      bootstrap/transform.alc
  16. 8 6
      bootstrap/typing.alc
  17. 8 18
      bootstrap/utils.alc
  18. 1 1
      integration/code/pdevs_client.alc
  19. 10 2
      integration/code/pn_design.mvc
  20. 106 0
      integration/log_output.py
  21. 39 0
      integration/log_output.xml
  22. 39 38
      integration/test_powerwindow.py
  23. 3 0
      interface/HUTN/hutn_compiler/hutnparser.py
  24. 2 0
      interface/HUTN/includes/modelling.alh
  25. 2 1
      interface/HUTN/includes/object_operations.alh
  26. 2 1
      interface/HUTN/includes/primitives.alh
  27. 0 1
      interface/HUTN/includes/utils.alh
  28. 7 4
      interface/HUTN/test/constructor_compilation_action_language/test_compile.py
  29. 22 18
      interface/PDEVS/console.py
  30. 0 1047
      interface/PDEVS/modelverse.py
  31. 19 0
      kernel/modelverse_kernel/compiled.py
  32. 72 0
      models/MM_render.mvc
  33. 20 3
      models/MM_rendered_graphical.mvc
  34. 31 9
      models/MM_rendered_plot.mvc
  35. 48 10
      models/SCCD.mvc
  36. 7 2
      models/SCCD_Trace.mvc
  37. 1 1
      models/SCCD_execute.alc
  38. 9 2
      models/architecture.mvc
  39. 6 1
      models/bfs.alc
  40. 2 2
      models/combine_EPN.mvc
  41. 27 8
      models/control_PW.mvc
  42. 1 1
      models/devs_to_string.alc
  43. 16 5
      models/environment_PW.mvc
  44. 1 1
      models/epn_print.alc
  45. 25 7
      models/petrinet_ports.mvc
  46. 4 0
      models/petrinets.mvc
  47. 26 8
      models/plant_PW.mvc
  48. 1 1
      models/pn_print.alc
  49. 7 2
      models/query.mvc
  50. 16 4
      models/reachability_graph.mvc
  51. 2 2
      models/reachabilitygraph_print.mvc
  52. 213 0
      models/render_OD.alc
  53. 182 0
      models/render_SCD.alc
  54. 2 0
      models/requirements.mvc
  55. 25 0
      models/test_compiler.alc
  56. 11 3
      models/trace.mvc
  57. 5 0
      scripts/HUTN_service.py
  58. 0 2
      scripts/JSON_service.py
  59. 65 60
      unit/test_all.py
  60. 177 0
      wrappers/classes/http_client.xml
  61. 2113 0
      wrappers/classes/modelverse.xml
  62. 2 0
      wrappers/compile.sh
  63. 353 928
      wrappers/modelverse.py
  64. 3724 0
      wrappers/modelverse_SCCD.py
  65. 25 0
      wrappers/modelverse_SCCD.xml
  66. 319 0
      wrappers/modelverse_coded.py

+ 43 - 8
bootstrap/90_core_formalism.mvc

@@ -3,6 +3,7 @@ include "primitives.alh"
 
 SimpleClassDiagrams CoreFormalism {
     SimpleAttribute String {
+        name = "String"
         constraint = $
             String function constraint(model : Element, name : String):
                 if (bool_not(is_physical_string(model["model"][name]))):
@@ -13,6 +14,7 @@ SimpleClassDiagrams CoreFormalism {
     }
 
     SimpleAttribute Permissions {
+        name = "Permissions"
         constraint = $
             String function constraint(model : Element, name : String):
                 Element self
@@ -32,6 +34,7 @@ SimpleClassDiagrams CoreFormalism {
     }
 
     SimpleAttribute Boolean {
+        name = "Boolean"
         constraint = $
             String function constraint(model : Element, name : String):
                 if (bool_not(is_physical_boolean(model["model"][name]))):
@@ -42,6 +45,7 @@ SimpleClassDiagrams CoreFormalism {
     }
 
     SimpleAttribute Natural {
+        name = "Natural"
         constraint = $
             String function constraint(model : Element, name : String):
                 if (bool_not(is_physical_int(model["model"][name]))):
@@ -54,76 +58,107 @@ SimpleClassDiagrams CoreFormalism {
     }
 
     Class User {
+        name = "User"
         name : String
         password : String
         admin : Boolean
     }
 
     Class Group {
+        name = "Group"
         name : String
     }
 
-    Association ownedBy (Group, User) {}
-    Association belongsTo (User, Group) {}
+    Association ownedBy (Group, User) {
+        name = "ownedBy"
+    }
+    Association belongsTo (User, Group) {
+        name = "belongsTo"
+    }
 
     Class Entry {
+        name = "Entry"
         name : String
         permissions : Permissions
     }
 
     Class Folder : Entry {
+        name = "Folder"
     }
 
-    Association contains (Folder, Entry) {}
+    Association contains (Folder, Entry) {
+        name = "contains"
+    }
 
     Class Model : Entry {
+        name = "Model"
         location : String
     }
 
-    Class TypeMapping : Model {}
+    Class TypeMapping : Model {
+        name = "TypeMapping"
+    }
 
     Association instanceOf (Model, Model) {
+        name = "instanceOf"
     }
 
     Association owner (Model, User) {
+        name = "owner"
         target_lower_cardinality = 1
         target_upper_cardinality = 1
     }
 
     Association group (Model, Group) {
+        name = "group"
         target_lower_cardinality = 1
         target_upper_cardinality = 1
     }
 
-    Class Transformation : Model {}
+    Class Transformation : Model {
+        name = "Transformation"
+    }
 
-    Class ModelTransformation : Transformation {}
+    Class ModelTransformation : Transformation {
+        name = "ModelTransformation"
+    }
 
-    Class ActionLanguage : Transformation {}
+    Class ActionLanguage : Transformation {
+        name = "ActionLanguage"
+    }
 
-    Class ManualOperation : Transformation {}
+    Class ManualOperation : Transformation {
+        name = "ManualOperation"
+    }
 
     Association transformInput (Model, Transformation) {
+        name = "transformInput"
         name : String
     }
+
     Association transformOutput (Transformation, Model) {
+        name = "transformOutput"
         name : String
     }
 
     Association tracability (Model, Model) {
+        name = "tracability"
         type : String
     }
 
     Association semantics (instanceOf, ActionLanguage) {
+        name = "semantics"
         target_lower_cardinality = 1
         target_upper_cardinality = 1
     }
     Association typing (instanceOf, TypeMapping) {
+        name = "typing"
         target_lower_cardinality = 1
         target_upper_cardinality = 1
     }
 
     Class Service {
+        name = "Service"
         name : String
         port : String
     }

+ 6 - 2
bootstrap/bottom.mvc

@@ -1,8 +1,12 @@
 import models/SimpleClassDiagrams as SCD
 
 SCD Bottom{
-    Class Node {}
-    Association Edge : Node (Node, Node) {}
+    Class Node {
+        name = "Node"
+    }
+    Association Edge : Node (Node, Node) {
+        name = "Edge"
+    }
 }
 
 export Bottom to models/Bottom

+ 26 - 26
bootstrap/conformance_finding.alc

@@ -32,10 +32,10 @@ Boolean function find_type_mapping(model : Element):
 	// 2) find a mapping based on the current partial mapping, but only if it is not yet complete
 	// TODO this must be expanded for other things than trivial metamodels!
 	if (dict_len(model["model"]) > dict_len(tm)):
-		//log("Model is incompletely typed!")
-		//log("Model has: " + set_to_string(dict_keys(model["model"])))
-		//log("Type mapping has: " + set_to_string(dict_keys(tm)))
-		//log("Missing: " + set_to_string(set_difference(dict_keys(model["model"]), dict_keys(tm))))
+		log("Model is incompletely typed!")
+		log("Model has: " + set_to_string(dict_keys(model["model"])))
+		log("Type mapping has: " + set_to_string(dict_keys(tm)))
+		log("Difference: " + set_to_string(set_difference(dict_keys(model["model"]), dict_keys(tm))))
 
 		// TODO for now, this only returns something for a simple case, where the MM has one edge, and one node
 		//      and it makes the assumption that SCD is the M3 level...
@@ -49,30 +49,30 @@ Boolean function find_type_mapping(model : Element):
 		node_element = read_root()
 		edge_element = read_root()
 
-		elems = dict_keys(model["metamodel"]["model"])
-		log("Elements in metamodel: " + set_to_string(elems))
-		while (set_len(elems) > 0):
-			elem = set_pop(elems)
-			// log("Check " + elem)
-
-			if (bool_not(is_edge(model["metamodel"]["model"][elem]))):
-				if (element_neq(node_element, read_root())):
-					log("Multiple nodes detected!")
-					return False!
-				node_element = elem
-			else:
-				// Is an edge, but might be the inheritance link...
-				log("type: " + read_type(model["metamodel"], elem))
-				if (read_type(model["metamodel"], elem) != "Inheritance"):
-					// Is not the inheritance link
-					if (element_neq(edge_element, read_root())):
-						log("Multiple edges detected")
-						return False!
-					edge_element = elem
+		Element nodes
+		Element edges
+		nodes = allInstances(model["metamodel"], "Class")
+		edges = allInstances(model["metamodel"], "Association")
 
-		if (bool_or(element_eq(node_element, read_root()), element_eq(edge_element, read_root()))):
-			log("Not both node and edge detected")
+		if (set_len(nodes) > 1):
+			log("Got nodes: " + set_to_string(nodes))
+			log("Multiple nodes detected!")
 			return False!
+		elif (set_len(edges) > 1):
+			log("Got edges: " + set_to_string(edges))
+			log("Multiple edges detected!")
+			return False!
+		elif (set_len(nodes) != 1):
+			log("No node found!")
+			return False!
+		elif (set_len(edges) != 1):
+			log("No edge found!")
+			return False!
+		else:
+			node_element = set_pop(nodes)
+			edge_element = set_pop(edges)
+			log("Found node: " + node_element)
+			log("Found edge: " + edge_element)
 
 		// Now we have bot an edge_element and node_element of the metamodel
 		// Now just trivially bind all elements!

+ 5 - 5
bootstrap/conformance_scd.alc

@@ -194,13 +194,13 @@ String function conformance_scd(model : Element):
 							// A lower cardinality was defined at the target
 							if (integer_gt(cardinalities[check_type]["tlc"], instances)):
 								String error
-								error = (("Lower cardinality violation for outgoing edge of type " + check_type) + " at ") + model_info(model, model_name)
+								error = "Lower cardinality violation for outgoing edge of type " + check_type + " at " + model_info(model, model_name)
 								return error!
 						if (dict_in(cardinalities[check_type], "tuc")):
 							// An upper cardinality was defined at the target
 							if (integer_lt(cardinalities[check_type]["tuc"], instances)):
 								String error
-								error = (("Upper cardinality violation for outgoing edge of type " + check_type) + " at ") + model_info(model, model_name)
+								error = "Upper cardinality violation for outgoing edge of type " + check_type + " at " + model_info(model, model_name)
 								return error!
 
 			// Identical, but for outgoing, and thus for A in the figure
@@ -218,13 +218,13 @@ String function conformance_scd(model : Element):
 							// A lower cardinality was defined at the source
 							if (integer_gt(cardinalities[check_type]["slc"], instances)):
 								String error
-								error = (("Lower cardinality violation for incoming edge of type " + check_type) + " at ") + model_info(model, model_name)
+								error = "Lower cardinality violation for incoming edge of type " + check_type + " at " + model_info(model, model_name)
 								return error!
 						if (dict_in(cardinalities[check_type], "suc")):
 							// An upper cardinality was defined at the source
 							if (integer_lt(cardinalities[check_type]["suc"], instances)):
 								String error
-								error = (("Upper cardinality violation for incoming edge of type " + check_type) + " at ") + model_info(model, model_name)
+								error = "Upper cardinality violation for incoming edge of type " + check_type + " at " + model_info(model, model_name)
 								return error!
 
 			constraint_function = read_attribute(metamodel, typing[model_name], "constraint")
@@ -281,7 +281,7 @@ String function conformance_scd(model : Element):
 			result = check_location_conformance(model["model"][complex_instance], complex_type)
 
 			if (result != "OK"):
-				return ((("Complex attribute doesn't match for: " + complex_instance) + "\n Message: ") + result)!
+				return "Complex attribute doesn't match for: " + complex_instance + "\n Message: " + result!
 
 	// Structure seems fine, now do global constraints
 	Element global_constraints

+ 168 - 96
bootstrap/core_algorithm.alc

@@ -110,9 +110,11 @@ Element function get_full_model(model_id : String, metamodel_id : String):
 			dict_add(m, "metamodel", mm)
 
 	if (element_neq(choice, read_root())):
-		if (set_len(allAssociationDestinations(core, choice, "typing")) == 1):
+		Element types
+		types = allAssociationDestinations(core, choice, "typing")
+		if (set_len(types) == 1):
 			// Add the preferred original type mapping
-			set_type_mapping(m, import_node(read_attribute(core, set_pop(allAssociationDestinations(core, choice, "typing")), "location")))
+			set_type_mapping(m, import_node(read_attribute(core, set_pop(types), "location")))
 		else:
 			// Start from scratch
 			new_type_mapping(m)
@@ -639,7 +641,7 @@ Element function execute_operation(operation_id : String, input_models : Element
 	while (set_len(iter) > 0):
 		edge = set_pop(iter)
 		dict_add(output_metamodels, read_attribute(core, edge, "name"), full_name(readAssociationDestination(core, edge)))
-
+	
 	// 1) Find merged metamodel
 
 	exact_type = read_type(core, operation_id)
@@ -835,7 +837,6 @@ Boolean function enact_action(pm : Element, element : String, prefix : String):
 Void function enact_PM(pm : Element, prefix : String):
 	Element worklist
 	String element
-	String start
 	String type
 	Boolean result
 	Element tuple
@@ -897,6 +898,7 @@ Void function enact_PM(pm : Element, prefix : String):
 			// This the difficult part!
 
 			result = enact_action(pm, element, prefix)
+			output("Success")
 
 		elif (type == "Decision"):
 			// If the previous result is true, we add the normal one, otherwise the false one
@@ -919,7 +921,6 @@ Void function enact_PM(pm : Element, prefix : String):
 			set_add_node(worklist, create_tuple(next, result))
 
 	// Reached a finish element, so stop
-	output("Success")
 	return !
 
 String function cmd_help():
@@ -975,7 +976,7 @@ String function cmd_help():
 
 	return result!
 
-String function cmd_model_add(type : String, name : String):
+String function cmd_model_add(type : String, name : String, code : String):
 	// Model addition operation, which uses model upload commands of the compiler
 	String location
 	String type_id
@@ -998,8 +999,7 @@ String function cmd_model_add(type : String, name : String):
 						if (element_eq(mm, read_root())):
 							return "Type is not typed by formalisms/SimpleClassDiagrams: " + type!
 
-						output("Waiting for model constructors...")
-						new_model = compile_model(input(), mm)
+						new_model = compile_model(code, mm)
 
 						if (element_eq(new_model, read_root())):
 							return "Compilation error"!
@@ -1036,40 +1036,96 @@ String function cmd_process_execute(process : String, prefix : String):
 	else:
 		return "Model not found: " + process!
 
-String function cmd_transformation_between(source_name : String, target_name : String):
-	String source_id
-	String target_id
-	Element onSource
-	Element onTarget
+String function cmd_transformation_between(source_dict : String, target_dict : String):
 	Element result
-	String transformation
+	Element subresult
+	String tag
+	String mm
+	String mm_id
+	Element links
+	String link
+	Element keys
 
-	source_id = get_entry_id(source_name)
-	if (source_id != ""):
-		target_id = get_entry_id(target_name)
+	result = allInstances(core, "Transformation")
 
-		if (target_id != ""):
-			onSource = allAssociationOrigins(core, source_id, "transformInput")
-			onTarget = allAssociationOrigins(core, target_id, "transformOutput")
+	// Iterate over all inputs
+	keys = dict_keys(source_dict)
+	while (set_len(keys) > 0):
+		subresult = set_create()
+		tag = set_pop(keys)
+		mm = source_dict[tag]
+		mm_id = get_entry_id(mm)
+
+		if (mm_id != ""):
+			links = allIncomingAssociationInstances(core, mm_id, "transformInput")
+
+			while (set_len(links) > 0):
+				link = set_pop(links)
+				if (value_eq(read_attribute(core, link, "name"), tag)):
+					// Correct tag, so make transformation a possibility
+					set_add(subresult, readAssociationSource(core, link))
+		else:
+			return "Model not found: " + mm!
 
-			result = set_overlap(onSource, onTarget)
+		// Got a set of subresults now, which we use to find the overlap
+		result = set_overlap(result, subresult)
 
-			String r
-			r = "Success: "
-			while (set_len(result) > 0):
-				transformation = set_pop(result)
-				if (allow_read(current_user_id, transformation)):
-					r = r + string_join(full_name(transformation), "\n")
-			return r!
+	keys = dict_keys(target_dict)
+	while (set_len(keys) > 0):
+		subresult = set_create()
+		tag = set_pop(keys)
+		mm = target_dict[tag]
+		mm_id = get_entry_id(mm)
+
+		if (mm_id != ""):
+			links = allIncomingAssociationInstances(core, mm_id, "transformOutput")
+
+			while (set_len(links) > 0):
+				link = set_pop(links)
+				if (value_eq(read_attribute(core, link, "name"), tag)):
+					// Correct tag, so make transformation a possibility
+					set_add(subresult, readAssociationSource(core, link))
 		else:
-			return "Model not found: " + target_name!
-	else:
-		return "Model not found: " + source_name!
+			return "Model not found: " + mm!
+
+		// Got a set of subresults now, which we use to find the overlap
+		result = set_overlap(result, subresult)
 
-String function cmd_model_render(model_name : String, mapper_name : String):
+	String r
+	String transformation
+	r = "Success: "
+	while (set_len(result) > 0):
+		transformation = set_pop(result)
+		if (allow_read(current_user_id, transformation)):
+			r = r + string_join(full_name(transformation), "\n")
+	return r!
+
+String function cmd_model_rendered(model_name : String, mapper_name : String):
+	Element trace_links
+	String rendered
+	String trace_link_id
+	String allowed_rendered
+	Element trace_links_2
+	String trace_link_2_id
+
+	allowed_rendered = "Success: "
+	trace_links = allOutgoingAssociationInstances(core, get_entry_id(model_name), "tracability")
+	while (set_len(trace_links) > 0):
+		trace_link_id = set_pop(trace_links)
+		if (value_eq(read_attribute(core, trace_link_id, "type"), "CS_perceptualized")):
+			rendered = readAssociationDestination(core, trace_link_id)
+			trace_links_2 = allOutgoingAssociationInstances(core, rendered, "tracability")
+			while (set_len(trace_links_2) > 0):
+				trace_link_2_id = set_pop(trace_links_2)
+				if (value_eq(read_attribute(core, readAssociationDestination(core, trace_link_2_id), "name"), mapper_name)):
+					if (value_eq(read_attribute(core, trace_link_2_id, "type"), "CS_mapper")):
+						allowed_rendered = string_join(allowed_rendered, read_attribute(core, rendered, "name")) + "\n"
+
+	return allowed_rendered!
+
+String function cmd_model_render(model_name : String, mapper_name : String, rendered_name : String):
 	String model_ID
 	String mapper_ID
-	String rendered_name
 	String tracability_name
 	Element inputs
 	Element output_map
@@ -1079,6 +1135,12 @@ String function cmd_model_render(model_name : String, mapper_name : String):
 	Element out_links
 	String link
 
+	String ID_rendered_M
+	String ID_rendered_MM
+	String ID_SCD
+	String ID_tracability_MM
+	String ID_tracability_M
+
 	model_ID = get_entry_id(model_name)
 
 	if (model_ID != ""):
@@ -1089,8 +1151,7 @@ String function cmd_model_render(model_name : String, mapper_name : String):
 				if (allow_read(current_user_id, mapper_ID)):
 					// Everything is fine; start the actual operation
 					// Find metamodel to render to
-					rendered_name = ("rendered/" + model_name) + mapper_name
-					tracability_name = ("tracability/" + model_name) + mapper_name
+					tracability_name = "tracability/" + rendered_name
 
 					// Take the abstract syntax model and the previously rendered model
 					inputs = dict_create()
@@ -1104,24 +1165,33 @@ String function cmd_model_render(model_name : String, mapper_name : String):
 						link = set_pop(out_links)
 						dict_add(output_map, read_attribute(core, link, "name"), full_name(readAssociationDestination(core, link)))
 
-					if (get_entry_id(rendered_name) == ""):
+					ID_rendered_MM = get_entry_id(output_map["rendered"])
+					ID_SCD = get_entry_id("formalisms/SimpleClassDiagrams")
+					ID_tracability_MM = get_entry_id("formalisms/Tracability")
+					ID_tracability_M = get_entry_id(tracability_name)
+					ID_rendered_M = get_entry_id(rendered_name)
+
+					if (ID_rendered_M == ""):
 						// Instantiate
 						Element rendered
-						rendered = get_full_model(get_entry_id(output_map["rendered"]), get_entry_id("formalisms/SimpleClassDiagrams"))
+						rendered = get_full_model(ID_rendered_MM, ID_SCD)
 						if (element_eq(rendered, read_root())):
-							return "Rendered metamodel cannot conform to formalisms/SimpleClassDiagrams"!
+							return "Rendered metamodel doesn't conform to formalisms/SimpleClassDiagrams"!
 
 						rendered_model = instantiate_model(rendered)
 
-						model_create(rendered_model, rendered_name, get_entry_id(output_map["rendered"]), "Model")
+						model_create(rendered_model, rendered_name, ID_rendered_MM, "Model")
 						
 						// Tracability model won't exist either
-						tracability_model = instantiate_model(get_full_model(get_entry_id("formalisms/Tracability"), get_entry_id("formalisms/SimpleClassDiagrams")))
-						model_create(tracability_model, tracability_name, get_entry_id("formalisms/Tracability"), "Model")
+						tracability_model = instantiate_model(get_full_model(ID_tracability_MM, ID_SCD))
+						model_create(tracability_model, tracability_name, ID_tracability_MM, "Model")
+
+						ID_rendered_M = get_entry_id(rendered_name)
+						ID_tracability_M = get_entry_id(tracability_name)
 
 					else:
 						// Read out tracability model
-						tracability_model = get_full_model(get_entry_id(tracability_name), get_entry_id("formalisms/Tracability"))
+						tracability_model = get_full_model(ID_tracability_M, ID_tracability_MM)
 						if (element_eq(tracability_model, read_root())):
 							return "Tracability model not typed by Tracability metamodel: " + tracability_name!
 
@@ -1129,17 +1199,24 @@ String function cmd_model_render(model_name : String, mapper_name : String):
 					result = execute_operation(mapper_ID, inputs, tracability_model)
 
 					// Overwrite the previous rendered model
-					model_overwrite(result["rendered"], get_entry_id(rendered_name), get_entry_id(output_map["rendered"]))
-					model_overwrite(result["abstract"], get_entry_id(model_name), get_entry_id(output_map["abstract"]))
+					model_overwrite(result["rendered"], ID_rendered_M, ID_rendered_MM)
 
 					// Tracability updated in-place
-					model_overwrite(tracability_model, get_entry_id(tracability_name), get_entry_id("formalisms/Tracability"))
-					tracability_model = get_full_model(get_entry_id(tracability_name), get_entry_id("formalisms/Tracability"))
+					model_overwrite(tracability_model, ID_tracability_M, ID_tracability_MM)
 					if (element_eq(tracability_model, read_root())):
 						return "Tracability model not typed by Tracability metamodel: " + tracability_name!
 
-					// Also output the resulting model
-					return "Success: " + JSON_print(get_full_model(get_entry_id(rendered_name), get_entry_id(output_map["rendered"])))!
+					// Link all information in the megamodel
+					String tr_link
+					tr_link = instantiate_link(core, "tracability", "", get_entry_id(model_name), ID_rendered_M)
+					instantiate_attribute(core, tr_link, "type", "CS_perceptualized")
+					tr_link = instantiate_link(core, "tracability", "", ID_rendered_M, get_entry_id(mapper_name))
+					instantiate_attribute(core, tr_link, "type", "CS_mapper")
+
+					// Output the resulting model
+					String value
+					value = JSON_print(result["rendered"])
+					return ("Success: " + value) !
 				else:
 					return "Permission denied to model: " + mapper_name!
 			else:
@@ -1149,6 +1226,13 @@ String function cmd_model_render(model_name : String, mapper_name : String):
 	else:
 		return "Model not found: " + model_name!
 
+	//trace_links = allOutgoingAssociationInstances(core, operation_id, "tracability")
+	//merged_metamodel_id = ""
+	//while (set_len(trace_links) > 0):
+	//	trace_link_id = set_pop(trace_links)
+	//	if (value_eq(read_attribute(core, trace_link_id, "type"), "operatesOn")):
+	//		merged_metamodel_id = readAssociationDestination(core, trace_link_id)
+
 String function cmd_transformation_execute(transformation_name : String, source_models : Element, target_models : Element, tracability_name : String):
 	// Execute a transformation, whatever type it is
 	// First we detect the type, so we know how to prepare for invocation
@@ -1205,26 +1289,24 @@ String function cmd_transformation_execute(transformation_name : String, source_
 					assoc_name = read_attribute(core, target, "name")
 					if (dict_in(target_models, assoc_name)):
 						target_model_name = target_models[assoc_name]
-					else:
-						return "Target model not bound: " + assoc_name!
 
-					if (get_entry_id(target_model_name) == ""):
-						// Doesn't exist yet, so we can easily create
-						if (get_entry_id(get_foldername(target_model_name)) == ""):
-							return "Folder not found: " + get_foldername(target_model_name)!
+						if (get_entry_id(target_model_name) == ""):
+							// Doesn't exist yet, so we can easily create
+							if (get_entry_id(get_foldername(target_model_name)) == ""):
+								return "Folder not found: " + get_foldername(target_model_name)!
+							else:
+								if (allow_write(current_user_id, get_entry_id(get_foldername(target_model_name)))):
+									dict_add(output_map, assoc_name, full_name(readAssociationDestination(core, target)))
+									dict_add(outputs, assoc_name, target_model_name)
+								else:
+									return "Permission denied to folder: " + get_foldername(target_model_name)!
 						else:
-							if (allow_write(current_user_id, get_entry_id(get_foldername(target_model_name)))):
-								dict_add(output_map, assoc_name, full_name(readAssociationDestination(core, target)))
+							// Already exists, so we need to check for write access
+							if (allow_write(current_user_id, get_entry_id(target_model_name))):
+								dict_add(output_map, assoc_name, read_attribute(core, readAssociationDestination(core, target), "name"))
 								dict_add(outputs, assoc_name, target_model_name)
 							else:
-								return "Permission denied to folder: " + get_foldername(target_model_name)!
-					else:
-						// Already exists, so we need to check for write access
-						if (allow_write(current_user_id, get_entry_id(target_model_name))):
-							dict_add(output_map, assoc_name, read_attribute(core, readAssociationDestination(core, target), "name"))
-							dict_add(outputs, assoc_name, target_model_name)
-						else:
-							return "Permission denied to model: " + target_model_name!
+								return "Permission denied to model: " + target_model_name!
 
 				if (read_type(core, transformation_id) == "ActionLanguage"):
 					output("Success: ready for AL execution")
@@ -1348,7 +1430,6 @@ String function cmd_model_modify(model_name : String, metamodel_name : String):
 				new_model = get_full_model(model_id, get_entry_id(metamodel_name))
 				if (element_eq(new_model, read_root())):
 					return "No conformance relation can be found between these models"!
-				output("Success")
 				modify(new_model, allow_write(current_user_id, model_id))
 				if (allow_write(current_user_id, model_id)):
 					// Overwrite the modified model
@@ -1436,7 +1517,7 @@ String function cmd_model_list_full(location : String):
 					name = get_filename(read_attribute(core, m, "name")) + "/"
 				else:
 					name = get_filename(read_attribute(core, m, "name"))
-				result = result + (((((((permissions + " ") + owner) + " ") + group) + " ") + name) + "\n")
+				result = result + permissions + " " + owner + " " + group + " " + name + "\n"
 			return result!
 		else:
 			return "Permission denied to folder: " + location!
@@ -1530,7 +1611,9 @@ String function transformation_add(source_models : Element, target_models : Elem
 		// New location is available, so write
 		if (bool_not(bool_and(dict_len(source_models) == 0, dict_len(target_models) == 0))):
 			merged_formalism = model_fuse(formalism_map)
+			model_create(merged_formalism, "merged/" + operation_name, get_entry_id("formalisms/SimpleClassDiagrams"), "Model")
 			modify(merged_formalism, True)
+			model_overwrite(merged_formalism, get_entry_id("merged/" + operation_name), get_entry_id("formalisms/SimpleClassDiagrams"))
 
 		if (operation_type == "manual"):
 			// Finished with all information, now create the model itself!
@@ -1555,7 +1638,6 @@ String function transformation_add(source_models : Element, target_models : Elem
 			model_id = get_entry_id(operation_name)
 
 		if (bool_not(bool_and(dict_len(source_models) == 0, dict_len(target_models) == 0))):
-			model_create(merged_formalism, "merged/" + operation_name, get_entry_id("formalisms/SimpleClassDiagrams"), "Model")
 			merged_formalism_id = get_entry_id("merged/" + operation_name)
 
 			// Add tracability links at this level
@@ -1671,7 +1753,9 @@ String function cmd_transformation_add_MT(source_models : Element, target_models
 			return "Model not found: " + name!
 
 	merged_formalism = model_fuse(to_ramify)
+	model_create(merged_formalism, "merged/" + operation_name, get_entry_id("formalisms/SimpleClassDiagrams"), "Model")
 	modify(merged_formalism, True)
+	model_overwrite(merged_formalism, get_entry_id("merged/" + operation_name), get_entry_id("formalisms/SimpleClassDiagrams"))
 
 	ramified_metamodel = ramify(merged_formalism)
 	model_create(ramified_metamodel, "RAMified/" + operation_name, get_entry_id("formalisms/SimpleClassDiagrams"), "Model")
@@ -2022,12 +2106,12 @@ String function cmd_transformation_signature(transformation_name : String):
 				inputs = allOutgoingAssociationInstances(core, model_id, "transformInput")
 				while (set_len(inputs) > 0):
 					elem = set_pop(inputs)
-					result = string_join(string_join(string_join(string_join("I ", read_attribute(core, elem, "name")), " "), full_name(readAssociationDestination(core, elem))), "\n")
+					result = string_join(string_join(string_join(string_join(result + "I ", read_attribute(core, elem, "name")), " "), full_name(readAssociationDestination(core, elem))), "\n")
 
 				outputs = allOutgoingAssociationInstances(core, model_id, "transformOutput")
 				while (set_len(outputs) > 0):
 					elem = set_pop(outputs)
-					result = string_join(string_join(string_join(string_join("O ", read_attribute(core, elem, "name")), " "), full_name(readAssociationDestination(core, elem))), "\n")
+					result = string_join(string_join(string_join(string_join(result + "O ", read_attribute(core, elem, "name")), " "), full_name(readAssociationDestination(core, elem))), "\n")
 
 				return result!
 			else:
@@ -2050,33 +2134,12 @@ String function cmd_model_types(model_name : String):
 		types = allAssociationDestinations(core, model_id, "instanceOf")
 		while (set_len(types) > 0):
 			type = set_pop(types)
-			log("Checking type: " + cast_value(type))
-			log("Name attr: " + cast_value(read_attribute(core, type, "name")))
 			result = string_join(result, full_name(type) + "\n")
 
 		return result!
 	else:
 		return "No such model: " + model_name!
 	
-String function cmd_element_list_nice(model_name : String, metamodel_name : String):
-	if (get_entry_id(model_name) != ""):
-		if (allow_read(current_user_id, get_entry_id(model_name))):
-			if (get_entry_id(metamodel_name) != ""):
-				if (allow_read(current_user_id, get_entry_id(metamodel_name))):
-					Element mm
-					mm = get_full_model(get_entry_id(model_name), get_entry_id(metamodel_name))
-					if (element_eq(mm, read_root())):
-						return "No conformance relation between these models"!
-					return "Success: " + JSON_print(mm)!
-				else:
-					return "Permission denied to model: " + metamodel_name!
-			else:
-				return "No such metamodel: " + metamodel_name!
-		else:
-			return "Permission denied to model: " + model_name!
-	else:
-		return "No such model: " + model_name!
-
 String function cmd_folder_create(folder_name : String):
 	if (get_entry_id(folder_name) == ""):
 		if (element_neq(create_folders(current_user_id, folder_name), read_root())):
@@ -2086,6 +2149,12 @@ String function cmd_folder_create(folder_name : String):
 	else:
 		return "Folder alreay exists: " + folder_name!
 
+String function cmd_conformance_add(model_name : String, metamodel_name : String):
+	// Create a new instanceOf relation now
+	String instance_of
+	instance_of = instantiate_link(core, "instanceOf", "", get_entry_id(model_name), get_entry_id(metamodel_name))
+	return "Success"!
+
 Void function user_function_skip_init(user_id : String):
 	String cmd
 	String result
@@ -2116,13 +2185,15 @@ Void function user_function_skip_init(user_id : String):
 		if (cmd == "help"):
 			output(cmd_help())
 		elif (cmd == "model_add"):
-			output(cmd_model_add(single_input("Model type?"), single_input("Model name?")))
+			output(cmd_model_add(single_input("Model type?"), single_input("Model name?"), single_input("Model textual representation?")))
 		elif (cmd == "process_execute"):
 			output(cmd_process_execute(single_input("Process to execute?"), single_input("Model prefix to use?")))
 		elif (cmd == "transformation_between"):
-			output(cmd_transformation_between(single_input("Source type?"), single_input("Target type?")))
+			output(cmd_transformation_between(dict_input("Source signature?"), dict_input("Target signature?")))
 		elif (cmd == "model_render"):
-			output(cmd_model_render(single_input("Model name?"), single_input("Mapper name?")))
+			output(cmd_model_render(single_input("Model name?"), single_input("Mapper name?"), single_input("Rendered name?")))
+		elif (cmd == "model_rendered"):
+			output(cmd_model_rendered(single_input("Model name?"), single_input("Mapper name?")))
 		elif (cmd == "transformation_execute"):
 			output(cmd_transformation_execute(single_input("Transformation name?"), dict_input("Source models?"), dict_input("Target models?"), single_input("Tracability model?")))
 		elif (cmd == "verify"):
@@ -2175,8 +2246,6 @@ Void function user_function_skip_init(user_id : String):
 			output(cmd_user_password(single_input("User name?"), single_input("New password?")))
 		elif (cmd == "transformation_read_signature"):
 			output(cmd_transformation_signature(single_input("Transformation name?")))
-		elif (cmd == "element_list_nice"):
-			output(cmd_element_list_nice(single_input("Model name?"), single_input("Metamodel name?")))
 		elif (cmd == "verbose"):
 			set_verbose(True)
 		elif (cmd == "quiet"):
@@ -2185,6 +2254,9 @@ Void function user_function_skip_init(user_id : String):
 			// Delete user from Core Formalism
 			model_delete_element(core, user_id)
 			return !
+		elif (cmd == "user_logout"):
+			// TODO
+			cmd = "FAIL"
 		elif (cmd == "exit"):
 			// Exit by actually removing the user and decoupling it from all of its models
 			// Restarting with the same user name will NOT grant you access to anything of the previous user with that same name
@@ -2194,7 +2266,7 @@ Void function user_function_skip_init(user_id : String):
 			output(cmd_folder_create(single_input("Folder name?")))
 		elif (cmd == "add_conformance"):
 			// TODO
-			cmd = "FAIL"
+			output(cmd_conformance_add(single_input("Model name?"), single_input("Metamodel name?")))
 		elif (cmd == "remove_conformance"):
 			// TODO
 			cmd = "FAIL"

+ 13 - 0
bootstrap/metamodels.alt

@@ -120,6 +120,18 @@ Element function initialize_SCD(location : String):
 	model_define_attribute(scd, "Association", "target_upper_cardinality", True, "Natural")
 	model_define_attribute(scd, "ComplexAttribute", "type", False, "Location")
 
+	model_define_attribute(scd, "Class", "name", False, "String")
+	model_define_attribute(scd, "SimpleAttribute", "name", False, "String")
+	model_define_attribute(scd, "Association", "name", False, "String")
+	instantiate_attribute(scd, "Class", "name", "Class")
+	instantiate_attribute(scd, "Association", "name", "Association")
+	instantiate_attribute(scd, "SimpleAttribute", "name", "SimpleAttribute")
+	instantiate_attribute(scd, "Location", "name", "Location")
+	instantiate_attribute(scd, "Natural", "name", "Natural")
+	instantiate_attribute(scd, "Boolean", "name", "Boolean")
+	instantiate_attribute(scd, "Inheritance", "name", "Inheritance")
+	instantiate_attribute(scd, "AttributeLink", "name", "AttributeLink")
+
 	// Export already, to allow AL to pick it up
 	export_node(location, scd)
 	// Add in the Action Language metamodel
@@ -139,6 +151,7 @@ Element function initialize_SCD(location : String):
 
 	instantiate_node(scd, "Class", "GlobalConstraint")
 	model_define_attribute(scd, "GlobalConstraint", "global_constraint", False, "ActionLanguage")
+	instantiate_attribute(scd, "GlobalConstraint", "name", "GlobalConstraint")
 
 	dict_overwrite(scd, "types", get_type_mapping(scd))
 

+ 154 - 24
bootstrap/mini_modify.alc

@@ -8,6 +8,7 @@ include "metamodels.alh"
 include "modelling.alh"
 include "typing.alh"
 include "compiler.alh"
+include "utils.alh"
 
 Boolean verbose = True
 
@@ -30,10 +31,10 @@ String function pretty_print(model : Element):
 		type = read_type(model["metamodel"], read_type(model, v_m))
 
 		if (bool_or(type == "Class", type == "Association")):
-			result = result + ((("  " + v_m) + " : ") + read_type(model, v_m))
+			result = result + "  " + v_m + " : " + read_type(model, v_m)
 			result = result + "\n"
 			if (type == "Association"):
-				result = result + ((("    " + reverseKeyLookup(model["model"], read_edge_src(model["model"][v_m]))) + " --> ") + reverseKeyLookup(model["model"], read_edge_dst(model["model"][v_m])))
+				result = result + "    " + reverseKeyLookup(model["model"], read_edge_src(model["model"][v_m])) + " --> " + reverseKeyLookup(model["model"], read_edge_dst(model["model"][v_m]))
 				result = result + "\n"
 
 			// Print all incoming associations
@@ -54,11 +55,11 @@ String function pretty_print(model : Element):
 
 			// Defines attributes
 			result = result + "    Defines attributes:\n"
-			attr_list = getInstantiatableAttributes(model, v_m)
+			attr_list = getInstantiatableAttributes(model, v_m, "AttributeLink")
 			attr_keys = dict_keys(attr_list)
 			while (set_len(attr_keys) > 0):
 				attr_key = set_pop(attr_keys)
-				result = result + (((("      " + attr_key) + " : ") + cast_value(attr_list[attr_key])))
+				result = result + "      " + attr_key + " : " + cast_value(attr_list[attr_key])
 				result = result + "\n"
 
 			// Has attributes
@@ -68,9 +69,9 @@ String function pretty_print(model : Element):
 			while (set_len(attr_keys) > 0):
 				attr_key = set_pop(attr_keys)
 				if (element_eq(read_attribute(model, v_m, attr_key), read_root())):
-					result = result + (((("      " + cast_value(attr_key)) + " : ") + cast_value(attr_list[attr_key])) + " = (undefined)")
+					result = result + "      " + cast_string(attr_key) + " : " + cast_string(attr_list[attr_key]) + " = (undefined)"
 				else:
-					result = result + ((((("      " + cast_value(attr_key)) + " : ") + cast_value(attr_list[attr_key])) + " = ") + cast_value(read_attribute(model, v_m, attr_key)))
+					result = result + "      " + cast_string(attr_key) + " : " + cast_string(attr_list[attr_key]) + " = " + cast_value(read_attribute(model, v_m, attr_key))
 				result = result + "\n"
 		else:
 			log("Skip instance: " + type)
@@ -161,7 +162,7 @@ String function cmd_define_attribute(write : Boolean, model : Element, element_n
 		if (dict_in(model["model"], element_name)):
 			if (dict_in(model["model"], type)):
 				Element attrs
-				attrs = getAttributeList(model, element_name)
+				attrs = getInstantiatableAttributes(model, element_name, "AttributeLink")
 				if (bool_not(set_in(dict_keys(attrs), attr_name))):
 					model_define_attribute(model, element_name, attr_name, False, type)
 					return "Success"!
@@ -225,6 +226,99 @@ String function cmd_attr_del(write : Boolean, model : Element, element_name : St
 	else:
 		return "Permission denied to write"!
 
+String function cmd_attr_name(write : Boolean, model : Element, element_name : String, attr_name : String, new_attr_name : String):
+	if (write):
+		if (dict_in(model["model"], element_name)):
+			Element attrs
+			attrs = getInstantiatableAttributes(model, element_name, "AttributeLink")
+			if (set_in(dict_keys(attrs), attr_name)):
+				if (set_in(dict_keys(attrs), attr_name)):
+					if (bool_not(set_in(dict_keys(attrs), new_attr_name))):
+						Boolean optional
+						String attr_edge
+						attr_edge = reverseKeyLookup(model["model"], dict_read_edge(model["model"][element_name], attr_name))
+						optional = read_attribute(model, attr_edge, "optional")
+						model_undefine_attribute(model, element_name, attr_name)
+						model_define_attribute_ID(model, element_name, new_attr_name, optional, attrs[attr_name], attr_edge)
+						return "Success"!
+					else:
+						return "Attribute already defined: " + new_attr_name!
+				else:
+					return "Attribute not defined: " + attr_name!
+			else:
+				return "Attribute not found: " + attr_name!
+		else:
+			return "Element not found: " + element_name!
+	else:
+		return "Permission denied to write"!
+
+String function cmd_attr_type(write : Boolean, model : Element, element_name : String, attr_name : String, new_attr_type : String):
+	if (write):
+		if (dict_in(model["model"], element_name)):
+			Element attrs
+			attrs = getInstantiatableAttributes(model, element_name, "AttributeLink")
+			if (set_in(dict_keys(attrs), attr_name)):
+				if (set_in(dict_keys(attrs), attr_name)):
+					Boolean optional
+					String attr_edge
+					attr_edge = reverseKeyLookup(model["model"], dict_read_edge(model["model"][element_name], attr_name))
+					optional = read_attribute(model, attr_edge, "optional")
+					model_undefine_attribute(model, element_name, attr_name)
+					model_define_attribute_ID(model, element_name, attr_name, optional, new_attr_type, attr_edge)
+					return "Success"!
+				else:
+					return "Attribute not defined: " + attr_name!
+			else:
+				return "Attribute not found: " + attr_name!
+		else:
+			return "Element not found: " + element_name!
+	else:
+		return "Permission denied to write"!
+
+String function cmd_attr_optional(write : Boolean, model : Element, element_name : String, attr_name : String, optional : Boolean):
+	if (write):
+		if (dict_in(model["model"], element_name)):
+			Element attrs
+			attrs = getInstantiatableAttributes(model, element_name, "AttributeLink")
+			if (set_in(dict_keys(attrs), attr_name)):
+				if (set_in(dict_keys(attrs), attr_name)):
+					String attr_edge
+					attr_edge = reverseKeyLookup(model["model"], dict_read_edge(model["model"][element_name], attr_name))
+					if (optional):
+						log("Setting to optional")
+					else:
+						log("Setting to mandatory")
+					model_undefine_attribute(model, element_name, attr_name)
+					model_define_attribute_ID(model, element_name, attr_name, optional, attrs[attr_name], attr_edge)
+					return "Success"!
+				else:
+					return "Attribute not defined: " + attr_name!
+			else:
+				return "Attribute not found: " + attr_name!
+		else:
+			return "Element not found: " + element_name!
+	else:
+		return "Permission denied to write"!
+
+String function cmd_undefine_attribute(write : Boolean, model : Element, element_name : String, attr_name : String):
+	if (write):
+		if (dict_in(model["model"], element_name)):
+			Element attrs
+			attrs = getInstantiatableAttributes(model, element_name, "AttributeLink")
+			if (set_in(dict_keys(attrs), attr_name)):
+				if (set_in(dict_keys(attrs), attr_name)):
+					model_undefine_attribute(model, element_name, attr_name)
+					log("REMOVE OK")
+					return "Success"!
+				else:
+					return "Attribute not defined: " + attr_name!
+			else:
+				return "Attribute not found: " + attr_name!
+		else:
+			return "Element not found: " + element_name!
+	else:
+		return "Permission denied to write"!
+
 String function cmd_delete(write : Boolean, model : Element, element_name : String):
 	if (write):
 		if (dict_in(model["model"], element_name)):
@@ -248,7 +342,7 @@ String function cmd_list(model : Element):
 		// Filter out anonymous objects
 		if (bool_not(string_startswith(v_m, "__"))):
 			typename = read_type(model, v_m)
-			result = (result + ((("  " + v_m) + " : ") + typename)) + "\n"
+			result = result + "  " + v_m + " : " + typename + "\n"
 	
 	return result!
 
@@ -264,7 +358,7 @@ String function cmd_list_full(model : Element):
 		v_m = set_pop(keys_m)
 		// Filter out anonymous objects
 		typename = read_type(model, v_m)
-		result = (result + ((("  " + v_m) + " : ") + typename)) + "\n"
+		result = result + "  " + v_m + " : " + typename + "\n"
 	
 	return result!
 
@@ -318,25 +412,49 @@ String function cmd_read(model : Element, element_name : String):
 
 	result = "Success: "
 	if (dict_in(model["model"], element_name)):
-		result = ((result + "ID: ") + element_name) + "\n"
-		result = ((result + "Type: ") + read_type(model, element_name)) + "\n"
+		result = result + "Type: " + read_type(model, element_name) + "\n"
 		if (is_edge(model["model"][element_name])):
-			result = ((result + "Source: ") + reverseKeyLookup(model["model"], read_edge_src(model["model"][element_name]))) + "\n"
-			result = ((result + "Destination: ") + reverseKeyLookup(model["model"], read_edge_dst(model["model"][element_name]))) + "\n"
+			result = result + "Source: " + reverseKeyLookup(model["model"], read_edge_src(model["model"][element_name])) + "\n"
+			result = result + "Destination: " + reverseKeyLookup(model["model"], read_edge_dst(model["model"][element_name])) + "\n"
 		if (has_value(model["model"][element_name])):
-			result = ((result + "Value: ") + cast_value(model["model"][element_name])) + "\n"
-		result = result + "Defines attributes:\n"
-		attr_list = getInstantiatableAttributes(model, element_name)
+			result = result + "Value: " + cast_value(model["model"][element_name]) + "\n"
+		return result!
+	else:
+		return "Element not found: " + element_name!
+
+String function cmd_read_attrs(model : Element, element_name : String):
+	String result
+	Element attr_list
+	Element attr_keys
+	String attr_key
+
+	result = "Success: "
+	if (dict_in(model["model"], element_name)):
+		attr_list = getAttributeList(model, element_name)
 		attr_keys = dict_keys(attr_list)
 		while (0 < set_len(attr_keys)):
 			attr_key = set_pop(attr_keys)
-			result = ((((result + "  ") + attr_key) + " : ") + cast_value(attr_list[attr_key])) + "\n"
-		result = result + "Attributes:\n"
-		attr_list = getAttributeList(model, element_name)
+			result = string_join(result, attr_key) + " : " + cast_value(attr_list[attr_key]) + " = " + cast_value(read_attribute(model, element_name, attr_key)) + "\n"
+		return result!
+	else:
+		return "Element not found: " + element_name!
+
+String function cmd_read_defined_attrs(model : Element, element_name : String):
+	String result
+	Element attr_list
+	Element attr_keys
+	String attr_key
+
+	result = "Success: "
+	if (dict_in(model["model"], element_name)):
+		attr_list = getInstantiatableAttributes(model, element_name, "AttributeLink")
 		attr_keys = dict_keys(attr_list)
 		while (0 < set_len(attr_keys)):
 			attr_key = set_pop(attr_keys)
-			result = ((((((result + "  ") + cast_value(attr_key)) + " : ") + cast_value(attr_list[attr_key])) + " = ") + cast_value(read_attribute(model, element_name, attr_key))) + "\n"
+			if (value_eq(read_attribute(model, reverseKeyLookup(model["model"], dict_read_edge(model["model"][element_name], attr_key)), "optional"), True)):
+				result = string_join(result, attr_key) + " ?: " + cast_value(attr_list[attr_key]) + "\n"
+			else:
+				result = string_join(result, attr_key) + " : " + cast_value(attr_list[attr_key]) + "\n"
 		return result!
 	else:
 		return "Element not found: " + element_name!
@@ -351,7 +469,7 @@ String function cmd_types(model : Element):
 	while (set_len(keys_t) > 0):
 		v_t = set_pop(keys_t)
 		if (bool_not(string_startswith(v_t, "__"))):
-			result = (result + string_join(("  " + v_t) + " : ", read_type(model["metamodel"], v_t))) + "\n"
+			result = result + string_join("  " + v_t + " : ", read_type(model["metamodel"], v_t)) + "\n"
 
 	return result!
 
@@ -421,7 +539,13 @@ Element function modify(model : Element, write : Boolean):
 		elif (cmd == "attr_add_code"):
 			output(cmd_attr_add_code(write, model, single_input("Name?"), single_input("Attribute name?")))
 		elif (cmd == "attr_del"):
-			output(cmd_attr_del(write, model, single_input("Name?"), single_input("Attribute_name?")))
+			output(cmd_attr_del(write, model, single_input("Name?"), single_input("Attribute name?")))
+		elif (cmd == "attr_name"):
+			output(cmd_attr_name(write, model, single_input("Name?"), single_input("Attribute name?"), single_input("New name?")))
+		elif (cmd == "attr_type"):
+			output(cmd_attr_type(write, model, single_input("Name?"), single_input("Attribute name?"), single_input("Type name?")))
+		elif (cmd == "attr_optional"):
+			output(cmd_attr_optional(write, model, single_input("Name?"), single_input("Attribute name?"), input()))
 		elif (cmd == "delete"):
 			output(cmd_delete(write, model, single_input("Name?")))
 		elif (cmd == "nice_list"):
@@ -430,14 +554,18 @@ Element function modify(model : Element, write : Boolean):
 			output(cmd_list(model))
 		elif (cmd == "list_full"):
 			output(cmd_list_full(model))
+		elif (cmd == "JSON"):
+			output("Success: " + JSON_print(model))
 		elif (cmd == "read_outgoing"):
 			output(cmd_read_outgoing(model, single_input("Name?"), single_input("Type?")))
 		elif (cmd == "read_incoming"):
 			output(cmd_read_incoming(model, single_input("Name?"), single_input("Type?")))
 		elif (cmd == "read"):
 			output(cmd_read(model, single_input("Name?")))
-		elif (cmd == "verify"):
-			output("Success: " + conformance_scd(model))
+		elif (cmd == "read_attrs"):
+			output(cmd_read_attrs(model, single_input("Name?")))
+		elif (cmd == "read_defined_attrs"):
+			output(cmd_read_defined_attrs(model, single_input("Name?")))
 		elif (cmd == "types"):
 			output(cmd_types(model))
 		elif (cmd == "retype"):
@@ -452,6 +580,8 @@ Element function modify(model : Element, write : Boolean):
 			output(cmd_all_instances(model, single_input("Type?")))
 		elif (cmd == "define_attribute"):
 			output(cmd_define_attribute(write, model, single_input("On which element?"), single_input("Attribute name?"), single_input("Type?")))
+		elif (cmd == "undefine_attribute"):
+			output(cmd_undefine_attribute(write, model, single_input("On which element?"), single_input("Attribute name?")))
 		else:
 			output("Unknown command while modelling: " + cast_value(cmd))
 			output("Use command 'help' to get a list of available commands")

+ 36 - 0
bootstrap/modelling.alc

@@ -360,10 +360,30 @@ String function instantiate_link(model : Element, type : String, name : String,
 	return actual_name!
 
 Void function model_delete_element(model : Element, name : String):
+	// Delete all attributes
+	//Element attrs
+	//String attr
+	//attrs = dict_keys(getAttributeList(model, name))
+	//while (set_len(attrs) > 0):
+	//	attr = set_pop(attrs)
+	//	log("Remove attr " + attr)
+	//	attr = reverseKeyLookup(model["model"], read_attribute(model, name, attr))
+	//	remove_type(model, attr)
+	//	delete_element(model["model"][attr])
+
+	// Delete element itself
 	remove_type(model, name)
 	delete_element(model["model"][name])
 	return!
 
+String function model_undefine_attribute(model : Element, elem : String, attr_name : String):
+	String attr_edge
+	attr_edge = reverseKeyLookup(model["model"], dict_read_edge(model["model"][elem], attr_name))
+	unset_attribute(model, attr_edge, "name")
+	unset_attribute(model, attr_edge, "optional")
+	model_delete_element(model, attr_edge)
+	return attr_edge!
+
 String function model_define_attribute(model : Element, elem : String, name : String, optional : Boolean, type : String):
 	// Create the necessary links to make it an attribute
 	String edge_name
@@ -380,6 +400,22 @@ String function model_define_attribute(model : Element, elem : String, name : St
 
 	return edge_name!
 
+String function model_define_attribute_ID(model : Element, elem : String, name : String, optional : Boolean, type : String, ID : String):
+	// Create the necessary links to make it an attribute
+	String edge_name
+
+	edge_name = ID
+	while (dict_in(model["model"], edge_name)):
+		// Already exists, so make random name
+		edge_name = edge_name + cast_id(model["model"][elem])
+		log("Name clash detected for attribute: try new name: " + edge_name)
+
+	edge_name = instantiate_link(model, "AttributeLink", edge_name, elem, type)
+	instantiate_attribute(model, edge_name, "name", name)
+	instantiate_attribute(model, edge_name, "optional", optional)
+
+	return edge_name!
+
 Element function read_attribute(model : Element, element : String, attribute : String):
 	if (dict_in(model["model"], element)):
 		Integer i

+ 31 - 5
bootstrap/object_operations.alc

@@ -128,24 +128,50 @@ Element function getAttributeList(model : Element, element : String):
 		while (set_len(keys) > 0):
 			attr_name = set_pop(keys)
 			if (is_physical_string(attr_name)):
-				attr_type = reverseKeyLookup(mm, mm[type][attr_name])
+				if (read_type(model["metamodel"], reverseKeyLookup(mm, dict_read_edge(mm[type], attr_name))) == "AttributeLink"):
+					attr_type = reverseKeyLookup(mm, mm[type][attr_name])
+					// WARNING: do not change this to dict_add_fast, as this crashes random code...
+					dict_add(result, attr_name, attr_type)
+
+	return result!
+
+Element function getAttributes(model : Element, element : String):
+	Element result
+	Element keys
+	Element type
+	Element attr_name
+	Element types
+	Element mm
+
+	result = dict_create()
+	mm = model["metamodel"]["model"]
+	types = get_superclasses(model["metamodel"], read_type(model, element))
+
+	while (set_len(types) > 0):
+		type = set_pop(types)
+
+		// Add our own attributes
+		keys = dict_keys(mm[type])
+		while (set_len(keys) > 0):
+			attr_name = set_pop(keys)
+			if (is_physical_string(attr_name)):
 				// WARNING: do not change this to dict_add_fast, as this crashes random code...
-				dict_add(result, attr_name, attr_type)
+				dict_add(result, attr_name, read_attribute(model, element, attr_name))
 
 	return result!
 
-Element function getInstantiatableAttributes(model : Element, element : String):
+Element function getInstantiatableAttributes(model : Element, element : String, type : String):
 	Element all_links
 	Element result
 	String link
 
 	result = dict_create()
 
-	all_links = allOutgoingAssociationInstances(model, element, "Attribute")
+	all_links = allOutgoingAssociationInstances(model, element, type)
 	while (set_len(all_links) > 0):
 		link = set_pop(all_links)
 		// WARNING: do not change this to dict_add_fast, as this crashes random code...
-		dict_add(result, read_attribute(model, link, "name"), read_type(model, readAssociationDestination(model, link)))
+		dict_add(result, read_attribute(model, link, "name"), readAssociationDestination(model, link))
 
 	return result!
 

+ 29 - 7
bootstrap/pm.mvc

@@ -3,8 +3,11 @@ include "primitives.alh"
 include "object_operations.alh"
 
 SimpleClassDiagrams ProcessModel {
-    SimpleAttribute String {}
+    SimpleAttribute String {
+        name = "String"
+    }
     SimpleAttribute MvCName {
+        name = "MvCName"
         constraint = $
             String function constraint(model : Element, name : String):
                 if (bool_not(is_physical_string(model["model"][name]))):
@@ -14,17 +17,26 @@ SimpleClassDiagrams ProcessModel {
             $
     }
 
-    Class Activity {}
+    Class Activity {
+        name = "Activity"
+    }
     Class Start : Activity {
+        name = "Start"
         lower_cardinality = 1
         upper_cardinality = 1
     }
     Class Finish : Activity {
+        name = "Finish"
         lower_cardinality = 1
     }
-    Class Fork : Activity {}
-    Class Join : Activity {}
+    Class Fork : Activity {
+        name = "Fork"
+    }
+    Class Join : Activity {
+        name = "Join"
+    }
     Class Decision : Activity {
+        name = "Decision"
         constraint = $
             String function constraint(model : Element, name : String):
                 if (read_nr_out(allOutgoingAssociationInstances(model, name, "Next")) == 0):
@@ -35,23 +47,33 @@ SimpleClassDiagrams ProcessModel {
     }
 
     Class Exec : Activity {
+        name = "Exec"
         name : MvCName
     }
 
     Class Data {
+        name = "Data"
         name : MvCName
         type : MvCName
     }
 
     Association Produces (Exec, Data) {
         name : String
+        name = "Produces"
     }
     Association Consumes (Exec, Data) {
         name : String
+        name = "Consumes"
+    }
+    Association Then (Decision, Activity) {
+        name = "Then"
+    }
+    Association Else (Decision, Activity) {
+        name = "Else"
+    }
+    Association Next (Activity, Activity) {
+        name = "Next"
     }
-    Association Then (Decision, Activity) {}
-    Association Else (Decision, Activity) {}
-    Association Next (Activity, Activity) {}
 }
 
 export ProcessModel to models/ProcessModel

+ 22 - 4
bootstrap/ramify.alc

@@ -49,10 +49,12 @@ Element function ramify(model : Element):
 	//  Class LHS : LHS_Root {}
 	instantiate_node(new_model, "Class", "LHS")
 	instantiate_link(new_model, "Inheritance", "", "LHS", "LHS_Root")
+	instantiate_attribute(new_model, "LHS", "name", "LHS")
 
 	//  Class NAC : LHS_Root {}
 	instantiate_node(new_model, "Class", "NAC")
 	instantiate_link(new_model, "Inheritance", "", "NAC", "LHS_Root")
+	instantiate_attribute(new_model, "NAC", "name", "NAC")
 
 	//	Class PreElement {
 	//		label : String
@@ -73,6 +75,7 @@ Element function ramify(model : Element):
 	instantiate_node(new_model, "Class", "RHS")
 	instantiate_attribute(new_model, "RHS", "lower_cardinality", 1)
 	instantiate_attribute(new_model, "RHS", "upper_cardinality", 1)
+	instantiate_attribute(new_model, "RHS", "name", "RHS")
 	model_define_attribute(new_model, "RHS", "action", True, "ActionLanguage")
 
 	//	Class PostElement {
@@ -111,6 +114,7 @@ Element function ramify(model : Element):
 	set_add(copied_attributes, "upper_cardinality")
 	set_add(copied_attributes, "source_upper_cardinality")
 	set_add(copied_attributes, "target_upper_cardinality")
+	set_add(copied_attributes, "name")
 
 	while (list_len(keys) > 0):
 		key = list_pop(keys, 0)
@@ -130,8 +134,12 @@ Element function ramify(model : Element):
 				attr_name = set_pop(local_copied_attributes)
 				if (element_neq(read_attribute(model, key, attr_name), read_root())):
 					// Attribute was defined, so reassign in both LHS and RHS
-					instantiate_attribute(new_model, "Pre_" + key, attr_name, read_attribute(model, key, attr_name))
-					instantiate_attribute(new_model, "Post_" + key, attr_name, read_attribute(model, key, attr_name))
+					if (attr_name == "name"):
+						instantiate_attribute(new_model, "Pre_" + key, attr_name, string_join("Pre_", read_attribute(model, key, attr_name)))
+						instantiate_attribute(new_model, "Post_" + key, attr_name, string_join("Post_", read_attribute(model, key, attr_name)))
+					else:
+						instantiate_attribute(new_model, "Pre_" + key, attr_name, read_attribute(model, key, attr_name))
+						instantiate_attribute(new_model, "Post_" + key, attr_name, read_attribute(model, key, attr_name))
 
 		elif (type_name == "AttributeLink"):
 			// Got an attribute, so find out the source and name
@@ -173,8 +181,12 @@ Element function ramify(model : Element):
 				attr_name = set_pop(local_copied_attributes)
 				if (element_neq(read_attribute(model, key, attr_name), read_root())):
 					// Attribute was defined, so reassign in both LHS and RHS
-					instantiate_attribute(new_model, "Pre_" + key, attr_name, read_attribute(model, key, attr_name))
-					instantiate_attribute(new_model, "Post_" + key, attr_name, read_attribute(model, key, attr_name))
+					if (attr_name == "name"):
+						instantiate_attribute(new_model, "Pre_" + key, attr_name, string_join("Pre_", read_attribute(model, key, attr_name)))
+						instantiate_attribute(new_model, "Post_" + key, attr_name, string_join("Post_", read_attribute(model, key, attr_name)))
+					else:
+						instantiate_attribute(new_model, "Pre_" + key, attr_name, read_attribute(model, key, attr_name))
+						instantiate_attribute(new_model, "Post_" + key, attr_name, read_attribute(model, key, attr_name))
 
 		elif (type_name == "Inheritance"):
 			old_source = reverseKeyLookup(model["model"], read_edge_src(entry))
@@ -196,10 +208,12 @@ Element function ramify(model : Element):
 	//	Class Success : Entry {}
 	instantiate_node(new_model, "Class", "Success")
 	instantiate_link(new_model, "Inheritance", "", "Success", "Entry")
+	instantiate_attribute(new_model, "Success", "name", "Success")
 
 	//	Class Failure : Entry {}
 	instantiate_node(new_model, "Class", "Failure")
 	instantiate_link(new_model, "Inheritance", "", "Failure", "Entry")
+	instantiate_attribute(new_model, "Failure", "name", "Failure")
 
 	//  Class Rule : Entry {}
 	instantiate_node(new_model, "Class", "Rule")
@@ -251,20 +265,24 @@ Element function ramify(model : Element):
 	// 	Class Query : LHSRule {}
 	instantiate_node(new_model, "Class", "Query")
 	instantiate_link(new_model, "Inheritance", "", "Query", "LHSRule")
+	instantiate_attribute(new_model, "Query", "name", "Query")
 
 	//	Class Atomic : LHSRule, RHSRule {}
 	instantiate_node(new_model, "Class", "Atomic")
 	instantiate_link(new_model, "Inheritance", "", "Atomic", "LHSRule")
 	instantiate_link(new_model, "Inheritance", "", "Atomic", "RHSRule")
+	instantiate_attribute(new_model, "Atomic", "name", "Atomic")
 
 	// 	Class ForAll : LHSRule, RHSRule {}
 	instantiate_node(new_model, "Class", "ForAll")
 	instantiate_link(new_model, "Inheritance", "", "ForAll", "LHSRule")
 	instantiate_link(new_model, "Inheritance", "", "ForAll", "RHSRule")
+	instantiate_attribute(new_model, "ForAll", "name", "ForAll")
 
 	//	Class Composite : Rule {}
 	instantiate_node(new_model, "Class", "Composite")
 	instantiate_link(new_model, "Inheritance", "", "Composite", "Rule")
+	instantiate_attribute(new_model, "Composite", "name", "Composite")
 
 	//	Association Initial(Composite, Entry){
 	//		target_lower_cardinality = 1

+ 9 - 1
bootstrap/semi_primitives.alc

@@ -464,9 +464,17 @@ Element function set_difference(sa : Element, sb : Element):
 		if (set_in(sa, elem)):
 			// Shared between both
 			set_remove(result, elem)
-
 	return result!
 
+Void function set_subtract(set1 : Element, set2 : Element):
+	Element elem
+	set2 = set_copy(set2)
+	while (set_len(set2) > 0):
+		elem = set_pop(set2)
+		if (set_in(set1, elem)):
+			set_remove(set1, elem)
+	return!
+
 Element function range(max : Integer):
 	Element result
 	Integer counter

+ 1 - 1
bootstrap/services.alc

@@ -16,7 +16,7 @@ String function comm_newPort():
 	String attempt
 	attempt = "__hierarchy"
 	while (dict_in(root, attempt)):
-		attempt = (("__" + get_taskname()) + "_") + cast_value(services)
+		attempt = "__" + get_taskname() + "_" + cast_value(services)
 		services = services + 1
 
 	// Create queues

+ 5 - 1
bootstrap/tracability.mvc

@@ -3,13 +3,17 @@ import models/SimpleClassDiagrams as SimpleClassDiagrams
 include "primitives.alh"
 
 SimpleClassDiagrams Tracability {
-    SimpleAttribute String {}
+    SimpleAttribute String {
+        name = "String"
+    }
 
     Class Reference {
         name : String
+        name = "Reference"
     }
     Association TracabilityLink {
         type : String
+        name = "TracabilityLink"
     }
 }
 

+ 12 - 4
bootstrap/transform.alc

@@ -512,10 +512,18 @@ Boolean function transform_composite(host_model : Element, schedule_model : Elem
 		elif (typename == "ForAll"):
 			result = transform_forall(host_model, schedule_model, current)
 
+		Element result_set
 		if (result):
-			current = set_pop(allAssociationDestinations(schedule_model, current, "OnSuccess"))
+			result_set = allAssociationDestinations(schedule_model, current, "OnSuccess")
 		else:
-			current = set_pop(allAssociationDestinations(schedule_model, current, "OnFailure"))
+			result_set = allAssociationDestinations(schedule_model, current, "OnFailure")
+
+		if (set_len(result_set) == 0):
+			log("ERROR: no next rule found for execution result " + cast_string(result))
+		elif (set_len(result_set) > 1):
+			log("WARNING: multiple next rules found for execution result " + cast_string(result))
+			log("Picking one at random...")
+		current = set_pop(result_set)
 
 	// No longer a rule, so it is either success or failure
 	if (is_nominal_instance(schedule_model, current, "Success")):
@@ -546,7 +554,7 @@ Boolean function transform_forall(host_model : Element, schedule_model : Element
 	Element mapping
 	Boolean result
 
-	log("Executing rule: " + current)
+	//log("Executing rule: " + current)
 
 	mappings = full_match(host_model, schedule_model, current, False)
 
@@ -555,7 +563,7 @@ Boolean function transform_forall(host_model : Element, schedule_model : Element
 	else:
 		result = False
 
-	log("Matches in forall: " + cast_value(set_len(mappings)))
+	//log("Matches in forall: " + cast_string(set_len(mappings)))
 	while (set_len(mappings) > 0):
 		mapping = set_pop(mappings)
 		RHS = set_pop(allAssociationDestinations(schedule_model, current, "RHSLink"))

+ 8 - 6
bootstrap/typing.alc

@@ -81,17 +81,19 @@ String function read_type(model : Element, name : String):
 Void function retype(model : Element, element : String, type : String):
 	// Retype a model, deleting any previous type the element had
 	// The type string is evaluated in the metamodel previously specified
-	if (dict_in(model["type_mapping"], element)):
-		dict_delete(model["type_mapping"], element)
-	dict_add_fast(model["type_mapping"], element, type)
+	dict_overwrite(model["type_mapping"], element, type)
 	return!
 
 Void function new_type_mapping(model : Element):
-	if (dict_in(model, "type_mapping")):
-		dict_delete(model, "type_mapping")
-	dict_add_fast(model, "type_mapping", dict_create())
+	dict_overwrite(model, "type_mapping", dict_create())
 	return !
 
 Void function remove_type(model : Element, name : String):
 	dict_delete(model["type_mapping"], name)
+	
+	String elem
+	elem = cast_id(model["model"][name])
+	if (dict_in(model["type_mapping"], elem)):
+		dict_delete(model["type_mapping"], elem)
+
 	return !

+ 8 - 18
bootstrap/utils.alc

@@ -28,11 +28,11 @@ String function JSON_print(model : Element):
 				first = False
 
 			result = result + "{"
-			result = (((result + "\"id\": \"") + v_m) + "\"")
-			result = (((result + ",") + "\"type\": \"") + read_type(model, v_m)) + "\""
+			result = result + "\"id\": \"" + v_m + "\""
+			result = result + "," + "\"type\": \"" + read_type(model, v_m) + "\""
 			if (type == "Association"):
-				result = (((result + ", \"__source\": \"") + reverseKeyLookup(model["model"], read_edge_src(model["model"][v_m]))) + "\"")
-				result = (((result + ", \"__target\": \"") + reverseKeyLookup(model["model"], read_edge_dst(model["model"][v_m]))) + "\"")
+				result = result + ", \"__source\": \"" + reverseKeyLookup(model["model"], read_edge_src(model["model"][v_m])) + "\""
+				result = result + ", \"__target\": \"" + reverseKeyLookup(model["model"], read_edge_dst(model["model"][v_m])) + "\""
 
 			// Has attributes
 			attr_keys = dict_keys(getAttributeList(model, v_m))
@@ -40,15 +40,15 @@ String function JSON_print(model : Element):
 				attr_key = set_pop(attr_keys)
 				attr_value = read_attribute(model, v_m, attr_key)
 				if (element_eq(attr_value, read_root())):
-					result = (((result + ", \"") + attr_key) + "\": null")
+					result = result + ", \"" + attr_key + "\": null"
 				else:
 					if (is_physical_boolean(attr_value)):
 						if (attr_value):
-							result = ((result + ", \"") + attr_key) + "\": true"
+							result = result + ", \"" + attr_key + "\": true"
 						else:
-							result = ((result + ", \"") + attr_key) + "\": false"
+							result = result + ", \"" + attr_key + "\": false"
 					else:
-						result = ((((result + ", \"") + attr_key) + "\": ") + cast_value(attr_value))
+						result = result + ", \"" + attr_key + "\": " + cast_value(attr_value)
 
 			result = result + "}"
 	result = result + "]"
@@ -88,15 +88,5 @@ Void function list_extend(lst : Element, ext : Element):
 
 	return!
 
-Void function set_difference(set1 : Element, set2 : Element):
-	set2 = set_copy(set2)
-
-	Element elem
-	while (set_len(set2) > 0):
-		elem = set_pop(set2)
-		if (set_in(set1, elem)):
-			set_remove(set1, elem)
-	return!
-
 String function get_taskname():
 	return reverseKeyLookup(read_root(), read_taskroot())!

+ 1 - 1
integration/code/pdevs_client.alc

@@ -31,4 +31,4 @@ Boolean function main(model : Element):
                 comm_set(port, the_input)
             else:
                 comm_set(port, cast_value(the_input))
-        sleep(0.05)
+        sleep(0.05)

+ 10 - 2
integration/code/pn_design.mvc

@@ -1,16 +1,24 @@
-SimpleAttribute Natural {}
-SimpleAttribute String {}
+SimpleAttribute Natural {
+    name = "Natural"
+}
+SimpleAttribute String {
+    name = "String"
+}
 
 Class Place {
+    name = "Place"
     tokens : Natural
     name : String
 }
 Class Transition {
+    name = "Transition"
     name : String
 }
 Association P2T (Place, Transition) {
+    name = "P2T"
     weight : Natural
 }
 Association T2P (Transition, Place) {
+    name = "T2P"
     weight : Natural
 }

+ 106 - 0
integration/log_output.py

@@ -0,0 +1,106 @@
+"""
+Generated by Statechart compiler by Glenn De Jonghe, Joeri Exelmans, Simon Van Mierlo, and Yentl Van Tendeloo (for the inspiration)
+
+Date:   Mon Aug 28 10:53:51 2017
+
+Model author: Yentl Van Tendeloo
+Model name:   Logging
+Model description:
+For testing: append all input to a log until finished
+"""
+
+from sccd.runtime.statecharts_core import *
+
+# package "Logging"
+
+class Logging(RuntimeClassBase):
+    def __init__(self, controller, log):
+        RuntimeClassBase.__init__(self, controller)
+        
+        self.semantics.big_step_maximality = StatechartSemantics.TakeMany
+        self.semantics.internal_event_lifeline = StatechartSemantics.Queue
+        self.semantics.input_event_lifeline = StatechartSemantics.FirstComboStep
+        self.semantics.priority = StatechartSemantics.SourceParent
+        self.semantics.concurrency = StatechartSemantics.Single
+        
+        # build Statechart structure
+        self.build_statechart_structure()
+        
+        # call user defined constructor
+        Logging.user_defined_constructor(self, log)
+    
+    def user_defined_constructor(self, log):
+        self.log = log
+    
+    def user_defined_destructor(self):
+        pass
+    
+    
+    # builds Statechart structure
+    def build_statechart_structure(self):
+        
+        # state <root>
+        self.states[""] = State(0, "", self)
+        
+        # state /init
+        self.states["/init"] = State(1, "/init", self)
+        self.states["/init"].setEnter(self._init_enter)
+        self.states["/init"].setExit(self._init_exit)
+        
+        # state /finished
+        self.states["/finished"] = State(2, "/finished", self)
+        
+        # add children
+        self.states[""].addChild(self.states["/init"])
+        self.states[""].addChild(self.states["/finished"])
+        self.states[""].fixTree()
+        self.states[""].default_state = self.states["/init"]
+        
+        # transition /init
+        _init_0 = Transition(self, self.states["/init"], [self.states["/init"]])
+        _init_0.setAction(self._init_0_exec)
+        _init_0.setTrigger(Event("input", "inp"))
+        self.states["/init"].addTransition(_init_0)
+        _init_1 = Transition(self, self.states["/init"], [self.states["/init"]])
+        _init_1.setTrigger(Event("_0after"))
+        self.states["/init"].addTransition(_init_1)
+        _init_2 = Transition(self, self.states["/init"], [self.states["/finished"]])
+        _init_2.setTrigger(Event("terminate", "inp"))
+        self.states["/init"].addTransition(_init_2)
+    
+    def _init_enter(self):
+        self.addTimer(0, 0.1)
+    
+    def _init_exit(self):
+        self.removeTimer(0)
+    
+    def _init_0_exec(self, parameters):
+        value = parameters[0]
+        self.log.append(value)
+        print("Got value: " + str(value))
+    
+    def initializeStatechart(self):
+        # enter default state
+        self.default_targets = self.states["/init"].getEffectiveTargetStates()
+        RuntimeClassBase.initializeStatechart(self)
+
+class ObjectManager(ObjectManagerBase):
+    def __init__(self, controller):
+        ObjectManagerBase.__init__(self, controller)
+    
+    def instantiate(self, class_name, construct_params):
+        if class_name == "Logging":
+            instance = Logging(self.controller, construct_params[0])
+            instance.associations = {}
+        else:
+            raise Exception("Cannot instantiate class " + class_name)
+        return instance
+
+class Controller(ThreadsControllerBase):
+    def __init__(self, log, keep_running = None, behind_schedule_callback = None):
+        if keep_running == None: keep_running = True
+        if behind_schedule_callback == None: behind_schedule_callback = None
+        ThreadsControllerBase.__init__(self, ObjectManager(self), keep_running, behind_schedule_callback)
+        self.addInputPort("inp")
+        self.addOutputPort("outp")
+        self.object_manager.createInstance("Logging", [log])

+ 39 - 0
integration/log_output.xml

@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<diagram author="Yentl Van Tendeloo" name="Logging">
+    <description>
+        For testing: append all input to a log until finished
+    </description>
+
+    <inport name="inp"/>
+    <outport name="outp"/>
+
+    <class name="Logging" default="true">
+        <constructor>
+            <parameter name="log"/>
+            <body>
+                self.log = log
+            </body>
+        </constructor>
+        <scxml initial="init">
+            <state id="init">
+                <transition event="input" port="inp" target=".">
+                    <parameter name="value"/>
+                    <script>
+                        self.log.append(value)
+                        print("Got value: " + str(value))
+                    </script>
+                </transition>
+
+                <transition after="0.1" target="."/>
+
+                <transition event="terminate" port="inp" target="../finished"/>
+            </state>
+
+            <state id="finished">
+                <script>
+                    print("FINISHED")
+                </script>
+            </state>
+        </scxml>
+    </class>
+</diagram>

+ 39 - 38
integration/test_powerwindow.py

@@ -5,6 +5,7 @@ import sys
 
 sys.path.append("wrappers")
 from modelverse import *
+import threading
 
 class TestPowerWindow(unittest.TestCase):
     def setUp(self):
@@ -35,20 +36,20 @@ class TestPowerWindow(unittest.TestCase):
         transformation_add_MANUAL({"Requirements": "formalisms/Requirements", "Query": "formalisms/Query"}, {"Query": "formalisms/Query"}, "models/revise_query")
         transformation_add_MANUAL({"Requirements": "formalisms/Requirements", "Architecture": "formalisms/Architecture"}, {"Architecture": "formalisms/Architecture"}, "models/revise_architecture")
 
-        def tracability_CTRL2EPN():
-            instantiate(None, "Association", ("PW_Control/State", "Encapsulated_PetriNet/Place"), ID="CTRL2EPN_link")
-            instantiate(None, "Association", ("PW_Control/Transition", "Encapsulated_PetriNet/Transition"), ID="CTRL2EPN_tlink")
+        def tracability_CTRL2EPN(context):
+            instantiate(None, "Association", ("PW_Control/State", "Encapsulated_PetriNet/Place"), ID="CTRL2EPN_link", context=context)
+            instantiate(None, "Association", ("PW_Control/Transition", "Encapsulated_PetriNet/Transition"), ID="CTRL2EPN_tlink", context=context)
 
-        def tracability_PLANT2EPN():
-            instantiate(None, "Association", ("PW_Plant/State", "Encapsulated_PetriNet/Place"), ID="PLANT2EPN_link")
-            instantiate(None, "Association", ("PW_Plant/Transition", "Encapsulated_PetriNet/Transition"), ID="PLANT2EPN_tlink")
+        def tracability_PLANT2EPN(context):
+            instantiate(None, "Association", ("PW_Plant/State", "Encapsulated_PetriNet/Place"), ID="PLANT2EPN_link", context=context)
+            instantiate(None, "Association", ("PW_Plant/Transition", "Encapsulated_PetriNet/Transition"), ID="PLANT2EPN_tlink", context=context)
 
-        def tracability_ENV2EPN():
-            instantiate(None, "Association", ("PW_Environment/Event", "Encapsulated_PetriNet/Place"), ID="ENV2EPN_link")
+        def tracability_ENV2EPN(context):
+            instantiate(None, "Association", ("PW_Environment/Event", "Encapsulated_PetriNet/Place"), ID="ENV2EPN_link", context=context)
 
-        def tracability_EPN2PN():
-            instantiate(None, "Association", ("Encapsulated_PetriNet/Place", "PetriNet/Place"), ID="EPN2PN_transition_link")
-            instantiate(None, "Association", ("Encapsulated_PetriNet/Transition", "PetriNet/Transition"), ID="EPN2PN_place_link")
+        def tracability_EPN2PN(context):
+            instantiate(None, "Association", ("Encapsulated_PetriNet/Place", "PetriNet/Place"), ID="EPN2PN_transition_link", context=context)
+            instantiate(None, "Association", ("Encapsulated_PetriNet/Transition", "PetriNet/Transition"), ID="EPN2PN_place_link", context=context)
 
         transformation_add_MT({}, {"PW_Plant": "formalisms/PW_Plant", "PW_Environment": "formalisms/PW_Environment", "PW_Control": "formalisms/PW_Control", "Query": "formalisms/Query", "Architecture": "formalisms/Architecture", "Requirements": "formalisms/Requirements"}, "models/make_initial_models", open("models/initialize.mvc", 'r').read())
         transformation_add_MT({"PW_Plant": "formalisms/PW_Plant"}, {"Encapsulated_PetriNet": "formalisms/Encapsulated_PetriNet"}, "models/plant_to_EPN", open("models/plant_to_EPN.mvc", 'r').read(), tracability_PLANT2EPN)
@@ -66,13 +67,13 @@ class TestPowerWindow(unittest.TestCase):
         nr_of_operations = 6
 
         def get_function(filename):
-            def func():
+            def func(context):
                 global called
                 if called > nr_of_operations:
                     raise Exception("Seemingly called some operation twice!")
                 else:
                     called += 1
-                model_overwrite(None, open(filename, "r").read())
+                model_overwrite(None, open(filename, "r").read(), context=context)
             return func
 
         cb_req = get_function("models/requirements_model.mvc")
@@ -123,20 +124,20 @@ class TestPowerWindow(unittest.TestCase):
         transformation_add_MANUAL({"Requirements": "formalisms/Requirements", "Query": "formalisms/Query"}, {"Query": "formalisms/Query"}, "models/revise_query")
         transformation_add_MANUAL({"Requirements": "formalisms/Requirements", "Architecture": "formalisms/Architecture"}, {"Architecture": "formalisms/Architecture"}, "models/revise_architecture")
 
-        def tracability_CTRL2EPN():
-            instantiate(None, "Association", ("PW_Control/State", "Encapsulated_PetriNet/Place"), ID="CTRL2EPN_link")
-            instantiate(None, "Association", ("PW_Control/Transition", "Encapsulated_PetriNet/Transition"), ID="CTRL2EPN_tlink")
+        def tracability_CTRL2EPN(context):
+            instantiate(None, "Association", ("PW_Control/State", "Encapsulated_PetriNet/Place"), ID="CTRL2EPN_link", context=context)
+            instantiate(None, "Association", ("PW_Control/Transition", "Encapsulated_PetriNet/Transition"), ID="CTRL2EPN_tlink", context=context)
 
-        def tracability_PLANT2EPN():
-            instantiate(None, "Association", ("PW_Plant/State", "Encapsulated_PetriNet/Place"), ID="PLANT2EPN_link")
-            instantiate(None, "Association", ("PW_Plant/Transition", "Encapsulated_PetriNet/Transition"), ID="PLANT2EPN_tlink")
+        def tracability_PLANT2EPN(context):
+            instantiate(None, "Association", ("PW_Plant/State", "Encapsulated_PetriNet/Place"), ID="PLANT2EPN_link", context=context)
+            instantiate(None, "Association", ("PW_Plant/Transition", "Encapsulated_PetriNet/Transition"), ID="PLANT2EPN_tlink", context=context)
 
-        def tracability_ENV2EPN():
-            instantiate(None, "Association", ("PW_Environment/Event", "Encapsulated_PetriNet/Place"), ID="ENV2EPN_link")
+        def tracability_ENV2EPN(context):
+            instantiate(None, "Association", ("PW_Environment/Event", "Encapsulated_PetriNet/Place"), ID="ENV2EPN_link", context=context)
 
-        def tracability_EPN2PN():
-            instantiate(None, "Association", ("Encapsulated_PetriNet/Place", "PetriNet/Place"), ID="EPN2PN_transition_link")
-            instantiate(None, "Association", ("Encapsulated_PetriNet/Transition", "PetriNet/Transition"), ID="EPN2PN_place_link")
+        def tracability_EPN2PN(context):
+            instantiate(None, "Association", ("Encapsulated_PetriNet/Place", "PetriNet/Place"), ID="EPN2PN_transition_link", context=context)
+            instantiate(None, "Association", ("Encapsulated_PetriNet/Transition", "PetriNet/Transition"), ID="EPN2PN_place_link", context=context)
 
         transformation_add_MT({}, {"PW_Plant": "formalisms/PW_Plant", "PW_Environment": "formalisms/PW_Environment", "PW_Control": "formalisms/PW_Control", "Query": "formalisms/Query", "Architecture": "formalisms/Architecture", "Requirements": "formalisms/Requirements"}, "models/make_initial_models", open("models/initialize.mvc", 'r').read())
         transformation_add_MT({"PW_Plant": "formalisms/PW_Plant"}, {"Encapsulated_PetriNet": "formalisms/Encapsulated_PetriNet"}, "models/plant_to_EPN", open("models/plant_to_EPN.mvc", 'r').read(), tracability_PLANT2EPN)
@@ -157,16 +158,16 @@ class TestPowerWindow(unittest.TestCase):
         executed = set([])
 
         def get_function(filename, fixed=None):
-            def func():
+            def func(context):
                 global called
                 if called > len(callbacks) * 2:
                     raise Exception("Seemingly called some operation twice!")
                 called += 1
                 if filename not in executed or fixed is None:
                     executed.add(filename)
-                    model_overwrite(None, open(filename, "r").read())
+                    model_overwrite(None, open(filename, "r").read(), context=context)
                 else:
-                    model_overwrite(None, open(fixed, "r").read())
+                    model_overwrite(None, open(fixed, "r").read(), context=context)
             return func
 
         cb_req = get_function("models/requirements_model.mvc")
@@ -176,10 +177,12 @@ class TestPowerWindow(unittest.TestCase):
         cb_query = get_function("models/query_model.mvc")
         cb_arch = get_function("models/architecture_model.mvc")
 
-        self.error_path = None
-
-        def got_path(path):
-            self.error_path = path
+        import log_output
+        error_path = []
+        ctrl = log_output.Controller(error_path, keep_running=False)
+        thrd = threading.Thread(target=ctrl.start)
+        thrd.daemon = True
+        thrd.start()
 
         callbacks = {
                 "models/revise_req": cb_req,
@@ -188,17 +191,15 @@ class TestPowerWindow(unittest.TestCase):
                 "models/revise_control": cb_ctrl,
                 "models/revise_query": cb_query,
                 "models/revise_architecture": cb_arch,
-                "models/bfs": got_path,
+                "models/bfs": (ctrl, "inp", "outp"),
             }
 
-        try:
-            process_execute("models/pm_powerwindow", "pm_", callbacks)
-        except:
-            import traceback
-            print(traceback.format_exc())
+        process_execute("models/pm_powerwindow", "pm_", callbacks)
+
+        thrd.join()
 
         if called != 11:
             print(called)
             raise Exception("Not executed sufficiently:" + str(called))
 
-        assert self.error_path != None
+        assert len(error_path) == 10

+ 3 - 0
interface/HUTN/hutn_compiler/hutnparser.py

@@ -122,6 +122,9 @@ class Tree(object):
             if isinstance(f, Tree):
                 f.fix_tracability(self.inputfile)
 
+    def pretty_print(self, i=0):
+        return "\t" * i + str(self.head) + "\n" + "\n".join([("\t" * (i+1) + st) if isinstance(st, str) else st.pretty_print(i+1) for st in self.get_tail()])
+
 class Parser(object):
     class Constants(object):
         Token = 'token'

+ 2 - 0
interface/HUTN/includes/modelling.alh

@@ -21,7 +21,9 @@ Void function unset_attribute(model : Element, elem : String, name : String)
 Void function construct_model()
 Element function read_attribute(model : Element, elem : String, name : String)
 Void function model_delete_element(model : Element, name : String)
+String function model_undefine_attribute(model : Element, elem : String, name : String)
 String function model_define_attribute(model : Element, elem : String, name : String, optional : Boolean, type : String)
+String function model_define_attribute_ID(model : Element, elem : String, name : String, optional : Boolean, type : String, ID : String)
 Element function construct_model_raw(metamodel : Element)
 Element function get_func_AL_model(model : Element)
 Void function add_code_model(model : Element, export_name : String, code : Element)

+ 2 - 1
interface/HUTN/includes/object_operations.alh

@@ -4,7 +4,8 @@ Element function selectPossibleOutgoing(model : Element, source : String, limit_
 Element function allOutgoingAssociationInstances(model: Element, source_name : String, assoc_name: String)
 Element function allIncomingAssociationInstances(model: Element, target_name : String, assoc_name: String)
 Element function getAttributeList(model: Element, element: String)
-Element function getInstantiatableAttributes(model: Element, element: String)
+Element function getAttributes(model: Element, element: String)
+Element function getInstantiatableAttributes(model: Element, element: String, type: String)
 String function print_dict(dict : Element)
 String function readAssociationSource(model : Element, name : String)
 String function readAssociationDestination(model : Element, name : String)

+ 2 - 1
interface/HUTN/includes/primitives.alh

@@ -99,8 +99,9 @@ String function set_to_string(set : Element)
 String function list_to_string(set : Element)
 String function dict_to_string(dict : Element)
 Element function set_overlap(sa : Element, sb : Element)
-Element function set_difference(sa : Element, sb : Element)
 Element function set_equality(sa : Element, sb : Element)
+Element function set_difference(sa : Element, sb : Element)
+Void function set_subtract(sa : Element, sb : Element)
 Element function dict_eq(da : Element, db : Element)
 Element function dict_copy(dict : Element)
 Element function set_to_list(s : Element)

+ 0 - 1
interface/HUTN/includes/utils.alh

@@ -2,6 +2,5 @@ String function JSON_print(model : Element)
 Element function list_reverse(lst : Element)
 Element function list_splice(lst : Element, start : Integer, end : Integer)
 Void function list_extend(lst : Element, ext : Element)
-Void function set_difference(set1 : Element, set2 : Element)
 String function get_taskname()
 Void function sleep(seconds : Float)

+ 7 - 4
interface/HUTN/test/constructor_compilation_action_language/test_compile.py

@@ -9,10 +9,10 @@ def compile_file(obj, filename):
     try:
         expected = json.loads(open(util.get_expected_path(filename)).read())
     except:
-        #f = open(util.get_expected_path(filename), 'w')
-        #f.write(json.dumps(result))
-        #f.close()
-        pass
+        f = open(util.get_expected_path(filename), 'w')
+        f.write(json.dumps(result))
+        f.close()
+        expected = None
     assert result == expected
 
 class TestCompile(unittest.TestCase):
@@ -69,3 +69,6 @@ class TestCompile(unittest.TestCase):
 
     def test_strange_return(self):
         compile_file(self, "strange_return.al")
+
+    def test_string_concat(self):
+        compile_file(self, "string_concat.al")

+ 22 - 18
interface/PDEVS/console.py

@@ -1,10 +1,13 @@
 import sys
+sys.path.append("../../wrappers")
 from modelverse import *
-import random, re, json, uuid
-from interface import Controller
+import random, re, json
 from pprint import pprint
 from multiprocessing import Process, Pipe, freeze_support
 
+#address = "msdl.uantwerpen.be:8001"
+address = "localhost:8001"
+
 def register_service():
     sys.path.append('pypdevs/src')
 
@@ -13,8 +16,8 @@ def register_service():
 
     print 'Registering service...'
 
-    #init('msdl.uantwerpen.be:8001')
-    init()
+    init(address)
+    #init()
     login("pypdevs_service", "my_password")
 
     def pypdevs_service(port):
@@ -62,8 +65,8 @@ def register_service():
         service_stop()
 
 def start_pypdevs_client(conn):
-    #init('msdl.uantwerpen.be:8001')
-    init()
+    init(address)
+    #init()
     login("admin", "admin")
 
     conn.send(get_taskname())
@@ -82,8 +85,6 @@ def start_pypdevs_client(conn):
     except ModelExists:
         pass
 
-    controller = Controller()
-    
     print("Starting PyPDEVS client...")
     transformation_execute_AL("models/paralleldevs_simulator", {"MyString": "models/example_PS_DEVS_String"}, {}, fetch_output=False)
         
@@ -93,8 +94,8 @@ if __name__ == '__main__':
     p.start()
     
     print("Init")
-    #init('msdl.uantwerpen.be:8001')
-    init()
+    init(address)
+    #init()
     print("Login")
     login("admin", "admin")
 
@@ -151,9 +152,11 @@ if __name__ == '__main__':
         except ModelExists:
             pass
 
-        def traceability_PS2DEVS():
-            instantiate(None, "Association", ("ProductionSystem/Machine", "ParallelDEVS/BaseDEVSBlock"), ID="PS2DEVS_typelink")
-            instantiate(None, "Association", ("ProductionSystem/Machine", "ParallelDEVS/DEVSInstance"), ID="PS2DEVS_instancelink")
+        def traceability_PS2DEVS(context):
+            print("Start in context")
+            instantiate(None, "Association", ("ProductionSystem/Machine", "ParallelDEVS/BaseDEVSBlock"), ID="PS2DEVS_typelink", context=context)
+            instantiate(None, "Association", ("ProductionSystem/Machine", "ParallelDEVS/DEVSInstance"), ID="PS2DEVS_instancelink", context=context)
+            print("OK")
             
         print("Remove PS->DEVS transformation model")
         try:
@@ -230,14 +233,15 @@ if __name__ == '__main__':
     # TODO: Make this a transformation to a trace metamodel
     print("Add ProductionSystem simulator")
     try:
-        def traceability_PS2DEVS_runtime():
-            instantiate(None, "Association", ("ProductionSystemRuntime/Machine", "ParallelDEVS/BaseDEVSBlock"), ID="PS2DEVS_typelink")
-            instantiate(None, "Association", ("ProductionSystemRuntime/Machine", "ParallelDEVS/DEVSInstance"), ID="PS2DEVS_instancelink")
+        def traceability_PS2DEVS_runtime(context):
+            instantiate(None, "Association", ("ProductionSystemRuntime/Machine", "ParallelDEVS/BaseDEVSBlock"), ID="PS2DEVS_typelink", context=context)
+            instantiate(None, "Association", ("ProductionSystemRuntime/Machine", "ParallelDEVS/DEVSInstance"), ID="PS2DEVS_instancelink", context=context)
 
         transformation_add_AL({"ProductionSystemRuntime": "formalisms/ProductionSystemRuntime", "ParallelDEVS": "formalisms/ParallelDEVS"}, {}, "models/ps_simulator", open("../../integration/code/ps_simulator.alc", "r").read().replace("SESSION", str(uuid.uuid4()).replace("-", "")), traceability_PS2DEVS_runtime)
     except ModelExists:
         pass
     
+    from interface import Controller
     controller = Controller()
 
     def set_defaults(inp, defaultlist):
@@ -375,8 +379,8 @@ if __name__ == '__main__':
     def outputter():
         while 1:
             msg = json.loads(output_listener.fetch(-1).getParameters()[0])
-            print("%s" % msg["name"])
-            pprint(msg["params"])
+            params = msg["parameters"] if "parameters" in msg else msg["params"]
+            pprint(params)
     output_thread = threading.Thread(target=outputter)
     output_thread.daemon = True
     output_thread.start()

File diff suppressed because it is too large
+ 0 - 1047
interface/PDEVS/modelverse.py


+ 19 - 0
kernel/modelverse_kernel/compiled.py

@@ -254,3 +254,22 @@ def list_pop_final(a, **remainder):
                                  ("RDE", [a, length -1])]
     _, = yield [("DE", [result_edge])]
     raise PrimitiveFinished(result)
+
+def instantiate_node(a, b, c, **remainder):
+    node, dict_entry, typing, name = \
+        yield [("CN", []),
+               ("RD", [a, "model"]),
+               ("RD", [a, "type_mapping"]),
+               ("RV", [c]),
+              ]
+
+    if name == "":
+        name = "__" + str(node)
+        name_node, = yield [("CNV", [name])]
+    else:
+        name_node = c
+
+    yield [("CD", [dict_entry, name, node])]
+    yield [("CD", [typing, name, b])]
+
+    raise PrimitiveFinished(name_node)

+ 72 - 0
models/MM_render.mvc

@@ -0,0 +1,72 @@
+include "primitives.alh"
+
+SimpleAttribute Natural {}
+SimpleAttribute String {}
+SimpleAttribute Boolean {}
+
+Class GraphicalElement {
+    x : Natural
+    y : Natural
+    layer : Natural
+}
+
+Class Group : GraphicalElement {
+    __asid : String
+    dirty : Boolean
+}
+
+Association ConnectingLine (Group, Group) {
+    offsetSourceX : Natural
+    offsetSourceY : Natural
+    offsetTargetX : Natural
+    offsetTargetY : Natural
+    lineWidth : Natural
+    lineColour : String
+    arrow : Boolean
+    __asid : String
+    dirty : Boolean
+    layer : Natural
+}
+
+Class LineElement : GraphicalElement {
+    lineWidth : Natural
+    lineColour : String
+}
+
+Class Text : LineElement {
+    text : String
+}
+
+Class Line : LineElement {
+    targetX : Natural
+    targetY : Natural
+    arrow : Boolean
+}
+
+Class Shape : LineElement {
+    fillColour : String
+    width : Natural
+    height : Natural
+}
+
+Class Figure : GraphicalElement {
+    width : Natural
+    height : Natural
+}
+
+Class SVG {
+    data : String
+}
+
+Class Rectangle : Shape {
+}
+
+Class Ellipse : Shape {
+}
+
+Association contains (Group, GraphicalElement) {}
+Association renders (Figure, SVG) {
+    source_lower_cardinality = 1
+    target_lower_cardinality = 1
+    target_upper_cardinality = 1
+}

+ 20 - 3
models/MM_rendered_graphical.mvc

@@ -1,54 +1,71 @@
 include "primitives.alh"
 
-SimpleAttribute Natural {}
-SimpleAttribute String {}
+SimpleAttribute Natural {
+    name = "Natural"
+}
+SimpleAttribute String {
+    name = "String"
+}
 
 Class GraphicalElement {
+    name = "GraphicalElement"
     x : Natural
     y : Natural
     __asid? : String
 }
 
 Class Group : GraphicalElement {
+    name = "Group"
 }
 
 Class LineElement : GraphicalElement {
+    name = "LineElement"
     lineWidth : Natural
     lineColour : String
 }
 
 Class Text : LineElement {
+    name = "Text"
     text : String
 }
 
 Class Line : LineElement {
+    name = "Line"
     targetX : Natural
     targetY : Natural
 }
 
 Class Shape : LineElement {
+    name = "Shape"
     fillColour : String
     width : Natural
     height : Natural
 }
 
 Class Figure : GraphicalElement {
+    name = "Figure"
     width : Natural
     height : Natural
 }
 
 Class SVG {
+    name = "SVG"
     data : String
 }
 
 Class Rectangle : Shape {
+    name = "Rectangle"
 }
 
 Class Ellipse : Shape {
+    name = "Ellipse"
 }
 
-Association contains (Group, GraphicalElement) {}
+Association contains (Group, GraphicalElement) {
+    name = "contains"
+}
 Association renders (Figure, SVG) {
+    name = "renders"
     source_lower_cardinality = 1
     target_lower_cardinality = 1
     target_upper_cardinality = 1

+ 31 - 9
models/MM_rendered_plot.mvc

@@ -1,34 +1,56 @@
-SimpleAttribute String {}
-SimpleAttribute Boolean {}
-SimpleAttribute Float {}
+SimpleAttribute String {
+    name = "String"
+}
+SimpleAttribute Boolean {
+    name = "Boolean"
+}
+SimpleAttribute Float {
+    name = "Float"
+}
 
 Class Plot {
+    name = "Plot"
     title : String
     legend : Boolean
 }
 
 Class Dataset {
+    name = "Dataset"
     legend : String
     color : String
     linestyle : String
 }
 
 Class Datapoint {
+    name = "Datapoint"
     x : Float
     y : Float
 }
 
 Class Axis {
+    name = "Axis"
     name : String
     unit : String
     lim_low? : Float
     lim_high? : Float
 }
 
-Class XAxis : Axis {}
-Class YAxis : Axis {}
+Class XAxis : Axis {
+    name = "XAxis"
+}
+Class YAxis : Axis {
+    name = "YAxis"
+}
 
-Association x (Plot, XAxis) {}
-Association y (Plot, YAxis) {}
-Association data (Plot, Dataset) {}
-Association point (Dataset, Datapoint) {}
+Association x (Plot, XAxis) {
+    name = "x"
+}
+Association y (Plot, YAxis) {
+    name = "y"
+}
+Association data (Plot, Dataset) {
+    name = "data"
+}
+Association point (Dataset, Datapoint) {
+    name = "point"
+}

+ 48 - 10
models/SCCD.mvc

@@ -2,12 +2,21 @@ include "primitives.alh"
 include "object_operations.alh"
 include "modelling.alh"
 
-SimpleAttribute Action {}
-SimpleAttribute Boolean {}
-SimpleAttribute String {}
-SimpleAttribute Natural {}
+SimpleAttribute Action {
+    name = "Action"
+}
+SimpleAttribute Boolean {
+    name = "Boolean"
+}
+SimpleAttribute String {
+    name = "String"
+}
+SimpleAttribute Natural {
+    name = "Natural"
+}
 
 Class Diagram{
+    name = "Diagram"
     name : String
     author : String
     description : String
@@ -16,6 +25,7 @@ Class Diagram{
 }
 
 Class Class{
+    name = "Class"
     name : String
     constructor_body? : Action
     destructor? : Action
@@ -24,73 +34,100 @@ Class Class{
 }
 
 Association diagram_classes(Diagram, Class){
+    name = "diagram_classes"
     target_lower_cardinality = 1
 }
 
 Class Attribute{
+    name = "Attribute"
     name : String
 }
-Association class_attributes(Class, Attribute){}
+Association class_attributes(Class, Attribute){
+    name = "class_attributes"
+}
 
 Class Method{
+    name = "Method"
     name : String
     body : Action
 }
 
 Association association(Class, Class){
+    name = "association"
     name : String
     source_upper_cardinality = 1
 }
 
 Association inheritance(Class, Class){
+    name = "inheritance"
     priority ?: Natural
     source_upper_cardinality = 1
 }
 
 Class State{
+    name = "State"
     name : String
 }
 
 Class BasicState : State{
+    name = "BasicState"
     isInitial : Boolean
     onEntryScript? : Action
     onExitScript? : Action
 }
 
 Class Raise{
+    name = "Raise"
     event : String
     scope? : String
     target? : String
     parameter? : Action
 }
-Association onEntryRaise (BasicState, Raise) {}
-Association onExitRaise (BasicState, Raise) {}
+Association onEntryRaise (BasicState, Raise) {
+    name = "onEntryRaise"
+}
+Association onExitRaise (BasicState, Raise) {
+    name = "onExitRaise"
+}
 
 Association behaviour(Class, BasicState){
+    name = "behaviour"
     target_lower_cardinality = 1
     target_upper_cardinality = 1
 }
 
 Association state_onentry_raises(BasicState, Raise){
+    name = "state_onentry_raises"
     order : Natural
 }
 Association state_onexit_raises(BasicState, Raise){
+    name = "state_onexit_raises"
     order : Natural
 }
 
-Class CompositeState : BasicState{}
+Class CompositeState : BasicState{
+    name = "CompositeState"
+}
+
 Association composite_children(CompositeState, State){
+    name = "composite_children"
     source_upper_cardinality = 1
 }
 
-Class ParallelState : BasicState{}
+Class ParallelState : BasicState{
+    name = "ParallelState"
+}
 Association parallel_children(ParallelState, CompositeState){
+    name = "parallel_children"
     source_upper_cardinality = 1
 }
 
-Class HistoryState : State{}
+Class HistoryState : State{
+    name = "HistoryState"
+}
 
 Association transition(State, State){
+    name = "transition"
     name: String
     cond? : Action
     script? : Action
@@ -99,5 +136,6 @@ Association transition(State, State){
     source_upper_cardinality = 1
 }
 Association transition_raises(transition, Raise){
+    name = "transition_raises"
     order : Natural
 }

+ 7 - 2
models/SCCD_Trace.mvc

@@ -1,7 +1,12 @@
-SimpleAttribute Float {}
-SimpleAttribute String {}
+SimpleAttribute Float {
+    name = "Float"
+}
+SimpleAttribute String {
+    name = "String"
+}
 
 Class Event{
+    name = "Event"
     timestamp : Float
     name : String
     parameter : String

+ 1 - 1
models/SCCD_execute.alc

@@ -442,7 +442,7 @@ Boolean function step_class(model : Element, data : Element, class : String):
 				// When leaving an orthogonal component, we must also pop all related states that might be processed in the future!
 				Element leaving
 				leaving = expand_current_state(model, current_state, data)
-				set_difference(states, leaving)
+				set_subtract(states, leaving)
 
 				transitioned = True
 				found = True

+ 9 - 2
models/architecture.mvc

@@ -1,6 +1,7 @@
 include "primitives.alh"
 
 SimpleAttribute String{
+    name = "String"
     constraint = $
         String function main(model : Element, name : String):
             if (is_physical_string(model["model"][name])):
@@ -12,11 +13,17 @@ SimpleAttribute String{
 
 Class Group {
     name : String
+    name = "Group"
 }
 
 Class Port {
     name : String
+    name = "Port"
 }
 
-Association Connects (Port, Port) {}
-Association Contains (Group, Port) {}
+Association Connects (Port, Port) {
+    name = "Connects"
+}
+Association Contains (Group, Port) {
+    name = "Contains"
+}

+ 6 - 1
models/bfs.alc

@@ -36,7 +36,12 @@ Boolean function bfs(model : Element):
 			// Found an error path!
 			log("Found error path!")
 			log(list_to_string(path))
-			output(list_to_string(path))
+			
+			Integer i
+			i = 0
+			while (i < list_len(path)):
+				output(list_read(path, i))
+				i = i + 1
 			break!
 
 		options = allOutgoingAssociationInstances(model, state, "ReachabilityGraph/Transition")

+ 2 - 2
models/combine_EPN.mvc

@@ -52,9 +52,9 @@ Composite schedule {
                     name_4 = read_attribute(model, mapping["4"], "name")
                     name_5 = read_attribute(model, mapping["5"], "name")
 
-                    if bool_not((name_0 + "/") + name_1 == name_2):
+                    if bool_not(name_0 + "/" + name_1 == name_2):
                         return False!
-                    if bool_not((name_3 + "/") + name_4 == name_5):
+                    if bool_not(name_3 + "/" + name_4 == name_5):
                         return False!
                     return True!
                 $

+ 27 - 8
models/control_PW.mvc

@@ -1,8 +1,13 @@
 include "primitives.alh"
 
-SimpleAttribute Boolean {}
-SimpleAttribute TriState {}
+SimpleAttribute Boolean {
+    name = "Boolean"
+}
+SimpleAttribute TriState {
+    name = "TriState"
+}
 SimpleAttribute String{
+    name = "String"
     constraint = $
         String function constraint(model : Element, name : String):
             if (is_physical_string(model["model"][name])):
@@ -13,18 +18,32 @@ SimpleAttribute String{
 }
 
 Class State {
+    name = "State"
     isInitial : Boolean
     isError : Boolean
     name : String
 }
 
-Class Up : State {}
-Class Down : State {}
-Class Neutral : State {}
+Class Up : State {
+    name = "Up"
+}
+Class Down : State {
+    name = "Down"
+}
+Class Neutral : State {
+    name = "Neutral"
+}
 
 Association Transition (State, State) {
+    name = "Transition"
     objDetected : TriState
 }
-Association UpPressed : Transition (State, State) {}
-Association NonePressed : Transition (State, State) {}
-Association DownPressed : Transition (State, State) {}
+Association UpPressed : Transition (State, State) {
+    name = "UpPressed"
+}
+Association NonePressed : Transition (State, State) {
+    name = "NonePressed"
+}
+Association DownPressed : Transition (State, State) {
+    name = "DownPressed"
+}

+ 1 - 1
models/devs_to_string.alc

@@ -118,4 +118,4 @@ Boolean function devs_to_string(model: Element):
     log("Printing model_rep...")
     log("\n" + model_rep)
     
-    return True!
+    return True!

+ 16 - 5
models/environment_PW.mvc

@@ -1,13 +1,24 @@
 include "primitives.alh"
 
-SimpleAttribute Boolean {}
-SimpleAttribute Natural{}
-SimpleAttribute String{}
+SimpleAttribute Boolean {
+    name = "Boolean"
+}
+SimpleAttribute Natural{
+    name = "Natural"
+}
+SimpleAttribute String{
+    name = "String"
+}
 
-Class Group {}
+Class Group {
+    name = "Group"
+}
 Class Event {
+    name = "Event"
     initial : Boolean
     name : String
 }
 
-Association Contains (Group, Event) {}
+Association Contains (Group, Event) {
+    name = "Contains"
+}

+ 1 - 1
models/epn_print.alc

@@ -17,7 +17,7 @@ Boolean function pn_print(model : Element):
 		name = read_attribute(model, place, "name")
 		tokens = read_attribute(model, place, "tokens")
 
-		log((("  " + name) + ": ") + cast_value(tokens))
+		log("  " + name + ": " + cast_string(tokens))
 
 	log("Transitions:")
 	all_places = allInstances(model, "Encapsulated_PetriNet/Transition")

+ 25 - 7
models/petrinet_ports.mvc

@@ -1,6 +1,7 @@
 include "primitives.alh"
 
 SimpleAttribute Natural{
+    name = "Natural"
     constraint = $
         String function constraint(model : Element, name : String):
             if (is_physical_int(model["model"][name])):
@@ -11,6 +12,7 @@ SimpleAttribute Natural{
 }
 
 SimpleAttribute String{
+    name = "String"
     constraint = $
         String function constraint(model : Element, name : String):
             if (is_physical_string(model["model"][name])):
@@ -20,18 +22,34 @@ SimpleAttribute String{
         $
 }
 
-SimpleAttribute Boolean {}
+SimpleAttribute Boolean {
+    name = "Boolean"
+}
 
 Class Named {
+    name = "Named"
     name : String
 }
 Class Place : Named {
+    name = "Place"
     tokens : Natural
 }
-Class Transition : Named {}
-Class Port : Named {}
+Class Transition : Named {
+    name = "Transition"
+}
+Class Port : Named {
+    name = "Port"
+}
 
-Association P2T (Place, Transition) {}
-Association T2P (Transition, Place) {}
-Association PortPlace (Port, Place) {}
-Association Related (Port, Port) {}
+Association P2T (Place, Transition) {
+    name = "P2T"
+}
+Association T2P (Transition, Place) {
+    name = "T2P"
+}
+Association PortPlace (Port, Place) {
+    name = "PortPlace"
+}
+Association Related (Port, Port) {
+    name = "Related"
+}

+ 4 - 0
models/petrinets.mvc

@@ -2,15 +2,19 @@ SimpleAttribute Natural {}
 SimpleAttribute String {}
 
 Class Place {
+    name = "Place"
     tokens : Natural
     name : String
 }
 Class Transition {
+    name = "Transition"
     name : String
 }
 Association P2T (Place, Transition) {
+    name = "P2T"
     weight : Natural
 }
 Association T2P (Transition, Place) {
+    name = "T2P"
     weight : Natural
 }

+ 26 - 8
models/plant_PW.mvc

@@ -1,17 +1,35 @@
-SimpleAttribute TriState {}
-SimpleAttribute String {}
-SimpleAttribute Boolean {}
+SimpleAttribute TriState {
+    name = "TriState"
+}
+SimpleAttribute String {
+    name = "String"
+}
+SimpleAttribute Boolean {
+    name = "Boolean"
+}
 
 Class State {
+    name = "State"
     name : String
     isInitial : Boolean
 }
-Class ErrorState : State {}
-Class NormalState : State {}
+Class ErrorState : State {
+    name = "ErrorState"
+}
+Class NormalState : State {
+    name = "NormalState"
+}
 
 Association Transition (State, State) {
+    name = "Transition"
     objPresent : TriState
 }
-Association OnUp : Transition (State, State) {}
-Association OnDown : Transition (State, State) {}
-Association OnNeutral : Transition (State, State) {}
+Association OnUp : Transition (State, State) {
+    name = "OnUp"
+}
+Association OnDown : Transition (State, State) {
+    name = "OnDown"
+}
+Association OnNeutral : Transition (State, State) {
+    name = "OnNeutral"
+}

+ 1 - 1
models/pn_print.alc

@@ -17,7 +17,7 @@ Boolean function pn_print(model : Element):
 		name = read_attribute(model, place, "name")
 		tokens = read_attribute(model, place, "tokens")
 
-		log((("  " + name) + ": ") + cast_value(tokens))
+		log("  " + name + ": " + cast_string(tokens))
 
 	log("Transitions:")
 	all_places = allInstances(model, "PetriNet/Transition")

+ 7 - 2
models/query.mvc

@@ -2,10 +2,15 @@ include "primitives.alh"
 include "object_operations.alh"
 include "modelling.alh"
 
-SimpleAttribute String {}
-SimpleAttribute Natural {}
+SimpleAttribute String {
+    name = "String"
+}
+SimpleAttribute Natural {
+    name = "Natural"
+}
 
 Class Place {
+    name = "Place"
     name : String
     tokens : Natural
     lower_cardinality = 1

+ 16 - 4
models/reachability_graph.mvc

@@ -2,27 +2,39 @@ include "primitives.alh"
 include "object_operations.alh"
 include "modelling.alh"
 
-SimpleAttribute String {}
-SimpleAttribute Natural {}
-SimpleAttribute Boolean {}
+SimpleAttribute String {
+    name = "String"
+}
+SimpleAttribute Natural {
+    name = "Natural"
+}
+SimpleAttribute Boolean {
+    name = "Boolean"
+}
 
 Class State {
+    name = "State"
     name : String
     error : Boolean
 }
 Class InitialState : State {
+    name = "InitialState"
     lower_cardinality = 1
     upper_cardinality = 1
 }
 
 Class Place {
+    name = "Place"
     name : String
     tokens : Natural
 }
 Association Transition (State, State) {
+    name = "Transition"
     name : String
 }
-Association Contains (State, Place) {}
+Association Contains (State, Place) {
+    name = "Contains"
+}
 
 GlobalConstraint {
     global_constraint = $

+ 2 - 2
models/reachabilitygraph_print.mvc

@@ -24,7 +24,7 @@ Composite schedule {
                         while (set_len(all_values) > 0):
                             place = set_pop(all_values)
                             dict_add(dict_values, read_attribute(model, place, "name"), read_attribute(model, place, "tokens"))
-                        output((cast_value(read_attribute(model, name, "name")) + ": ") + dict_to_string(dict_values))
+                        output(cast_string(read_attribute(model, name, "name")) + ": " + dict_to_string(dict_values))
                         return!
                     $
             }
@@ -54,7 +54,7 @@ Composite schedule {
                 label = "2"
                 action = $
                     Void function action(model : Element, name : String, mapping : Element):
-                        output((((cast_value(read_attribute(model, mapping["0"], "name")) + " --[") + cast_value(read_attribute(model, name, "name"))) + "]--> ") + cast_value(read_attribute(model, mapping["1"], "name")))
+                        output(cast_string(read_attribute(model, mapping["0"], "name")) + " --[" + cast_string(read_attribute(model, name, "name")) + "]--> " + cast_string(read_attribute(model, mapping["1"], "name")))
                         return!
                     $
             }

+ 213 - 0
models/render_OD.alc

@@ -0,0 +1,213 @@
+include "primitives.alh"
+include "modelling.alh"
+include "object_operations.alh"
+
+Boolean function main(model : Element):
+	Element elements
+	String class
+	Element attrs
+	Element attr_keys
+	String attr_key
+	String group
+	String elem
+	Integer loc_x
+	Integer loc_y
+	Integer text_loc
+	loc_x = 10
+	loc_y = 10
+
+	Element to_remove
+	String elem_to_remove
+	Element groups
+	Element class_types
+	Element metamodel
+	metamodel = model["metamodel"]
+	String class_type
+
+	// Construct our own kind of tracability
+	Element cs_to_as
+	Element as_to_cs
+	String asid
+	cs_to_as = dict_create()
+	as_to_cs = dict_create()
+	groups = allInstances(model, "rendered/Group")
+	while (set_len(groups) > 0):
+		group = set_pop(groups)
+		asid = read_attribute(model, group, "__asid")
+		dict_add(cs_to_as, group, "abstract/" + asid)
+		dict_add(as_to_cs, "abstract/" + asid, group)
+
+	// Now render everything
+	groups = dict_create()
+	class_types = allInstances(metamodel, "Class")
+	while (set_len(class_types) > 0):
+		class_type = set_pop(class_types)
+
+		if (string_startswith(class_type, "abstract/")):
+			elements = allInstances(model, class_type)
+
+			while (set_len(elements) > 0):
+				class = set_pop(elements)
+
+				if (is_edge(model["model"][class])):
+					continue!
+				
+				Integer x
+				Integer y
+				x = loc_x
+				y = loc_y
+
+				// Check if there is already an associated element
+				if (dict_in(as_to_cs, class)):
+					// Yes, but is it still clean?
+					Element related_groups
+					group = as_to_cs[class]
+
+					if (bool_not(read_attribute(model, group, "dirty"))):
+						dict_add(groups, class, group)
+						continue!
+					else:
+						group = as_to_cs[class]
+						to_remove = allAssociationDestinations(model, group, "rendered/contains")
+						x = create_value(read_attribute(model, group, "x"))
+						y = create_value(read_attribute(model, group, "y"))
+
+						while (set_len(to_remove) > 0):
+							elem_to_remove = set_pop(to_remove)
+							if (read_type(model, elem_to_remove) == "rendered/Group"):
+								set_add(to_remove, elem_to_remove)
+							else:
+								model_delete_element(model, elem_to_remove)
+						model_delete_element(model, group)
+						dict_delete(as_to_cs, class)
+
+				if (dict_in(groups, class)):
+					// Already rendered this, so skip
+					continue!
+
+				text_loc = 5
+
+				group = instantiate_node(model, "rendered/Group", "")
+				instantiate_attribute(model, group, "x", x)
+				instantiate_attribute(model, group, "y", y)
+				instantiate_attribute(model, group, "__asid", list_read(string_split(class, "/"), 1))
+				instantiate_attribute(model, group, "layer", 0)
+				dict_add(groups, class, group)
+
+				loc_x = loc_x + 250
+				if (loc_x > 2000):
+					loc_x = 10
+					loc_y = loc_y + 300
+
+				elem = instantiate_node(model, "rendered/Rectangle", "")
+				instantiate_attribute(model, elem, "x", 0)
+				instantiate_attribute(model, elem, "y", 0)
+				instantiate_attribute(model, elem, "height", 40 + set_len(getAttributes(model, class)) * 20)
+				instantiate_attribute(model, elem, "width", 200)
+				instantiate_attribute(model, elem, "lineWidth", 2) 
+				instantiate_attribute(model, elem, "lineColour", "black")
+				instantiate_attribute(model, elem, "fillColour", "white")
+				instantiate_attribute(model, elem, "layer", 1)
+				instantiate_link(model, "rendered/contains", "", group, elem)
+
+				elem = instantiate_node(model, "rendered/Text", "")
+				instantiate_attribute(model, elem, "x", 5)
+				instantiate_attribute(model, elem, "y", 3)
+				instantiate_attribute(model, elem, "lineWidth", 1)
+				instantiate_attribute(model, elem, "lineColour", "black")
+				instantiate_attribute(model, elem, "text", string_join(cast_value(list_read(string_split(class, "/"), 1)), " : " + cast_value(list_read(string_split(read_type(model, class), "/"), 1))))
+				instantiate_attribute(model, elem, "layer", 2)
+				instantiate_link(model, "rendered/contains", "", group, elem)
+
+				elem = instantiate_node(model, "rendered/Line", "")
+				instantiate_attribute(model, elem, "x", 0)
+				instantiate_attribute(model, elem, "y", 20)
+				instantiate_attribute(model, elem, "targetX", 200)
+				instantiate_attribute(model, elem, "targetY", 20)
+				instantiate_attribute(model, elem, "lineWidth", 1)
+				instantiate_attribute(model, elem, "lineColour", "black")
+				instantiate_attribute(model, elem, "arrow", False)
+				instantiate_attribute(model, elem, "layer", 2)
+				instantiate_link(model, "rendered/contains", "", group, elem)
+
+				attrs = getAttributes(model, class)
+				attr_keys = dict_keys(attrs)
+				while (dict_len(attr_keys) > 0):
+					attr_key = set_pop(attr_keys)
+					elem = instantiate_node(model, "rendered/Text", "")
+					instantiate_attribute(model, elem, "x", 5)
+					instantiate_attribute(model, elem, "y", text_loc + 20)
+					instantiate_attribute(model, elem, "lineWidth", 1)
+					instantiate_attribute(model, elem, "lineColour", "black")
+					instantiate_attribute(model, elem, "text", (attr_key + " = ") + cast_value(attrs[attr_key]))
+					instantiate_attribute(model, elem, "layer", 2)
+					instantiate_link(model, "rendered/contains", "", group, elem)
+					text_loc = text_loc + 15
+
+	// Flush all associations
+	elements = allInstances(model, "rendered/ConnectingLine")
+	while (set_len(elements) > 0):
+		class = set_pop(elements)
+		model_delete_element(model, class)
+
+	// Rerender associations
+	Element to_render
+	to_render = set_create()
+	class_types = allInstances(metamodel, "Association")
+	while (set_len(class_types) > 0):
+		class_type = set_pop(class_types)
+		log("Checking type " + class_type)
+
+		if (string_startswith(class_type, "abstract/")):
+			elements = allInstances(model, class_type)
+			log("    Checking instance " + class)
+			while (set_len(elements) > 0):
+				class = set_pop(elements)
+				if (is_edge(model["model"][class])):
+					if (bool_not(set_in(to_render, class))):
+						set_add(to_render, class)
+						log("Added!")
+
+	to_render = set_to_list(to_render)
+	Element delayed_elements
+	Integer num_to_render
+	delayed_elements = list_create()
+	while (list_len(to_render) > 0):
+		num_to_render = list_len(to_render)
+		while (list_len(to_render) > 0):
+			class = list_pop_final(to_render)
+			attr_keys = dict_keys(getAttributes(model, class))
+
+			if (bool_not(bool_and(dict_in(groups, readAssociationSource(model, class)), dict_in(groups, readAssociationDestination(model, class))))):
+				list_append(delayed_elements, class)
+				continue!
+
+			elem = instantiate_link(model, "rendered/ConnectingLine", "", groups[readAssociationSource(model, class)], groups[readAssociationDestination(model, class)])
+			dict_add(groups, class, elem)
+			if (is_edge(model["model"][readAssociationSource(model, class)])):
+				instantiate_attribute(model, elem, "offsetSourceX", 0)
+				instantiate_attribute(model, elem, "offsetSourceY", 0)
+			else:
+				instantiate_attribute(model, elem, "offsetSourceX", 100)
+				instantiate_attribute(model, elem, "offsetSourceY", 30)
+			if (is_edge(model["model"][readAssociationDestination(model, class)])):
+				instantiate_attribute(model, elem, "offsetTargetX", 0)
+				instantiate_attribute(model, elem, "offsetTargetY", 0)
+			else:
+				instantiate_attribute(model, elem, "offsetTargetX", 100)
+				instantiate_attribute(model, elem, "offsetTargetY", 30)
+			instantiate_attribute(model, elem, "lineWidth", 3)
+			instantiate_attribute(model, elem, "lineColour", "black")
+			instantiate_attribute(model, elem, "arrow", True)
+			instantiate_attribute(model, elem, "__asid", list_read(string_split(class, "/"), 1))
+			instantiate_attribute(model, elem, "layer", 0)
+			instantiate_link(model, "rendered/contains", "", group, elem)
+
+		if (num_to_render == list_len(delayed_elements)):
+			log("Could not decrease number of rendered elements anymore... Giving up!")
+			return True!
+		else:
+			to_render = delayed_elements
+			delayed_elements = list_create()
+
+	return True!

+ 182 - 0
models/render_SCD.alc

@@ -0,0 +1,182 @@
+include "primitives.alh"
+include "modelling.alh"
+include "object_operations.alh"
+
+Boolean function main(model : Element):
+	Element elements
+	String class
+	Element attrs
+	Element attr_keys
+	String attr_key
+	String group
+	String elem
+	Integer loc
+	Integer text_loc
+	Element related_groups
+	loc = 10
+
+	Element groups
+	groups = dict_create()
+
+	elements = allInstances(model, "rendered/Group")
+	while (set_len(elements) > 0):
+		group = set_pop(elements)
+		if (set_len(allIncomingAssociationInstances(model, group, "TracabilityClass")) == 0):
+			Element to_remove
+			String elem_to_remove
+			to_remove = allAssociationDestinations(model, group, "rendered/contains")
+			while (set_len(to_remove) > 0):
+				elem_to_remove = set_pop(to_remove)
+				if (read_type(model, elem_to_remove) == "rendered/Group"):
+					set_add(to_remove, elem_to_remove)
+				else:
+					model_delete_element(model, elem_to_remove)
+			model_delete_element(model, group)
+
+	elements = allInstances(model, "abstract/Class")
+	while (set_len(elements) > 0):
+		class = set_pop(elements)
+		
+		Integer x
+		Integer y
+		x = loc
+		y = 10
+
+		// Check if there is already an associated element
+		if (set_len(allOutgoingAssociationInstances(model, class, "TracabilityClass")) > 0):
+			// Yes, but is it still clean?
+			Boolean dirty
+			dirty = False
+
+			related_groups = allAssociationDestinations(model, class, "TracabilityClass")
+			while (set_len(related_groups) > 0):
+				group = set_pop(related_groups)
+				if (value_eq(read_attribute(model, group, "dirty"), True)):
+					// No, so mark all as dirty
+					dirty = True
+					break!
+				else:
+					// Yes, so just ignore this!
+					continue!
+
+			if (bool_not(dirty)):
+				dict_add(groups, class, group)
+				continue!
+			else:
+				related_groups = allAssociationDestinations(model, class, "TracabilityClass")
+				Element to_remove
+				String elem_to_remove
+				while (set_len(related_groups) > 0):
+					group = set_pop(related_groups)
+					to_remove = allAssociationDestinations(model, group, "rendered/contains")
+					x = create_value(read_attribute(model, group, "x"))
+					y = create_value(read_attribute(model, group, "y"))
+					while (set_len(to_remove) > 0):
+						elem_to_remove = set_pop(to_remove)
+						if (read_type(model, elem_to_remove) == "rendered/Group"):
+							set_add(to_remove, elem_to_remove)
+						else:
+							model_delete_element(model, elem_to_remove)
+					model_delete_element(model, group)
+
+		attr_keys = dict_keys(getAttributeList(model, class))
+		text_loc = 5
+
+		group = instantiate_node(model, "rendered/Group", "")
+		instantiate_attribute(model, group, "x", x)
+		instantiate_attribute(model, group, "y", y)
+		instantiate_attribute(model, group, "__asid", list_read(string_split(class, "/"), 1))
+		instantiate_attribute(model, group, "layer", 0)
+		dict_add(groups, class, group)
+		loc = loc + 200
+
+		elem = instantiate_node(model, "rendered/Rectangle", "")
+		instantiate_attribute(model, elem, "x", 0)
+		instantiate_attribute(model, elem, "y", 0)
+		instantiate_attribute(model, elem, "height", 40 + set_len(getInstantiatableAttributes(model, class, "abstract/AttributeLink")) * 20)
+		instantiate_attribute(model, elem, "width", 150)
+		instantiate_attribute(model, elem, "lineWidth", 2) 
+		instantiate_attribute(model, elem, "lineColour", "black")
+		instantiate_attribute(model, elem, "fillColour", "white")
+		instantiate_attribute(model, elem, "layer", 1)
+		instantiate_link(model, "rendered/contains", "", group, elem)
+
+		String multiplicities
+		String lower_card
+		String upper_card
+		if (element_eq(read_attribute(model, class, "lower_cardinality"), read_root())):
+			lower_card = "*"
+		else:
+			lower_card = cast_value(read_attribute(model, class, "lower_cardinality"))
+		if (element_eq(read_attribute(model, class, "upper_cardinality"), read_root())):
+			upper_card = "*"
+		else:
+			upper_card = cast_value(read_attribute(model, class, "upper_cardinality"))
+		multiplicities = ((("[" + lower_card) + "..") + upper_card) + "]"
+
+		elem = instantiate_node(model, "rendered/Text", "")
+		instantiate_attribute(model, elem, "x", 5)
+		instantiate_attribute(model, elem, "y", 3)
+		instantiate_attribute(model, elem, "lineWidth", 1)
+		instantiate_attribute(model, elem, "lineColour", "black")
+		if (element_neq(read_attribute(model, class, "name"), read_root())):
+			instantiate_attribute(model, elem, "text", string_join(read_attribute(model, class, "name"), "  " + multiplicities))
+		else:
+			instantiate_attribute(model, elem, "text", "(unnamed) " + multiplicities)
+		instantiate_attribute(model, elem, "layer", 2)
+		instantiate_link(model, "rendered/contains", "", group, elem)
+
+		elem = instantiate_node(model, "rendered/Line", "")
+		instantiate_attribute(model, elem, "x", 0)
+		instantiate_attribute(model, elem, "y", 20)
+		instantiate_attribute(model, elem, "targetX", 150)
+		instantiate_attribute(model, elem, "targetY", 20)
+		instantiate_attribute(model, elem, "lineWidth", 1)
+		instantiate_attribute(model, elem, "lineColour", "black")
+		instantiate_attribute(model, elem, "arrow", False)
+		instantiate_attribute(model, elem, "layer", 2)
+		instantiate_link(model, "rendered/contains", "", group, elem)
+
+		attrs = getInstantiatableAttributes(model, class, "abstract/AttributeLink")
+		attr_keys = dict_keys(attrs)
+		while (dict_len(attr_keys) > 0):
+			attr_key = set_pop(attr_keys)
+			elem = instantiate_node(model, "rendered/Text", "")
+			instantiate_attribute(model, elem, "x", 5)
+			instantiate_attribute(model, elem, "y", text_loc + 20)
+			instantiate_attribute(model, elem, "lineWidth", 1)
+			instantiate_attribute(model, elem, "lineColour", "black")
+			instantiate_attribute(model, elem, "text", (attr_key + " : ") + cast_string(list_read(string_split(attrs[attr_key], "/"), 1)))
+			instantiate_attribute(model, elem, "layer", 2)
+			instantiate_link(model, "rendered/contains", "", group, elem)
+			text_loc = text_loc + 15
+
+		instantiate_link(model, "TracabilityClass", "", class, group)
+
+	// Flush all associations
+	elements = allInstances(model, "rendered/ConnectingLine")
+	while (set_len(elements) > 0):
+		class = set_pop(elements)
+		model_delete_element(model, class)
+
+	// Rerender associations
+	elements = allInstances(model, "abstract/Association")
+	while (set_len(elements) > 0):
+		class = set_pop(elements)
+
+		attr_keys = dict_keys(getAttributeList(model, class))
+
+		elem = instantiate_link(model, "rendered/ConnectingLine", "", groups[readAssociationSource(model, class)], groups[readAssociationDestination(model, class)])
+		instantiate_attribute(model, elem, "offsetSourceX", 75)
+		instantiate_attribute(model, elem, "offsetSourceY", 30)
+		instantiate_attribute(model, elem, "offsetTargetX", 75)
+		instantiate_attribute(model, elem, "offsetTargetY", 30)
+		instantiate_attribute(model, elem, "lineWidth", 1)
+		instantiate_attribute(model, elem, "lineColour", "black")
+		instantiate_attribute(model, elem, "arrow", True)
+		instantiate_attribute(model, elem, "__asid", list_read(string_split(class, "/"), 1))
+		instantiate_attribute(model, elem, "layer", 0)
+		log("Found ASID " + cast_value(list_read(string_split(class, "/"), 1)))
+		instantiate_link(model, "rendered/contains", "", group, elem)
+
+	return True!

+ 2 - 0
models/requirements.mvc

@@ -1,6 +1,7 @@
 include "primitives.alh"
 
 SimpleAttribute String{
+    name = "String"
     constraint = $
         String function constraint_String(model : Element, name : String):
             if (is_physical_string(model["model"][name])):
@@ -11,6 +12,7 @@ SimpleAttribute String{
 }
 
 Class UseCase {
+    name = "UseCase"
     name : String
     scope : String
     level : String

+ 25 - 0
models/test_compiler.alc

@@ -0,0 +1,25 @@
+include "compiler.alh"
+include "primitives.alh"
+
+Void function main(model : Element):
+	String code
+	code = ""
+	code = code + "include \"primitives.alh\"\n"
+	code = code + "Void function test_function():\n"
+	code = code + "    log(\"Function!\")\n"
+	code = code + "    abcd\n"
+	code = code + "    return!"
+
+	log("Got code: ")
+	log(code)
+	
+	Element result
+	result = compile_code(code)
+
+	log("Executing result: " + cast_value(result))
+	if (element_eq(result, read_root())):
+		log("ERROR: compilation error")
+	else:
+		result()
+
+	return!

+ 11 - 3
models/trace.mvc

@@ -1,13 +1,21 @@
-SimpleAttribute String {}
-SimpleAttribute Float {}
+SimpleAttribute String {
+    name = "String"
+}
+SimpleAttribute Float {
+    name = "Float"
+}
 
 Class Signal {
+    name = "Signal"
     name : String
 }
 
 Class Point {
+    name = "Point"
     x : Float
     y : Float
 }
 
-Association contains (Signal, Point) {}
+Association contains (Signal, Point) {
+    name = "contains"
+}

+ 5 - 0
scripts/HUTN_service.py

@@ -26,6 +26,7 @@ def clean_code(code):
     return code
 
 def compile_service(port):
+    print("Start with port " + str(port))
     start = time.time()
     temp_file = ".tmp_%s" % port
 
@@ -59,6 +60,7 @@ def compile_service(port):
 
     mode = service_get(port)
     code = service_get(port)
+    print("Service set: " + str(port))
 
     try:
         if mode == "code":
@@ -76,7 +78,10 @@ def compile_service(port):
         raise
     print("Compile took %ss" % (time.time() - start))
 
+print("Start service")
 service_register("compiler", compile_service)
+print("Service OK")
+
 try:
     while raw_input() != "STOP":
         pass

+ 0 - 2
scripts/JSON_service.py

@@ -61,14 +61,12 @@ def json_service(port):
             json_str = service_get(port)
             json_data = json.loads(json_str)
             print_out_json(json_data)
-            print("Decoded %s" % json_str)
 
         elif mode == "encode":
             service_set(port, "OK")
             json_data = fetch_data()
             json_str = json.dumps(json_data)
             service_set(port, json_str)
-            print("Encoded %s" % json_str)
             
         else:
             raise Exception("No such mode: " + mode)

+ 65 - 60
unit/test_all.py

@@ -6,6 +6,8 @@ from utils import *
 sys.path.append("wrappers")
 from modelverse import *
 
+import threading
+
 model_hierarchy = \
             {"formalisms/": {"SimpleClassDiagrams": {},
                              "TypeMapping": {},
@@ -97,6 +99,7 @@ class TestModelverse(unittest.TestCase):
         model_delete("test")
         model_delete("type mappings/test")
         verify_clean()
+        pass
 
     def test_list_full(self):
         assert model_list_full("") == set([("formalisms/", "admin", "admin", "221"),
@@ -134,13 +137,15 @@ class TestModelverse(unittest.TestCase):
 
         # Check that an instantiate of "A" fails
         try:
-            instantiate("test/my_empty", "A")
+            res = instantiate("test/my_empty", "A")
+            print("RESULT: " + str(res))
             assert False
         except UnknownIdentifier:
             assert verify("test/Empty", "formalisms/SimpleClassDiagrams") == "OK"
 
         # Create something in the formalism
         instantiate("test/Empty", "Class", ID="A")
+        attr_assign("test/Empty", "A", "name", "A")
         assert verify("test/Empty", "formalisms/SimpleClassDiagrams") == "OK"
 
         # Now instantiate that in the model as well, which now works
@@ -154,7 +159,7 @@ class TestModelverse(unittest.TestCase):
         instantiate("test/Empty", "Class", ID="A")
         assert element_list("test/Empty") == set([("A", "Class")])
 
-        model_overwrite("test/Empty")
+        model_overwrite("test/Empty", "")
         assert element_list("test/Empty") == set([])
         compare_locations("test", set(["Empty"]))
         compare_locations("type mappings/test", set(["Empty"]))
@@ -168,41 +173,38 @@ class TestModelverse(unittest.TestCase):
     def test_operations(self):
         log = []
 
-        def callback(value):
-            log.append(value)
-
-        def manual_callback():
-            p1 = instantiate(None, "PetriNet_Runtime/Place")
-            attr_assign(None, p1, "tokens", 1)
-            attr_assign(None, p1, "name", "p1")
-            p2 = instantiate(None, "PetriNet_Runtime/Place")
-            attr_assign(None, p2, "tokens", 2)
-            attr_assign(None, p2, "name", "p2")
-            p3 = instantiate(None, "PetriNet_Runtime/Place")
-            attr_assign(None, p3, "tokens", 3)
-            attr_assign(None, p3, "name", "p3")
-            t1 = instantiate(None, "PetriNet_Runtime/Transition")
-            attr_assign(None, t1, "name", "t1")
-            attr_assign(None, t1, "executing", False)
-            p2t1 = instantiate(None, "PetriNet_Runtime/P2T", (p1, t1))
-            attr_assign(None, p2t1, "weight", 1)
-            p2t2 = instantiate(None, "PetriNet_Runtime/P2T", (p2, t1))
-            attr_assign(None, p2t2, "weight", 1)
-            t2p1 = instantiate(None, "PetriNet_Runtime/T2P", (t1, p3))
-            attr_assign(None, t2p1, "weight", 2)
+        def manual_callback(context):
+            p1 = instantiate(None, "PetriNet_Runtime/Place", context=context)
+            attr_assign(None, p1, "tokens", 1, context=context)
+            attr_assign(None, p1, "name", "p1", context=context)
+            p2 = instantiate(None, "PetriNet_Runtime/Place", context=context)
+            attr_assign(None, p2, "tokens", 2, context=context)
+            attr_assign(None, p2, "name", "p2", context=context)
+            p3 = instantiate(None, "PetriNet_Runtime/Place", context=context)
+            attr_assign(None, p3, "tokens", 3, context=context)
+            attr_assign(None, p3, "name", "p3", context=context)
+            t1 = instantiate(None, "PetriNet_Runtime/Transition", context=context)
+            attr_assign(None, t1, "name", "t1", context=context)
+            attr_assign(None, t1, "executing", False, context=context)
+            p2t1 = instantiate(None, "PetriNet_Runtime/P2T", (p1, t1), context=context)
+            attr_assign(None, p2t1, "weight", 1, context=context)
+            p2t2 = instantiate(None, "PetriNet_Runtime/P2T", (p2, t1), context=context)
+            attr_assign(None, p2t2, "weight", 1, context=context)
+            t2p1 = instantiate(None, "PetriNet_Runtime/T2P", (t1, p3), context=context)
+            attr_assign(None, t2p1, "weight", 2, context=context)
 
         model_add("test/PetriNet", "formalisms/SimpleClassDiagrams", open("integration/code/pn_design.mvc", "r").read())
         model_add("test/PetriNet_Runtime", "formalisms/SimpleClassDiagrams", open("integration/code/pn_runtime.mvc", "r").read())
 
         model_add("test/my_pn", "test/PetriNet", open("integration/code/pn_design_model.mvc", "r").read())
 
-        def add_tracability_D2R():
-            instantiate(None, "Association", ("PetriNet/Place", "PetriNet_Runtime/Place"), ID="D2R_PlaceLink")
-            instantiate(None, "Association", ("PetriNet/Transition", "PetriNet_Runtime/Transition"), ID="D2R_TransitionLink")
+        def add_tracability_D2R(context):
+            instantiate(None, "Association", ("PetriNet/Place", "PetriNet_Runtime/Place"), ID="D2R_PlaceLink", context=context)
+            instantiate(None, "Association", ("PetriNet/Transition", "PetriNet_Runtime/Transition"), ID="D2R_TransitionLink", context=context)
 
-        def add_tracability_R2D():
-            instantiate(None, "Association", ("PetriNet_Runtime/Place", "PetriNet/Place"), ID="R2D_PlaceLink")
-            instantiate(None, "Association", ("PetriNet_Runtime/Transition", "PetriNet/Transition"), ID="R2D_TransitionLink")
+        def add_tracability_R2D(context):
+            instantiate(None, "Association", ("PetriNet_Runtime/Place", "PetriNet/Place"), ID="R2D_PlaceLink", context=context)
+            instantiate(None, "Association", ("PetriNet_Runtime/Transition", "PetriNet/Transition"), ID="R2D_TransitionLink", context=context)
 
         transformation_add_MT({"PetriNet": "test/PetriNet"}, {}, "test/print_pn", open("integration/code/pn_print.mvc").read())
         transformation_add_MANUAL({"PetriNet": "test/PetriNet"}, {"PetriNet_Runtime": "test/PetriNet_Runtime"}, "test/pn_design_to_runtime", add_tracability_D2R)
@@ -216,7 +218,7 @@ class TestModelverse(unittest.TestCase):
         thrd.daemon = True
         thrd.start()
 
-        assert transformation_execute_MT("test/print_pn", {"PetriNet": "test/my_pn"}, {}, (ctrl, "inp", "outp")) == None
+        assert transformation_execute_MT("test/print_pn", {"PetriNet": "test/my_pn"}, {}, (ctrl, "inp", "outp")) == True
         thrd.join()
         assert set(log) == set(['"p1" --> 1',
                                 '"p2" --> 2',
@@ -232,7 +234,7 @@ class TestModelverse(unittest.TestCase):
         thrd.daemon = True
         thrd.start()
 
-        assert transformation_execute_MT("test/print_pn", {"PetriNet": "test/my_pn"}, {}, (ctrl, "inp", "outp")) == None
+        assert transformation_execute_MT("test/print_pn", {"PetriNet": "test/my_pn"}, {}, (ctrl, "inp", "outp")) == True
         thrd.join()
         assert set(log) == set(['"p1" --> 0',
                                 '"p2" --> 1',
@@ -252,26 +254,30 @@ class TestModelverse(unittest.TestCase):
         transformation_add_AL({"PetriNet": "test/PetriNet"}, {"ReachabilityGraph": "test/ReachabilityGraph"}, "test/reachability", open("integration/code/reachability_subfunction.alc", "r").read())
         transformation_add_MT({"ReachabilityGraph": "test/ReachabilityGraph"}, {}, "test/reachability_print", open("integration/code/reachabilitygraph_print.mvc", 'r').read())
 
-        def callback_refine_PN():
-            p1 = instantiate(None, "PetriNet/Place")
-            attr_assign(None, p1, "name", "p1")
-            attr_assign(None, p1, "tokens", 1)
+        def callback_refine_PN(context):
+            p1 = instantiate(None, "PetriNet/Place", context=context)
+            attr_assign(None, p1, "name", "p1", context=context)
+            attr_assign(None, p1, "tokens", 1, context=context)
 
-            t1 = instantiate(None, "PetriNet/Transition")
-            attr_assign(None, t1, "name", "t1")
+            t1 = instantiate(None, "PetriNet/Transition", context=context)
+            attr_assign(None, t1, "name", "t1", context=context)
 
-            p2t = instantiate(None, "PetriNet/P2T", (p1, t1))
-            attr_assign(None, p2t, "weight", 1)
+            p2t = instantiate(None, "PetriNet/P2T", (p1, t1), context=context)
+            attr_assign(None, p2t, "weight", 1, context=context)
 
-        log = set([])
-        def callback_print(value):
-            log.add(value)
+        import log_output
+        log = []
+        ctrl = log_output.Controller(log, keep_running=False)
+        thrd = threading.Thread(target=ctrl.start)
+        thrd.daemon = True
+        thrd.start()
 
-        process_execute("test/pn_reachability", "", {"test/refine_PN": callback_refine_PN, "test/reachability_print": callback_print})
+        process_execute("test/pn_reachability", "", {"test/refine_PN": callback_refine_PN, "test/reachability_print": (ctrl, "inp", "outp")})
+        thrd.join()
 
-        assert log == set(['"0": {"p1": 1, }',
-                           '"1": {"p1": 0, }',
-                           '"0" --["t1"]--> "1"'])
+        assert set(log) == set(['"0": {"p1": 1}',
+                                '"1": {"p1": 0}',
+                                '"0" --["t1"]--> "1"'])
 
         model_delete("RAMified")
         model_delete("merged")
@@ -283,20 +289,19 @@ class TestModelverse(unittest.TestCase):
         model_add("test/MM_rendered_graphical", "formalisms/SimpleClassDiagrams", open("models/MM_rendered_graphical.mvc", 'r').read())
         model_add("test/my_CBD", "test/CausalBlockDiagrams", open("integration/code/my_cbd.mvc", 'r').read())
 
-        def add_tracability():
-            instantiate(None, "Association", ("abstract/Block", "rendered/Group"), ID="TracabilityLink")
+        def add_tracability(context):
+            instantiate(None, "Association", ("abstract/Block", "rendered/Group"), ID="TracabilityLink", context=context)
 
         transformation_add_MT({"abstract": "test/CausalBlockDiagrams", "rendered": "test/MM_rendered_graphical"}, {"abstract": "test/CausalBlockDiagrams", "rendered": "test/MM_rendered_graphical"}, "test/render_graphical_CBD", open("models/CBD_mapper.mvc", 'r').read(), add_tracability)
-        result = model_render("test/my_CBD", "test/render_graphical_CBD")
+        result = model_render("test/my_CBD", "test/render_graphical_CBD", "test/my_perceptualized_CBD")
+
         assert len(result) == 23
 
         model_delete("RAMified")
         model_delete("merged")
         model_delete("type mappings/RAMified")
         model_delete("type mappings/merged")
-        model_delete("rendered")
         model_delete("tracability")
-        model_delete("type mappings/rendered")
         model_delete("type mappings/tracability")
 
     def test_SCCD_basic(self):
@@ -373,16 +378,16 @@ class TestModelverse(unittest.TestCase):
         
         got = element_list_nice("test/PetriNet")
         expected = \
-            [{'id': 'Natural', 'type': 'SimpleAttribute', 'constraint': None},
-             {'id': 'String', 'type': 'SimpleAttribute', 'constraint': None},
-             {'id': 'Place', 'type': 'Class', 'lower_cardinality': None, 'upper_cardinality': None, 'constraint': None},
+            [{'id': 'Natural', 'type': 'SimpleAttribute', 'constraint': None, 'name': 'Natural'},
+             {'id': 'String', 'type': 'SimpleAttribute', 'constraint': None, 'name': 'String'},
+             {'id': 'Place', 'type': 'Class', 'lower_cardinality': None, 'upper_cardinality': None, 'constraint': None, 'name': 'Place'},
              {'id': 'Place_tokens', 'type': 'AttributeLink', '__source': 'Place', '__target': 'Natural', 'name': 'tokens', 'optional': False, 'constraint': None},
              {'id': 'Place_name', 'type': 'AttributeLink', '__source': 'Place', '__target': 'String', 'name': 'name', 'optional': False, 'constraint': None},
-             {'id': 'Transition', 'type': 'Class', 'lower_cardinality': None, 'upper_cardinality': None, 'constraint': None},
+             {'id': 'Transition', 'type': 'Class', 'lower_cardinality': None, 'upper_cardinality': None, 'constraint': None, 'name': 'Transition'},
              {'id': 'Transition_name', 'type': 'AttributeLink', '__source': 'Transition', '__target': 'String', 'name': 'name', 'optional': False, 'constraint': None},
-             {'id': 'P2T', 'type': 'Association', '__source': 'Place', '__target': 'Transition', 'source_lower_cardinality': None, 'target_lower_cardinality': None, 'source_upper_cardinality': None, 'target_upper_cardinality': None, 'constraint': None},
+             {'id': 'P2T', 'type': 'Association', '__source': 'Place', '__target': 'Transition', 'source_lower_cardinality': None, 'target_lower_cardinality': None, 'source_upper_cardinality': None, 'target_upper_cardinality': None, 'constraint': None, 'name': 'P2T'},
              {'id': 'P2T_weight', 'type': 'AttributeLink', '__source': 'P2T', '__target': 'Natural', 'name': 'weight', 'optional': False, 'constraint': None},
-             {'id': 'T2P', 'type': 'Association', '__source': 'Transition', '__target': 'Place', 'source_lower_cardinality': None, 'target_lower_cardinality': None, 'source_upper_cardinality': None, 'target_upper_cardinality': None, 'constraint': None},
+             {'id': 'T2P', 'type': 'Association', '__source': 'Transition', '__target': 'Place', 'source_lower_cardinality': None, 'target_lower_cardinality': None, 'source_upper_cardinality': None, 'target_upper_cardinality': None, 'constraint': None, 'name': 'T2P'},
              {'id': 'T2P_weight', 'type': 'AttributeLink', '__source': 'T2P', '__target': 'Natural', 'name': 'weight', 'optional': False, 'constraint': None}
             ]
         compare_unordered_lists(got, expected)
@@ -412,8 +417,8 @@ class TestModelverse(unittest.TestCase):
             else:
                 assert len(entry) == 4
                 count_edges += 1
-        assert count_nodes == 14
-        assert count_edges == 17
+        assert count_nodes == 20
+        assert count_edges == 23
 
         count_nodes = 0
         count_edges = 0

+ 177 - 0
wrappers/classes/http_client.xml

@@ -0,0 +1,177 @@
+<class name="HTTPClient">
+    <constructor>
+        <body>
+            <![CDATA[
+            self.socket = None
+            self.received_data = ""
+            self.send_data = ""
+            self.queue = []
+            self.IDs = []
+            ]]>
+        </body>
+    </constructor>
+
+    <scxml initial="init">
+        <state id="init">
+            <onentry>
+                <script>
+                    self.ID = str(uuid.uuid4())
+                </script>
+                <raise scope="output" event="create_socket" port="socket_out">
+                    <parameter expr="self.ID"/>
+                </raise>
+            </onentry>
+
+            <transition port="socket_in" event="created_socket" cond="self.ID == ID" target="../waiting">
+                <parameter name="socket"/>
+                <parameter name="ID"/>
+                <script>
+                    self.socket = socket
+                </script>
+            </transition>
+        </state>
+
+        <state id="waiting">
+            <onentry>
+                <raise scope="broad" event="http_client_initialized"/>
+            </onentry>
+
+            <transition event="connect" target="../connecting">
+                <parameter name="address"/>
+                <parameter name="timeout"/>
+
+                <script>
+                    self.address = address
+                    self.timeout = timeout
+                </script>
+            </transition>
+        </state>
+
+        <state id="connecting" initial="connecting">
+            <state id="connecting">
+                <onentry>
+                    <raise scope="output" event="connect_socket" port="socket_out">
+                        <parameter expr="self.socket"/>
+                        <parameter expr="self.address"/>
+                    </raise>
+                </onentry>
+
+                <transition port="socket_in" event="error_socket" target="../cooldown"/>
+
+                <transition port="socket_in" event="connected_socket" cond="self.socket == socket" target="../../connected">
+                    <parameter name="socket"/>
+                    <raise scope="broad" event="http_client_ready"/>
+                </transition>
+            </state>
+
+            <state id="cooldown">
+                <transition after="0.1" target="../connecting"/>
+            </state>
+
+            <transition after="self.timeout" target="../waiting">
+                <raise scope="broad" event="http_client_timeout"/>
+            </transition>
+        </state>
+
+        <parallel id="connected">
+            <state id="listening" initial="listen">
+                <state id="listen">
+                    <onentry>
+                        <raise scope="output" port="socket_out" event="recv_socket">
+                            <parameter expr="self.socket"/>
+                        </raise>
+                    </onentry>
+                    <transition event="received_socket" port="socket_in" cond="(self.socket == socket) and (len(data) > 0)" target=".">
+                        <parameter name="socket"/>
+                        <parameter name="data"/>
+                        <script>
+                            self.received_data += data
+                        </script>
+                    </transition>
+                    <transition event="received_socket" port="socket_in" cond="(self.socket == socket) and (len(data) == 0)" target="../close">
+                        <parameter name="socket"/>
+                        <parameter name="data"/>
+                    </transition>
+                </state>
+                <state id="close">
+                </state>
+            </state>
+
+            <state id="sending" initial="waiting_for_data">
+                <state id="waiting_for_data">
+                    <transition cond="len(self.send_data) > 0" target="../transferring">
+                        <raise scope="output" port="socket_out" event="send_socket">
+                            <parameter expr="self.socket"/>
+                            <parameter expr="self.send_data"/>
+                        </raise>
+                    </transition>
+                </state>
+                <state id="transferring">
+                    <transition event="sent_socket" port="socket_in" cond="self.socket == socket" target="../waiting_for_data">
+                        <parameter name="socket"/>
+                        <parameter name="sent_bytes"/>
+                        <script>
+                            self.send_data = self.send_data[sent_bytes:]
+                        </script>
+                    </transition>
+                </state>
+            </state>
+
+            <state id="queueing">
+                <state id="queueing">
+                    <onentry>
+                    </onentry>
+                    <transition event="HTTP_input" target=".">
+                        <parameter name="data"/>
+                        <parameter name="ID"/>
+                        <script>
+                            self.send_data += "POST / HTTP/1.0\r\n"
+                            self.send_data += "Content-Length: %i\r\n" % len(str(data))
+                            self.send_data += "\r\n"
+                            self.send_data += data
+                            self.IDs.append(ID)
+                        </script>
+                    </transition>
+                </state>
+            </state>
+
+            <state id="parsing" initial="wait_for_header">
+                <state id="wait_for_header">
+                    <transition cond="'\r\n\r\n' in self.received_data" target="../wait_for_payload">
+                        <script>
+                            header, self.received_data = self.received_data.split("\r\n\r\n", 1)
+                            header = header.lower()
+                            if "content-length" in header:
+                                _, after = header.split("content-length:", 1)
+                                after, _ = after.split("\r\n", 1)
+                                after = after.strip()
+                                self.length = int(after)
+                            else:
+                                self.length = float('inf')
+                        </script>
+                    </transition>
+                </state>
+                <state id="wait_for_payload">
+                    <transition cond="len(self.received_data) >= self.length and self.IDs[0] is not None" target="../wait_for_header">
+                        <script>
+                            data = self.received_data[:self.length]
+                            self.received_data = self.received_data[self.length:]
+                        </script>
+                        <raise event="HTTP_output" scope="broad">
+                            <parameter expr="data"/>
+                            <parameter expr="self.IDs.pop(0)"/>
+                        </raise>
+                    </transition>
+
+                    <transition cond="len(self.received_data) >= self.length and self.IDs[0] is None" target="../wait_for_header">
+                        <script>
+                            # Drop data
+                            self.received_data = self.received_data[self.length:]
+                            self.IDs.pop(0)
+                        </script>
+                    </transition>
+                </state>
+            </state>
+        </parallel>
+    </scxml>
+</class>

File diff suppressed because it is too large
+ 2113 - 0
wrappers/classes/modelverse.xml


+ 2 - 0
wrappers/compile.sh

@@ -0,0 +1,2 @@
+#!/bin/bash
+python -m sccd.compiler.sccdc -p threads modelverse_SCCD.xml

File diff suppressed because it is too large
+ 353 - 928
wrappers/modelverse.py


File diff suppressed because it is too large
+ 3724 - 0
wrappers/modelverse_SCCD.py


+ 25 - 0
wrappers/modelverse_SCCD.xml

@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<diagram author="Yentl Van Tendeloo" name="MvK Server">
+    <description>
+        Modelverse Kernel client.
+    </description>
+    <top>
+        import sccd.runtime.accurate_time as accurate_time
+        import time
+        import os
+        import sys
+        import uuid
+        import urllib
+        import json
+        import sys
+    </top>
+
+    <inport name="socket_in"/>
+    <outport name="socket_out"/>
+    <inport name="action_in"/>
+    <outport name="action_out"/>
+    <outport name="ready"/>
+
+    <class src="classes/modelverse.xml" default="true"/>
+    <class src="classes/http_client.xml"/>
+</diagram>

+ 319 - 0
wrappers/modelverse_coded.py

@@ -0,0 +1,319 @@
+import urllib
+import urllib2
+import json
+import random
+from urllib2 import URLError
+import sys
+import time
+import threading
+
+COMPILER_PATH = "interface/HUTN"
+
+MODE_UNCONNECTED = 0
+MODE_UNAUTHORIZED = 1
+MODE_MODELLING = 2
+MODE_SERVICE = 6
+
+# Bind to the compiler (might have to update path manually!)
+sys.path.append(COMPILER_PATH)
+from hutn_compiler.compiler import main as do_compile
+
+# Exceptions
+class ModelverseException(Exception):
+    pass
+
+class UnknownError(ModelverseException):
+    pass
+
+class UnknownIdentifier(ModelverseException):
+    pass
+
+class CompilationError(ModelverseException):
+    pass
+
+class NoSuchAttribute(ModelverseException):
+    pass
+
+class UnknownModel(ModelverseException):
+    pass
+
+class ConnectionError(ModelverseException):
+    pass
+
+class ModelExists(ModelverseException):
+    pass
+
+class PermissionDenied(ModelverseException):
+    pass
+
+class InvalidMode(ModelverseException):
+    pass
+
+class InterfaceMismatch(ModelverseException):
+    pass
+
+class UnknownMetamodellingHierarchy(ModelverseException):
+    pass
+
+# Helper functions and configuration: do not use yourself!
+taskname = None
+address = None
+last_output = None
+mode = MODE_UNCONNECTED
+prev_mode = None
+current_model = None
+registered_metamodels = {}
+
+def _check_type(value):
+    if not isinstance(value, (int, long, float, str, unicode, bool)):
+        raise UnsupportedValue("%s : %s" % (value, str(type(value))))
+
+def _check_type_list(value):
+    if isinstance(value, list):
+        [_check_type(i) for i in value]
+    else:
+        _check_type(value)
+
+def _goto_mode(new_mode, model_name=None):
+    global mode
+
+    if mode == new_mode:
+        return
+    else:
+        # Go to a mode that we have no automatic transfer to: raise exception
+        raise InvalidMode("Required mode: %s, current mode: %s" % (new_mode, mode))
+
+def _input(value, port=None):
+    # Ugly json encoding of primitives
+    if port is None:
+        port = taskname
+    if isinstance(value, type([])):
+        value = json.dumps(value)
+        urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "set_input", "data": value, "taskname": port}))).read()
+    else:
+        value = json.dumps(value)
+        #print("Set input: " + str(value))
+        urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "set_input", "value": value, "taskname": port}))).read()
+
+def _input_raw(value, taskname):
+    # Ugly json encoding of primitives
+    urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "set_input", "value": value, "taskname": taskname}))).read()
+
+def _compile_AL(code):
+    # Compile an action language file and send the compiled code
+    code_fragments = code.split("\n")
+    code_fragments = [i for i in code_fragments if i.strip() != ""]
+    code_fragments = [i.replace("    ", "\t") for i in code_fragments]
+    initial_tabs = min([len(i) - len(i.lstrip("\t")) for i in code_fragments])
+    code_fragments = [i[initial_tabs:] for i in code_fragments]
+    code_fragments.append("")
+    code = "\n".join(code_fragments)
+
+    with open(".code.alc", "w") as f:
+        f.write(code)
+        f.flush()
+
+    compiled = do_compile(".code.alc", COMPILER_PATH + "/grammars/actionlanguage.g", "CS")
+    return compiled
+
+def _compile_model(code):
+    # Compile a model and send the compiled graph
+    # First change multiple spaces to a tab
+    code_fragments = code.split("\n")
+    code_fragments = [i for i in code_fragments if i.strip() != ""]
+    code_fragments = [i.replace("    ", "\t") for i in code_fragments]
+    initial_tabs = min([len(i) - len(i.lstrip("\t")) for i in code_fragments])
+    code_fragments = [i[initial_tabs:] for i in code_fragments]
+    code_fragments.append("")
+    code = "\n".join(code_fragments)
+
+    with open(".model.mvc", "w") as f:
+        f.write(code)
+        f.flush()
+
+    return do_compile(".model.mvc", COMPILER_PATH + "/grammars/modelling.g", "M")
+
+def _output(expected=None,port=None):
+    if port is None:
+        port = taskname
+    try:
+        global last_output
+        last_output = json.loads(urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "get_output", "taskname": port}))).read())
+        #print("[OUT] %s" % last_output)
+    except:
+        raise UnknownError()
+    if expected is not None and last_output != expected:
+        raise InterfaceMismatch(_last_output(), expected)
+    return last_output
+
+def _last_output():
+    return last_output
+
+# Raise common exceptions
+def _handle_output(requested=None, split=None):
+    value = _output()
+    if value.startswith("Model exists: "):
+        raise ModelExists(value.split(": ", 1)[1])
+    elif value.startswith("Permission denied"):
+        raise PermissionDenied(value.split(": ", 1)[1])
+    elif value.startswith("Model not found: "):
+        raise UnknownModel(value.split(": ", 1)[1])
+    elif value.startswith("Element not found: "):
+        raise UnknownIdentifier(value.split(": ", 1)[1])
+    elif value.startswith("Element exists: "):
+        raise ElementExists(value.split(": ", 1)[1])
+    elif value.startswith("Attribute not found: "):
+        raise NoSuchAttribute(value.split(": ", 1)[1])
+    elif requested is not None and value.startswith(requested):
+        if split is None:
+            return value
+        else:
+            splitted = value.strip().split(split, 1)
+            if len(splitted) == 1:
+                return ""
+            else:
+                return splitted[1].rstrip()
+    else:
+        raise InterfaceMismatch(value)
+    
+def _model_modify(model_name, metamodel_name):
+    """Modify an existing model."""
+    global mode
+    global prev_mode
+
+    if mode == MODE_MANUAL:
+        prev_mode = MODE_MANUAL
+        mode = MODE_MODIFY
+        return None
+
+    _goto_mode(MODE_MODELLING)
+    prev_mode = MODE_MODELLING
+
+    _input(["model_modify", model_name, metamodel_name])
+    _handle_output("Success")
+    global current_model
+    current_model = model_name
+    # Mode has changed
+    mode = MODE_MODIFY
+    _output("Model loaded, ready for commands!")
+
+def _model_exit():
+    """Leave model modify mode."""
+    global mode
+    global prev_mode
+
+    if prev_mode == MODE_MANUAL:
+        mode = MODE_MANUAL
+        return
+
+    if mode != MODE_MODIFY:
+        raise InvalidMode()
+
+    _input("exit")
+    _output("Success")
+    mode = MODE_MODELLING
+
+def alter_context(model_name, metamodel_name):
+    global registered_metamodels
+    registered_metamodels[model_name] = metamodel_name
+
+# Main MvC operations
+def init(address_param="127.0.0.1:8001", timeout=20.0):
+    """Starts up the connection to the Modelverse."""
+    global mode
+    global address
+    global taskname
+    address = "http://%s" % address_param
+    start_time = time.time()
+    taskname = random.random()
+    while 1:
+        try:
+            _input_raw('"%s"' % taskname, "task_manager")
+            mode = MODE_UNAUTHORIZED
+            break
+        except URLError as e:
+            if time.time() - start_time > timeout:
+                raise ConnectionError(e.reason)
+            else:
+                time.sleep(0.1)
+
+def login(username, password):
+    """Log in a user, if user doesn't exist, it is created."""
+    global mode
+    _goto_mode(MODE_UNAUTHORIZED)
+
+    _output("Log on as which user?")
+    _input(username)
+    if _output() == "Password for existing user?":
+        _input(password)
+        if _output() == "Welcome to the Model Management Interface v2.0!":
+            _output("Use the 'help' command for a list of possible commands")
+            _input("quiet")
+            mode = MODE_MODELLING
+        elif _last_output() == "Wrong password!":
+            raise PermissionDenied()
+        else:
+            raise InterfaceMismatch(_last_output())
+    elif _last_output() == "This is a new user: please give password!":
+        _input(password)
+        _output("Please repeat the password")
+        _input(password)
+        if _output() == "Passwords match!":
+            _output("Welcome to the Model Management Interface v2.0!")
+            _output("Use the 'help' command for a list of possible commands")
+            _input("quiet")
+            mode = MODE_MODELLING
+        elif _last_output() == "Not the same password!":
+            # We just sent the same password, so it should be identical, unless the interface changed
+            raise InterfaceMismatch(_last_output())
+        else:
+            raise InterfaceMismatch(_last_output())
+    else:
+        raise InterfaceMismatch(_last_output())
+
+def service_register(name, function):
+    """Register a function as a service with a specific name."""
+
+    def service_process(port):
+        while 1:
+            thrd = threading.Thread(target=function, args=[service_get(port)])
+            thrd.daemon = True
+            thrd.start()
+
+    global mode
+    _goto_mode(MODE_MODELLING)
+
+    _input(["service_register", name])
+
+    # Now we are in service-mode
+    mode = MODE_SERVICE
+    port = _handle_output("Success: ", split=" ")
+
+    # Process events in the background!
+    threading.Thread(target=service_process, args=[port]).start()
+
+def service_stop():
+    """Stop the currently executing process."""
+    _goto_mode(MODE_SERVICE)
+    _input("service_stop")
+    _handle_output("Success")
+
+    global mode
+    mode = MODE_MODELLING
+
+def service_get(port):
+    """Get the values on the specified port."""
+    _goto_mode(MODE_SERVICE)
+
+    return _output(port=port)
+
+def service_set(port, value):
+    """Set a value on a specified port."""
+    _check_type_list(value)
+    _goto_mode(MODE_SERVICE)
+
+    _input(value, port=port)
+
+def service_poll(port):
+    """Checks whether or not the Modelverse side has any input ready to be processed."""
+    raise NotImplementedError()