Quellcode durchsuchen

Merge branch 'stable' into pn_debugging

Yentl Van Tendeloo vor 8 Jahren
Ursprung
Commit
1da5f45844
55 geänderte Dateien mit 3137 neuen und 1525 gelöschten Zeilen
  1. 10 2
      bootstrap/90_core_formalism.mvc
  2. 239 169
      bootstrap/99_core.mvc
  3. 4 2
      bootstrap/bootstrap.py
  4. 2 2
      bootstrap/bottom.mvc
  5. 11 5
      bootstrap/conformance_finding.alc
  6. 383 247
      bootstrap/core_algorithm.alc
  7. 5 9
      bootstrap/metamodels.alt
  8. 4 1
      bootstrap/mini_modify.alc
  9. 20 17
      bootstrap/modelling.alc
  10. 1 1
      bootstrap/object_operations.alc
  11. 1 0
      bootstrap/primitives.alc
  12. 1 1
      bootstrap/semi_primitives.alc
  13. 4 0
      bootstrap/services.alc
  14. 5 2
      bootstrap/transform.alc
  15. 61 5
      bootstrap/typing.alc
  16. 4 4
      doc/actionlanguage.rst
  17. 1 0
      doc/components.rst
  18. 4 21
      doc/concrete_syntax.rst
  19. 8 4
      doc/conformance.rst
  20. 25 25
      doc/metamodelling.rst
  21. 2 2
      doc/modeller_modellanguage.rst
  22. 44 32
      doc/modelling.rst
  23. 240 7
      doc/operations.rst
  24. 17 8
      doc/permissions.rst
  25. 3 3
      doc/process_enactment.rst
  26. 9 0
      doc/scripts.rst
  27. 172 6
      doc/services.rst
  28. 195 9
      doc/transformations.rst
  29. 119 116
      doc/wrappers.rst
  30. 688 0
      integration/code/SCCD_all.mvc
  31. 8 8
      integration/code/pm_pn_reachability.mvc
  32. 0 373
      integration/test_mvc.py
  33. 76 157
      integration/test_powerwindow.py
  34. 25 19
      interface/HUTN/hutn_compiler/hutnparser.py
  35. 0 78
      interface/HUTN/hutn_compiler/loader.py
  36. 2 1
      interface/HUTN/hutn_compiler/model_bootstrap_visitor.py
  37. 1 0
      interface/HUTN/includes/primitives.alh
  38. 4 2
      interface/HUTN/includes/typing.alh
  39. 10 10
      interface/PN/main.py
  40. 24 24
      interface/PN/modelverse.py
  41. 8 3
      kernel/modelverse_kernel/compiled.py
  42. 13 6
      kernel/modelverse_kernel/primitives.py
  43. 1 1
      kernel/test/primitives/test_cast.py
  44. 9 0
      models/SCCD_Trace.mvc
  45. 27 7
      models/SCCD_execute.alc
  46. 0 1
      models/bfs.alc
  47. 40 40
      models/pm_req_analyse.mvc
  48. 46 46
      models/pm_req_analyse_debug.mvc
  49. 7 1
      scripts/run_fast_tests.py
  50. 3 1
      scripts/run_tests.py
  51. 8 11
      state/modelverse_state/main.py
  52. 8 12
      state/test/test_read_reverse_dict.py
  53. 429 0
      unit/test_all.py
  54. 82 0
      unit/utils.py
  55. 24 24
      wrappers/modelverse.py

+ 10 - 2
bootstrap/90_core_formalism.mvc

@@ -66,12 +66,20 @@ SimpleClassDiagrams CoreFormalism {
     Association ownedBy (Group, User) {}
     Association belongsTo (User, Group) {}
 
-    Class Model {
+    Class Entry {
         name : String
-        location : String
         permissions : Permissions
     }
 
+    Class Folder : Entry {
+    }
+
+    Association contains (Folder, Entry) {}
+
+    Class Model : Entry {
+        location : String
+    }
+
     Class TypeMapping : Model {}
 
     Association instanceOf (Model, Model) {

+ 239 - 169
bootstrap/99_core.mvc

@@ -18,131 +18,255 @@ CF core {
     ownedBy (admin_group, admin_user) {}
     belongsTo (admin_user, admin_group) {}
 
-    Model SimpleClassDiagrams {
-        name = "SimpleClassDiagrams"
-        location = "models/SimpleClassDiagrams/model"
+    Folder root {
+        name = ""
         permissions = "221"
-    }
-
-    group (SimpleClassDiagrams, admin_group) {}
-    owner (SimpleClassDiagrams, admin_user) {}
-
-    TypeMapping TM_SimpleClassDiagrams {
-        name = "TM_SimpleClassDiagrams"
-        location = "models/SimpleClassDiagrams/type_mapping"
-        permissions = "221"
-    }
-
-    group (TM_SimpleClassDiagrams, admin_group) {}
-    owner (TM_SimpleClassDiagrams, admin_user) {}
 
-    Model TypeMapping {
-        name = "TypeMapping"
-        location = "models/TypeMapping/model"
-        permissions = "221"
+        Folder formalisms {
+            name = "formalisms"
+            permissions = "221"
+
+            Model SimpleClassDiagrams {
+                name = "formalisms/SimpleClassDiagrams"
+                location = "models/SimpleClassDiagrams/model"
+                permissions = "221"
+            }
+
+            Model TypeMapping {
+                name = "formalisms/TypeMapping"
+                location = "models/TypeMapping/model"
+                permissions = "221"
+            }
+
+            Model Tracability {
+                name = "formalisms/Tracability"
+                location = "models/Tracability/model"
+                permissions = "221"
+            }
+
+            Model ProcessModel {
+                name = "formalisms/ProcessModel"
+                location = "models/ProcessModel/model"
+                permissions = "221"
+            }
+
+            Model ActionLanguage {
+                name = "formalisms/ActionLanguage"
+                location = "models/ActionLanguage/model"
+                permissions = "221"
+            }
+
+            Model ManualOperation {
+                name = "formalisms/ManualOperation"
+                location = "models/ManualOperation/model"
+                permissions = "221"
+            }
+
+            Model Bottom {
+                name = "formalisms/Bottom"
+                location = "models/Bottom/model"
+                permissions = "221"
+            }
+        }
+
+        Folder models {
+            name = "models"
+            permissions = "221"
+
+            ActionLanguage conformance_mv {
+                name = "models/conformance_mv"
+                location = "models/Conformance_MV/model"
+                permissions = "221"
+            }
+
+        }
+
+        Folder typemappings {
+            name = "type mappings"
+            permissions = "221"
+
+            Folder tm_formalisms {
+                name = "type mappings/formalisms"
+                permissions = "110"
+
+                TypeMapping TM_SimpleClassDiagrams {
+                    name = "type mappings/formalisms/SimpleClassDiagrams"
+                    location = "models/SimpleClassDiagrams/types"
+                    permissions = "221"
+                }
+
+                TypeMapping TM_TypeMapping {
+                    name = "type mappings/formalisms/TypeMapping"
+                    location = "models/TypeMapping/types"
+                    permissions = "221"
+                }
+
+                TypeMapping TM_Tracability{
+                    name = "type mappings/formalisms/Tracability"
+                    location = "models/Tracability/types"
+                    permissions = "221"
+                }
+
+                TypeMapping TM_ProcessModel {
+                    name = "type mappings/formalisms/ProcessModel"
+                    location = "models/ProcessModel/types"
+                    permissions = "221"
+                }
+
+                TypeMapping TM_ActionLanguage {
+                    name = "type mappings/formalisms/ActionLanguage"
+                    location = "models/ActionLanguage/types"
+                    permissions = "221"
+                }
+
+                TypeMapping TM_ManualOperation {
+                    name = "type mappings/formalisms/ManualOperation"
+                    location = "models/ManualOperation/types"
+                    permissions = "221"
+                }
+
+                TypeMapping TM_Bottom {
+                    name = "type mappings/formalisms/Bottom"
+                    location = "models/Bottom/types"
+                    permissions = "221"
+                }
+
+            }
+
+            Folder tm_models {
+                name = "type mappings/models"
+                permissions = "110"
+
+                TypeMapping TM_conformance_mv {
+                    name = "type mappings/models/conformance_mv"
+                    location = "models/Conformance_MV/types"
+                    permissions = "221"
+                }
+            }
+
+            Folder tm_administration {
+                name = "type mappings/administration"
+                permissions = "100"
+
+                TypeMapping TM_Core {
+                    name = "type mappings/administration/core"
+                    location = "models/core/types"
+                    permissions = "220"
+                }
+
+                TypeMapping TM_CoreFormalism {
+                    name = "type mappings/administration/CoreFormalism"
+                    location = "models/CoreFormalism/types"
+                    permissions = "221"
+                }
+            }
+
+            Folder tm_users {
+                name = "type mapping/users"
+                permissions = "110"
+
+                Folder tm_users_admin {
+                    name = "type mapping/users/admin"
+                    permissions = "100"
+                }
+            }
+        }
+
+        Folder users {
+            name = "users"
+            permissions = "221"
+
+            Folder users_admin {
+                name = "users/admin"
+                permissions = "200"
+            }
+        }
+
+        Folder administration {
+            name = "administration"
+            permissions = "110"
+
+            Model CoreFormalism {
+                name = "administration/CoreFormalism"
+                location = "models/CoreFormalism/model"
+                permissions = "221"
+            }
+
+            Model Core {
+                name = "administration/core"
+                location = "models/core/model"
+                permissions = "220"
+            }
+        }
     }
 
+    group (root, admin_group) {}
+    group (formalisms, admin_group) {}
+    group (SimpleClassDiagrams, admin_group) {}
     group (TypeMapping, admin_group) {}
-    owner (TypeMapping, admin_user) {}
-
-    TypeMapping TM_TypeMapping {
-        name = "TM_TypeMapping"
-        location = "models/TypeMapping/type_mapping"
-        permissions = "221"
-    }
-
-    group (TM_TypeMapping, admin_group) {}
-    owner (TM_TypeMapping, admin_user) {}
-
-    Model Tracability {
-        name = "Tracability"
-        location = "models/Tracability/model"
-        permissions = "221"
-    }
-
     group (Tracability, admin_group) {}
-    owner (Tracability, admin_user) {}
-
-    TypeMapping TM_Tracability{
-        name = "TM_Tracability"
-        location = "models/Tracability/type_mapping"
-        permissions = "221"
-    }
-
-    group (TM_Tracability, admin_group) {}
-    owner (TM_Tracability, admin_user) {}
-
-    Model ProcessModel {
-        name = "ProcessModel"
-        location = "models/ProcessModel/model"
-        permissions = "221"
-    }
-
     group (ProcessModel, admin_group) {}
-    owner (ProcessModel, admin_user) {}
-
-    TypeMapping TM_ProcessModel {
-        name = "TM_ProcessModel"
-        location = "models/ProcessModel/type_mapping"
-        permissions = "221"
-    }
-
-    group (TM_ProcessModel, admin_group) {}
-    owner (TM_ProcessModel, admin_user) {}
-
-    Model ActionLanguage {
-        name = "ActionLanguage"
-        location = "models/ActionLanguage/model"
-        permissions = "221"
-    }
-
     group (ActionLanguage, admin_group) {}
-    owner (ActionLanguage, admin_user) {}
-
-    TypeMapping TM_ActionLanguage {
-        name = "TM_ActionLanguage"
-        location = "models/ActionLanguage/type_mapping"
-        permissions = "221"
-    }
-
-    group (TM_ActionLanguage, admin_group) {}
-    owner (TM_ActionLanguage, admin_user) {}
-
-    Model ManualOperation {
-        name = "ManualOperation"
-        location = "models/ManualOperation/model"
-        permissions = "221"
-    }
-
     group (ManualOperation, admin_group) {}
-    owner (ManualOperation, admin_user) {}
-
-    TypeMapping TM_ManualOperation {
-        name = "TM_ManualOperation"
-        location = "models/ManualOperation/type_mapping"
-        permissions = "221"
-    }
+    group (Bottom, admin_group) {}
+    group (models, admin_group) {}
+    group (conformance_mv, admin_group) {}
+    group (administration, admin_group) {}
+    group (CoreFormalism, admin_group) {}
+    group (Core, admin_group) {}
+    group (users, admin_group) {}
+    group (users_admin, nobody) {}
 
+    group (typemappings, admin_group) {}
+    group (tm_formalisms, admin_group) {}
+    group (TM_SimpleClassDiagrams, admin_group) {}
+    group (TM_TypeMapping, admin_group) {}
+    group (TM_Tracability, admin_group) {}
+    group (TM_ProcessModel, admin_group) {}
+    group (TM_ActionLanguage, admin_group) {}
+    group (TM_Bottom, admin_group) {}
     group (TM_ManualOperation, admin_group) {}
-    owner (TM_ManualOperation, admin_user) {}
-
-    ActionLanguage conformance_mv {
-        name = "conformance_mv"
-        location = "models/Conformance_MV/model"
-        permissions = "221"
-    }
+    group (tm_models, admin_group) {}
+    group (TM_conformance_mv, admin_group) {}
+    group (tm_administration, admin_group) {}
+    group (TM_CoreFormalism, admin_group) {}
+    group (TM_Core, admin_group) {}
+    group (tm_users, admin_group) {}
+    group (tm_users_admin, admin_group) {}
 
-    group (conformance_mv, admin_group) {}
+    owner (root, admin_user) {}
+    owner (formalisms, admin_user) {}
+    owner (SimpleClassDiagrams, admin_user) {}
+    owner (TypeMapping, admin_user) {}
+    owner (Tracability, admin_user) {}
+    owner (ProcessModel, admin_user) {}
+    owner (ActionLanguage, admin_user) {}
+    owner (ManualOperation, admin_user) {}
+    owner (Bottom, admin_user) {}
+    owner (models, admin_user) {}
     owner (conformance_mv, admin_user) {}
+    owner (administration, admin_user) {}
+    owner (CoreFormalism, admin_user) {}
+    owner (Core, admin_user) {}
+    owner (users, admin_user) {}
+    owner (users_admin, admin_user) {}
 
-    TypeMapping TM_conformance_mv {
-        name = "TM_conformance_mv"
-        location = "models/Conformance_MV/type_mapping"
-        permissions = "221"
-    }
-
-    group (TM_conformance_mv, admin_group) {}
+    owner (typemappings, admin_user) {}
+    owner (tm_formalisms, admin_user) {}
+    owner (TM_SimpleClassDiagrams, admin_user) {}
+    owner (TM_TypeMapping, admin_user) {}
+    owner (TM_Tracability, admin_user) {}
+    owner (TM_ProcessModel, admin_user) {}
+    owner (TM_ActionLanguage, admin_user) {}
+    owner (TM_ManualOperation, admin_user) {}
+    owner (TM_Bottom, admin_user) {}
+    owner (tm_models, admin_user) {}
     owner (TM_conformance_mv, admin_user) {}
+    owner (tm_administration, admin_user) {}
+    owner (TM_CoreFormalism, admin_user) {}
+    owner (TM_Core, admin_user) {}
+    owner (tm_users, admin_user) {}
+    owner (tm_users_admin, admin_user) {}
 
     transformInput (conformance_mv, SimpleClassDiagrams) {
         name = "model"
@@ -151,60 +275,6 @@ CF core {
         name = "metamodel"
     }
 
-    Model CoreFormalism {
-        name = "CoreFormalism"
-        location = "models/CoreFormalism/model"
-        permissions = "221"
-    }
-
-    group (CoreFormalism, admin_group) {}
-    owner (CoreFormalism, admin_user) {}
-
-    TypeMapping TM_CoreFormalism {
-        name = "TM_CoreFormalism"
-        location = "models/CoreFormalism/type_mapping"
-        permissions = "221"
-    }
-
-    group (TM_CoreFormalism, admin_group) {}
-    owner (TM_CoreFormalism, admin_user) {}
-
-    Model Core {
-        name = "core"
-        location = "models/core/model"
-        permissions = "220"
-    }
-
-    group (Core, admin_group) {}
-    owner (Core, admin_user) {}
-
-    TypeMapping TM_Core {
-        name = "TM_core"
-        location = "models/core/type_mapping"
-        permissions = "220"
-    }
-
-    group (TM_Core, admin_group) {}
-    owner (TM_Core, admin_user) {}
-
-    Model bottom {
-        name = "bottom"
-        location = "models/bottom/model"
-        permissions = "221"
-    }
-
-    group (bottom, admin_group) {}
-    owner (bottom, admin_user) {}
-
-    TypeMapping TM_bottom {
-        name = "TM_bottom"
-        location = "models/bottom/type_mapping"
-        permissions = "221"
-    }
-
-    group (TM_bottom, admin_group) {}
-    owner (TM_bottom, admin_user) {}
-
     instanceOf inst_SCD (SimpleClassDiagrams, SimpleClassDiagrams) {}
     instanceOf inst_TM (TypeMapping, SimpleClassDiagrams) {}
     instanceOf inst_trace (Tracability, SimpleClassDiagrams) {}
@@ -214,7 +284,7 @@ CF core {
     instanceOf inst_conf (conformance_mv, ActionLanguage) {}
     instanceOf inst_cf (CoreFormalism, SimpleClassDiagrams) {}
     instanceOf inst_core (Core, CoreFormalism) {}
-    instanceOf inst_bottom (bottom, SimpleClassDiagrams) {}
+    instanceOf inst_Bottom (Bottom, SimpleClassDiagrams) {}
 
     instanceOf TM_inst_SCD (TM_SimpleClassDiagrams, TypeMapping) {}
     instanceOf TM_inst_TM (TM_TypeMapping, TypeMapping) {}
@@ -225,7 +295,7 @@ CF core {
     instanceOf TM_inst_conf (TM_conformance_mv, TypeMapping) {}
     instanceOf TM_inst_cf (TM_CoreFormalism, TypeMapping) {}
     instanceOf TM_inst_core (TM_Core, TypeMapping) {}
-    instanceOf TM_inst_bottom (TM_bottom, TypeMapping) {}
+    instanceOf TM_inst_Bottom (TM_Bottom, TypeMapping) {}
 
     typing (inst_SCD, TM_SimpleClassDiagrams) {}
     typing (inst_TM, TM_TypeMapping) {}
@@ -236,7 +306,7 @@ CF core {
     typing (inst_conf, TM_conformance_mv) {}
     typing (inst_cf, TM_CoreFormalism) {}
     typing (inst_core, TM_Core) {}
-    typing (inst_bottom, TM_bottom) {}
+    typing (inst_Bottom, TM_Bottom) {}
 
     semantics (inst_SCD, conformance_mv) {}
     semantics (inst_TM, conformance_mv) {}
@@ -247,7 +317,7 @@ CF core {
     semantics (inst_conf, conformance_mv) {}
     semantics (inst_cf, conformance_mv) {}
     semantics (inst_core, conformance_mv) {}
-    semantics (inst_bottom, conformance_mv) {}
+    semantics (inst_Bottom, conformance_mv) {}
 
     semantics (TM_inst_SCD, conformance_mv) {}
     semantics (TM_inst_TM, conformance_mv) {}
@@ -258,7 +328,7 @@ CF core {
     semantics (TM_inst_conf, conformance_mv) {}
     semantics (TM_inst_cf, conformance_mv) {}
     semantics (TM_inst_core, conformance_mv) {}
-    semantics (TM_inst_bottom, conformance_mv) {}
+    semantics (TM_inst_Bottom, conformance_mv) {}
 }
 
 export core to models/core

+ 4 - 2
bootstrap/bootstrap.py

@@ -75,6 +75,7 @@ def bootstrap():
                     "create_edge": ["Element", "Element", "Element"],
                     "create_value": ["Element", "Element"],
                     "is_edge": ["Boolean", "Element"],
+                    "is_error": ["Boolean", "Element"],
                     "read_nr_out": ["Integer", "Element"],
                     "read_out": ["Element", "Element", "Integer"],
                     "read_nr_in": ["Integer", "Element"],
@@ -190,7 +191,7 @@ def bootstrap():
                     return model_code + "\n"
 
                 # Compile all model definitions to ALC directly
-                total_alc = ""
+                total_alc = []
                 binding_alc = 'Void function initialize_MMs():\n\tinitialize_SCD("models/SimpleClassDiagrams")\n'
 
                 bootstrap_models = sorted(glob.glob("bootstrap/*.mvc"))
@@ -200,8 +201,9 @@ def bootstrap():
                     model_name = bootstrap_model.rsplit(".mvc", 1)[0].rsplit("/", 1)[1]
                     print("[MVC] %s" % model_name)
                     alc = compile_code_MO(bootstrap_model, model_name)
-                    total_alc += alc
+                    total_alc.append(alc)
                     binding_alc += "\tinitialize_%s()\n" % model_name
+                total_alc = "".join(total_alc)
 
                 # Write out the ALC to a new .metamodels.alc
                 binding_alc += "\treturn!\n"

+ 2 - 2
bootstrap/bottom.mvc

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

+ 11 - 5
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("Missing: " + 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...
@@ -50,22 +50,28 @@ Boolean function find_type_mapping(model : Element):
 		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
 
 		if (bool_or(element_eq(node_element, read_root()), element_eq(edge_element, read_root()))):
+			log("Not both node and edge detected")
 			return False!
 
 		// Now we have bot an edge_element and node_element of the metamodel
@@ -74,7 +80,7 @@ Boolean function find_type_mapping(model : Element):
 		while (set_len(elems) > 0):
 			elem = set_pop(elems)
 
-			if (is_edge(elem)):
+			if (is_edge(model["model"][elem])):
 				retype(model, elem, edge_element)
 			else:
 				retype(model, elem, node_element)

Datei-Diff unterdrückt, da er zu groß ist
+ 383 - 247
bootstrap/core_algorithm.alc


+ 5 - 9
bootstrap/metamodels.alt

@@ -3,6 +3,7 @@ include "object_operations.alh"
 include "library.alh"
 include "conformance_scd.alh"
 include "modelling.alh"
+include "typing.alh"
 
 String function constraint_Natural(model : Element, name : String):
 	Element self
@@ -139,16 +140,9 @@ Element function initialize_SCD(location : String):
 	instantiate_node(scd, "Class", "GlobalConstraint")
 	model_define_attribute(scd, "GlobalConstraint", "global_constraint", False, "ActionLanguage")
 
-	return scd!
-
-Void function create_metamodels():
-	String location_SCD
-	String location_PN
-
-	location_SCD = "models/SimpleClassDiagrams"
-	location_PN = "models/PetriNets"
+	dict_overwrite(scd, "types", get_type_mapping(scd))
 
-	return!
+	return scd!
 
 Void function initialize_AL(scd_location : String, export_location : String):
 	// TODO this should be written in a file-based model and not created like this in the bootstrap
@@ -304,5 +298,7 @@ Void function initialize_AL(scd_location : String, export_location : String):
 	instantiate_attribute(model, "call_params", "target_upper_cardinality", 1)
 	instantiate_attribute(model, "call_last_param", "target_upper_cardinality", 1)
 
+	dict_overwrite(model, "types", get_type_mapping(model))
+
 	export_node(export_location, model)
 	return !

+ 4 - 1
bootstrap/mini_modify.alc

@@ -6,6 +6,7 @@ include "conformance_scd.alh"
 include "io.alh"
 include "metamodels.alh"
 include "modelling.alh"
+include "typing.alh"
 
 Boolean verbose = True
 
@@ -50,6 +51,8 @@ String function pretty_print(model : Element):
 				else:
 					result = result + ((((("      " + cast_v2s(attr_key)) + " : ") + cast_v2s(attr_list[attr_key])) + " = ") + cast_v2s(read_attribute(model, v_m, attr_key)))
 				result = result + "\n"
+		else:
+			log("Skip instance: " + type)
 	return result!
 
 String function cmd_help_m(write : Boolean):
@@ -86,7 +89,7 @@ String function cmd_upload(write : Boolean, model : Element):
 		output("Waiting for model constructors...")
 		new_model = construct_model_raw(model["metamodel"])
 		dict_overwrite(model, "model", new_model["model"])
-		dict_overwrite(model, "type_mapping", new_model["type_mapping"])
+		set_type_mapping(model, get_type_mapping(new_model))
 		return "Success"!
 	else:
 		return "Permission denied to write"!

+ 20 - 17
bootstrap/modelling.alc

@@ -75,10 +75,7 @@ String function model_add_edge(model : Element, name : String, source : String,
 
 Void function retype_model(model : Element, metamodel : Element):
 	// Remove the type mapping and add a new one for the specified metamodel
-	if (dict_in(model, "type_mapping")):
-		dict_delete(model, "type_mapping")
-
-	dict_add_fast(model, "type_mapping", new_type_mapping())
+	new_type_mapping(model)
 	dict_add_fast(model, "metamodel", metamodel)
 	return!
 
@@ -145,7 +142,7 @@ String function find_attribute_type(model : Element, elem : String, name : Strin
 	
 	mm_elem = find_attribute_definer(model["metamodel"], direct_type, name)
 
-	if (value_eq(mm_elem, "")):
+	if (mm_elem == ""):
 		// Couldn't find element, so is not allowed!
 		return ""!
 	else:
@@ -189,6 +186,7 @@ Element function get_superclasses(model : Element, name : String):
 	Element edge
 	String elem
 	Element nodes
+	String edge_name
 
 	nodes = set_create()
 	set_add(nodes, name)
@@ -205,8 +203,9 @@ Element function get_superclasses(model : Element, name : String):
 			j = 0
 			while (j < num_edges):
 				edge = read_out(model["model"][elem], j)
-				if (dict_in(model["model"], reverseKeyLookup(model["model"], edge))):
-					if (read_type(model, reverseKeyLookup(model["model"], edge)) == "Inheritance"):
+				edge_name = reverseKeyLookup(model["model"], edge)
+				if (edge_name != ""):
+					if (read_type(model, edge_name) == "Inheritance"):
 						set_add(nodes, reverseKeyLookup(model["model"], read_edge_dst(edge)))
 				j = j + 1
 
@@ -218,18 +217,22 @@ String function find_attribute_definer(model : Element, elem_name : String, name
 	Integer nr_out
 	Element out
 	String name_attr
+	String elem
 
 	superclasses = get_superclasses(model, elem_name)
 	while (set_len(superclasses) > 0):
 		current = set_pop(superclasses)
 		
+		// TODO is it possible to use allOutgoingAssociationInstances here?
 		nr_out = read_nr_out(model["model"][current])
 		while (nr_out > 0):
 			nr_out = nr_out - 1
 			out = read_out(model["model"][current], nr_out)
-			name_attr = read_attribute(model, reverseKeyLookup(model["model"], out), "name")
-			if (name_attr == name):
-				return current!
+			elem = reverseKeyLookup(model["model"], out)
+			if (elem != ""):
+				name_attr = read_attribute(model, elem, "name")
+				if (name_attr == name):
+					return current!
 
 	return ""!
 
@@ -245,9 +248,6 @@ Void function instantiate_attribute(model : Element, element : String, attribute
 	attr_type = find_attribute_type(model, element, attribute_name)
 
 	if (attr_type == ""):
-		log("Could not find attribute " + cast_v2s(attribute_name))
-		log("For element " + element)
-		log("Type: " + read_type(model, element))
 		return!
 
 	if (has_value(value)):
@@ -339,10 +339,12 @@ String function instantiate_link(model : Element, type : String, name : String,
 
 	if (bool_not(dict_in(model["model"], source))):
 		log("ERROR: source of link undefined: " + source)
+		set_pop(create_node())
 		return ""!
 
 	if (bool_not(dict_in(model["model"], destination))):
 		log("ERROR: destination of link undefined: " + destination)
+		set_pop(create_node())
 		return ""!
 
 	if (bool_not(dict_in(model["metamodel"]["model"], type))):
@@ -385,7 +387,7 @@ Element function read_attribute(model : Element, element : String, attribute : S
 		Element edge
 		Element edge_type
 		Element elem
-		Element name
+		String name
 
 		elem = model["model"][element]
 		count = read_nr_out(elem)
@@ -394,9 +396,10 @@ Element function read_attribute(model : Element, element : String, attribute : S
 		while (i < count):
 			edge = read_out(elem, i)
 			name = reverseKeyLookup(model["model"], edge)
-			edge_type = model["metamodel"]["model"][read_type(model, name)]
-			if (element_eq(edge_type, dict_read_edge(read_edge_src(edge_type), attribute))):
-				return read_edge_dst(edge)!
+			if (name != ""):
+				edge_type = model["metamodel"]["model"][read_type(model, name)]
+				if (element_eq(edge_type, dict_read_edge(read_edge_src(edge_type), attribute))):
+					return read_edge_dst(edge)!
 			i = i + 1
 
 	else:

+ 1 - 1
bootstrap/object_operations.alc

@@ -19,7 +19,7 @@ Element function allInstances(model : Element, type_name : String):
 
 		while (set_len(accepted) > 0):
 			class = set_pop(accepted)
-			set_merge(result, get_elements_typed_by(model, class))
+			set_merge(result, elements_typed_by(model, class))
 		return result!
 	else:
 		log("No such type in the metamodel: " + type_name)

+ 1 - 0
bootstrap/primitives.alc

@@ -67,3 +67,4 @@ Boolean function is_physical_action(a: Element) = ?primitives/is_physical_action
 Float function time() = ?primitives/time
 String function hash(a : String) = ?primitives/hash
 Float function __sleep(a : Float, b : Boolean) = ?primitives/__sleep
+Boolean function is_error() = ?primitives/is_error

+ 1 - 1
bootstrap/semi_primitives.alc

@@ -385,7 +385,7 @@ String function reverseKeyLookup(dict : Element, element : Element):
 
 		counter = counter + 1
 	
-	return string_join(string_join("(unknown: ", cast_e2s(element)), " )")!
+	return ""!
 
 Element function reverseKeyLookupMulti(dict : Element, element : Element):
 	// TODO don't know if this AL will actually work...

+ 4 - 0
bootstrap/services.alc

@@ -68,6 +68,10 @@ Element function comm_get(comm : String):
 String function comm_connect(service : String):
 	// Connect to an existing service
 	service = get_service_id(service)
+
+	if (service == ""):
+		return ""!
+
 	service = read_attribute(core, service, "port")
 
 	String port

+ 5 - 2
bootstrap/transform.alc

@@ -355,8 +355,8 @@ Void function rewrite(host_model : Element, schedule_model : Element, RHS : Stri
 	String new_name
 	Element RHS_map
 	String tmp
-	Element value
-	Element action
+	String value
+	String action
 	Element original_RHS_labels
 
 	Element reverse
@@ -437,6 +437,9 @@ Void function rewrite(host_model : Element, schedule_model : Element, RHS : Stri
 				elif (has_value(result)):
 					// New value defined, so assign!
 					instantiate_attribute(host_model, new_mapping[label], string_substr(attribute, string_len("value_"), string_len(attribute) + 1), result)
+				elif (is_error(result)):
+					log("Error in evaluation of attribute " + attribute)
+					log("On element with label " + label)
 				else:
 					// Try to interpret as code
 					instantiate_attribute_code(host_model, new_mapping[label], string_substr(attribute, string_len("value_"), string_len(attribute) + 1), result)

+ 61 - 5
bootstrap/typing.alc

@@ -1,6 +1,62 @@
 include "primitives.alh"
 include "utils.alh"
 
+Element function get_type_mapping(model : Element):
+	// Deserialize dictionary as model
+	Element tm_model
+	tm_model = dict_create()
+
+	Element m
+	Element mm
+	Element edge
+	Element keys
+	String key
+
+	keys = dict_keys(model["type_mapping"])
+
+	while (set_len(keys) > 0):
+		key = set_pop(keys)
+		m = model["model"][key]
+		mm = model["metamodel"]["model"][model["type_mapping"][key]]
+		edge = create_edge(m, mm)
+
+		dict_add_fast(tm_model, cast_id2s(m), m)
+		if (bool_not(dict_in(tm_model, cast_id2s(mm)))):
+			dict_add_fast(tm_model, cast_id2s(mm), mm)
+		dict_add_fast(tm_model, cast_id2s(edge), edge)
+
+	return tm_model!
+
+Void function set_type_mapping(model : Element, type_mapping_model : Element):
+	// Serialize model to dictionary
+	Element type_mapping
+	Element rev_model
+	Element rev_metamodel
+	Element keys
+	String key
+
+	type_mapping = dict_create()
+	keys = dict_keys(type_mapping_model)
+	rev_model = make_reverse_dictionary(model["model"])
+	rev_metamodel = make_reverse_dictionary(model["metamodel"]["model"])
+
+	while (set_len(keys) > 0):
+		key = set_pop(keys)
+		if (is_edge(type_mapping_model[key])):
+			if (bool_not(bool_or(dict_in(rev_model, cast_id2s(type_mapping_model[key])), dict_in(rev_metamodel, cast_id2s(type_mapping_model[key]))))):
+				// Element is in neither model or metamodel
+				// Must be a typing link!
+				// So add it
+				dict_add_fast(type_mapping, rev_model[cast_id2s(read_edge_src(type_mapping_model[key]))], rev_metamodel[cast_id2s(read_edge_dst(type_mapping_model[key]))])
+
+	dict_overwrite(model, "type_mapping", type_mapping)
+	return!
+
+Element function elements_typed_by(model : Element, type_name : String):
+	Element result
+	result = reverseKeyLookupMulti(get_type_mapping_as_dict(model), type_name)
+	return result!
+
 Element function get_type_mapping_as_dict(model : Element):
 	return model["type_mapping"]!
 
@@ -13,7 +69,6 @@ String function read_type(model : Element, name : String):
 	if (dict_in(model["model"], name)):
 		if (dict_in(model["type_mapping"], name)):
 			result = model["type_mapping"][name]
-
 			if (dict_in(model["metamodel"]["model"], result)):
 				return result!
 			else:
@@ -26,15 +81,16 @@ 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)
 	return!
 
-Element function new_type_mapping():
-	return dict_create()!
+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())
+	return !
 
 Void function remove_type(model : Element, name : String):
 	dict_delete(model["type_mapping"], name)

+ 4 - 4
doc/actionlanguage.rst

@@ -24,13 +24,13 @@ In the context of Finite State Automata, it is often necessary to define actions
 Upon execution of the transition, the associated action is executed.
 When defining a model, we can also upload code snippets as attribute values::
 
-    >>> instantiate_attr_code("my_fsa", "transition_1", "action", open("actions/transition_1.alc", "r").read())
+    >>> instantiate_attr_code("models/my_fsa", "transition_1", "action", open("actions/transition_1.alc", "r").read())
 
 This will assign the action stored in the file *actions/transition_1.alc* to the *action* attribute of the transition.
 Of course, it is not necessary to open a file for this, as the function itself could just as well be inlined.
 The only requirement on the value, is that it is a string that can be parsed as Action Language::
 
-    >>> instantiate_attr_code("my_fsa", "transition_1", "action", \
+    >>> instantiate_attr_code("models/my_fsa", "transition_1", "action", \
     ...     """
     ...     include "primitives.alh"
     ...     Void function action(model : Element):
@@ -42,8 +42,8 @@ Action language attributes are considered identical to any other attribute.
 The exception is of course that they are assigned using the code specific function, as otherwise we would have uploaded a string.
 Note the difference between the assignment of a string, and the assignment of action code::
 
-    >>> instantiate_attr("my_fsa", "transition_1", "action", open("actions/transition_1.alc", "r").read())
-    >>> instantiate_attr_code("my_fsa", "transition_1", "action", open("actions/transition_1.alc", "r").read())
+    >>> instantiate_attr("models/my_fsa", "transition_1", "action", open("actions/transition_1.alc", "r").read())
+    >>> instantiate_attr_code("models/my_fsa", "transition_1", "action", open("actions/transition_1.alc", "r").read())
 
 The first line assigns a string to the attribute, and is therefore not executable, nor is it parsed or considered in any way.
 The second line assigns code to the attribute, and is therefore executable, and parsed upon assignment.

+ 1 - 0
doc/components.rst

@@ -7,6 +7,7 @@ A socket interface to communicate with the other components is provided.
 By using sockets, it becomes possible to switch everything about the implementation, even the implementation language, without endangering interoperability.
 
 The three projects are:
+
 1. Modelverse State
 2. Modelverse Kernel
 3. Modelverse Interface

+ 4 - 21
doc/concrete_syntax.rst

@@ -1,25 +1,8 @@
 Concrete Syntax
 ===============
 
-Explain concrete syntax and why we want to have it
-+ refer to paper
+Concrete syntax is an unavoidable aspect of modelling if you want to have decent usability.
+However, as the Modelverse is only a back-end, we leave concrete syntax up to the front-end to specify.
+Nonetheless, we have described and implemented a relatively simple way of supporting flexible concrete syntax, in an explicitly defined way.
 
-Defining Concrete Syntax
-------------------------
-
-Explain how we define concrete syntax in the Modelverse
-
-MM_Rendered
-^^^^^^^^^^^
-
-Explain how MM_Rendered looks like and what it can be used for
-
-Modelverse Client
------------------
-
-Explain the simple client, and why it can be minimal
-
-Modelverse Perceptualization
-----------------------------
-
-Explain perceptualization and how it communicates with the Modelverse
+TODO

+ 8 - 4
doc/conformance.rst

@@ -1,19 +1,23 @@
 Conformance
 ===========
 
-Explain the conformance relation in the context of the Modelverse
+The conformance relation takes a central role in the Modelverse.
+In contrast to most other tools, the conformance relation is, as all other things, explicitly modelled.
+As such, it can be altered quite easily and offers many degrees of flexibility not found in other tools.
+
+Many of these dimensions, however, are still work in progres...
 
 Multi-Conformance
 -----------------
 
-Explain the notion of multi-conformance and why it is useful
+TODO
 
 Metamodel Finding
 -----------------
 
-Explain why we can do metamodel finding, and why we would like it
+TODO
 
 Conformance Bottom
 ------------------
 
-Explain the use of conformance bottom, and how it contributes in combination with metamodel finding, or finding a binding
+TODO

+ 25 - 25
doc/metamodelling.rst

@@ -12,7 +12,7 @@ To create a metamodel, you have to instantiate the *SimpleClassDiagrams* metamod
 While there is no restriction that this has to be *SimpleClassDiagrams*, for now we assume that this is the only possible meta-language.
 Therefore, to create the PetriNets language, we execute::
 
-    >>> model_add("PetriNets", "SimpleClassDiagrams")
+    >>> model_add("formalisms/PetriNets", "models/SimpleClassDiagrams")
 
 From then on, we can simply use the PetriNets metamodel as if it were a model.
 Again, the *model_add* operation can take an optional third parameter, specifying the textual representation of the model.
@@ -31,20 +31,20 @@ Class
 Creating a class is easy, and very similar to usual modelling.
 To define the *Place* concept, which can later on be used in models, we merely define this as a new class::
 
-    >>> instantiate("PetriNets", "Class", ID="Place")
+    >>> instantiate("formalisms/PetriNets", "Class", ID="Place")
 
 Note that here we must define an ID to get a sensible name to use.
 Of course, this ID does not need to be used, and the returned (randomized) ID can just as well be used.
 Nonetheless, if it is used, it becomes non-intuitive to use in future requests.
 In the instance of this newly created language, we can immediately use the new concept::
 
-    >>> instantiate("PetriNets", "Class", ID="Place")
+    >>> instantiate("formalisms/PetriNets", "Class", ID="Place")
     >>> instantiate("my_pn", "Place")
 
 If the ID was not used, we must remember the ID that was provided to us::
 
-    >>> pn_place = instantiate("PetriNets", "Class")
-    >>> instantiate("my_pn", pn_place)
+    >>> pn_place = instantiate("formalisms/PetriNets", "Class")
+    >>> instantiate("models/my_pn", pn_place)
 
 While this is more reliable, it becomes difficult to refer to the concept in the future without any clue as to what it resembles.
 Later on, this can be solved with concrete syntax, in which case modellers can identify the concepts based on their visual representation.
@@ -55,7 +55,7 @@ Association
 Creating a link between two concepts is very similar.
 For example, to create the *P2T* association, merely create an instance of association, using the same remarks as before::
 
-    >>> instantiate("PetriNets", "Association", edge=("Place", "Transition"), ID="P2T")
+    >>> instantiate("formalisms/PetriNets", "Association", edge=("Place", "Transition"), ID="P2T")
 
 Inheritance
 ^^^^^^^^^^^
@@ -64,29 +64,29 @@ A useful concept is the use of inheritance, whereby the inheriting class can tak
 To create this, simply instantiate the *Inheritance* association as usual.
 For example, it is possible to structure the *PetriNets* model (in an admittedly strange way) such that there is a generic concept of *NamedElement* and *Arc*::
 
-    >>> instantiate("PetriNets", "Class", ID="NamedElement")
-    >>> instantiate("PetriNets", "Class", ID="Place")
-    >>> instantiate("PetriNets", "Class", ID="Transition")
-    >>> instantiate("PetriNets", "Inheritance", edge=("Place", "NamedElement"))
-    >>> instantiate("PetriNets", "Inheritance", edge=("Transition", "NamedElement"))
+    >>> instantiate("formalisms/PetriNets", "Class", ID="NamedElement")
+    >>> instantiate("formalisms/PetriNets", "Class", ID="Place")
+    >>> instantiate("formalisms/PetriNets", "Class", ID="Transition")
+    >>> instantiate("formalisms/PetriNets", "Inheritance", edge=("Place", "NamedElement"))
+    >>> instantiate("formalisms/PetriNets", "Inheritance", edge=("Transition", "NamedElement"))
 
 In the Modelverse, inheritance is possible between any two instances, including associations.
 To define the *Arc* relation, we act similar::
 
-    >>> instantiate("PetriNets", "Association", edge=("NamedElement", "NamedElement"), ID="Arc")
-    >>> instantiate("PetriNets", "Association", edge=("Place", "Transition"), ID="P2T")
-    >>> instantiate("PetriNets", "Association", edge=("Transition", "Place"), ID="T2P")
-    >>> instantiate("PetriNets", "Inheritance", edge=("P2T", "Arc"))
-    >>> instantiate("PetriNets", "Inheritance", edge=("T2P", "Arc"))
+    >>> instantiate("formalisms/PetriNets", "Association", edge=("NamedElement", "NamedElement"), ID="Arc")
+    >>> instantiate("formalisms/PetriNets", "Association", edge=("Place", "Transition"), ID="P2T")
+    >>> instantiate("formalisms/PetriNets", "Association", edge=("Transition", "Place"), ID="T2P")
+    >>> instantiate("formalisms/PetriNets", "Inheritance", edge=("P2T", "Arc"))
+    >>> instantiate("formalisms/PetriNets", "Inheritance", edge=("T2P", "Arc"))
 
 Instantiating attributes
 ^^^^^^^^^^^^^^^^^^^^^^^^
 
 Instantiating attributes is similar as it was before::
 
-    >>> attr_assign("PetriNets", "NamedElement", "name", "NamedElement")
-    >>> attr_assign("PetriNets", "Transition", "name", "Transition")
-    >>> attr_assign("PetriNets", "Place", "name", "Place")
+    >>> attr_assign("formalisms/PetriNets", "NamedElement", "name", "NamedElement")
+    >>> attr_assign("formalisms/PetriNets", "Transition", "name", "Transition")
+    >>> attr_assign("formalisms/PetriNets", "Place", "name", "Place")
 
 Note that this *name* attribute is in no way related to the *name* attributes at the *my_pn* level.
 Indeed, the *name* in *PetriNets* specifies the name of the class.
@@ -112,7 +112,7 @@ We distinguish between two types of constraints: local and global constraints.
 Local constraints are defined at the level of a single class or association.
 For example, if the number of tokens must always be non-negative, this can be specified as follows::
 
-    >>> attr_assign_code("PetriNets", "Place", "constraints", \
+    >>> attr_assign_code("formalisms/PetriNets", "Place", "constraints", \
     ...     """
     ...     String function constraint(model : Element, name : String):
     ...         if (integer_gte(read_attribute(model, name, "tokens"), 0)):
@@ -125,8 +125,8 @@ Instead, a specific *GlobalConstraint* element must be instantiated.
 This element has a *constraint* attribute, which specifies the constraint to evaluate in the global context.
 For example, when, for some reason, the number of places must always be larger than the number of transitions::
 
-    >>> constraint = instantiate("PetriNets", "GlobalConstraint")
-    >>> attr_assign_code("PetriNets", constraint, "constraint", \
+    >>> constraint = instantiate("formalisms/PetriNets", "GlobalConstraint")
+    >>> attr_assign_code("formalisms/PetriNets", constraint, "constraint", \
     ...     """
     ...     String function constraint(model : Element):
     ...         if (set_len(allInstances(model, "Place")) > set_len(allInstances(model, "Transition"))):
@@ -156,12 +156,12 @@ While many other tools provide a set of primitives, which cannot be altered in a
 If the language engineer wants the concepts of a *Natural*, for example, it is not required that the attribute is specified as an Integer, with an additional constraint on each and every attribute that is defined this way.
 It is possible to define a new attribute type as follows::
 
-    >>> instantiate("PetriNets", "SimpleAttribute", ID="Natural")
+    >>> instantiate("formalisms/PetriNets", "SimpleAttribute", ID="Natural")
 
 But now, the attribute is constrained in no way: it is merely called Natural, but can even contain a string.
 The attribute type must be constrained::
 
-    >>> attr_assign_code("PetriNets", "Natural", "constraint", \
+    >>> attr_assign_code("formalisms/PetriNets", "Natural", "constraint", \
     ...     """
     ...     String function constraint(value : Element):
     ...         if (is_physical_integer(value)):
@@ -179,6 +179,6 @@ This offers additional possibilities, such as defining complex attributes as if
 The next step is to define the attribute itself.
 This can be easily done as follows::
 
-    >>> define_attribute("PetriNets", "Place", "tokens", "Natural")
+    >>> define_attribute("formalisms/PetriNets", "Place", "tokens", "Natural")
 
 For the name, *String* can be defined similarly (using *is_physical_string*).

+ 2 - 2
doc/modeller_modellanguage.rst

@@ -176,11 +176,11 @@ The question remains how to use this model representation with the Modelverse.
 The *model_add* operation can be extended with an additional parameter, specifying a textual model.
 It will then create a new model and initialize it with the model specified textually::
 
-    >>> model_add("my_pn", "PetriNets", open("my_pn.mvc", "r").read())
+    >>> model_add("models/my_pn", "formalisms/PetriNets", open("my_pn.mvc", "r").read())
 
 Another function that can be used, is the *model_overwrite* function, which overwrites an existing model with the content of the provided textual model::
 
-    >>> model_overwrite("my_pn", "PetriNets", open("my_pn.mvc", "r").read())
+    >>> model_overwrite("models/my_pn", "formalisms/PetriNets", open("my_pn.mvc", "r").read())
 
 Note that *model_overwrite* can also be used without the textual model parameter, in which case it will clear the specified model.
 That is, overwrite the model with an empty model.

+ 44 - 32
doc/modelling.rst

@@ -15,7 +15,7 @@ Creating a model
 
 Creating a new instance of PetriNets is simple, and can be done using the *model_add* operation of the wrapper::
 
-    >>> model_add("my_pn", "PetriNets")
+    >>> model_add("models/my_pn", "formalisms/PetriNets")
 
 The first parameter of the operation indicates the name that we would like to give to our newly created model.
 This name is available to all users, and is therefore a unique identifier.
@@ -24,11 +24,23 @@ The value needs to be a name that is available in the Modelverse, and readable t
 
 To get a list of currently available models, users can query the Modelverse with the *model_list* operation::
 
-    >>> model_list()
-    [("PetriNets", "SimpleClassDiagrams"), ("SimpleClassDiagrams", "SimpleClassDiagrams"), ("ProcessModel", "SimpleClassDiagrams"), ...]
+    >>> model_list("")
+    ["formalisms/", "models/", "administration/", "users/", "type mappings/"]
 
-This list contains the various models, and the metamodel of these models.
-Note that this operation does not specify anything about the permissions of the supplied models.
+This list contains the various entries in the specified location.
+Note that this operation does not specify anything about the permissions of the supplied entries.
+Entries that end with a forward slash (/) are folders, and can subsequently be listed::
+
+    >>> model_list("formalisms")
+    ["Bottom", "SimpleClassDiagrams", "Tracability", ...]
+
+    >>> model_list("users/admin")
+    []
+
+Depending on permissions, not all folders can be listed::
+    
+    >>> model_list("administration")
+    PermissionDenied("administration")
 
 Modifying a model
 -----------------
@@ -43,7 +55,7 @@ Create
 
 1. *instantiate* creates a new instance in the model, for example a new PetriNet place::
 
-        >>> instantiate("my_pn", "Place")
+        >>> instantiate("models/my_pn", "Place")
         __12345
 
     The operation requires the model on which we are working, and the type of the element you want to instantiate.
@@ -52,20 +64,20 @@ Create
 
     When instantiating an edge, the optional *edge* parameter must be passed with the identifiers to connect::
         
-        >>> instantiate("my_pn", "Place")
+        >>> instantiate("models/my_pn", "Place")
         p1
-        >>> instantiate("my_pn", "Transition")
+        >>> instantiate("models/my_pn", "Transition")
         t1
-        >>> instantiate("my_pn", "P2T", edge=("p1", "t1"))
+        >>> instantiate("models/my_pn", "P2T", edge=("p1", "t1"))
         p2t1
 
 2. *attr_assign* assigns attributes of a specific model element.
    For example, it specifies the name and number of tokens of our PetriNet place::
    
-        >>> instantiate("my_pn", "Place")
+        >>> instantiate("models/my_pn", "Place")
         p1
-        >>> attr_assign("my_pn", "p1", "name", "place 1")
-        >>> attr_assign("my_pn", "p1", "tokens", 2)
+        >>> attr_assign("models/my_pn", "p1", "name", "place 1")
+        >>> attr_assign("models/my_pn", "p1", "tokens", 2)
 
    The value of the attribute can be any simple primitive: string, integer, float, or boolean.
    When the attribute already exists, its value is overwritten.
@@ -76,39 +88,39 @@ Read
 
 1. *read* reads out basic information about a queried element, such as its type and the source and target (if it is an edge)::
 
-        >>> instantiate("my_pn", "Place")
+        >>> instantiate("models/my_pn", "Place")
         p1
-        >>> read("my_pn", "p1")
+        >>> read("models/my_pn", "p1")
         ("Place, None)
-        >>> instantiate("my_pn", "Transition")
+        >>> instantiate("models/my_pn", "Transition")
         t1
-        >>> instantiate("my_pn", "P2T", edge=("p1", "t1"))
+        >>> instantiate("models/my_pn", "P2T", edge=("p1", "t1"))
         p2t1
-        >>> read("my_pn", "p2t1")
+        >>> read("models/my_pn", "p2t1")
         ("P2T", ("p1", "t1"))
 
 2. *read_attrs* reads out the attributes of a specific element, in a dictionary form.
    This operation can be used to read out, for example, the number of tokens of a specific place::
 
-        >>> instantiate("my_pn", "Place")
+        >>> instantiate("models/my_pn", "Place")
         p1
-        >>> attr_assign("my_pn", "p1", "name", "place 1")
-        >>> attr_assign("my_pn", "p1", "tokens", 2)
-        >>> read_attrs("my_pn", "p1")
+        >>> attr_assign("models/my_pn", "p1", "name", "place 1")
+        >>> attr_assign("models/my_pn", "p1", "tokens", 2)
+        >>> read_attrs("models/my_pn", "p1")
         {"name": "place 1", "tokens": 2}
    
 3. *types* reads out the list of types that can be instantiated in this model.
    All calls to instantiate should act upon one of these types.
    For PetriNets, this returns the concepts of the domain::
 
-        >>> types()
+        >>> types("models/my_pn")
         ["Place", "Transition", "P2T", "T2P", ...]
 
 4. *element_list_nice* reads out a simple JSON-like representation of the model.
    This includes all information about the model and can be used to fetch the complete model in one go.
    For example, to read out a simple PetriNet::
 
-        >>> element_list_nice("my_pn")
+        >>> element_list_nice("models/my_pn")
         [{"id": "p1", "type": "Place", "name": "place 1", "tokens": 1},
          {"id": "p2", "type": "Place", "name": "place 2", "tokens": 2},
          {"id": "t1", "type": "Transition"},
@@ -120,7 +132,7 @@ Read
    This takes into account inheritance relation.
    For example, to read out all outgoing P2T links of a place::
 
-        >>> read_outgoing("my_pn", "p1", "P2T")
+        >>> read_outgoing("models/my_pn", "p1", "P2T")
         ["p2t"]
 
    It is possible to get all outgoing associations as well, by leaving the type empty (the empty string).
@@ -128,7 +140,7 @@ Read
 6. *read_incoming* similarly reads out all incoming associations of a certain type, for a specific element.
    For example, to read out all incoming T2P links of a place::
 
-        >>> read_incoming("my_pn", "p2", "T2P")
+        >>> read_incoming("models/my_pn", "p2", "T2P")
         ["t2p"]
 
    Again, the type can be set to the empty string to return all incoming associations.
@@ -136,20 +148,20 @@ Read
 7. *read_association_source* reads out the source of a specific association, and can be used in conjunction with *read_outgoing* and *read_incoming*.
    For example, to read out which is the source of an arc::
 
-        >>> read_association_source("my_pn", "p2t")
+        >>> read_association_source("models/my_pn", "p2t")
         p1
 
 8. *read_association_destination* similarly reads out the destination of a specific association.
    For example, to read out the target of an arc::
 
-        >>> read_association_destination("my_pn", "p2t")
+        >>> read_association_destination("models/my_pn", "p2t")
         t1
 
 9. *connections_between* reads out the set of all association types that can be created between two elements in the model.
    This also takes into account inheritance information.
    For example, to find out which association types can connect a place and transition::
 
-        >>> connections_between("my_pn", "p1", "t1")
+        >>> connections_between("models/my_pn", "p1", "t1")
         ["P2T"]
 
     If no associations are allowed, the list is empty.
@@ -157,7 +169,7 @@ Read
 10. *all_instances* read out the set of all instances of a specific type in the model.
     For example, to find all Places in our PetriNet model::
 
-        >>> all_instances("my_pn", "Place")
+        >>> all_instances("models/my_pn", "Place")
         ["p1", "p2"]
 
 Delete
@@ -169,15 +181,15 @@ Delete
    Associations, however, are removed.
    For example, to remove place p1::
 
-        >>> element_list_nice("my_pn")
+        >>> element_list_nice("models/my_pn")
         [{"id": "p1", "type": "Place", "name": "place 1", "tokens": 1},
          {"id": "p2", "type": "Place", "name": "place 2", "tokens": 2},
          {"id": "t1", "type": "Transition"},
          {"id": "p2t", "type": "P2T", "__source": "p1", "__target": "t1", "weight": 1},
          {"id": "t2p", "type": "T2P", "__source": "t1", "__target": "p2", "weight": 2},
         ]
-        >>> delete_element("my_pn", "p1")
-        >>> element_list_nice("my_pn")
+        >>> delete_element("models/my_pn", "p1")
+        >>> element_list_nice("models/my_pn")
         [{"id": "p2", "type": "Place", "name": "place 2", "tokens": 2},
          {"id": "t1", "type": "Transition"},
          {"id": "t2p", "type": "T2P", "__source": "t1", "__target": "p2", "weight": 2},

+ 240 - 7
doc/operations.rst

@@ -1,24 +1,257 @@
 Operations
 ==========
 
-Describe the different types of operations
+While model transformations are in many tools considered to be the only types of operations on models, this is not necessarily the case in the Modelverse.
+Indeed, MPM advocates to use the most appropriate formalism, and, as model transformations are models themselves, this should also apply to specifying operations.
+As such, we extend the notion of model transformations to more general model operations.
+
+Model operations come in three types, depending on how they are ideally specified: model transformations, manual operations, and action language.
+In the remainder of this section, we will show all types, and what situation they are ideally suited for.
 
 Model Transformations
 ---------------------
 
-Link back to previous section
+When model matching and rewriting is involved, model transformations are clearly the ideal solution in most cases.
+The LHS is used to match elements in the host model, and the RHS is put in its place.
+As this could also be combined with a visual concrete syntax, it could even become possible for domain experts to create a model transformation without too much programming knowledge.
 
-Manual Operations
------------------
+Nonetheless, model transformations are a completely different paradigm, based solely on matching and rewriting.
+Some operations have a long history in computer science, and have therefore efficient or simple procedural algorithms.
+For example, computing the reachability graph of a PetriNets instance can certainly be done with model transformations, but will be overly difficult.
+On the other hand, procedural algorithms are relatively easy (when implemented naively).
+
+The functions on model transformations are exposed using the *transformation_add_MT* and *transformation_execute_MT* operations.
+Examples as shown in the previous section::
+
+    >>> def callback():
+    ...     instantiate(None, "Association", edge=("PetriNets/Transition", "ReachabilityGraph/Transition"), ID="PN2RG_Transition")
+    >>> transformation_add_MT({"PetriNets": "formalisms/PetriNets"}, {"ReachabilityGraph": "formalisms/ReachabilityGraph"}, "models/pn_analyse", open("models/pn_analyse.mvc", "r").read(), callback)
+
+A situation in which model transformations are effective, is when the transformation is to map a Domain Specific Language (DSL) to a General Purpose Language (GPL).
+For example, when defining a mapping between a RPGame DSL and the PetriNets formalism, to facilitate formal analysis::
 
-Explain manual operations: do the merge and split automatically
+    >>> def callback():
+    ...     instantiate(None, "Association", edge=("RPGame/Tile", "PetriNets/Place"), ID="Tracability_link")
+    >>> rule = \
+    ...     """
+    ...     Composite composite {
+    ...         {Contains} ForAll create_places_for_tiles {
+    ...             LHS {
+    ...                 Pre_RPGame/Tile pre_t1 {
+    ...                     label = "tile"
+    ...                 }
+    ...             }
+    ...             RHS {
+    ...                 Post_RPGame/Tile post_t1 {
+    ...                     label = "tile"
+    ...                 }
+    ...                 Post_PetriNets/Place post_p1 {
+    ...                     label = "place"
+    ...                 }
+    ...                 Post_Tracability_link (post_t1, post_p1) {
+    ...                     label = "tracability"
+    ...                 }
+    ...             }
+    ...         }
+    ...         ...
+    ...     }
+    ...     Initial (composite, create_places_for_tiles) {}
+    ...     OnSuccess (create_places_for_tiles, ...) {}
+    ...     OnFailure (create_places_for_tiles, ...) {}
+    ...     ...
+    ...     """
+    >>> transformation_add_MT({"RPGame": "formalisms/RPGame"}, {"PetriNets": "formalisms/PetriNets"}, "models/rpg_to_pn", rule, callback)
+    >>> transformation_execute_MT("models/rpg_to_pn", {"RPGame": "models/my_rpg"}, {"PetriNets": "models/exported_rpg"})
 
 Action Language
 ---------------
 
-Explain how to do things programmatically
+Doing operations in a procedural way is supported in the Modelverse through the use of action language models.
+As action language is explicitly modelled, we can create models consisting solely of action language.
+They are therefore similar to model transformations, which are also models, but only define a different operational semantics.
+Instead of having a model transformation interpreter apply the schedule and rules, we merely call the action language functions enclosed in the model.
+
+The functions on action language are exposed using the *transformation_add_AL* and *transformation_execute_AL* operations.
+Their signature is identical to that of model transformations.
+
+The action language model itself requires a single *main* function, or it will execute the topmost function in the specified code.
+This function should take a single argument, being the merged model.
+From this model, the merged metamodel can easily be accessed.
+As with model transformations, the names of model entities are prefixed with their tags.
+
+For example, when defining the PetriNets analysis in action language, which is a much better match than model transformations::
+
+    >>> def callback():
+    ...     instantiate(None, "Association", edge=("PetriNets/Transition", "ReachabilityGraph/Transition"), ID="PN2RG_Transition")
+    >>> code = \
+    ...     """
+    ...     include "primitives.alh"
+    ...     Element function explore_state(model : Element, state : Element):
+    ...         ...
+    ...     
+    ...     Element function main(model : Element):
+    ...         String initial
+    ...         initial = instantiate_node(model, "ReachabilityGraph/Initial")
+    ...         instantiate_attribute(model, initial, "name", "state_0")
+    ...         ...
+    ...         return model!
+    ...     """
+    >>> transformation_add_AL({"PetriNets": "formalisms/PetriNets"}, {"ReachabilityGraph": "formalisms/ReachabilityGraph"}, "models/pn_analyse", code, callback)
+    >>> transformation-execute_AL("models/pn_analyse", {"PetriNets": "models/my_pn"}, {"ReachabilityGraph": "models/generated_reachability"})
+
+Manual Operations
+-----------------
+
+Some other operations simply cannot be automated.
+For example, translating natural language descriptions of requirements of a model to the actual model is not something that can be automated, at least for now.
+The only solution, therefore, is to do these operations manually, thereby requiring user intervention.
+
+Even though they are manual, they can still be partially automated: merging and splitting based on tags can still be done before and after the manual operation.
+
+The functions on action language are exposed using the *transformation_add_MANUAL* and *transformation_execute_MANUAL* operations.
+Their signature is identical to that of model transformations, except that now there is no model to provide with the *transformation_add_MANUAL*.
+As with model transformations, the names of model entities are prefixed with their tags.
+
+For example, when defining an operation to refine or revise a model in a DSL with respect to the requirements, such as the RPGame language from before::
+
+    >>> def callback():
+    ...     instantiate(None, "Association", edge=("Requirements/Actor", "RPGame/Player"))
+    >>> transformation_add_MANUAL({"RPGame": "formalisms/RPGame", "Requirements": "formalisms/Requirements"}, {"RPGame": "formalisms/RPGame"}, "revise_rpg", callback)
+
+Its execution, however, differs significantly from before, as we will see next.
+
+Execution callbacks
+^^^^^^^^^^^^^^^^^^^
+
+As specified before, manual operations require user intervention during execution.
+The *transformation_execute_MANUAL* operation is therefore not a simple function that is executed and returns after some time: user input must be given.
+For this, an additional argument is provided as callback function.
+This callback function can be any sequence of operations again, such that it is possible to create or alter the model.
+Certainly, if it were to be possible automatically, we could have done it that way in the Modelverse.
+Therefore, the callback function can do much more than just these calls: all expressive power of Python is available, such as the possibility for user input (*raw_input()*).
+Based on this, the callback can define whatever structure is desired, possibly even going as far as starting up a dialog box or using a graphical interface.
+As before, the first parameter of the operations (*model_name*) should be set to *None*.
+
+For example, to specify the operations that have to be done in a manual operation, all in Python syntax::
+
+    >>> def callback():
+    ...     tiles = [instantiate(None, "RPGame/Tile") for _ in range(100)]
+    ...     left_links = [instantiate(None, "RPGame/left", edge=(tiles[i], tiles[i+1])) for i in range(len(tiles) - 1)]
+    ...     ...
+    >>> transformation_execute_MANUAL("revise_rpg", {"RPGame": "models/my_rpg", "Requirements": "models/rpg_requirements"}, {"RPGame": "models/my_rpg"}, callback)
+
+Alternatively, the operations might only be known at runtime, thereby requiring user interaction.
+Note that, in this case, the code in the callback is effectively the user interface offered to the user in this specific context.
+As such, the code can be completely tailored to the domain and problem at hand (e.g., a *create random level* operation).
+
+    >>> def callback():
+    ...     while True:
+    ...         print("Please perform operation on RPGame!)"
+    ...         inp = raw_input()
+    ...         if inp == "new tile":
+    ...             instantiate(None, "RPGame/Tile")
+    ...         elif inp == "create random level":
+    ...             ...
+    ...         ...
+    >>> transformation_execute_MANUAL("revise_rpg", {"RPGame": "models/my_rpg", "Requirements": "models/rpg_requirements"}, {"RPGame": "models/my_rpg"}, callback)
+
+Manual operations are not the only operations which might require user input.
+Model transformations and action language can just as well query the users for input.
+This is done the same way: by defining a callback function.
+Note that they are slightly different, in the sense that their callback functions have no option to call any of the Modelverse operations.
+Instead, the callback is invoked when the action language, either standalone or in the actions/constraints of model transformations, executes the *input* or *output* operations.
+The return value of the callback is made available to the action language (fragment) when it executes the *input* operations, and can be a single primitive type, or a list.
+In case it is a list, the primitive types are offered to the action language individually, requiring multiple *input* calls.
 
 Process Model
 -------------
 
-Explain how process model can combine different operations and manage data flow as well as control flow
+A next logical step is the chaining of these various operations, or activities.
+For this reason, the FTG+PM was previously developed.
+The Modelverse provides both modelling and enactment support for the FTG+PM.
+
+When modelling, the FTG+PM is just a metamodel like all others, and instances can easily be created of it.
+There are notions of activities, decision nodes, forks, joins, and so on.
+Apart from control flow, an FTG+PM also specifies data flow: which models are used in this scope, and how are they interrelated.
+Indeed, we could define the sequence of activities to execute, but we still need information on what are the input models of the various activities.
+For example, the same activity might execute multiple times, but each time on different input models and generating different output models.
+
+A simple process model is shown, which executes the operations stored in *models/A* and *models/B* in parallel.
+Both operations use some data, where *models/A* modifies one of its data inputs in-place, and *models/B* generates new data::
+
+    Start start {}
+    Finish finish {}
+
+    Fork fork1 {}
+
+    Exec activity_A {
+        name = "models/A"
+    }
+
+    Exec activity_B {
+        name = "models/B"
+    }
+
+    Join join1{}
+
+    Data data_a1 {
+        name = "models/data_a1"
+        type = "formalisms/Bottom"
+    }
+
+    Data data_a2 {
+        name = "models/data_a2"
+        type = "formalisms/Bottom"
+    }
+
+    Data data_b1 {
+        name = "models/data_b1"
+        type = "formalisms/SimpleClassDiagrams"
+    }
+
+    Data data_b2 {
+        name = "models/data_b2"
+        type = "formalisms/Bottom"
+    }
+
+    Next (start, fork1) {}
+    Next (fork1, activity_A) {}
+    Next (fork1, activity_B) {}
+    Next (activity_A, join1) {}
+    Next (activity_B, join1) {}
+    Next (join1, finish) {}
+
+    Consumes (activity_A, data_a1) {
+        name = "First"
+    }
+    Consumes (activity_A, data_a2) {
+        name = "Second"
+    }
+    Produces (activity_A, data_a2) {
+        name = "Second"
+    }
+
+    Consumes (activity_B, data_b1) {
+        name = "input"
+    }
+    Produces (activity_B, data_b2) {
+        name = "output"
+    }
+
+This simple Process Model is equivalent to the following statements in the code.
+Note that we do not know the type of the activity in the operations, and therefore put an asterisk (\*) there.
+Also, the order in which these operations execute is undefined, as either order is fine.
+We therefore leave it up to the Modelverse to decide upon an order::
+
+    >>> execute_transformation_*("models/A", {"First": "my_models/data_a1", "Second": "my_models/data_a2"}, {"Second": "my_models/data_a2"})
+    >>> execute_transformation_*("models/B", {"input": "my_models/data_b1"}, {"output": "my_models/data_b2"})
+
+In this case, it doesn't seem useful to use processes, though it is possible for such processes to contain *Decision* nodes, which can be used for arbitrary control flows.
+As everything runs on the Modelverse, instead of constantly requiring communication with the client, performance should be higher due to reduced network latency.
+
+When enacting, the FTG+PM is executed by starting at the initial node.
+The first element it points to is executed, thereby branching, deciding, or executing an activity.
+Due to the branching, it is possible for multiple activities to be scheduled concurrently.
+As for now, the Modelverse sequentializes the various activities internally.
+When executing an activity, which can either be a model transformation, action language, or manual operation, the operation is executed with the required operation.
+More information on enactment was previously given.

+ 17 - 8
doc/permissions.rst

@@ -28,17 +28,26 @@ This indicates that the owner can read and write the model, the owning group can
 
 Models can be queried for their permission information with the *model_list_full* operation::
 
-    >>> model_list_full()
-    [("my_pn", "PetriNets", "user1", "group1", "200"),
-     ("my_pn2", "PetriNets", "user2", "group1", "210"),
-     ("my_pn3", "PetriNets", "user2", "group2", "210"),
-     ("my_pn4", "PetriNets", "user2", "group2", "211"),
+    >>> model_list_full("models/petrinets/")
+    [("my_pn", "user1", "group1", "200"),
+     ("my_pn2", "user2", "group1", "210"),
+     ("my_pn3", "user2", "group2", "210"),
+     ("my_pn4", "user2", "group2", "211"),
      ...
     ]
 
 In this case, user1 can modify my_pn, as he is the owner (permission 2: read/write), read my_pn2, as he is a member of group1 (permission 1: read), and has no access to my_pn3, as he is neither owner, nor in the group (permission 0: none).
 All users can read my_pn4, independent of their group (permission 1: read).
 
+Folder permissions
+^^^^^^^^^^^^^^^^^^
+
+Similar to models, folders can also have permissions, owners, and an owning group.
+In this case, a readable folder is required to perform a *model_list* operation on that folder.
+A writable folder is required to create new entries (either models or new folders) in that folder.
+Note that this permission system leads to interesting situations, as in the UNIX permission model, where a folder cannot be read, but its contained files can (if their exact name is used).
+When a file was already created, overwriting that file does not require any permissions of the folder at all.
+
 Permission management
 ^^^^^^^^^^^^^^^^^^^^^
 
@@ -49,17 +58,17 @@ To alter the permissions, we have access to several operations.
 1. *permission_modify* changes the permissions of a model to the specified string.
    For example, to make my_pn readable for everyone::
    
-    >>> permission_modify("my_pn", "211")
+    >>> permission_modify("models/my_pn", "211")
 
 2. *permission_owner* changes the owner of a model, thereby possibly revoking our own permission, but passing it on to someone else.
    For example, to make user2 the owner of our PetriNet model::
 
-    >>> permission_owner("my_pn", "user2")
+    >>> permission_owner("models/my_pn", "user2")
 
 3. *permission_group* changes the owning group of a model, thereby possibly revoking permissions for several users.
    For example, to make group2 the owning group of our PetriNet model::
 
-    >>> permission_group("my_pn", "group2")
+    >>> permission_group("models/my_pn", "group2")
 
 Meta-level propagation
 ^^^^^^^^^^^^^^^^^^^^^^

+ 3 - 3
doc/process_enactment.rst

@@ -15,9 +15,9 @@ Enacting process models
 
 To enact the process model, such as *query_state_space*, you must execute the following operation::
 
-    >>> process_execute("query_state_space", "my_", {})
+    >>> process_execute("models/query_state_space", "my_", {})
 
-There are three parameters to this function, the first obviously being the name of the process model, as shown in *model_list*.
+There are three parameters to this function, the first obviously being the name of the process model.
 The second parameter is a prefix for the models used in the process model.
 When executing a process model, data is processed and produced.
 By providing a prefix, the names of these models are first prefixed with the prefix, before they are resolved.
@@ -40,7 +40,7 @@ The call then becomes::
     ...     instantiate(None, "P2T", (p, t))
     ...     # Alternatively, we could have used raw_input() or so to prompt the user
     ...
-    >>> process_execute("query_state_space", "my_", {"refine_petrinet": callback})
+    >>> process_execute("models/query_state_space", "my_", {"models/refine_petrinet": callback})
 
 When the process executes *refine_petrinet*, the callback function is executed, and the manual operation is terminated when the function terminates.
 Other types of operation, such as *model transformations* and *action language* can also have a callback function, but this is usually less important to end users and is therefore not elaborated on here.

+ 9 - 0
doc/scripts.rst

@@ -76,3 +76,12 @@ Run the tests for all parts of the Modelverse.
 Invocation::
 
     python scripts/run_tests.py
+
+run_basic_tests.py
+------------------
+
+Run the relatively fast tests for the Modelverse.
+
+Invocation::
+
+    python scripts/run_basic_tests.py

+ 172 - 6
doc/services.rst

@@ -1,19 +1,185 @@
 External Services
 =================
 
-Explain the use of services.
+Up to now, everything was contained in the Modelverse.
+Nonetheless, this is not always possible, or desired: sometimes we want to run an operation through an external tool, or a service.
+There are two main reasons why we would like this: performance, and feasability.
+
+For performance, an external service is likely to be faster than a Modelverse-based implementation.
+Not only because the action language execution of the Modelverse is comparatively slow, but also because of the optimized implementation of the external tool.
+It is clear that a specialized tool will be much faster than a naive implementation, even if the naive implementation were to be done in an efficient language.
+For example, PythonPDEVS uses many non-trivial algorithms to speed up DEVS simulation, which are not easily mimicked in a new implementation.
+While not impossible, of course, it seems wasteful to spend a significant amount of time to reimplement a tool.
+
+For feasability, an external service can offer operations that the Modelverse would otherwise not be able to execute.
+This links back to performance, but also to the algorithms required for the operation.
+For example, reimplementing a differential equation solver is wasteful, knowing that specialized tools have spent many man-years to implement and optimize it.
+We can never dream to come even close to these implementations.
+
+Both go hand-in-hand: a naive implementation is often possible, but there is always the trade-of between spending more time in developing the algorithm, and more time in using the algorithm.
+The ideal solution would be to use an existing tool, thereby relying on the expertise of other developers, and merely making it available in the Modelverse.
+
+When making this available in the Modelverse, we strife to minimize the difference between an internal and external operation.
+This can be done using services: instead of modelling the operation, we model the communication with a tool that does the operation.
+While there remains a big difference, as we cannot alter the semantics of the external tool, we come fairly close, abstracting away from the tool that is actually used for the operation.
+
+In this section, we will illustrate this operation through the use of a *Fibonacci* service, which takes a single integer, and returns the Fibonacci number corresponding to it.
 
 Modelverse-side
 ---------------
 
-Explain how services work in the Modelverse.
+On the Modelverse, users can use the *comm* operations to communicate with external tools.
+This is highly similar to communication for input and output to the designated user.
+Code on the Modelverse can set up a connection to a registered service as follows::
+
+    Integer function fibonacci(n : Integer):
+        String port
+
+        // Set up a dedicated communication port to the service
+        port = comm_connect("fibonacci")
+
+        // Send some data: the integer we got ourselves
+        comm_send(port, n)
+
+        // The external service computes the result
+
+        // The comm_get function is blocking
+        Integer result
+        result = comm_get(port)!
+
+        // Close the communication port from our side
+        comm_close(port)
+        return result!
+
+In this example, we send an integer parameter to the external service, and expect it to reply with another integer, containing the final result.
+When we got our result, we should always call *comm_close* to close the port at the Modelverse side.
+
+Multiple calls to *comm_send* and *comm_get* are possible, depending on the communication protocol devised between the Modelverse and the external service.
+For example, if we want to wait for the service to accept the result (e.g., the service can give an error on non-integer input)::
+
+    Integer function fibonacci(n : Integer):
+        String port
+        port = comm_connect("fibonacci")
+
+        comm_send(port, n)
+
+        String response
+        response = comm_get(port)
+        if (response == "OK"):
+            Integer result
+            result = comm_get(port)
+            comm_close(port)
+            return result!
+        elif (response == "Integer value expected!"):
+            // error out, as it is not an integer!
+            log("Non-integer input to fibonacci function!")
+            comm_close(port)
+            return -1!
+
+Sometimes, for example when coupled to Statecharts, we want to poll whether or not there is input from the service.
+This can be done using the *comm_poll* function, whose signature is identical to *comm_get*, though it returns a boolean and returns immediately.
+As expected, it does not consume the value itself, which can subsequently be read out using *comm_get*, which will now return immediately if *comm_poll* was True.
 
 Service-side
 ------------
 
-Explain how services work in the Python wrapper.
+The external service itself should be slightly augmented with a minimal wrapper.
+This wrapper is responsible for the communication with the Modelverse, by importing the Modelverse wrapper, deserializing the input from the Modelverse, and serializing the output from the tool.
+For our fibonacci example, where the fibonacci code is written in Python as well, we can code this as follows::
+
+    >>> def fibonacci_service(port):
+    ...     def fibonacci(n):
+    ...         if n <= 2:
+    ...             return 1
+    ...         else:
+    ...             return fibonacci(n - 1) + fibonacci(n - 2)
+    ...     mv_input = service_get(port)
+    ...     result = fibonacci(mv_input)
+    ...     service_set(port, result)
+    >>> service_register("fibonacci", fibonacci_service)
+    >>> try:
+    ...     while raw_input() != "STOP":
+    ...         pass
+    ... finally:
+    ...     service_stop()
+
+In this simple piece of code, we define the actual fibonacci code in the function *fibonacci*, which is totally unrelated to the Modelverse and is just pure Python code.
+To wrap it, we define *fibonacci_service*, which is a function taking the communication port.
+This function is responsible for communication with the Modelverse, through the operations *service_get* and *service_set*.
+Both functions have the expected signature.
+The service function is invoked on a thread, and therefore runs concurrently with other invocations of this same service.
+Upon termination of the function, the thread ceases to exist.
+All threads are daemon threads, such that, if the service wants to exit, it can immediately exit without first finishing up the processing of tasks in the Modelverse.
+The Modelverse tasks should be resilient to service failure or termination anyway, possibly through the use of a statechart that contains timeouts.
+
+When the service is registered, we have to specify the name of the service to be used in the Modelverse on a *comm_connect* call.
+The second parameter is the function to invoke when a connection is made, and must take one parameter: the communication port to be used.
+After the service is registered, the main thread simply continues.
+This makes it possible for the administrators of the service to maintain full control over their service.
+If the main thread terminates, all currently running services are terminated.
+Note that, after registration, this program is not able to do any other operations, until the *service_stop* operation is sent.
+
+When doing type checking first, we can do multiple *service_set* operations::
+
+    >>> def fibonacci_service(port):
+    ...     def fibonacci(n):
+    ...         if n <= 2:
+    ...             return 1
+    ...         else:
+    ...             return fibonacci(n - 1) + fibonacci(n - 2)
+    ...     mv_input = service_get(port)
+    ...     if isinstance(mv_input, int):
+    ...         service_set(port, "OK")
+    ...         result = fibonacci(mv_input)
+    ...         service_set(port, result)
+    ...     else:
+    ...         service_set(port, "Integer value expected!")
+    >>> service_register("fibonacci", fibonacci_service)
+    >>> try:
+    ...     while raw_input() != "STOP":
+    ...         pass
+    ... finally:
+    ...     service_stop()
+
+The invoked function is in this case other Python code, though this does not need to be that way.
+It would also be possible to invoke another executable, such as a *fibonacci* program, as long as the program can be used in an automated way.
+Therefore, the external services are not limited to Python in any way, and we merely use Python as a convencience, since we already have a wrapper for it.
+Using an external program happens through the *subprocess* Python module::
+
+    >>> def fibonacci_service(port):
+    ...     def fibonacci(n):
+    ...         import subprocess
+    ...         result = subprocess.check_output(["./fibonacci", str(n)])
+    ...         return int(result)
+    ...     mv_input = service_get(port)
+    ...     result = fibonacci(mv_input)
+    ...     service_set(port, result)
+    >>> service_register("fibonacci", fibonacci_service)
+    >>> try:
+    ...     while raw_input() != "STOP":
+    ...         pass
+    ... finally:
+    ...     service_stop()
+
+HUTN compiler
+-------------
+
+While it might not be obvious, some of the internal Modelverse operations are implemented using external services already.
+This is the case for the *compile_code* and *compile_model* functions.
+These functions rely on a HUTN compiler running as a service: the string is sent there, and the service sends back the list of operations to execute on the Modelverse (i.e., the compiled data).
+This was done to prevent a complete implementation of a parser in the Modelverse.
+While this would certainly be possible, at the moment it is not of high priority, and it is easy to shift it externally.
+Also, by using external services, it becomes possible to rely on trusted parsers, such as ANTLR or Ply, instead of building one from scratch as well.
+
+Users of the *compile_code* and *compile_model* functions are never exposed to the use of an external service: it looks exactly like any other function call.
+Nonetheless, there is a small difference: when the external service encounters a problem, or is simply not running, the functions will not be able to operate, even though the input is correct.
+In any case, the compile_code and compile_model operations return the root element when they encounter a problem.
+This problem could be anything, such as a syntax error or a semantical analysis error.
+Otherwise, the functions return the action language function and model, respectively.
+
+Before you can execute these functions, the HUTN compilation service must be running, which can be done by executing::
 
-Example
--------
+    >>> python scripts/HUTN_service.py
 
-Give a full example of a service.
+The service will connect to the Modelverse specified in its configuration (i.e., the *init* call).
+It stays connected until the *STOP* input is given on *stdin*.

+ 195 - 9
doc/transformations.rst

@@ -12,7 +12,7 @@ To create a model transformation, users must specify a set of source metamodels
 The model transformation will afterwards get a signature of these models.
 To create a simple Petri Net simulation transformation, we use the operation *transformation_add_MT* as follows::
 
-    >>> transformation_add_MT({"PN": "PetriNets"}, {"PN": "PetriNets"}, "pn_simulate", open("pn_simulate.mvc", "r").read())
+    >>> transformation_add_MT({"PN": "formalisms/PetriNets"}, {"PN": "formalisms/PetriNets"}, "models/pn_simulate", open("pn_simulate.mvc", "r").read())
 
 The first dictionary we pass, is the input dictionary: it specifies the name the model elements will get in the LHS, and the expected type of them.
 Similarly, the output dictionary specifies the name of output elements and their types.
@@ -24,7 +24,7 @@ For example, all types in the previous model transformation will be renamed by p
 All types are renamed, in order to make multiple inputs with the same type possible.
 For example, when combining two models, both of the same type, but one of them being the *master*, it is possible to do the following::
 
-    >>> transformation_add_MT({"master": "PetriNets", "slave": "PetriNets"}, {"result": "PetriNets"}, "pn_merge", open("pn_merge.mvc", "r").read())
+    >>> transformation_add_MT({"master": "formalisms/PetriNets", "slave": "formalisms/PetriNets"}, {"result": "formalisms/PetriNets"}, "models/pn_merge", open("pn_merge.mvc", "r").read())
 
 In this case, the LHS can match specifically for elements of the master (e.g., *master/Place*).
 
@@ -38,38 +38,224 @@ We will now continue on how to specify a model transformation through modelling.
 RAMification
 ^^^^^^^^^^^^
 
-Explain how RAMification works and what it does.
-Specifically, which attributes does it add etc.
+To support model transformation, the Modelverse makes use of RAMification.
+In RAMification, the original metamodel is Relaxed, Augmented, and Modified.
+As such, the new metamodel can be used to define model transformation rules.
+
+This consists of the following three phases::
+
+1. The metamodel is **Relaxed**, such that lower cardinalities are no longer applied. Similarly, constraints are removed, and abstract entities can be instantiated. This is done because in model transformations, we only use a specific part of the metamodel.
+2. The metamodel is **Augmented**, such that new attributes and concepts are added. These new attributes are *label* and *constraint* in the LHS, and *label* and *action* in the right hand side. New concepts that are added, are the LHS and RHS entity. These are the containers for all elements of the LHS and the RHS, respectively.
+3. The metamodel is **Modified**, such that existing attributes are renamed and their types are altered. All attributes have to become constraints on that specific attribute in the LHS, and actions for the new value in the RHS. For example, the *tokens* attribute of a place becomes a constraint function (returning True or False), instead of an attribute of type integer.
+
+RAMification happens in the background in the Modelverse.
+Users can, of course, open the RAMified metamodel just like any other metamodel.
+
+As RAMification makes a distinction between the LHS and RHS, all entities in the metamodel are effectively duplicated: the LHS entities are prefixed with *Pre_*, and the RHS entities are prefixed with *Post_*.
+Similarly, the names of all attributes are prefixed with *constraint_* and *value_*, respectively.
+
+Implicit merge
+^^^^^^^^^^^^^^
+
+As a model transformation considers multiple languages, both for its input and output, it must be possible somewhere to join them.
+For example, a transformation from PetriNets to a ReachabilityGraph formalism, makes use of entities from both metamodels, in the same model.
+To allow for this, all used metamodels are implicitly merged before RAMification.
+Therefore, the metamodel becomes a combination of all metamodels that were originally specified, making it possible to use all of them in a single model.
+
+Note, however, that the metamodels might use similar concepts: both a PetriNet and a ReachabilityGraph have the notion of a *Place*, but it means something different in both cases.
+Therefore, the elemens are prepended with the tag that was used to define them.
+As such, the model transformation has no notion of *Place*, but only of *PetriNets/Place* and *ReachabilityGraph/Place*.
+In all operations in the transformation, it is necessary to use this notation.
+
+As the same metamodel might be used multiple times, but in different contexts (i.e., with different tags), the metamodels are sometimes added multiple times.
+Each time, however, a different tag is prepended.
+This allows model transformations that combine, or alter, models of the same type, while still distinguishing between the two of them.
+Recall the *master/Place* discussion from the beginning of this section.
 
 Rule specification
 ^^^^^^^^^^^^^^^^^^
 
-How to specify a rule
+Now we actually get to define a rule.
+Rules are themselves just models of the RAMified (and merged) metamodel.
+As such, they are created just like any other model.
+This is the code parameter of the *transformation_add_MT* operation, which takes a Modelverse model.
+An example specification is shown below, which will copy the highest number of tokens between places that have the same name, assuming that only one has a non-zero number of tokens::
+
+    LHS {
+        Pre_PetriNets/Place {
+            label = "pn_place_master"
+            constraint_tokens = $
+                    Boolean function constraint(value : Integer):
+                        return value > 0!
+                $
+        }
+
+        Pre_PetriNets/Place {
+            label = "pn_place_slave"
+            constraint_tokens = $
+                    Boolean function constraint(value : Integer):
+                        return value == 0!
+                $
+
+        constraint = $
+            Boolean function constraint(host_model : Element, mapping : Element):
+                return value_eq(read_attribute(host_model, mapping["pn_place_master"], "name"), 
+                                read_attribute(host_model, mapping["pn_place_slave"], "name"))!
+                $
+    }
+    RHS {
+        Pre_PetriNets/Place {
+            label = "pn_place_master"
+        }
+        Pre_PetriNets/Place {
+            label = "pn_place_slave"
+            value_tokens = $
+                Integer function value(host_model : Element, name : Element, mapping : Element):
+                $
+        }
+    }
+
+Some remarks, specifically in relation to users of AToMPM:
+1. Unspecified attributes in the LHS are always fulfilled (i.e., *result = True* in AToMPM notation).
+2. Unspecified attributes in the RHS are always copied as-is (i.e., *result = get_attr()* in AToMPM notation).
+3. Just like in AToMPM, labels are strings and can be used as such.
+4. While *mapping* contains a mapping for the labels to their elements in the host model, all elements of the host model an technically be used, even those not occuring in the LHS.
+5. During rewriting, it is possible to access the values of all elements of the host model, including those matched before in the LHS. Newly created elements in the RHS can of course not be referenced. Elements removed in the RHS can no longer be referenced either, though this will likely be updated in future versions.
 
 Schedule
 ^^^^^^^^
 
-Scheduling constructs
+The rule we have previously applied, does not yet do much in itself: it needs to be scheduled.
+Scheduling consists of defining in which order rules are executed, but also defining how the rule is to be executed: as a query (*Query*), for one match (*Atomic*), or for all matches (*ForAll*).
+For scheduling purposes, each rule has a *onSuccess* and *onFailure* association.
+*onSuccess* associations are followed when the rule has been applied successfully (i.e., at least one match was found), and the *onFailure* association is followed otherwise.
+Rules can also be composites, in which case they define a schedule themselves.
+
+Each schedule, including the main schedule, has exactly one *Initial* element, and possibly (some) *Success* and *Failure* elements.
+When the *Success* (*Failure*) node is reached, the composite rule is said to succeed (fail).
+On the topmost schedule, success indicates that the model transformation is to be applied.
+When the topmost schedule ends in a failure node, the model transformation as a whole is deemed to fail, and all changes are reverted.
+As such, users are guarenteed that an intermediate model will never be visible, or corrupt previous models.
+
+An example schedule, which applies the previous rule for as long as it matches, is shown below::
+
+    Composite composite {
+        ForAll copy_tokens {
+            LHS {
+                ...
+            }
+            RHS {
+                ...
+            }
+        }
+        Success success {}
+    }
+
+    Initial (composite, copy_tokens) {}
+    OnSuccess (copy_tokens, copy_tokens) {}
+    OnFailure (copy_tokens, success) {}
 
 Invocation
 ----------
 
 The actual binding is only done later on, upon invocation, and goes as follows::
 
-    >>> transformation_execute_MT("pn_simulate", {"PN": "my_pn"}, {"PN": "my_pn"})
+    >>> transformation_execute_MT("models/pn_simulate", {"PN": "models/my_pn"}, {"PN": "models/my_pn"})
 
 In this case, the model transformation takes the model *my_pn* as input for the *pn_simulate* model transformation, and writes out the result in *my_pn*.
 As the output model matches the input model, the model transformation is effectively in-place.
 For out-place transformations, it is possible to specify a different output model, in which case the model is implicitly copied as well::
 
-    >>> transformation_execute_MT("pn_simulate", {"PN": "my_pn"}, {"PN": "my_simulated_pn"})
+    >>> transformation_execute_MT("pn_simulate", {"PN": "models/my_pn"}, {"PN": "models/my_simulated_pn"})
 
 Model transformation invocation has no effect on the original model in this case.
 Also, model transformations always happen on a copy of the original model.
 As such, it is also possible to restore the model to before the transformation.
 When a model transformation fails (i.e., the schedule ends in a *Failure*), no output models are written and the models are left untouched.
 
+Signature
+^^^^^^^^^
+
+After a model transformation is defined, it is easy to forget exactly which parameters it takes, and what were the types of these parameters.
+Therefore, the *transformation_read_signature* function can be used to read out the signature of model transformations::
+
+    >>> transformation_read_signature("models/pn_simulate")
+    ({"pn": "formalisms/PetriNets"}, {"pn": "formalisms/PetriNets"})
+    >>> transformation_read_signature("models/pn_merge")
+    ({"master": "formalisms/PetriNets", "slave": "formalisms/Petrinets"}, {"result": "formalisms/PetriNets"})
+
+Querying
+^^^^^^^^
+
+Similarly, it is often easy to forget which transformations are supported between a source and target metamodel.
+Therefore the *transformation_between* function can be used to query for all transformations that take a certain metamodel as input, and generate another metamodel as output::
+
+    >>> transformation_between("formalisms/PetriNets", "formalisms/PetriNets")
+    ["models/pn_optimize", "models/pn_simulate", "models/pn_rename", "models/pn_combine"]
+
+    >>> transformation_between("formalisms/PetriNets", "formalisms/ReachabilityGraph")
+    ["models/pn_analyze"]
+
+Note that this operation does not take into account other input or output metamodels::
+
+    >>> transformation_between("formalisms/PetriNets", "formalisms/Boolean")
+    ["models/analyze_query", "models/is_safe", "models/conforms"]
+
 Tracability links
 -----------------
 
-How do tracability links work?
+As the metamodels are merged together into a single metamodel, models can contain elements from both.
+It is often useful to create some kind of link between these different metamodels.
+However, a simple merge does not allow for this, as the different metamodels form their own "islands".
+To create links between them, for example for matching or tracability, we need tracability links.
+
+Definition
+^^^^^^^^^^
+
+Tracability links, or links between different metamodels, can be created by passing a callback function.
+As the intermediate merged metamodels are not designed to be modified by users (though they can be), the callback operation is executed on the merged metamodel.
+Only after the changes in the callback function are applied, is the metamodel RAMified.
+
+This callback function takes a series of operations, just like all previous operations.
+The only exception is that the model is to be put to *None*, as changes to other models are not allowed in this context.
+For example, to define a tracability link between a *PetriNets/Transition* and *ReachabilityGraph/Transition*, we can do the following when defining the transformation::
+
+    >>> def callback():
+    ...     instantiate(None, "Association", edge=("PetriNets/Transition", "ReachabilityGraph/Transition"), ID="PN2RG_Transition")
+    >>> transformation_add_MT({"PetriNets": "formalisms/PetriNets"}, {"ReachabilityGraph": "formalisms/ReachabilityGraph"}, "models/pn_analyse", open("models/pn_analyse.mvc", "r").read(), callback)
+
+As the forward slash (/) is already used to distinguish between the metamodel tag and the original entity, entities defined through a callback function should not contain this same symbol.
+
+Use
+^^^
+
+In the transformation, it is now possible to use all (RAMified) entities of the *PetriNets* metamodel (e.g., *PetriNets/Place*, *PetriNets/Transition*), the *ReachabilityGraph* metamodel (e.g., *ReachabilityGraph/Transition*, *ReachabilityGraph/State*), and the newly defined *PN2RG_Transition* element.
+As specified in the callback, the *PN2RG_Transition* association connects only the *PetriNets/Transition* and *ReachabilityGraph/Transition* elements.
+Therefore, no links can be created between any other elements.
+
+This new entity can be used in the rules like any other, either in the LHS, RHS, or the NACs::
+
+    LHS {
+        Pre_PetriNets/Transition pre_pn_t {
+            ...
+        }
+    }
+    RHS {
+        Post_PetriNets/Transition post_pn_t {
+            ...
+        }
+        Post_ReachabilityGraph/Transition post_rg_t {
+            ...
+        }
+        Post_PN2RG_Transition (post_pn_t, post_rg_t) {
+            ...
+        }
+    }
+
+This is in contrast to AToMPM, where there was a notion of *GenericLink*, which could connect all entities, but was not defined at the domain-specific level.
+In the Modelverse, these tracability links are also modelled explicitly, such that their use is also governed by the usual conformance rules.
+
+When the transformation is finished, all entities in the model are split across the multiple tagged metamodels.
+Tracability links, not belonging to any of them, are stored in a separate model, typed by the *Tracability* metamodel.
+Future model merge operations can pass this tracability model, in order to restore the original model that was split.
+This is mostly useful to developers of the Modelverse, and is not elaborated here.

+ 119 - 116
doc/wrappers.rst

@@ -79,15 +79,15 @@ Functions
 
    * To create a new model called PetriNets, conforming to SimpleClassDiagrams, and load the model stored in models/PetriNets.mvc.
 
-     >>> model_add("PetriNets", "SimpleClassDiagrams", open("models/PetriNets.mvc", "r").read())
+     >>> model_add("formalisms/PetriNets", "formalisms/SimpleClassDiagrams", open("models/PetriNets.mvc", "r").read())
 
    * To create a minimal instance of the language afterwards, which only contains a single place (and no attributes).
 
-     >>> model_add("my_pn", "PetriNets", "Place p1 {}")
+     >>> model_add("models/my_pn", "formalisms/PetriNets", "Place p1 {}")
 
    * To create a less minimal instance of the language, stored in models/my_pn2.mvc.
 
-     >>> model_add("my_pn2", "PetriNets", open("models/my_pn2.mvc", "r").read())
+     >>> model_add("models/my_pn2", "formalisms/PetriNets", open("models/my_pn2.mvc", "r").read())
 
 .. function:: upload_code(code)
 
@@ -115,50 +115,60 @@ Functions
 
    * To delete a previously created model.
 
-     >>> model_delete("my_pn2")
+     >>> model_delete("models/my_pn2")
 
    * Or to delete a metamodel, which is itself just a model.
 
-     >>> model_delete("PetriNets")
+     >>> model_delete("formalisms/PetriNets")
 
-.. function:: model_list()
+   * To delete a full folder.
 
-   Returns a list of all models existing in the Modelverse, together with their type.
+     >>> model_delete("formalisms")
+
+.. function:: model_list(location)
+
+   Returns a list of all models existing in the specified folder.
+   Sub-folders can be recognized because they have a trailing forward slash in their name.
 
    Examples:
 
-   * To get a list of all currently present models.
+   * To get a list of all models in the formalisms directory.
 
-     >>> model_list()
-     [("my_pn", "PetriNets"), ("my_pn2", "PetriNets"), ("PetriNets", "SimpleClassDiagrams"), ("SimpleClassDiagrams", "SimpleClassDiagrams")]
+     >>> model_list("formalisms")
+     ["PetriNets", "SimpleClassDiagrams", "Bottom", "Tracability", ...]
 
-.. function:: model_list_full()
+.. function:: model_list_full(location)
 
-   Returns a detailed list of all models existing in the Modelverse.
+   Returns a detailed list of all models existing in the specified folder in the Modelverse.
    This list includes information on permissions, owner, and group.
 
    Examples:
 
    * To get a detailed list of all currently present models.
 
-     >>> model_list_full()
-     [("my_pn", "PetriNets", "user1", "users", "200"), ("my_pn2", "PetriNets", "user1", "users", "200"), ("PetriNets", "SimpleClassDiagrams", "user1", "users", "211"), ("SimpleClassDiagrams", "SimpleClassDiagrams", "admin", "admin", "211")]
+     >>> model_list_full("models")
+     [("my_pn", "user1", "users", "200"), ("my_pn2", "user1", "users", "200"), ...]
 
-.. function:: verify(model_name)
+.. function:: verify(model_name, metamodel_name)
 
-   Verify whether *model_name* conforms to its specified metamodel, as stored in the Modelverse.
+   Verify whether *model_name* conforms to *metammodel_name*, as both stored in the Modelverse.
    Returns either "OK" if the model conforms, or a string specifying the reason for non-conformance.
 
    Examples:
 
    * Verifying a conforming model.
 
-     >>> verify("PetriNets")
+     >>> verify("formalisms/PetriNets", "formalisms/SimpleClassDiagrams")
+     OK
+
+   * Or verify using the alternative conformance relation (conformance bottom).
+    
+     >>> verify("formalisms/PetriNets", "formalisms/Bottom")
      OK
 
    * Verifying a non-conforming model.
 
-     >>> verify("my_pn")
+     >>> verify("models/my_pn")
      Lower cardinality violation for attribute "name" at Place p1.
 
 .. function:: model_overwrite(model_name, new_model_code=None)
@@ -171,11 +181,11 @@ Functions
 
    * To overwrite the PetriNets metamodel with a newer version, thereby also updating the metamodel of all existing instances ("my_pn" and "my_pn2").
 
-     >>> model_overwrite("PetriNets", open("models/PetriNets2.mvc", "r").read())
+     >>> model_overwrite("formalisms/PetriNets", open("models/PetriNets2.mvc", "r").read())
 
    * To overwrite an existing PetriNets instance.
 
-     >>> model_overwrite("my_pn", """Place p2 {}""")
+     >>> model_overwrite("models/my_pn", """Place p2 {}""")
 
 .. function:: user_logout()
 
@@ -213,7 +223,7 @@ Functions
 
    * To render the PetriNets instance using the PetriNetsMapper.
 
-     >>> model_render("my_pn", "PetriNetsMapper")
+     >>> model_render("models/my_pn", "formalisms/PetriNetsMapper")
      [{"id": "__12345", "type": "Ellipse", "x": 100, "y": 150, "height": 20, "width: "20"}]
 
 .. function:: transformation_between(source, target)
@@ -225,7 +235,7 @@ Functions
 
    * To fetch all endogenous transformations on PetriNets, assuming that some were previously defined.
 
-     >>> transformation_between("PetriNets", "PetriNets")
+     >>> transformation_between("formalisms/PetriNets", "formalisms/PetriNets")
      ["PN_simulate", "PN_optimize"]
 
    * To fetch all transformations from a DSL to PetriNets, assuming that multiple people created different denotational semantics.
@@ -249,18 +259,18 @@ Functions
 
    * To create a new model transformation for PetriNets simulation.
 
-     >>> transformation_add_MT({"pn": "PetriNets"}, {"pn": "PetriNets"}, "pn_simulate", open("models/PN_simulate.mvc", "r").read())
+     >>> transformation_add_MT({"pn": "formalisms/PetriNets"}, {"pn": "formalisms/PetriNets"}, "models/pn_simulate", open("models/PN_simulate.mvc", "r").read())
 
    * To create a model transformation from a DSL to PetriNets, which requires tracability links.
 
      >>> def tracability_links():
      ...     instantiate("Association", ID="Tile2Place", ("dsl/Tile", "pn/Place"))
      ...     instantiate("Association", ID="Dirrection2Transition", ("dsl/Direction", "pn/Transition"))
-     >>> transformation_add_MT({"dsl": "RPGame"}, {"pn": "PetriNets"}, "denotational_1", open("models/denotational_1.mvc", "r").read(), tracability_links)
+     >>> transformation_add_MT({"dsl": "formalisms/RPGame"}, {"pn": "formalisms/PetriNets"}, "models/denotational_1", open("models/denotational_1.mvc", "r").read(), tracability_links)
 
    * To create a multi-input model transformation.
 
-     >>> transformation_add_MT({"pn_1": "PetriNets", "pn_2": "PetriNets", "architecture: "Architecture"}, {"result": "PetriNets"}, "PN_merge", open("models/PN_merge.mvc", "r").read())
+     >>> transformation_add_MT({"pn_1": "formalisms/PetriNets", "pn_2": "formalisms/PetriNets", "architecture: "formalisms/Architecture"}, {"result": "formalisms/PetriNets"}, "models/PN_merge", open("models/PN_merge.mvc", "r").read())
 
 .. function:: transformation_add_AL(source_metamodels, target_metamodels, operation_name, code, callback=lambda: None)
 
@@ -272,13 +282,13 @@ Functions
 
    * To create a new action language operation for PetriNets reachability analysis.
 
-     >>> transformation_add_AL({"pn": "PetriNets"}, {"graph": "ReachabilityGraph"}, "pn_analyze", open("models/PN_reachability.alc", "r").read())
+     >>> transformation_add_AL({"pn": "formalisms/PetriNets"}, {"graph": "formalisms/ReachabilityGraph"}, "models/pn_analyze", open("models/PN_reachability.alc", "r").read())
 
    * To create an action language operation from a Scheduling DSL to a list, which requires tracability links.
 
      >>> def tracability_links():
      ...     instantiate("Association", ID="Task2Event", ("schedule/Task", "list/Event"))
-     >>> transformation_add_AL({"schedule": "SchedulingDSL"}, {"list": "EventList"}, "sequentialize", open("models/sequentialize_schedule.alc", "r").read(), tracability_links)
+     >>> transformation_add_AL({"schedule": "formalisms/SchedulingDSL"}, {"list": "formalisms/EventList"}, "models/sequentialize", open("models/sequentialize_schedule.alc", "r").read(), tracability_links)
 
 .. function:: transformation_add_MANUAL(source_metamodels, target_metamodels, operation_name, callback=lambda: None)
 
@@ -289,11 +299,11 @@ Functions
 
    * To create a manual refinement operation on PetriNets.
 
-     >>> transformation_add_MANUAL({"pn": "PetriNets"}, {"pn": "PetriNets"}, "pn_refine")
+     >>> transformation_add_MANUAL({"pn": "formalisms/PetriNets"}, {"pn": "formalisms/PetriNets"}, "models/pn_refine")
 
    * To create a multi-input refinement operation on PetriNets.
 
-     >>> transformation_add_MANUAL({"pn": "PetriNets", "requirements": "Requirements"}, {"pn": "PetriNets"}, "pn_refine_req")
+     >>> transformation_add_MANUAL({"pn": "formalisms/PetriNets", "requirements": "formalisms/Requirements"}, {"pn": "formalisms/PetriNets"}, "models/pn_refine_req")
 
 .. function:: transformation_execute_AL(operation_name, input_models_dict, output_models_dict, callback=lambda i: None)
 
@@ -307,14 +317,14 @@ Functions
 
    * To execute reachability analysis on an existing petri net.
 
-     >>> transformation_execute_AL("pn_analyze", {"pn": "my_pn"}, {"graph": "my_pn_reachability"})
+     >>> transformation_execute_AL("models/pn_analyze", {"pn": "models/my_pn"}, {"graph": "models/my_pn_reachability"})
 
    * To execute reachability analysis which prompts the user, for example because it is a debugging prompt.
 
      >>> def callback(value):
      ...     print(value)       # Prints out the prompt of the execution of the Action Language fragment
      ...     return raw_input() # Sends a raw request from the user to the Modelverse, consumed in the Action Language
-     >>> transformation_execute_AL("pn_simulate", {"pn": "my_pn"}, {"graph": "my_pn_reachability"}, callback)
+     >>> transformation_execute_AL("models/pn_simulate", {"pn": "models/my_pn"}, {"graph": "models/my_pn_reachability"}, callback)
 
 .. function:: transformation_execute_MANUAL(operation_name, input_models_dict, output_models_dict, callback=lambda i: None)
 
@@ -331,7 +341,7 @@ Functions
      ...     p1 = instantiate(None, "pn/Place")
      ...     t1 = instantiate(None, "pn/Transition")
      ...     instantiate(None, "pn/P2T", (p1, t1))
-     >>> transformation_execute_MANUAL("pn_refine", {"pn": "my_pn"}, {"pn": "my_pn"}, callback)
+     >>> transformation_execute_MANUAL("models/pn_refine", {"pn": "models/my_pn"}, {"pn": "models/my_pn"}, callback)
 
 .. function:: transformation_execute_MT(operation_name, input_models_dict, output_models_dict, callback=lambda i: None)
 
@@ -342,25 +352,14 @@ Functions
 
    * To execute a model transformation on a PetriNets instance, thereby putting the result in a different model.
 
-     >>> transformation_execute_MT("pn_simulate", {"pn": "my_pn"}, {"pn": "my_simulated_pn"})
+     >>> transformation_execute_MT("models/pn_simulate", {"pn": "models/my_pn"}, {"pn": "models/my_simulated_pn"})
 
    * To execute a model transformation which prompts the user.
 
      >>> def callback(value):
      ...     print(value)
      ...     return raw_input()
-     >>> transformation_execute_MT("pn_simulate_prompt", {"pn": "my_pn"}, {"pn": "my_simulated_pn"}, callback)
-
-.. function:: transformation_list()
-
-   Returns a list of all operations specified in the Modelverse, together with their type.
-
-   Examples:
-
-   * To fetch a list of all transformations and their type of operation.
-
-     >>> transformation_list()
-     [("pn_simulate", "ModelTransformation"), ("pn_reachability", "ActionLanguage"), ("pn_simulate_prompt", "ModelTransformation"), ("pn_refine", "ManualOperation")]
+     >>> transformation_execute_MT("models/pn_simulate_prompt", {"pn": "models/my_pn"}, {"pn": "models/my_simulated_pn"}, callback)
 
 .. function:: process_execute(process_name, prefix, callbacks)
 
@@ -374,7 +373,7 @@ Functions
 
    * To execute a process model for the power window example.
 
-     >>> process_execute("pm_powerwindow", "pw_")
+     >>> process_execute("models/pm_powerwindow", "pw_")
 
    * To execute a process model for the power window example, which requires user input for some operations.
 
@@ -392,7 +391,7 @@ Functions
      ...     # Do some operation on the safety query model here
      ...     p1 = instantiate(None, "Place")
      ...     attr_assign(None, p1, "tokens", 2)
-     >>> process_execute("pm_powerwindow", "pw_", {"refine_plant": refine_plant, "refine_control": refine_control, "refine_query": refine_query})
+     >>> process_execute("models/pm_powerwindow", "pw_", {"models/refine_plant": refine_plant, "models/refine_control": refine_control, "models/refine_query": refine_query})
 
 .. function:: permission_modify(model_name, permissions)
 
@@ -406,11 +405,11 @@ Functions
 
    * To modify the permissions of the PetriNets metamodel, allowing only the owner to read and write to it.
 
-     >>> permission_modify("PetriNets", "200")
+     >>> permission_modify("formalisms/PetriNets", "200")
 
    * To modify the permissions of a PetriNets model, granting everyone read/write access.
 
-     >>> permission_modify("PetriNets", "222")
+     >>> permission_modify("formalisms/PetriNets", "222")
 
 .. function:: permission_owner(model_name, owner)
 
@@ -421,7 +420,7 @@ Functions
 
    * To change the owning user of the PetriNets metamodel to user2.
 
-     >>> permission_owner("PetriNets", "user2")
+     >>> permission_owner("formalisms/PetriNets", "user2")
 
 .. function:: permission_group(model_name, group)
 
@@ -432,7 +431,7 @@ Functions
 
    * To change the owning group of the PetriNets metamodel to group1.
 
-     >>> permission_group("PetriNets", "group1")
+     >>> permission_group("formalisms/PetriNets", "group1")
 
 .. function:: group_create(group_name)
 
@@ -530,7 +529,7 @@ Functions
 
    * To get a list of all elements in the PetriNets metamodel.
 
-     >>> element_list("PetriNets")
+     >>> element_list("formalisms/PetriNets")
      [("Place", "Class"), ("Transition", "Class"), ("P2T", "Association"), ("T2P", "Association"), ...]
 
 .. function:: types(model_name)
@@ -543,7 +542,7 @@ Functions
 
    * To get a list of all types usable in the PetriNets metamodel (i.e., when altering the metamodel itself).
 
-     >>> types("PetriNets")
+     >>> types("formalisms/PetriNets")
      ["Class", "Association", "SimpleAttribute", ...]
 
 .. function:: types_full(model_name)
@@ -555,7 +554,7 @@ Functions
 
    * To get a list of all types usable in the PetriNets metamodel (i.e., when altering the metamodel itself).
 
-     >>> types("PetriNets")
+     >>> types("formalisms/PetriNets")
      ["Class", "Association", "SimpleAttribute", "__12345", ...]
 
 .. function:: read(model_name, ID)
@@ -567,12 +566,12 @@ Functions
 
    * To read out the P2T link in the PetriNets metamodel.
 
-     >>> read("PetriNets", "P2T")
+     >>> read("formalisms/PetriNets", "P2T")
      ["Association", ("Place", "Transition")]
 
    * To read out the Place node in the PetriNets metamodel.
 
-     >>> read("PetriNets", "Place")
+     >>> read("formalisms/PetriNets", "Place")
      ["Class", None]
 
    * To read out some P2T instance in a PetriNets model.
@@ -595,12 +594,12 @@ Functions
 
    * To read out the attributes of the Place class.
 
-     >>> read_attrs("PetriNets", "Place")
+     >>> read_attrs("formalisms/PetriNets", "Place")
      {"lower_cardinality": None, "upper_cardinality": None}
 
    * To read out the attributes of a Place instance.
 
-     >>> read_attrs("my_pn", "p1")
+     >>> read_attrs("models/my_pn", "p1")
      {"name": "critical_section", "tokens": 1}
 
 .. function:: instantiate(model_name, typename, edge=None, ID="")
@@ -615,29 +614,29 @@ Functions
 
    * To create a new Place instance in a PetriNets model.
 
-     >>> instantiate("my_pn", "Place")
+     >>> instantiate("models/my_pn", "Place")
      "__12345"
 
    * To create a new Place instance with a preferred ID, which is granted.
 
-     >>> instantiate("my_pn", "Place", ID="critical_section")
+     >>> instantiate("models/my_pn", "Place", ID="critical_section")
     "critical_section"
 
    * To create a new Place instance with a preferred ID, which is not granted.
 
-     >>> instantiate("my_pn", "Place", ID="critical_section")
+     >>> instantiate("models/my_pn", "Place", ID="critical_section")
      critical_section_12345"
 
    * To create a new P2T instance in a PetriNets model.
 
-     >>> instantiate("my_pn", "P2T", ("p1", "t1"))
+     >>> instantiate("models/my_pn", "P2T", ("p1", "t1"))
      "__12345"
 
    * To create a new concept in the PetriNets metamodel, which can later on be used in all instances immediately.
 
-     >>> instantiate("PetriNets", "Association", ("Place", "Transition"), ID="InhibitorArc")
+     >>> instantiate("formalisms/PetriNets", "Association", ("Place", "Transition"), ID="InhibitorArc")
      "InhibitorArc"
-     >>> instantiate("my_pn", "InhibitorArc", ("p1", "t1"))
+     >>> instantiate("models/my_pn", "InhibitorArc", ("p1", "t1"))
      "__12345"
 
 .. function:: delete_element(model_name, ID)
@@ -649,16 +648,16 @@ Functions
 
    * To delete an existing element in a PetriNets model.
 
-     >>> delete_element("my_pn", "critical_section")
+     >>> delete_element("models/my_pn", "critical_section")
 
    * To delete an existing exdge in a PetriNets model.
 
-     >>> delete_element("my_pn", "p1_to_t1")
+     >>> delete_element("models/my_pn", "p1_to_t1")
 
    * When deleting an element "p1", the arc "p1_to_t1" is also removed automatically.
 
-     >>> delete_element("my_pn", "p1")
-     >>> delete_element("my_pn", "p1_to_t1")
+     >>> delete_element("models/my_pn", "p1")
+     >>> delete_element("models/my_pn", "p1_to_t1")
      UnknownIdentifierException("p1_to_t1")
 
 .. function:: attr_assign(model_name, ID, attr, value)
@@ -670,12 +669,12 @@ Functions
 
    * To assign some attributes to a Place instance.
 
-     >>> attr_assign("my_pn", "p1", "name", "my first place")
-     >>> attr_assign("my_pn", "p1", "tokens", 1)
+     >>> attr_assign("models/my_pn", "p1", "name", "my first place")
+     >>> attr_assign("models/my_pn", "p1", "tokens", 1)
 
    * To assign some attributes to the Place class itself.
 
-     >>> attr_assign("PetriNets", "Place", "upper_cardinality", 1)
+     >>> attr_assign("formalisms/PetriNets", "Place", "upper_cardinality", 1)
 
 .. function:: attr_assign_code(model_name, ID, attr, code)
 
@@ -687,7 +686,7 @@ Functions
 
    * To assign a piece of action code to a Statecharts transition, loaded from file.
 
-     >>> attr_assign_code("my_sc", "t1", "script", open("models/t1_script", "r").read())
+     >>> attr_assign_code("models/my_sc", "t1", "script", open("models/t1_script", "r").read())
 
    * To assign a piece of action code to a Statecharts transition, defined inline.
 
@@ -697,7 +696,7 @@ Functions
      ...        dict_overwrite(attributes, "counter", 1)
      ...        return!
      ...   """
-     >>> attr_assign_code("my_sc", "t1", "script", code)
+     >>> attr_assign_code("models/my_sc", "t1", "script", code)
 
 .. function:: attr_delete(model_name, ID, attr)
 
@@ -709,7 +708,7 @@ Functions
 
    * To unset the name attribute of a place.
 
-     >>> attr_delete("my_pn", "p1", "name")
+     >>> attr_delete("models/my_pn", "p1", "name")
 
 .. function:: read_outgoing(model_name, ID, typename)
 
@@ -721,12 +720,12 @@ Functions
 
    * To get all arcs starting in place p1.
 
-     >>> read_outgoing("my_pn", "p1", "P2T")
+     >>> read_outgoing("models/my_pn", "p1", "P2T")
      ["p1_to_t1"]
 
    * To get all allowed connections starting in a Place.
 
-     >>> read_outgoing("PetriNets", "Place", "Association")
+     >>> read_outgoing("formalisms/PetriNets", "Place", "Association")
      ["P2T", "InhibitorArc"]
 
 .. function:: read_incoming(model_name, ID, typename)
@@ -739,12 +738,12 @@ Functions
 
    * To get all arcs going to place p1.
 
-     >>> read_incoming("my_pn", "p1", "T2P")
+     >>> read_incoming("models/my_pn", "p1", "T2P")
      ["t1_to_p1"]
 
    * To get all allowed connections going to a Place.
 
-     >>> read_incoming("PetriNets", "Place", "Association")
+     >>> read_incoming("formalisms/PetriNets", "Place", "Association")
      ["T2P"]
 
 .. function:: read_association_source(model_name, ID)
@@ -755,12 +754,12 @@ Functions
 
    * To read out the source of the P2T link.
 
-     >>> read_association_source("PetriNets", "P2T")
+     >>> read_association_source("formalisms/PetriNets", "P2T")
      "Place"
 
    * To read out the source of an arc.
 
-     >>> read_association_source("my_pn", "p1_to_t1")
+     >>> read_association_source("models/my_pn", "p1_to_t1")
      "p1"
 
 .. function:: read_association_destination(model_name, ID)
@@ -771,12 +770,12 @@ Functions
 
    * To read out the target of the P2T link.
 
-     >>> read_association_destination("PetriNets", "P2T")
+     >>> read_association_destination("formalisms/PetriNets", "P2T")
      "Transition"
 
    * To read out the target of an arc.
 
-     >>> read_association_destination("my_pn", "p1_to_t1")
+     >>> read_association_destination("models/my_pn", "p1_to_t1")
      "t1"
 
 .. function:: service_register(name, function)
@@ -868,8 +867,8 @@ Functions
 
    * To read out the signature of the "plant_refine" operation.
 
-     >>> transformation_read_signature("plant_refine")
-     ({"req": "Requirements", "plant": "Plant"}, {"plant": "Plant"})
+     >>> transformation_read_signature("models/plant_refine")
+     ({"req": "formalisms/Requirements", "plant": "formalisms/Plant"}, {"plant": "formalisms/Plant"})
 
 .. function:: element_list_nice(model_name)
 
@@ -881,7 +880,7 @@ Functions
 
    * To read out a list of a PetriNets instance model.
 
-     >>> element_list_nice("my_pn")
+     >>> element_list_nice("models/my_pn")
      [{"id": "p1", "name": "a place", "tokens": 1}, {"id": "t1", "name": "a transition"}, {"id": "p1_to_t1", "name": "transition", "__source": "p1", "__target": "t1", "weight": 1}]
 
 .. function:: connections_between(model_name, source_element, target_element)
@@ -893,12 +892,12 @@ Functions
 
    * To read out the allowed connections between elements "p1" and "t1".
 
-     >>> connections_between("my_pn", "p1", "t1")
+     >>> connections_between("models/my_pn", "p1", "t1")
      ["P2T"]
 
    * To read out the allowed connections from the Place class to itself.
 
-     >>> connections_between("PetriNets", "Place", "Place")
+     >>> connections_between("formalisms/PetriNets", "Place", "Place")
      ["Association", "Inheritance"]
 
 .. function:: define_attribute(model_name, node, attr_name, attr_type)
@@ -913,7 +912,7 @@ Functions
 
    * To define a new attribute "tokens" on a PetriNet Place, which is of a Natural type.
 
-     >>> define_attribute("PetriNets", "Place", "tokens", Natural)
+     >>> define_attribute("formalisms/PetriNets", "Place", "tokens", Natural)
 
 .. function:: all_instances(model_name, type_name)
 
@@ -924,12 +923,12 @@ Functions
 
    * To get all places in a PetriNet model.
 
-     >>> all_instances("my_pn", "Place")
+     >>> all_instances("models/my_pn", "Place")
      ["p1", "__12345"]
 
    * To get all nodes that can be instantiated directly for PetriNets.
 
-     >>> all_instances("PetriNets", "Class")
+     >>> all_instances("formalisms/PetriNets", "Class")
      ["Place", "Transition", "P2T", "T2P"]
 
 .. function:: service_poll(port)
@@ -976,7 +975,9 @@ Functions
 
    Examples:
 
-   * To 
+   * To change the default metamodel of PetriNets to Bottom.
+
+     >>> alter_context("formalisms/PetriNets", "formalisms/Bottom")
 
 .. function:: allowed_metamodels(model_name)
 
@@ -990,17 +991,17 @@ Functions
 
    * To fetch the metamodels to which a PetriNet model conforms, where it is explicitly set that it also conforms to PetriNets which use inhibitor arcs.
 
-     >>> allowed_metamodels("my_pn")
-     ["PetriNets", "Bottom", "PetriNets_Inhibitor"]
+     >>> allowed_metamodels("models/my_pn")
+     ["formalisms/PetriNets", "formalisms/Bottom", "formalisms/PetriNets_Inhibitor"]
 
    * When all typing relations were explicitly removed.
 
-     >>> allowed_metamodels("my_pn")
+     >>> allowed_metamodels("models/my_pn")
      []
 
    * Usual situation when there is no metamodel specified.
 
-     >>> allowed_metamodels("my_pn")
+     >>> allowed_metamodels("models/my_pn")
      ["Bottom"]
 
 .. function:: remove_metamodel(model_name, metamodel_name)
@@ -1012,7 +1013,7 @@ Functions
 
    * To remove the PetriNets_Inhibitor metamodel for my_pn.
 
-     >>> remove_metamodel("my_pn", "PetriNets_Inhibitor")
+     >>> remove_metamodel("models/my_pn", "formalisms/PetriNets_Inhibitor")
 
 .. function:: add_metamodel(model_name, metamodel_name, partial_type_mapping=None)
 
@@ -1020,12 +1021,14 @@ Functions
    It creates the relation, and will try to find a type mapping between them automatically.
    A *partial_type_mapping* can be passed, which will be used as a starting point when searching for a conformance relation.
    Multiple possible relations might be found, in which case an arbitrary type mapping is taken.
+
+   TODO: allow for the partial type mapping
    
    Examples:
 
    * To try and make my_pn conform to PetriNets_Inhibitor again; this results in multiple options!
 
-     >>> add_metamodel("my_pn", "PetriNets_Inhibitor")
+     >>> add_metamodel("models/my_pn", "formalisms/PetriNets_Inhibitor")
 
 Exceptions
 ^^^^^^^^^^
@@ -1049,7 +1052,7 @@ Below is a list of all exceptions that the wrappers can raise, together with a s
 
    * When the Modelverse is suddenly killed during execution, while there was an outstanding request.
 
-     >>> element_list("PetriNets") # <-- Modelverse killed during execution
+     >>> element_list("formalisms/PetriNets") # <-- Modelverse killed during execution
      UnknownError()
 
 .. exception:: UnknownIdentifier
@@ -1061,22 +1064,22 @@ Below is a list of all exceptions that the wrappers can raise, together with a s
 
    * When reading out a non-existing element in a PetriNets model.
 
-     >>> read("my_pn", "p0")
+     >>> read("models/my_pn", "p0")
      UnknownIdentifier("p0")
 
    * When reading out the allowed connections between two elements, of which neither exists.
 
-     >>> connections_between("my_pn", "p0", "t0")
+     >>> connections_between("models/my_pn", "p0", "t0")
      UnkownIdentifier("p0")
 
    * When reading out the allowed connections between two elements, of which the target doesn't exists.
 
-     >>> connections_between("my_pn", "p1", "t0")
+     >>> connections_between("models/my_pn", "p1", "t0")
      UnkownIdentifier("t0")
 
    * When instantiating a non-existing element in the meta-model.
 
-     >>> instantiate("my_pn", "CapacityConstrainedPlace")
+     >>> instantiate("models/my_pn", "CapacityConstrainedPlace")
      UnkownIdentifier("CapacityConstrainedPlace")
 
 .. exception:: UnsupportedValue
@@ -1088,12 +1091,12 @@ Below is a list of all exceptions that the wrappers can raise, together with a s
 
    * When assigning a list as attribute.
 
-      >>> attr_assign("my_pn", "p1", "tokens", [1, 2, 3])
+      >>> attr_assign("models/my_pn", "p1", "tokens", [1, 2, 3])
       UnsupporteValue("[1, 2, 3] : list")
 
    * When assigning a None value to an attribute.
 
-      >>> attr_assign("my_pn", "p1", "name", None)
+      >>> attr_assign("models/my_pn", "p1", "name", None)
       UnsupportedValue("None : NoneType")
 
 .. exception:: CompilationError
@@ -1106,7 +1109,7 @@ Below is a list of all exceptions that the wrappers can raise, together with a s
 
    * When assigning a code block which cannot be parsed as action language.
 
-     >>> attr_assign_code("my_pn", "p1", "tokens", "1")
+     >>> attr_assign_code("models/my_pn", "p1", "tokens", "1")
      CompilationError("Parsing error at line 1: ...")
 
 .. exception:: NoSuchAttribute
@@ -1118,7 +1121,7 @@ Below is a list of all exceptions that the wrappers can raise, together with a s
 
    * When assigning a non-existing attribute.
 
-     >>> attr_assign("my_pn", "p1", "capacity", 2)
+     >>> attr_assign("models/my_pn", "p1", "capacity", 2)
      NoSuchAttribute("capacity")
 
 .. exception:: UnknownModel
@@ -1130,7 +1133,7 @@ Below is a list of all exceptions that the wrappers can raise, together with a s
 
    * When trying to execute a non-existing transformation.
 
-     >>> transformation_execute_MT("pn_optimize", {"pn": "my_pn"}, {"pn": "my_optimized_pn"})
+     >>> transformation_execute_MT("models/pn_optimize", {"pn": "models/my_pn"}, {"pn": "models/my_optimized_pn"})
      UnknownModel("pn_optimize")
 
 .. exception:: ConnectionError
@@ -1157,8 +1160,8 @@ Below is a list of all exceptions that the wrappers can raise, together with a s
 
    * When the model "my_pn" already exists.
 
-     >>> model_add("my_pn", "PetriNets")
-     ModelExists("my_pn")
+     >>> model_add("models/my_pn", "PetriNets")
+     ModelExists("models/my_pn")
 
 .. exception:: PermissionDenied
 
@@ -1174,13 +1177,13 @@ Below is a list of all exceptions that the wrappers can raise, together with a s
 
    * When listing the elements of a model which we aren't allowed to read.
 
-     >>> element_list("secret_model")
-     PermissionDenied("secret_model")
+     >>> element_list("models/secret_model")
+     PermissionDenied("models/secret_model")
 
    * When altering a model which we are only allowed to read.
 
-     >>> instantiate("SimpleClassDiagrams", "NewClass")
-     PermissionDenied("SimpleClassDiagrams")
+     >>> instantiate("formalisms/SimpleClassDiagrams", "NewClass")
+     PermissionDenied("formalisms/SimpleClassDiagrams")
 
 .. exception:: InvalidMode
 
@@ -1194,7 +1197,7 @@ Below is a list of all exceptions that the wrappers can raise, together with a s
 
      >>> login("admmin", "wrong_password")
      PermissionDenied("admin")
-     >>> element_list("SimpleClassDiagrams")
+     >>> element_list("formalisms/SimpleClassDiagrams")
      InvalidMode()
 
 .. exception:: InterfaceMismatch
@@ -1213,9 +1216,9 @@ Below is a list of all exceptions that the wrappers can raise, together with a s
 
    * When erroneously trying to interpret a petrinet model as a Class Diagram.
    
-     >>> alter_context("my_pn", "SimpleClassDiagrams")
+     >>> alter_context("models/my_pn", "formalisms/SimpleClassDiagrams")
      >>> element_list()
-     UnknownMetamodellingHierarchy("my_pn")
+     UnknownMetamodellingHierarchy("models/my_pn")
 
 Custom
 ------

+ 688 - 0
integration/code/SCCD_all.mvc

@@ -0,0 +1,688 @@
+include "primitives.alh"
+
+Diagram dynamic_trafficlight {
+    name = "Dynamic Traffic Light"
+    author = "Yentl Van Tendeloo"
+}
+
+Class manager {
+    name = "Manager"
+    default = True
+    constructor_body = $
+            Void function constructor(attributes : Element, parameters : Element):
+                dict_add(attributes, "trafficlights", list_create())
+                return !
+        $
+
+    {behaviour} CompositeState manager_main {
+        name = "main"
+        isInitial = True
+        {composite_children} ParallelState manager_main_parallel {
+            name = "parallel"
+            isInitial = True
+
+            {parallel_children} CompositeState manager_main_parallel_core {
+                name = "core"
+                isInitial = True
+
+                {composite_children} BasicState manager_main_parallel_core_start {
+                    name = "start"
+                    isInitial = True
+                }
+            }
+
+            {parallel_children} CompositeState manager_main_parallel_input {
+                name = "input"
+                isInitial = True
+
+                {composite_children} BasicState manager_main_parallel_input_1 {
+                    name = "1"
+                    isInitial = True
+                }
+
+                {composite_children} BasicState manager_main_parallel_input_2 {
+                    name = "2"
+                    isInitial = False
+                }
+
+                {composite_children} BasicState manager_main_parallel_input_3 {
+                    name = "3"
+                    isInitial = False
+                }
+
+                {composite_children} BasicState manager_main_parallel_input_4 {
+                    name = "4"
+                    isInitial = False
+                }
+
+                {composite_children} BasicState manager_main_parallel_input_5 {
+                    name = "5"
+                    isInitial = False
+                }
+
+                {composite_children} BasicState manager_main_parallel_input_6 {
+                    name = "6"
+                    isInitial = False
+                }
+
+                {composite_children} BasicState manager_main_parallel_input_7 {
+                    name = "7"
+                    isInitial = False
+                }
+
+                {composite_children} BasicState manager_main_parallel_input_8 {
+                    name = "8"
+                    isInitial = False
+                }
+
+                {composite_children} BasicState manager_main_parallel_input_9 {
+                    name = "9"
+                    isInitial = False
+                }
+
+                {composite_children} BasicState manager_main_parallel_input_10 {
+                    name = "10"
+                    isInitial = False
+                }
+
+                {composite_children} BasicState manager_main_parallel_input_11 {
+                    name = "11"
+                    isInitial = False
+                }
+
+                {composite_children} BasicState manager_main_parallel_input_12 {
+                    name = "12"
+                    isInitial = False
+                }
+            }
+        }
+    }
+}
+
+transition (manager_main_parallel_input_1, manager_main_parallel_input_2) {
+    name = ""
+    after = $
+                Element function after(attributes : Element):
+                    return 1.0!
+            $
+
+    {transition_raises} Raise {
+        scope = "broad"
+        event = "create"
+        parameter = $
+                Element function raise(attributes : Element, parameters : Element):
+                    return list_create()!
+            $
+    }
+}
+
+transition (manager_main_parallel_input_2, manager_main_parallel_input_3) {
+    name = ""
+    after = $
+                Element function after(attributes : Element):
+                    return 4.0!
+            $
+
+    {transition_raises} Raise {
+        scope = "broad"
+        event = "toggle"
+        parameter = $
+                Element function raise(attributes : Element, parameters : Element):
+                    return list_create()!
+            $
+    }
+}
+
+transition (manager_main_parallel_input_3, manager_main_parallel_input_4) {
+    name = ""
+    after = $
+                Element function after(attributes : Element):
+                    return 10.0!
+            $
+
+    {transition_raises} Raise {
+        scope = "broad"
+        event = "create"
+        parameter = $
+                Element function raise(attributes : Element, parameters : Element):
+                    return list_create()!
+            $
+    }
+}
+
+transition (manager_main_parallel_input_4, manager_main_parallel_input_5) {
+    name = ""
+    after = $
+                Element function after(attributes : Element):
+                    return 5.0!
+            $
+
+    {transition_raises} Raise {
+        scope = "broad"
+        event = "police_interrupt"
+        parameter = $
+                Element function raise(attributes : Element, parameters : Element):
+                    return list_create()!
+            $
+    }
+}
+
+transition (manager_main_parallel_input_5, manager_main_parallel_input_6) {
+    name = ""
+    after = $
+                Element function after(attributes : Element):
+                    return 9.4!
+            $
+
+    {transition_raises} Raise {
+        scope = "broad"
+        event = "toggle"
+        parameter = $
+                Element function raise(attributes : Element, parameters : Element):
+                    return list_create()!
+            $
+    }
+}
+
+transition (manager_main_parallel_input_6, manager_main_parallel_input_7) {
+    name = ""
+    after = $
+                Element function after(attributes : Element):
+                    return 100.0!
+            $
+
+    {transition_raises} Raise {
+        scope = "broad"
+        event = "toggle"
+        parameter = $
+                Element function raise(attributes : Element, parameters : Element):
+                    return list_create()!
+            $
+    }
+}
+
+transition (manager_main_parallel_input_7, manager_main_parallel_input_8) {
+    name = ""
+    after = $
+                Element function after(attributes : Element):
+                    return 10.0!
+            $
+
+    {transition_raises} Raise {
+        scope = "broad"
+        event = "police_interrupt"
+        parameter = $
+                Element function raise(attributes : Element, parameters : Element):
+                    return list_create()!
+            $
+    }
+}
+
+transition (manager_main_parallel_input_8, manager_main_parallel_input_9) {
+    name = ""
+    after = $
+                Element function after(attributes : Element):
+                    return 5.0!
+            $
+
+    {transition_raises} Raise {
+        scope = "broad"
+        event = "delete"
+        parameter = $
+                Element function raise(attributes : Element, parameters : Element):
+                    return list_create()!
+            $
+    }
+}
+
+transition (manager_main_parallel_input_9, manager_main_parallel_input_10) {
+    name = ""
+    after = $
+                Element function after(attributes : Element):
+                    return 4.0!
+            $
+
+    {transition_raises} Raise {
+        scope = "broad"
+        event = "delete"
+        parameter = $
+                Element function raise(attributes : Element, parameters : Element):
+                    return list_create()!
+            $
+    }
+}
+
+transition (manager_main_parallel_input_10, manager_main_parallel_input_11) {
+    name = ""
+    after = $
+                Element function after(attributes : Element):
+                    return 5.0!
+            $
+
+    {transition_raises} Raise {
+        scope = "broad"
+        event = "exit"
+        parameter = $
+                Element function raise(attributes : Element, parameters : Element):
+                    return list_create()!
+            $
+    }
+}
+
+transition (manager_main_parallel_core_start, manager_main_parallel_core_start) {
+    name = "create"
+    event = "create"
+
+    {transition_raises} Raise {
+        scope = "cd"
+        event = "create_instance"
+        parameter = $
+                Element function raise(attributes : Element, parameters : Element):
+                    Element result
+                    result = list_create()
+                    list_append(result, "trafficlights")
+                    list_append(result, "TrafficLight")
+                    list_append(result, read_root())
+                    return result!
+            $
+    }
+}
+
+transition (manager_main_parallel_core_start, manager_main_parallel_core_start) {
+    name = "created"
+    event = "instance_created"
+
+    script = $
+            Void function script(attributes : Element, parameters : Element):
+                list_append(attributes["trafficlights"], parameters[0])
+                return!
+        $
+}
+
+transition (manager_main_parallel_core_start, manager_main_parallel_core_start) {
+    name = "exit"
+    event = "exit"
+
+    {transition_raises} Raise {
+        scope = "cd"
+        event = "delete_instance"
+        parameter = $
+                Element function raise(attributes : Element, parameters : Element):
+                    Element result
+                    result = list_create()
+                    list_append(result, "0")
+                    return result!
+            $
+    }
+}
+
+transition (manager_main_parallel_core_start, manager_main_parallel_core_start) {
+    name = "delete"
+    event = "delete"
+
+    {transition_raises} Raise {
+        scope = "cd"
+        event = "delete_instance"
+        parameter = $
+                Element function raise(attributes : Element, parameters : Element):
+                    Element result
+                    result = list_create()
+                    list_append(result, list_pop_final(attributes["trafficlights"]))
+                    return result!
+            $
+    }
+}
+
+Class trafficlight {
+    name = "TrafficLight"
+    default = False
+    constructor_body = $
+            Void function constructor(attributes : Element, parameters : Element):
+                dict_add(attributes, "counter", 0)
+                dict_add(attributes, "colour", "")
+                return!
+        $
+
+    {behaviour} CompositeState trafficlight_main {
+        name = "main"
+        isInitial = True
+        {composite_children} BasicState trafficlight_main_off {
+            name = "off"
+            isInitial = True
+        }
+
+        {composite_children} ParallelState trafficlight_main_main {
+            name = "main"
+            isInitial = False
+
+            {onExitRaise} Raise {
+                event = "displayNone"
+                scope = "output"
+            }
+
+            {parallel_children} CompositeState trafficlight_main_main_trafficlight {
+                name = "trafficlight"
+                isInitial = False
+
+                {composite_children} CompositeState trafficlight_main_main_trafficlight_normal {
+                    name = "normal"
+                    isInitial = True
+
+                    onExitScript = $
+                            Void function onexit(attributes : Element):
+                                dict_overwrite(attributes, "colour", "")
+                                return!
+                        $
+
+                    {composite_children} BasicState trafficlight_main_main_trafficlight_normal_red {
+                        name = "red"
+                        isInitial = True
+
+                        onEntryScript = $
+                                Void function onentry(attributes : Element):
+                                    dict_overwrite(attributes, "counter", 60)
+                                    dict_overwrite(attributes, "colour", "red")
+                                    return!
+                            $
+
+                        {onEntryRaise} Raise {
+                            event = "displayRed"
+                            scope = "output"
+                        }
+                        {onEntryRaise} Raise {
+                            event = "resetTimer"
+                            scope = "output"
+                        }
+                    }
+
+                    {composite_children} BasicState trafficlight_main_main_trafficlight_normal_green {
+                        name = "green"
+                        isInitial = False
+
+                        onEntryScript = $
+                                Void function onentry(attributes : Element):
+                                    dict_overwrite(attributes, "counter", 55)
+                                    dict_overwrite(attributes, "colour", "green")
+                                    return!
+                            $
+
+                        {onEntryRaise} Raise {
+                            event = "displayGreen"
+                            scope = "output"
+                        }
+                        {onEntryRaise} Raise {
+                            event = "resetTimer"
+                            scope = "output"
+                        }
+                    }
+
+                    {composite_children} BasicState trafficlight_main_main_trafficlight_normal_yellow {
+                        name = "yellow"
+                        isInitial = False
+
+                        onEntryScript = $
+                                Void function onentry(attributes : Element):
+                                    dict_overwrite(attributes, "colour", "")
+                                    return!
+                            $
+
+                        {onEntryRaise} Raise {
+                            event = "displayYellow"
+                            scope = "output"
+                        }
+                        {onEntryRaise} Raise {
+                            event = "disableTimer"
+                            scope = "output"
+                        }
+                    }
+
+                    {composite_children} HistoryState trafficlight_main_main_trafficlight_normal_hist {
+                        name = "hist"
+                    }
+                }
+
+                {composite_children} CompositeState trafficlight_main_main_trafficlight_interrupted {
+                    {composite_children} BasicState trafficlight_main_main_trafficlight_interrupted_yellow {
+                        name = "yellow"
+                        isInitial = True
+
+                        {onEntryRaise} Raise {
+                            event = "displayYellow"
+                            scope = "output"
+                        }
+                    }
+
+                    {composite_children} BasicState trafficlight_main_main_trafficlight_interrupted_black {
+                        name = "black"
+                        isInitial = False
+
+                        {onEntryRaise} Raise {
+                            event = "displayNone"
+                            scope = "output"
+                        }
+                    }
+                }
+            }
+
+            {parallel_children} CompositeState trafficlight_main_main_timer {
+                name = "timer"
+                isInitial = False
+
+                {composite_children} BasicState trafficlight_main_main_timer_disabled {
+                    name = "disabled"
+                    isInitial = False
+                }
+
+                {composite_children} CompositeState trafficlight_main_main_timer_running {
+                    name = "running"
+                    isInitial = True
+
+                    {onExitRaise} Raise {
+                        event = "updateTimerValue"
+                        scope = "output"
+                        parameter = $
+                                Element function parameter(attributes : Element):
+                                    return -1!
+                            $
+                    }
+
+                    {composite_children} BasicState trafficlight_main_main_timer_running_decidingcolor {
+                        name = "decidingcolor"
+                        isInitial = True
+                    }
+
+                    {composite_children} BasicState trafficlight_main_main_timer_running_green {
+                        name = "green"
+                        isInitial = False
+
+                        {onEntryRaise} Raise {
+                            event = "updateTimerValue"
+                            scope = "output"
+                            parameter = $
+                                    Element function raise(attributes : Element):
+                                        return attributes["counter"]!
+                                $
+                        }
+                    }
+
+                    {composite_children} BasicState trafficlight_main_main_timer_running_red {
+                        name = "red"
+                        isInitial = False
+
+                        {onEntryRaise} Raise {
+                            event = "updateTimerValue"
+                            parameter = $
+                                    Element function raise(attributes : Element):
+                                        return attributes["counter"]!
+                                $
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+
+transition (trafficlight_main_off, trafficlight_main_main) {
+    name = "toggle"
+    event = "toggle"
+}
+
+transition (trafficlight_main_main, trafficlight_main_off) {
+    name = "toggle"
+    event = "toggle"
+}
+
+transition (trafficlight_main_main_trafficlight_normal, trafficlight_main_main_trafficlight_interrupted) {
+    name = "police_interrupt / disableTimer"
+    event = "police_interrupt"
+
+    {transition_raises} Raise {
+        event = "disableTimer"
+    }
+}
+
+transition (trafficlight_main_main_trafficlight_interrupted, trafficlight_main_main_trafficlight_normal_hist) {
+    name = "police_interrupt / enableTimer"
+    event = "police_interrupt"
+
+    {transition_raises} Raise {
+        event = "enableTimer"
+    }
+}
+
+transition (trafficlight_main_main_trafficlight_normal_red, trafficlight_main_main_trafficlight_normal_green) {
+    name = "after 60s"
+    after = $
+            Float function after(attributes : Element):
+                return 60.0!
+        $
+}
+
+transition (trafficlight_main_main_trafficlight_normal_green, trafficlight_main_main_trafficlight_normal_yellow) {
+    name = "after 55s"
+    after = $
+            Float function after(attributes : Element):
+                return 55.0!
+        $
+}
+
+transition (trafficlight_main_main_trafficlight_normal_yellow, trafficlight_main_main_trafficlight_normal_red) {
+    name = "after 5s"
+    after = $
+            Float function after(attributes : Element):
+                return 5.0!
+        $
+
+    {transition_raises} Raise {
+        event = "enableTimer"
+    }
+}
+
+transition (trafficlight_main_main_trafficlight_interrupted_yellow, trafficlight_main_main_trafficlight_interrupted_black) {
+    name = "after 500ms"
+    after = $
+            Float function after(attributes : Element):
+                return 0.5!
+        $
+}
+
+transition (trafficlight_main_main_trafficlight_interrupted_black, trafficlight_main_main_trafficlight_interrupted_yellow) {
+    name = "after 500ms"
+    after = $
+            Float function after(attributes : Element):
+                return 0.5!
+        $
+}
+
+transition (trafficlight_main_main_timer_disabled, trafficlight_main_main_timer_running) {
+    name = "enableTimer"
+    event = "enableTimer"
+}
+
+transition (trafficlight_main_main_timer_running, trafficlight_main_main_timer_disabled) {
+    name = "disableTimer"
+    event = "disableTimer"
+}
+
+transition (trafficlight_main_main_timer_running, trafficlight_main_main_timer_running) {
+    name = "resetTimer"
+    event = "resetTimer"
+}
+
+transition (trafficlight_main_main_timer_running_decidingcolor, trafficlight_main_main_timer_running_green) {
+    name = "[green] / updateTimerColour"
+    cond = $
+            Boolean function cond(attributes : Element, parameters : Element):
+                return value_eq(attributes["colour"], "green")!
+        $
+
+    {transition_raises} Raise {
+        event = "updateTimerColour"
+        scope = "output"
+        parameter = $
+                Element function param(attributes : Element, parameters : Element):
+                    return "green"!
+            $
+    }
+}
+
+transition (trafficlight_main_main_timer_running_decidingcolor, trafficlight_main_main_timer_running_red) {
+    name = "[red] / updateTimerColour"
+    cond = $
+            Boolean function cond(attributes : Element, parameters : Element):
+                return value_eq(attributes["colour"], "red")!
+        $
+
+    {transition_raises} Raise {
+        event = "updateTimerColour"
+        scope = "output"
+        parameter = $
+                Element function param(attributes : Element, parameters : Element):
+                    return "red"!
+            $
+    }
+}
+
+transition (trafficlight_main_main_timer_running_red, trafficlight_main_main_timer_running_red) {
+    name = "after 1s / counter -= 1"
+    after = $
+            Float function after(attributes : Element):
+                return 1.0!
+        $
+    script = $
+            Void function script(attributes : Element, parameters : Element):
+                dict_overwrite(attributes, "counter", integer_subtraction(attributes["counter"], 1))
+                return!
+        $
+
+    {transition_raises} Raise {
+        event = "updateTimerValue"
+        scope = "output"
+        parameter = $
+                Element function param(attributes : Element, parameters : Element):
+                    return attributes["counter"]!
+            $
+    }
+}
+
+transition (trafficlight_main_main_timer_running_green, trafficlight_main_main_timer_running_green) {
+    name = "after 1s / counter -= 1"
+    after = $
+            Float function after(attributes : Element):
+                return 1.0!
+        $
+    script = $
+            Void function script(attributes : Element, parameters : Element):
+                dict_overwrite(attributes, "counter", integer_subtraction(attributes["counter"], 1))
+                return!
+        $
+    {transition_raises} Raise {
+        event = "updateTimerValue"
+        scope = "output"
+        parameter = $
+                Element function param(attributes : Element, parameters : Element):
+                    return attributes["counter"]!
+            $
+    }
+}

+ 8 - 8
integration/code/pm_pn_reachability.mvc

@@ -1,25 +1,25 @@
 Start start {}
 Finish finish {}
 Exec initializePN {
-    name = "initialize_PN"
+    name = "test/initialize_PN"
 }
 Exec refinePN {
-    name = "refine_PN"
+    name = "test/refine_PN"
 }
 Exec reachability {
-    name = "reachability"
+    name = "test/reachability"
 }
 Exec reachability_print{
-    name = "reachability_print"
+    name = "test/reachability_print"
 }
 
 Data pn {
-    name = "pn"
-    type = "PetriNet"
+    name = "test/pn"
+    type = "test/PetriNet"
 }
 Data reachability_graph {
-    name = "reachability"
-    type = "ReachabilityGraph"
+    name = "test/reachability"
+    type = "test/ReachabilityGraph"
 }
 
 Next (start, initializePN) {}

+ 0 - 373
integration/test_mvc.py

@@ -1,373 +0,0 @@
-import unittest
-
-from utils import *
-import sys
-
-sys.path.append("wrappers")
-from modelverse import *
-
-expected_model_list = set([("SimpleClassDiagrams", "SimpleClassDiagrams"),
-                           ("CoreFormalism", "SimpleClassDiagrams"),
-                           ("ManualOperation", "SimpleClassDiagrams"),
-                           ("ActionLanguage", "SimpleClassDiagrams"),
-                           ("ProcessModel", "SimpleClassDiagrams"),
-                           ("Tracability", "SimpleClassDiagrams"),
-                           ("conformance_mv", "ActionLanguage"),
-                           ("bottom", "SimpleClassDiagrams"),
-                           ("core", "CoreFormalism"),
-                           ('TypeMapping', 'SimpleClassDiagrams'),
-                           ('TM_SimpleClassDiagrams', 'TypeMapping'),
-                           ('TM_TypeMapping', 'TypeMapping'),
-                           ('TM_Tracability', 'TypeMapping'),
-                           ('TM_ProcessModel', 'TypeMapping'),
-                           ('TM_ActionLanguage', 'TypeMapping'),
-                           ('TM_ManualOperation', 'TypeMapping'),
-                           ('TM_conformance_mv', 'TypeMapping'),
-                           ('TM_CoreFormalism', 'TypeMapping'),
-                           ('TM_core', 'TypeMapping'),
-                           ('TM_bottom', 'TypeMapping'),
-                          ])
-
-expected_model_full_list = set([("SimpleClassDiagrams", "SimpleClassDiagrams", "admin", "admin", "221"),
-                                ("CoreFormalism", "SimpleClassDiagrams", "admin", "admin", "221"),
-                                ("ManualOperation", "SimpleClassDiagrams", "admin", "admin", "221"),
-                                ("ActionLanguage", "SimpleClassDiagrams", "admin", "admin", "221"),
-                                ("ProcessModel", "SimpleClassDiagrams", "admin", "admin", "221"),
-                                ("Tracability", "SimpleClassDiagrams", "admin", "admin", "221"),
-                                ("bottom", "SimpleClassDiagrams", "admin", "admin", "221"),
-                                ("conformance_mv", "ActionLanguage", "admin", "admin", "221"),
-                                ("core", "CoreFormalism", "admin", "admin", "220"),
-                                ('TypeMapping', 'SimpleClassDiagrams', "admin", "admin", "221"),
-                                ('TM_SimpleClassDiagrams', 'TypeMapping', "admin", "admin", "221"),
-                                ('TM_TypeMapping', 'TypeMapping', "admin", "admin", "221"),
-                                ('TM_Tracability', 'TypeMapping', "admin", "admin", "221"),
-                                ('TM_ProcessModel', 'TypeMapping', "admin", "admin", "221"),
-                                ('TM_ActionLanguage', 'TypeMapping', "admin", "admin", "221"),
-                                ('TM_ManualOperation', 'TypeMapping', "admin", "admin", "221"),
-                                ('TM_conformance_mv', 'TypeMapping', "admin", "admin", "221"),
-                                ('TM_CoreFormalism', 'TypeMapping', "admin", "admin", "221"),
-                                ('TM_core', 'TypeMapping', "admin", "admin", "220"),
-                                ('TM_bottom', 'TypeMapping', "admin", "admin", "221"),
-                                ])
-
-expected_transformation_list = set([("ActionLanguage", "conformance_mv")])
-
-class TestModelverseCore(unittest.TestCase):
-    def setUp(self):
-        self.proc, self.address = start_mvc()
-        init(self.address)
-        login("admin", "admin")
-
-    def tearDown(self):
-        try:
-            kill(self.proc)
-        except:
-            print("Got exception during teardown.")
-
-    def test_list(self):
-        assert model_list() == expected_model_list
-        
-    def test_list_full(self):
-        assert model_list_full() == expected_model_full_list
-
-    def test_model_add_empty(self):
-        model_add("Empty", "SimpleClassDiagrams")
-        assert model_list() == set(list(expected_model_list) + [("Empty", "SimpleClassDiagrams"), ("TM_Empty", "TypeMapping")])
-        assert model_list_full() == set(list(expected_model_full_list) + [("Empty", "SimpleClassDiagrams", "admin", "nobody", "200"),
-                                                                          ("TM_Empty", "TypeMapping", "admin", "nobody", "200"),
-                                                                         ])
-
-    def test_model_verify(self):
-        assert verify("SimpleClassDiagrams", "SimpleClassDiagrams") == "OK"
-
-    def test_model_empty_instantiate(self):
-        model_add("Empty", "SimpleClassDiagrams")
-        assert model_list() == set(list(expected_model_list) + [("Empty", "SimpleClassDiagrams"), ("TM_Empty", "TypeMapping")])
-        assert element_list("Empty") == set([])
-        instantiate("Empty", "Class", ID="A")
-        assert element_list("Empty") == set([("A", "Class")])
-        assert model_list() == set(list(expected_model_list) + [("Empty", "SimpleClassDiagrams"), ("TM_Empty", "TypeMapping")])
-
-    def test_model_overwrite(self):
-        model_add("Empty", "SimpleClassDiagrams")
-        assert model_list() == set(list(expected_model_list) + [("Empty", "SimpleClassDiagrams"), ("TM_Empty", "TypeMapping")])
-        assert element_list("Empty") == set([])
-        instantiate("Empty", "Class", ID="A")
-        assert element_list("Empty") == set([("A", "Class")])
-        model_overwrite("Empty")
-        assert element_list("Empty") == set([])
-        assert model_list() == set(list(expected_model_list) + [("Empty", "SimpleClassDiagrams"), ("TM_Empty", "TypeMapping")])
-        instantiate("Empty", "Class", ID="B")
-        assert element_list("Empty") == set([("B", "Class")])
-
-    def test_transform_add_MT(self):
-        model_add("PetriNet", "SimpleClassDiagrams", open("integration/code/petrinets.mvc", "r").read())
-        assert model_list() == set(list(expected_model_list) + [("PetriNet", "SimpleClassDiagrams"),
-                                                                ("TM_PetriNet", "TypeMapping"),
-                                                               ])
-        transformation_add_MT({"PetriNet": "PetriNet"}, {}, "print_pn", open("integration/code/pn_print.mvc").read())
-        assert model_list() == set(list(expected_model_list) + [("PetriNet", "SimpleClassDiagrams"),
-                                                                ("TM_PetriNet", "TypeMapping"),
-                                                                ("__RAM_print_pn", "SimpleClassDiagrams"),
-                                                                ("TM___RAM_print_pn", "TypeMapping"),
-                                                                ("__merged_print_pn", "SimpleClassDiagrams"),
-                                                                ("TM___merged_print_pn", "TypeMapping"),
-                                                                ("print_pn", "__RAM_print_pn"),
-                                                                ("TM_print_pn", "TypeMapping")])
-        assert transformation_list() == set(list(expected_transformation_list) + [("ModelTransformation", "print_pn")])
-
-    def test_transform_add_MT_pn_print_exec(self):
-        log = []
-
-        def callback(value):
-            log.append(value)
-
-        model_add("PetriNet", "SimpleClassDiagrams", open("integration/code/pn_runtime.mvc", "r").read())
-        model_add("my_pn", "PetriNet", open("integration/code/pn_runtime_model.mvc", "r").read())
-        assert model_list() == set(list(expected_model_list) + [("PetriNet", "SimpleClassDiagrams"),
-                                                                ("TM_PetriNet", "TypeMapping"),
-                                                                ("my_pn", "PetriNet"),
-                                                                ("TM_my_pn", "TypeMapping"),
-                                                               ])
-        transformation_add_MT({"PetriNet": "PetriNet"}, {}, "print_pn", open("integration/code/pn_print.mvc").read())
-        assert model_list() == set(list(expected_model_list) + [("PetriNet", "SimpleClassDiagrams"),
-                                                                ("TM_PetriNet", "TypeMapping"),
-                                                                ("my_pn", "PetriNet"),
-                                                                ("TM_my_pn", "TypeMapping"),
-                                                                ("__RAM_print_pn", "SimpleClassDiagrams"),
-                                                                ("TM___RAM_print_pn", "TypeMapping"),
-                                                                ("__merged_print_pn", "SimpleClassDiagrams"),
-                                                                ("TM___merged_print_pn", "TypeMapping"),
-                                                                ("print_pn", "__RAM_print_pn"),
-                                                                ("TM_print_pn", "TypeMapping")])
-        assert transformation_list() == set(list(expected_transformation_list) + [("ModelTransformation", "print_pn")])
-        assert transformation_execute_MT("print_pn", {"PetriNet": "my_pn"}, {}, callback) == True
-
-        assert set(log) == set(['"p1" --> 1',
-                                '"p2" --> 2',
-                                '"p3" --> 3'])
-
-    def test_transform_add_MT_pn_simulate(self):
-        """
-        This method tests the execution of a somewhat realistic use of the Modelverse and its core functions, through the use of a simple Petri nets example.
-        First, Petri net metamodels are created for both the design language and the runtime language.
-        Both languages only differ marginally from each other, with the runtime language only adding information on the currently selected transition for execution.
-        Afterwards, a trivial Petri net model is created in the design language.
-        This part tests the domain-specific and meta-modelling concepts of the Modelverse.
-
-        After all models are created, transformations are defined to map between both languages: from design to runtime, and vice versa.
-        Additional transformations are created for in-place model simulation and the printing of a Petri net model.
-        This tests the modification of models, through the use of model transformations.
-        Due to the use of a separate design and runtime language, we test exogenous transformations.
-        The simulation transformation takes a single step in the Petri net model, by firing one of the applicable transitions, and therefore tests endogenous transformations.
-        """
-        log = []
-
-        def callback(value):
-            log.append(value)
-
-        model_add("PetriNet", "SimpleClassDiagrams", open("integration/code/pn_design.mvc", "r").read())
-        model_add("PetriNet_Runtime", "SimpleClassDiagrams", open("integration/code/pn_runtime.mvc", "r").read())
-
-        model_add("my_pn", "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_R2D():
-            instantiate(None, "Association", ("PetriNet_Runtime/Place", "PetriNet/Place"), ID="R2D_PlaceLink")
-            instantiate(None, "Association", ("PetriNet_Runtime/Transition", "PetriNet/Transition"), ID="R2D_TransitionLink")
-
-        transformation_add_MT({"PetriNet": "PetriNet"}, {}, "print_pn", open("integration/code/pn_print.mvc").read())
-        transformation_add_MT({"PetriNet": "PetriNet"}, {"PetriNet_Runtime": "PetriNet_Runtime"}, "pn_design_to_runtime", open("integration/code/pn_design_to_runtime.mvc").read(), add_tracability_D2R)
-        transformation_add_MT({"PetriNet_Runtime": "PetriNet_Runtime"}, {"PetriNet_Runtime": "PetriNet_Runtime"}, "pn_simulate", open("integration/code/pn_simulate.mvc").read())
-        transformation_add_MT({"PetriNet_Runtime": "PetriNet_Runtime"}, {"PetriNet": "PetriNet"}, "pn_runtime_to_design", open("integration/code/pn_runtime_to_design.mvc").read(), add_tracability_R2D)
-
-        log = []
-        assert transformation_execute_MT("print_pn", {"PetriNet": "my_pn"}, {}, callback) == True
-        assert set(log) == set(['"p1" --> 1',
-                                '"p2" --> 2',
-                                '"p3" --> 3'])
-
-        assert transformation_execute_MT("pn_design_to_runtime", {"PetriNet": "my_pn"}, {"PetriNet_Runtime": "my_pn_RT"}) == True
-        assert transformation_execute_MT("pn_simulate", {"PetriNet_Runtime": "my_pn_RT"}, {"PetriNet_Runtime": "my_pn_RT"}) == True
-        assert transformation_execute_MT("pn_runtime_to_design", {"PetriNet_Runtime": "my_pn_RT"}, {"PetriNet": "my_pn"}) == True
-
-        log = []
-        assert transformation_execute_MT("print_pn", {"PetriNet": "my_pn"}, {}, callback) == True
-        assert set(log) == set(['"p1" --> 0',
-                                '"p2" --> 1',
-                                '"p3" --> 5'])
-
-    def test_transform_add_AL_pn_simulate(self):
-        log = []
-
-        def callback(value):
-            log.append(value)
-
-        model_add("PetriNet", "SimpleClassDiagrams", open("integration/code/pn_design.mvc", "r").read())
-        model_add("PetriNet_Runtime", "SimpleClassDiagrams", open("integration/code/pn_runtime.mvc", "r").read())
-
-        model_add("my_pn", "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_R2D():
-            instantiate(None, "Association", ("PetriNet_Runtime/Place", "PetriNet/Place"), ID="R2D_PlaceLink")
-            instantiate(None, "Association", ("PetriNet_Runtime/Transition", "PetriNet/Transition"), ID="R2D_TransitionLink")
-
-        transformation_add_MT({"PetriNet": "PetriNet"}, {}, "print_pn", open("integration/code/pn_print.mvc").read())
-        transformation_add_MT({"PetriNet": "PetriNet"}, {"PetriNet_Runtime": "PetriNet_Runtime"}, "pn_design_to_runtime", open("integration/code/pn_design_to_runtime.mvc").read(), add_tracability_D2R)
-        transformation_add_AL({"PetriNet_Runtime": "PetriNet_Runtime"}, {"PetriNet_Runtime": "PetriNet_Runtime"}, "pn_simulate", open("integration/code/pn_simulate.alc").read())
-        transformation_add_MT({"PetriNet_Runtime": "PetriNet_Runtime"}, {"PetriNet": "PetriNet"}, "pn_runtime_to_design", open("integration/code/pn_runtime_to_design.mvc").read(), add_tracability_R2D)
-
-        log = []
-        assert transformation_execute_MT("print_pn", {"PetriNet": "my_pn"}, {}, callback) == True
-        assert set(log) == set(['"p1" --> 1',
-                                '"p2" --> 2',
-                                '"p3" --> 3'])
-
-        assert transformation_execute_MT("pn_design_to_runtime", {"PetriNet": "my_pn"}, {"PetriNet_Runtime": "my_pn_RT"}) == True
-        assert transformation_execute_AL("pn_simulate", {"PetriNet_Runtime": "my_pn_RT"}, {"PetriNet_Runtime": "my_pn_RT"}) == True
-        assert transformation_execute_MT("pn_runtime_to_design", {"PetriNet_Runtime": "my_pn_RT"}, {"PetriNet": "my_pn"}) == True
-
-        log = []
-        assert transformation_execute_MT("print_pn", {"PetriNet": "my_pn"}, {}, callback) == True
-        assert set(log) == set(['"p1" --> 0',
-                                '"p2" --> 1',
-                                '"p3" --> 5'])
-
-    def test_transform_add_MANUAL_pn_simulate(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)
-
-        model_add("PetriNet", "SimpleClassDiagrams", open("integration/code/pn_design.mvc", "r").read())
-        model_add("PetriNet_Runtime", "SimpleClassDiagrams", open("integration/code/pn_runtime.mvc", "r").read())
-
-        model_add("my_pn", "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_R2D():
-            instantiate(None, "Association", ("PetriNet_Runtime/Place", "PetriNet/Place"), ID="R2D_PlaceLink")
-            instantiate(None, "Association", ("PetriNet_Runtime/Transition", "PetriNet/Transition"), ID="R2D_TransitionLink")
-
-        transformation_add_MT({"PetriNet": "PetriNet"}, {}, "print_pn", open("integration/code/pn_print.mvc").read())
-        transformation_add_MANUAL({"PetriNet": "PetriNet"}, {"PetriNet_Runtime": "PetriNet_Runtime"}, "pn_design_to_runtime", add_tracability_D2R)
-        transformation_add_AL({"PetriNet_Runtime": "PetriNet_Runtime"}, {"PetriNet_Runtime": "PetriNet_Runtime"}, "pn_simulate", open("integration/code/pn_simulate.alc").read())
-        transformation_add_MT({"PetriNet_Runtime": "PetriNet_Runtime"}, {"PetriNet": "PetriNet"}, "pn_runtime_to_design", open("integration/code/pn_runtime_to_design.mvc").read(), add_tracability_R2D)
-
-        log = []
-        assert transformation_execute_MT("print_pn", {"PetriNet": "my_pn"}, {}, callback) == True
-        assert set(log) == set(['"p1" --> 1',
-                                '"p2" --> 2',
-                                '"p3" --> 3'])
-
-        assert transformation_execute_MANUAL("pn_design_to_runtime", {"PetriNet": "my_pn"}, {"PetriNet_Runtime": "my_pn_RT"}, manual_callback) == True
-        assert transformation_execute_AL("pn_simulate", {"PetriNet_Runtime": "my_pn_RT"}, {"PetriNet_Runtime": "my_pn_RT"}) == True
-        assert transformation_execute_MT("pn_runtime_to_design", {"PetriNet_Runtime": "my_pn_RT"}, {"PetriNet": "my_pn"}) == True
-
-        log = []
-        assert transformation_execute_MT("print_pn", {"PetriNet": "my_pn"}, {}, callback) == True
-        assert set(log) == set(['"p1" --> 0',
-                                '"p2" --> 1',
-                                '"p3" --> 5'])
-
-    def test_process_model_trivial_pn_bigmain(self):
-        model_add("PetriNet", "SimpleClassDiagrams", open("integration/code/pn_design.mvc", "r").read())
-        model_add("ReachabilityGraph", "SimpleClassDiagrams", open("integration/code/reachability_graph.mvc", "r").read())
-        model_add("pn_reachability", "ProcessModel", open("integration/code/pm_pn_reachability.mvc", "r").read())
-        transformation_add_MT({}, {"PetriNet": "PetriNet"}, "initialize_PN", open("integration/code/initialize_PN.mvc", "r").read())
-        transformation_add_MANUAL({"PetriNet": "PetriNet"}, {"PetriNet": "PetriNet"}, "refine_PN")
-        transformation_add_AL({"PetriNet": "PetriNet"}, {"ReachabilityGraph": "ReachabilityGraph"}, "reachability", open("integration/code/reachability.alc", "r").read())
-        transformation_add_MT({"ReachabilityGraph": "ReachabilityGraph"}, {}, "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)
-
-            t1 = instantiate(None, "PetriNet/Transition")
-            attr_assign(None, t1, "name", "t1")
-
-            p2t = instantiate(None, "PetriNet/P2T", (p1, t1))
-            attr_assign(None, p2t, "weight", 1)
-
-        log = set([])
-        def callback_print(value):
-            log.add(value)
-
-        process_execute("pn_reachability", "my_", {"refine_PN": callback_refine_PN, "reachability_print": callback_print})
-
-        assert log == set(['"0": {"p1": 1, }',
-                           '"1": {"p1": 0, }',
-                           '"0" --["t1"]--> "1"'])
-
-    def test_process_model_trivial_pn_subfunction(self):
-        model_add("PetriNet", "SimpleClassDiagrams", open("integration/code/pn_design.mvc", "r").read())
-        model_add("ReachabilityGraph", "SimpleClassDiagrams", open("integration/code/reachability_graph.mvc", "r").read())
-        model_add("pn_reachability", "ProcessModel", open("integration/code/pm_pn_reachability.mvc", "r").read())
-        transformation_add_MT({}, {"PetriNet": "PetriNet"}, "initialize_PN", open("integration/code/initialize_PN.mvc", "r").read())
-        transformation_add_MANUAL({"PetriNet": "PetriNet"}, {"PetriNet": "PetriNet"}, "refine_PN")
-        transformation_add_AL({"PetriNet": "PetriNet"}, {"ReachabilityGraph": "ReachabilityGraph"}, "reachability", open("integration/code/reachability_subfunction.alc", "r").read())
-        transformation_add_MT({"ReachabilityGraph": "ReachabilityGraph"}, {}, "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)
-
-            t1 = instantiate(None, "PetriNet/Transition")
-            attr_assign(None, t1, "name", "t1")
-
-            p2t = instantiate(None, "PetriNet/P2T", (p1, t1))
-            attr_assign(None, p2t, "weight", 1)
-
-        log = set([])
-        def callback_print(value):
-            log.add(value)
-
-        process_execute("pn_reachability", "my_", {"refine_PN": callback_refine_PN, "reachability_print": callback_print})
-
-        assert log == set(['"0": {"p1": 1, }',
-                           '"1": {"p1": 0, }',
-                           '"0" --["t1"]--> "1"'])
-
-    def test_render(self):
-        model_add("CausalBlockDiagrams", "SimpleClassDiagrams", open("integration/code/cbd_design.mvc", 'r').read())
-        model_add("MM_rendered_graphical", "SimpleClassDiagrams", open("models/MM_rendered_graphical.mvc", 'r').read())
-        model_add("my_CBD", "CausalBlockDiagrams", open("integration/code/my_cbd.mvc", 'r').read())
-
-        def add_tracability():
-            instantiate(None, "Association", ("abstract/Block", "rendered/Group"), ID="TracabilityLink")
-
-        transformation_add_MT({"abstract": "CausalBlockDiagrams", "rendered": "MM_rendered_graphical"}, {"abstract": "CausalBlockDiagrams", "rendered": "MM_rendered_graphical"}, "render_graphical_CBD", open("models/CBD_mapper.mvc", 'r').read(), add_tracability)
-        result = model_render("my_CBD", "render_graphical_CBD")
-        assert len(result) == 23

+ 76 - 157
integration/test_powerwindow.py

@@ -16,68 +16,24 @@ class TestPowerWindow(unittest.TestCase):
         kill(self.proc)
 
     def test_process_powerwindow_fast(self):
-        model_add("ReachabilityGraph", "SimpleClassDiagrams", open("models/reachability_graph.mvc", "r").read())
-        model_add("PetriNet", "SimpleClassDiagrams", open("integration/code/pn_design.mvc", 'r').read())
-        model_add("Encapsulated_PetriNet", "SimpleClassDiagrams", open("models/petrinet_ports.mvc", 'r').read())
-
-        model_add("PW_Plant", "SimpleClassDiagrams", open("models/plant_PW.mvc", 'r').read())
-        model_add("PW_Environment", "SimpleClassDiagrams", open("models/environment_PW.mvc", 'r').read())
-        model_add("PW_Control", "SimpleClassDiagrams", open("models/control_PW.mvc", 'r').read())
-        model_add("Requirements", "SimpleClassDiagrams", open("models/requirements.mvc", 'r').read())
-        model_add("Query", "SimpleClassDiagrams", open("models/query.mvc", 'r').read())
-        model_add("Architecture", "SimpleClassDiagrams", open("models/architecture.mvc", 'r').read())
-
-        model_add("pm_powerwindow", "ProcessModel", open("models/pm_req_analyse.mvc", 'r').read())
-
-        assert model_list() == set([\
-                ("ReachabilityGraph", "SimpleClassDiagrams"),
-                ("PetriNet", "SimpleClassDiagrams"),
-                ("Encapsulated_PetriNet", "SimpleClassDiagrams"),
-                ("PW_Plant", "SimpleClassDiagrams"),
-                ("PW_Environment", "SimpleClassDiagrams"),
-                ("PW_Control", "SimpleClassDiagrams"),
-                ("Requirements", "SimpleClassDiagrams"),
-                ("Query", "SimpleClassDiagrams"),
-                ("Architecture", "SimpleClassDiagrams"),
-                ("pm_powerwindow", "ProcessModel"),
-                ("SimpleClassDiagrams", "SimpleClassDiagrams"),
-                ("TypeMapping", "SimpleClassDiagrams"),
-                ("CoreFormalism", "SimpleClassDiagrams"),
-                ("ManualOperation", "SimpleClassDiagrams"),
-                ("bottom", "SimpleClassDiagrams"),
-                ("ActionLanguage", "SimpleClassDiagrams"),
-                ("ProcessModel", "SimpleClassDiagrams"),
-                ("Tracability", "SimpleClassDiagrams"),
-                ("conformance_mv", "ActionLanguage"),
-                ("core", "CoreFormalism"),
-                ("TM_ReachabilityGraph", "TypeMapping"),
-                ("TM_PetriNet", "TypeMapping"),
-                ("TM_Encapsulated_PetriNet", "TypeMapping"),
-                ("TM_PW_Plant", "TypeMapping"),
-                ("TM_PW_Environment", "TypeMapping"),
-                ("TM_PW_Control", "TypeMapping"),
-                ("TM_Requirements", "TypeMapping"),
-                ("TM_Query", "TypeMapping"),
-                ("TM_Architecture", "TypeMapping"),
-                ("TM_pm_powerwindow", "TypeMapping"),
-                ("TM_SimpleClassDiagrams", "TypeMapping"),
-                ("TM_TypeMapping", "TypeMapping"),
-                ("TM_CoreFormalism", "TypeMapping"),
-                ("TM_ManualOperation", "TypeMapping"),
-                ("TM_bottom", "TypeMapping"),
-                ("TM_ActionLanguage", "TypeMapping"),
-                ("TM_ProcessModel", "TypeMapping"),
-                ("TM_Tracability", "TypeMapping"),
-                ("TM_conformance_mv", "TypeMapping"),
-                ("TM_core", "TypeMapping"),
-            ])
-
-        transformation_add_MANUAL({"Requirements": "Requirements"}, {"Requirements": "Requirements"}, "revise_req")
-        transformation_add_MANUAL({"Requirements": "Requirements", "PW_Environment": "PW_Environment"}, {"PW_Environment": "PW_Environment"}, "revise_environment")
-        transformation_add_MANUAL({"Requirements": "Requirements", "PW_Plant": "PW_Plant"}, {"PW_Plant": "PW_Plant"}, "revise_plant")
-        transformation_add_MANUAL({"Requirements": "Requirements", "PW_Control": "PW_Control"}, {"PW_Control": "PW_Control"}, "revise_control")
-        transformation_add_MANUAL({"Requirements": "Requirements", "Query": "Query"}, {"Query": "Query"}, "revise_query")
-        transformation_add_MANUAL({"Requirements": "Requirements", "Architecture": "Architecture"}, {"Architecture": "Architecture"}, "revise_architecture")
+        model_add("formalisms/ReachabilityGraph", "formalisms/SimpleClassDiagrams", open("models/reachability_graph.mvc", "r").read())
+        model_add("formalisms/PetriNet", "formalisms/SimpleClassDiagrams", open("integration/code/pn_design.mvc", 'r').read())
+        model_add("formalisms/Encapsulated_PetriNet", "formalisms/SimpleClassDiagrams", open("models/petrinet_ports.mvc", 'r').read())
+        model_add("formalisms/PW_Plant", "formalisms/SimpleClassDiagrams", open("models/plant_PW.mvc", 'r').read())
+        model_add("formalisms/PW_Environment", "formalisms/SimpleClassDiagrams", open("models/environment_PW.mvc", 'r').read())
+        model_add("formalisms/PW_Control", "formalisms/SimpleClassDiagrams", open("models/control_PW.mvc", 'r').read())
+        model_add("formalisms/Requirements", "formalisms/SimpleClassDiagrams", open("models/requirements.mvc", 'r').read())
+        model_add("formalisms/Query", "formalisms/SimpleClassDiagrams", open("models/query.mvc", 'r').read())
+        model_add("formalisms/Architecture", "formalisms/SimpleClassDiagrams", open("models/architecture.mvc", 'r').read())
+
+        model_add("models/pm_powerwindow", "formalisms/ProcessModel", open("models/pm_req_analyse.mvc", 'r').read())
+
+        transformation_add_MANUAL({"Requirements": "formalisms/Requirements"}, {"Requirements": "formalisms/Requirements"}, "models/revise_req")
+        transformation_add_MANUAL({"Requirements": "formalisms/Requirements", "PW_Environment": "formalisms/PW_Environment"}, {"PW_Environment": "formalisms/PW_Environment"}, "models/revise_environment")
+        transformation_add_MANUAL({"Requirements": "formalisms/Requirements", "PW_Plant": "formalisms/PW_Plant"}, {"PW_Plant": "formalisms/PW_Plant"}, "models/revise_plant")
+        transformation_add_MANUAL({"Requirements": "formalisms/Requirements", "PW_Control": "formalisms/PW_Control"}, {"PW_Control": "formalisms/PW_Control"}, "models/revise_control")
+        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")
@@ -94,23 +50,22 @@ class TestPowerWindow(unittest.TestCase):
             instantiate(None, "Association", ("Encapsulated_PetriNet/Place", "PetriNet/Place"), ID="EPN2PN_transition_link")
             instantiate(None, "Association", ("Encapsulated_PetriNet/Transition", "PetriNet/Transition"), ID="EPN2PN_place_link")
 
-        transformation_add_MT({}, {"PW_Plant": "PW_Plant", "PW_Environment": "PW_Environment", "PW_Control": "PW_Control", "Query": "Query", "Architecture": "Architecture", "Requirements": "Requirements"}, "make_initial_models", open("models/initialize.mvc", 'r').read())
-        transformation_add_MT({"PW_Plant": "PW_Plant"}, {"Encapsulated_PetriNet": "Encapsulated_PetriNet"}, "plant_to_EPN", open("models/plant_to_EPN.mvc", 'r').read(), tracability_PLANT2EPN)
-        transformation_add_MT({"PW_Control": "PW_Control"}, {"Encapsulated_PetriNet": "Encapsulated_PetriNet"}, "control_to_EPN", open("models/control_to_EPN.mvc", 'r').read(), tracability_CTRL2EPN)
-        transformation_add_MT({"PW_Environment": "PW_Environment"}, {"Encapsulated_PetriNet": "Encapsulated_PetriNet"}, "environment_to_EPN", open("models/environment_to_EPN.mvc", 'r').read(), tracability_ENV2EPN)
-        transformation_add_MT({"Encapsulated_PetriNet": "Encapsulated_PetriNet", "Architecture": "Architecture"}, {"PetriNet": "PetriNet"}, "combine_EPN", open("models/combine_EPN.mvc", 'r').read(), tracability_EPN2PN)
-        transformation_add_MT({"ReachabilityGraph": "ReachabilityGraph", "Query": "Query"}, {}, "match", open("models/matches.mvc", 'r').read())
+        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)
+        transformation_add_MT({"PW_Control": "formalisms/PW_Control"}, {"Encapsulated_PetriNet": "formalisms/Encapsulated_PetriNet"}, "models/control_to_EPN", open("models/control_to_EPN.mvc", 'r').read(), tracability_CTRL2EPN)
+        transformation_add_MT({"PW_Environment": "formalisms/PW_Environment"}, {"Encapsulated_PetriNet": "formalisms/Encapsulated_PetriNet"}, "models/environment_to_EPN", open("models/environment_to_EPN.mvc", 'r').read(), tracability_ENV2EPN)
+        transformation_add_MT({"Encapsulated_PetriNet": "formalisms/Encapsulated_PetriNet", "Architecture": "formalisms/Architecture"}, {"PetriNet": "formalisms/PetriNet"}, "models/combine_EPN", open("models/combine_EPN.mvc", 'r').read(), tracability_EPN2PN)
+        transformation_add_MT({"ReachabilityGraph": "formalisms/ReachabilityGraph", "Query": "formalisms/Query"}, {"ReachabilityGraph": "formalisms/ReachabilityGraph"}, "models/match", open("models/matches.mvc", 'r').read())
 
-        transformation_add_AL({"PetriNet": "PetriNet"}, {"ReachabilityGraph": "ReachabilityGraph"}, "reachability", open("models/reachability.alc", 'r').read())
-        transformation_add_AL({"ReachabilityGraph": "ReachabilityGraph"}, {}, "bfs", open("models/bfs.alc", 'r').read())
-        transformation_add_AL({"EPN_Plant": "Encapsulated_PetriNet", "EPN_Control": "Encapsulated_PetriNet", "EPN_Environment": "Encapsulated_PetriNet"}, {"Encapsulated_PetriNet": "Encapsulated_PetriNet"}, "merge_EPN", open("models/merge_EPN.alc", 'r').read())
+        transformation_add_AL({"PetriNet": "formalisms/PetriNet"}, {"ReachabilityGraph": "formalisms/ReachabilityGraph"}, "models/reachability", open("models/reachability.alc", 'r').read())
+        transformation_add_AL({"ReachabilityGraph": "formalisms/ReachabilityGraph"}, {}, "models/bfs", open("models/bfs.alc", 'r').read())
+        transformation_add_AL({"EPN_Plant": "formalisms/Encapsulated_PetriNet", "EPN_Control": "formalisms/Encapsulated_PetriNet", "EPN_Environment": "formalisms/Encapsulated_PetriNet"}, {"Encapsulated_PetriNet": "formalisms/Encapsulated_PetriNet"}, "models/merge_EPN", open("models/merge_EPN.alc", 'r').read())
 
         global called
         called = 0
 
         def get_function(filename):
             def func():
-                print("Uploading " + filename)
                 global called
                 if called > len(callbacks):
                     raise Exception("Seemingly called some operation twice!")
@@ -127,16 +82,16 @@ class TestPowerWindow(unittest.TestCase):
         cb_arch = get_function("models/architecture_model.mvc")
 
         callbacks = {
-                "revise_req": cb_req,
-                "revise_plant": cb_plant,
-                "revise_environment": cb_env,
-                "revise_control": cb_ctrl,
-                "revise_query": cb_query,
-                "revise_architecture": cb_arch,
+                "models/revise_req": cb_req,
+                "models/revise_plant": cb_plant,
+                "models/revise_environment": cb_env,
+                "models/revise_control": cb_ctrl,
+                "models/revise_query": cb_query,
+                "models/revise_architecture": cb_arch,
             }
 
         try:
-            process_execute("pm_powerwindow", "pm_", callbacks)
+            process_execute("models/pm_powerwindow", "pm_", callbacks)
         except:
             import traceback
             print(traceback.format_exc())
@@ -147,68 +102,24 @@ class TestPowerWindow(unittest.TestCase):
 
     @slow
     def test_process_powerwindow_debug(self):
-        model_add("ReachabilityGraph", "SimpleClassDiagrams", open("models/reachability_graph.mvc", "r").read())
-        model_add("PetriNet", "SimpleClassDiagrams", open("integration/code/pn_design.mvc", 'r').read())
-        model_add("Encapsulated_PetriNet", "SimpleClassDiagrams", open("models/petrinet_ports.mvc", 'r').read())
-
-        model_add("PW_Plant", "SimpleClassDiagrams", open("models/plant_PW.mvc", 'r').read())
-        model_add("PW_Environment", "SimpleClassDiagrams", open("models/environment_PW.mvc", 'r').read())
-        model_add("PW_Control", "SimpleClassDiagrams", open("models/control_PW.mvc", 'r').read())
-        model_add("Requirements", "SimpleClassDiagrams", open("models/requirements.mvc", 'r').read())
-        model_add("Query", "SimpleClassDiagrams", open("models/query.mvc", 'r').read())
-        model_add("Architecture", "SimpleClassDiagrams", open("models/architecture.mvc", 'r').read())
-
-        model_add("pm_powerwindow", "ProcessModel", open("models/pm_req_analyse.mvc", 'r').read())
-
-        assert model_list() == set([\
-                ("ReachabilityGraph", "SimpleClassDiagrams"),
-                ("PetriNet", "SimpleClassDiagrams"),
-                ("Encapsulated_PetriNet", "SimpleClassDiagrams"),
-                ("PW_Plant", "SimpleClassDiagrams"),
-                ("PW_Environment", "SimpleClassDiagrams"),
-                ("PW_Control", "SimpleClassDiagrams"),
-                ("Requirements", "SimpleClassDiagrams"),
-                ("Query", "SimpleClassDiagrams"),
-                ("Architecture", "SimpleClassDiagrams"),
-                ("pm_powerwindow", "ProcessModel"),
-                ("SimpleClassDiagrams", "SimpleClassDiagrams"),
-                ("TypeMapping", "SimpleClassDiagrams"),
-                ("CoreFormalism", "SimpleClassDiagrams"),
-                ("ManualOperation", "SimpleClassDiagrams"),
-                ("bottom", "SimpleClassDiagrams"),
-                ("ActionLanguage", "SimpleClassDiagrams"),
-                ("ProcessModel", "SimpleClassDiagrams"),
-                ("Tracability", "SimpleClassDiagrams"),
-                ("conformance_mv", "ActionLanguage"),
-                ("core", "CoreFormalism"),
-                ("TM_ReachabilityGraph", "TypeMapping"),
-                ("TM_PetriNet", "TypeMapping"),
-                ("TM_Encapsulated_PetriNet", "TypeMapping"),
-                ("TM_PW_Plant", "TypeMapping"),
-                ("TM_PW_Environment", "TypeMapping"),
-                ("TM_PW_Control", "TypeMapping"),
-                ("TM_Requirements", "TypeMapping"),
-                ("TM_Query", "TypeMapping"),
-                ("TM_Architecture", "TypeMapping"),
-                ("TM_pm_powerwindow", "TypeMapping"),
-                ("TM_SimpleClassDiagrams", "TypeMapping"),
-                ("TM_TypeMapping", "TypeMapping"),
-                ("TM_CoreFormalism", "TypeMapping"),
-                ("TM_ManualOperation", "TypeMapping"),
-                ("TM_bottom", "TypeMapping"),
-                ("TM_ActionLanguage", "TypeMapping"),
-                ("TM_ProcessModel", "TypeMapping"),
-                ("TM_Tracability", "TypeMapping"),
-                ("TM_conformance_mv", "TypeMapping"),
-                ("TM_core", "TypeMapping"),
-            ])
-
-        transformation_add_MANUAL({"Requirements": "Requirements"}, {"Requirements": "Requirements"}, "revise_req")
-        transformation_add_MANUAL({"Requirements": "Requirements", "PW_Environment": "PW_Environment"}, {"PW_Environment": "PW_Environment"}, "revise_environment")
-        transformation_add_MANUAL({"Requirements": "Requirements", "PW_Plant": "PW_Plant"}, {"PW_Plant": "PW_Plant"}, "revise_plant")
-        transformation_add_MANUAL({"Requirements": "Requirements", "PW_Control": "PW_Control"}, {"PW_Control": "PW_Control"}, "revise_control")
-        transformation_add_MANUAL({"Requirements": "Requirements", "Query": "Query"}, {"Query": "Query"}, "revise_query")
-        transformation_add_MANUAL({"Requirements": "Requirements", "Architecture": "Architecture"}, {"Architecture": "Architecture"}, "revise_architecture")
+        model_add("formalisms/ReachabilityGraph", "formalisms/SimpleClassDiagrams", open("models/reachability_graph.mvc", "r").read())
+        model_add("formalisms/PetriNet", "formalisms/SimpleClassDiagrams", open("integration/code/pn_design.mvc", 'r').read())
+        model_add("formalisms/Encapsulated_PetriNet", "formalisms/SimpleClassDiagrams", open("models/petrinet_ports.mvc", 'r').read())
+        model_add("formalisms/PW_Plant", "formalisms/SimpleClassDiagrams", open("models/plant_PW.mvc", 'r').read())
+        model_add("formalisms/PW_Environment", "formalisms/SimpleClassDiagrams", open("models/environment_PW.mvc", 'r').read())
+        model_add("formalisms/PW_Control", "formalisms/SimpleClassDiagrams", open("models/control_PW.mvc", 'r').read())
+        model_add("formalisms/Requirements", "formalisms/SimpleClassDiagrams", open("models/requirements.mvc", 'r').read())
+        model_add("formalisms/Query", "formalisms/SimpleClassDiagrams", open("models/query.mvc", 'r').read())
+        model_add("formalisms/Architecture", "formalisms/SimpleClassDiagrams", open("models/architecture.mvc", 'r').read())
+
+        model_add("models/pm_powerwindow", "formalisms/ProcessModel", open("models/pm_req_analyse.mvc", 'r').read())
+
+        transformation_add_MANUAL({"Requirements": "formalisms/Requirements"}, {"Requirements": "formalisms/Requirements"}, "models/revise_req")
+        transformation_add_MANUAL({"Requirements": "formalisms/Requirements", "PW_Environment": "formalisms/PW_Environment"}, {"PW_Environment": "formalisms/PW_Environment"}, "models/revise_environment")
+        transformation_add_MANUAL({"Requirements": "formalisms/Requirements", "PW_Plant": "formalisms/PW_Plant"}, {"PW_Plant": "formalisms/PW_Plant"}, "models/revise_plant")
+        transformation_add_MANUAL({"Requirements": "formalisms/Requirements", "PW_Control": "formalisms/PW_Control"}, {"PW_Control": "formalisms/PW_Control"}, "models/revise_control")
+        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")
@@ -225,16 +136,16 @@ class TestPowerWindow(unittest.TestCase):
             instantiate(None, "Association", ("Encapsulated_PetriNet/Place", "PetriNet/Place"), ID="EPN2PN_transition_link")
             instantiate(None, "Association", ("Encapsulated_PetriNet/Transition", "PetriNet/Transition"), ID="EPN2PN_place_link")
 
-        transformation_add_MT({}, {"PW_Plant": "PW_Plant", "PW_Environment": "PW_Environment", "PW_Control": "PW_Control", "Query": "Query", "Architecture": "Architecture", "Requirements": "Requirements"}, "make_initial_models", open("models/initialize.mvc", 'r').read())
-        transformation_add_MT({"PW_Plant": "PW_Plant"}, {"Encapsulated_PetriNet": "Encapsulated_PetriNet"}, "plant_to_EPN", open("models/plant_to_EPN.mvc", 'r').read(), tracability_PLANT2EPN)
-        transformation_add_MT({"PW_Control": "PW_Control"}, {"Encapsulated_PetriNet": "Encapsulated_PetriNet"}, "control_to_EPN", open("models/control_to_EPN.mvc", 'r').read(), tracability_CTRL2EPN)
-        transformation_add_MT({"PW_Environment": "PW_Environment"}, {"Encapsulated_PetriNet": "Encapsulated_PetriNet"}, "environment_to_EPN", open("models/environment_to_EPN.mvc", 'r').read(), tracability_ENV2EPN)
-        transformation_add_MT({"Encapsulated_PetriNet": "Encapsulated_PetriNet", "Architecture": "Architecture"}, {"PetriNet": "PetriNet"}, "combine_EPN", open("models/combine_EPN.mvc", 'r').read(), tracability_EPN2PN)
-        transformation_add_MT({"ReachabilityGraph": "ReachabilityGraph", "Query": "Query"}, {}, "match", open("models/matches.mvc", 'r').read())
+        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)
+        transformation_add_MT({"PW_Control": "formalisms/PW_Control"}, {"Encapsulated_PetriNet": "formalisms/Encapsulated_PetriNet"}, "models/control_to_EPN", open("models/control_to_EPN.mvc", 'r').read(), tracability_CTRL2EPN)
+        transformation_add_MT({"PW_Environment": "formalisms/PW_Environment"}, {"Encapsulated_PetriNet": "formalisms/Encapsulated_PetriNet"}, "models/environment_to_EPN", open("models/environment_to_EPN.mvc", 'r').read(), tracability_ENV2EPN)
+        transformation_add_MT({"Encapsulated_PetriNet": "formalisms/Encapsulated_PetriNet", "Architecture": "formalisms/Architecture"}, {"PetriNet": "formalisms/PetriNet"}, "models/combine_EPN", open("models/combine_EPN.mvc", 'r').read(), tracability_EPN2PN)
+        transformation_add_MT({"ReachabilityGraph": "formalisms/ReachabilityGraph", "Query": "formalisms/Query"}, {"ReachabilityGraph": "formalisms/ReachabilityGraph"}, "models/match", open("models/matches.mvc", 'r').read())
 
-        transformation_add_AL({"PetriNet": "PetriNet"}, {"ReachabilityGraph": "ReachabilityGraph"}, "reachability", open("models/reachability.alc", 'r').read())
-        transformation_add_AL({"ReachabilityGraph": "ReachabilityGraph"}, {}, "bfs", open("models/bfs.alc", 'r').read())
-        transformation_add_AL({"EPN_Plant": "Encapsulated_PetriNet", "EPN_Control": "Encapsulated_PetriNet", "EPN_Environment": "Encapsulated_PetriNet"}, {"Encapsulated_PetriNet": "Encapsulated_PetriNet"}, "merge_EPN", open("models/merge_EPN.alc", 'r').read())
+        transformation_add_AL({"PetriNet": "formalisms/PetriNet"}, {"ReachabilityGraph": "formalisms/ReachabilityGraph"}, "models/reachability", open("models/reachability.alc", 'r').read())
+        transformation_add_AL({"ReachabilityGraph": "formalisms/ReachabilityGraph"}, {}, "models/bfs", open("models/bfs.alc", 'r').read())
+        transformation_add_AL({"EPN_Plant": "formalisms/Encapsulated_PetriNet", "EPN_Control": "formalisms/Encapsulated_PetriNet", "EPN_Environment": "formalisms/Encapsulated_PetriNet"}, {"Encapsulated_PetriNet": "formalisms/Encapsulated_PetriNet"}, "models/merge_EPN", open("models/merge_EPN.alc", 'r').read())
 
         global called
         called = 0
@@ -258,17 +169,23 @@ 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
+
         callbacks = {
-                "revise_req": cb_req,
-                "revise_plant": cb_plant,
-                "revise_environment": cb_env,
-                "revise_control": cb_ctrl,
-                "revise_query": cb_query,
-                "revise_architecture": cb_arch,
+                "models/revise_req": cb_req,
+                "models/revise_plant": cb_plant,
+                "models/revise_environment": cb_env,
+                "models/revise_control": cb_ctrl,
+                "models/revise_query": cb_query,
+                "models/revise_architecture": cb_arch,
+                "models/bfs": got_path,
             }
 
         try:
-            process_execute("pm_powerwindow", "pm_", callbacks)
+            process_execute("models/pm_powerwindow", "pm_", callbacks)
         except:
             import traceback
             print(traceback.format_exc())
@@ -276,3 +193,5 @@ class TestPowerWindow(unittest.TestCase):
         if called != 11:
             print(called)
             raise Exception("Not executed sufficiently:" + str(called))
+
+        assert self.error_path != None

+ 25 - 19
interface/HUTN/hutn_compiler/hutnparser.py

@@ -28,6 +28,8 @@ from copy import deepcopy
 
 from position import Position
 
+line_cache = {}
+
 class Tree(object):
     def __init__(self, head, tail, startpos, endpos, inputfile = None):
         self.head = head
@@ -323,31 +325,35 @@ class Parser(object):
                 result['tree'] = Parser.PositionPostProcessor(self.convertToLineColumn).visit(result['tree'])
         return result
 
-    def convertToLineColumn(self, pos):
+    def generate_line_cache(self):
+        global line_cache
+        if self in line_cache:
+            return
+
+        line_cache[self] = []
+        lc = line_cache[self]
+
         line = 1
         column = 0
-        l = len(self.input)
-        for i in range(0, l):
-            if (i > pos):
-                break
-            if self.input[i] == '\n':
+
+        for i in self.input:
+            if i == "\n":
                 line += 1
                 column = 0
-            elif self.input[i] == '\t':
-                column += self.tabsize #changed by Daniel: this used to be 4
+            elif i == "\t":
+                column += self.tabsize
             else:
                 column += 1
-        if pos >= l: #the end of the text
-            """
-            added by Daniel: needed for the case of the last word/character.
-            Assume a text on one word 'foo'
-            in absolute position the tree says word is from 1 to 4 (as always 'to' means not included)
-            in this method we only count until the range so we would return line 1 col 1 to line 1 col 3
-            but we need col 4
-            we could just says pos == l but i think its better to say any position bigger than the text is simply the end of the text
-            """
-            column += 1
-        return {'line': line, 'column': column}
+            lc.append((line, column))
+
+    def convertToLineColumn(self, pos):
+        global line_cache
+        self.generate_line_cache()
+
+        if pos < len(line_cache[self]):
+            return {'line': line_cache[self][pos][0], 'column': line_cache[self][pos][1]}
+        else:
+            return {'line': line_cache[self][-1][0], 'column': line_cache[self][-1][1] + 1}
 
     def findlargerresultat(self, pos):
         endpos = pos

+ 0 - 78
interface/HUTN/hutn_compiler/loader.py

@@ -1,78 +0,0 @@
-import os
-import sys
-from codecs import open
-
-from grammar_compiler_visitor import GrammarCompilerVisitor
-from hutnparser import Parser
-from meta_grammar import Grammar
-
-
-def dump(obj, nested_level=0, output=sys.stdout):
-    spacing = '   '
-    if type(obj) == dict:
-        print >> output, '%s{' % ((nested_level) * spacing)
-        for k, v in obj.items():
-            if hasattr(v, '__iter__'):
-                print >> output, '%s%s:' % ((nested_level + 1) * spacing, k)
-                dump(v, nested_level + 1, output)
-            else:
-                print >> output, '%s%s: %s' % ((nested_level + 1) * spacing, k, v)
-        print >> output, '%s}' % (nested_level * spacing)
-    elif type(obj) == list:
-        print >> output, '%s[' % ((nested_level) * spacing)
-        for v in obj:
-            if hasattr(v, '__iter__'):
-                dump(v, nested_level + 1, output)
-            else:
-                print >> output, '%s%s' % ((nested_level + 1) * spacing, v)
-        print >> output, '%s]' % ((nested_level) * spacing)
-    else:
-        print >> output, '%s%s' % (nested_level * spacing, obj)
-
-
-def read(n, *args):
-  CUR_PATH = os.path.split(__file__)[0]
-  with open(os.path.join(CUR_PATH, n), *args, encoding='utf-8') as f:
-    return f.read()
-
-def loader(inputfile = './grammars/examples/meta_grammar.g', grammarfile = './grammars/examples/meta_grammar.g'):
-    grammar = Grammar()
-
-#    out1 = open('previous.rules', 'w')
-#    dump(grammar.rules, 0, out1)
-#    out2 = open('previous.tokens', 'w')
-#    dump(grammar.tokens, 0, out2)
-
-    result = parser = Parser(grammar, hide_implicit = True).parse(read(grammarfile))
-    if(not result['status'] == Parser.Constants.Success):
-        print 'not a valid grammar!'
-        print result
-        return
-    
-    tree = result['tree']
-    visitor = GrammarCompilerVisitor()
-    structure = visitor.visit(tree)            
-    grammar.rules = structure['rules']
-    grammar.tokens = structure['tokens']        
-
-#    out3 = open('after.rules', 'w')
-#    dump(structure['rules'], 0, out3)
-#    out4 = open('after.tokens', 'w')
-#    dump(structure['tokens'], 0, out4)
-
-    result = parser = Parser(grammar, hide_implicit = True).parse(read(inputfile))
-    if(not result['status'] == Parser.Constants.Success):
-        print 'not a valid input file!'
-        print result
-        return
-
-    Parser.DefaultPrinter().visit(result['tree'])
-    Parser.PrettyPrinter().visit(result['tree'])
-
-
-if __name__ == "__main__":
-    if(len(sys.argv) == 3):
-        fileName = sys.argv[1]
-        loader(sys.argv[1], sys.argv[2])
-    else:
-        loader()

+ 2 - 1
interface/HUTN/hutn_compiler/model_bootstrap_visitor.py

@@ -29,6 +29,7 @@ class ModelBootstrapVisitor(Visitor):
     def visit_start(self, tree):
         for t in tree.get_tail():
             self.visit(t)
+        self.code += '\tdict_overwrite(%s, "types", get_type_mapping(%s))\n' % (self.current_model, self.current_model)
 
     def visit_include_files(self, tree):
         self.includes.append('include %s' % tree.get_children("STRVALUE")[0].get_text())
@@ -139,7 +140,7 @@ class ModelBootstrapVisitor(Visitor):
                 contains_link = ""
             entry = self.visit(tree.get_children("model_element")[0])
             #self.constructors.extend(["instantiate_link", self.current_model, contains_link, "__%s" % self.free_id, self.current_element[-1], entry])
-            self.code += '\tinstantiate_link(%s, %s, "__%s", %s, %s)\n' % (self.current_model, contains_link, self.free_id, self.current_element[-1], entry)
+            self.code += '\tinstantiate_link(%s, "%s", "__%s", "%s", "%s")\n' % (self.current_model, contains_link, self.free_id, self.current_element[-1], entry)
             self.names.add("__%s" % self.free_id)
             self.free_id += 1
 

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

@@ -123,3 +123,4 @@ Element function dict_create()
 String function reverseKeyLookup(a: Element, b: Element)
 Element function reverseKeyLookupMulti(a: Element, b: Element)
 Element function dict_values(dict : Element)
+Boolean function is_error(a : Element)

+ 4 - 2
interface/HUTN/includes/typing.alh

@@ -1,6 +1,8 @@
 Element function get_type_mapping_as_dict(model : Element)
-Element function get_elements_typed_by(model : Element, type : String)
 String function read_type(model : Element, name : String)
 Void function retype(model : Element, element : String, type : String)
-Element function new_type_mapping()
+Void function new_type_mapping(model : Element)
 Void function remove_type(model : Element, name : String)
+Void function set_type_mapping(model : Element, type_mapping : Element)
+Element function get_type_mapping(model : Element)
+Element function elements_typed_by(model : Element, type_name : String)

+ 10 - 10
interface/PN/main.py

@@ -379,33 +379,33 @@ class PNInterface(tk.Tk):
         # Add the metamodels for PetriNet and ReachabilityGraph
         print("Add metamodels")
         try:
-            model_add("PetriNet", "SimpleClassDiagrams", open("../../models/petrinets.mvc").read())
+            model_add("formalisms/PetriNet", "formalisms/SimpleClassDiagrams", open("../../models/petrinets.mvc").read())
         except ModelExists:
             pass
 
         try:
-            model_add("ReachabilityGraph", "SimpleClassDiagrams", open("../../models/reachability_graph.mvc").read())
+            model_add("formalisms/ReachabilityGraph", "formalisms/SimpleClassDiagrams", open("../../models/reachability_graph.mvc").read())
         except ModelExists:
             pass
             
         # Remove the action language code to transform between them
         print("Remove AL model")
         try:
-            model_delete("analyseReachability")
+            model_delete("models/analyseReachability")
         except ModelverseException:
             pass
 
         # Add the action language code to transform between them
         print("Add AL model")
         try:
-            transformation_add_AL({"PetriNet": "PetriNet"}, {"ReachabilityGraph": "ReachabilityGraph"}, "analyseReachability", open("../../models/reachability.alc", "r").read())
+            transformation_add_AL({"PetriNet": "formalisms/PetriNet"}, {"ReachabilityGraph": "formalisms/ReachabilityGraph"}, "models/analyseReachability", open("../../models/reachability.alc", "r").read())
         except ModelExists:
             pass
             
         # Add an example model transformation to print the reachability graph
         print("Add MT model")
         try:
-            transformation_add_MT({"ReachabilityGraph": "ReachabilityGraph"}, {}, "printReachability", open("../../models/reachabilitygraph_print.mvc").read())
+            transformation_add_MT({"ReachabilityGraph": "formalisms/ReachabilityGraph"}, {}, "models/printReachability", open("../../models/reachabilitygraph_print.mvc").read())
         except ModelExists:
             pass
         
@@ -612,25 +612,25 @@ class PNInterface(tk.Tk):
 
         print("Delete model")
         try:
-            model_delete("my_pn")
+            model_delete("models/my_pn")
         except ModelverseException:
             pass
             
         print("Add model")
         try:
-            model_add("my_pn", "PetriNet", open("model.mvc").read())
+            model_add("models/my_pn", "formalisms/PetriNet", open("model.mvc").read())
         except ModelExists:
             pass
             
         def executeAL():
             print("Execute AL")
-            status = transformation_execute_AL("analyseReachability", {"PetriNet": "my_pn"}, {"ReachabilityGraph": "my_reachability"}, callback=self.callback)
+            status = transformation_execute_AL("models/analyseReachability", {"PetriNet": "models/my_pn"}, {"ReachabilityGraph": "models/my_reachability"}, callback=self.callback)
             print("Reachability generation success: " + str(status))
         
             print("Execute MT")
             def print_mv(value):
                 print(value)
-            status = transformation_execute_MT("printReachability", {"ReachabilityGraph": "my_reachability"}, {}, callback=print_mv)
+            status = transformation_execute_MT("models/printReachability", {"ReachabilityGraph": "models/my_reachability"}, {}, callback=print_mv)
             print("Reachability printing success: " + str(status))
             
         # Start the reachability graph generation
@@ -848,4 +848,4 @@ class VerticallyScrolledWindow(tk.Frame):
         
 if __name__ == "__main__":
     app = PNInterface()
-    app.mainloop()
+    app.mainloop()

+ 24 - 24
interface/PN/modelverse.py

@@ -341,28 +341,16 @@ def model_delete(model_name):
     _input(["model_delete", model_name])
     _handle_output("Success")
 
-def model_list():
+def model_list(location):
     """List all models."""
     _goto_mode(MODE_MODELLING)
-    _input("model_list")
-    output = _handle_output("Success: ", split=" ")
-    if output == "":
-        return set([])
-
-    lst = set([])
-    value = output.strip().split("\n")
-    for v in value:
-        m, mm = v.split(":")
-        m = m.strip()
-        mm = mm.strip()
-        lst.add((m, mm))
+    _input(["model_list", location])
+    return set(_handle_output("Success: ", split=" ").split("\n"))
 
-    return lst
-
-def model_list_full():
+def model_list_full(location):
     """List full information on all models."""
     _goto_mode(MODE_MODELLING)
-    _input("model_list_full")
+    _input(["model_list_full", location])
     output = _handle_output("Success: ", split=" ")
     if output == "":
         return set([])
@@ -370,11 +358,9 @@ def model_list_full():
     lst = set([])
     value = output.strip().split("\n")
     for v in value:
-        m, mm = v.split(":")
-        m = m.strip()
-        mm = mm.strip()
-        perm, own, grp, m = m.split(" ")
-        lst.add((m, mm, own, grp, perm))
+        m = v.strip()
+        perm, own, grp, m = m.split(" ", 3)
+        lst.add((m, own, grp, perm))
 
     return lst
 
@@ -617,7 +603,9 @@ def process_execute(process_name, prefix, callbacks):
     _input(["process_execute", process_name, prefix])
     _handle_output("Success")
 
-    while _output() != "Success":
+    reuse = False
+    while reuse or _output() != "Success":
+        reuse = False
         output = _last_output()
         if output.startswith("Enacting "):
             # Next activity!
@@ -632,6 +620,8 @@ def process_execute(process_name, prefix, callbacks):
                         mode = MODE_MODELLING
                         if reply is not None:
                             _input(reply)
+                    if _last_output().startswith("Enacting "):
+                        reuse = True
                 elif t == "ManualOperation":
                     _handle_output("Please perform manual operation ")
                     _output("Model loaded, ready for commands!")
@@ -948,7 +938,12 @@ def element_list_nice(model_name):
 
     _input(["element_list_nice", model_name, _get_metamodel(model_name)])
 
-    return json.loads(_handle_output("Success: ", split=" "))
+    data = _handle_output("Success: ", split=" ")
+    try:
+        return json.loads(data)
+    except:
+        print(data)
+        raise
 
 def connections_between(model_name, source_element, target_element):
     """Gets a list of all allowed connections between the source and target element in the model."""
@@ -999,3 +994,8 @@ def add_conformance(model_name, metamodel_name, partial_type_mapping=None):
     _goto_mode(MODE_MODELLING)
     _input(["add_conformance", model_name, metamodel_name])
     _handle_output("Success")
+
+def folder_create(folder_name):
+    _goto_mode(MODE_MODELLING)
+    _input(["folder_create", folder_name])
+    _handle_output("Success")

+ 8 - 3
kernel/modelverse_kernel/compiled.py

@@ -32,7 +32,7 @@ def reverseKeyLookup(a, b, **remainder):
         e, = yield [("RE", [out_edge])]
         result = e[1]
     else:
-        result, = yield [("CNV", ["(unknown: %s)" % b])]
+        result, = yield [("CNV", [""])]
     raise PrimitiveFinished(result)
 
 def instantiated_name(a, b, **remainder):
@@ -211,6 +211,8 @@ def get_superclasses(a, b, **remainder):
     worklist = set([name_value])
     found = set([])
 
+    cache_value = {}
+
     while worklist:
         name = worklist.pop()
         if name in found:
@@ -233,8 +235,11 @@ def get_superclasses(a, b, **remainder):
                 edge, = yield [("RE", [link])]
                 src, dst = edge
                 # Look up dst's name and add it
-                dst_name, = yield [("CALL_ARGS", [reverseKeyLookup, [model_dict, dst]])]
-                dst_name_value, = yield [("RV", [dst_name])]
+                if dst not in cache_value:
+                    dst_name, = yield [("CALL_ARGS", [reverseKeyLookup, [model_dict, dst]])]
+                    dst_name_value, = yield [("RV", [dst_name])]
+                    cache_value[dst] = dst_name_value
+                dst_name_value = cache_value[dst]
                 worklist.add(dst_name_value)
 
     result, = yield [("CN", [])]

+ 13 - 6
kernel/modelverse_kernel/primitives.py

@@ -1,4 +1,5 @@
 import time as python_time
+import json
 
 class PrimitiveFinished(Exception):
     """Exception to indicate the result value of a primitive, as a return cannot be used."""
@@ -209,13 +210,12 @@ def cast_e2s(a, **remainder):
 
 def cast_v2s(a, **remainder):
     a_value, = yield [("RV", [a])]
-    if isinstance(a_value, (str, unicode)):
-        # String should be encoded to distinguish between 3 and "3"
-        a_value = '"%s"' % a_value
-    elif isinstance(a_value, dict):
+    if isinstance(a_value, dict):
         # Action or type
-        a_value = a_value["value"]
-    result, = yield [("CNV", ["%s" % (a_value)])]
+        value = a_value["value"]
+    else:
+        value = json.dumps(a_value)
+    result, = yield [("CNV", [value])]
     raise PrimitiveFinished(result)
 
 def cast_id2s(a, **remainder):
@@ -409,3 +409,10 @@ def __sleep(a, b, **remainder):
     timeout, interruptable = yield [("RV", [a]), ("RV", [b])]
     yield [("SLEEP", [timeout, interruptable])]
     raise PrimitiveFinished(a)
+
+def is_error(a, **remainder):
+    if a is None:
+        result, = yield [("CNV", [True])]
+    else:
+        result, = yield [("CNV", [False])]
+    raise PrimitiveFinished(result)

+ 1 - 1
kernel/test/primitives/test_cast.py

@@ -107,7 +107,7 @@ class TestCast(unittest.TestCase):
         self.helper_primitives_1_params("cast_v2s", "3", "\"3\"")
 
     def test_cast_v2s_bool(self):
-        self.helper_primitives_1_params("cast_v2s", True, "True")
+        self.helper_primitives_1_params("cast_v2s", True, "true")
 
     def test_cast_v2s_action(self):
         self.helper_primitives_1_params("cast_v2s", {"value": "call"}, "call")

+ 9 - 0
models/SCCD_Trace.mvc

@@ -0,0 +1,9 @@
+SimpleAttribute Float {}
+SimpleAttribute String {}
+
+Class Event{
+    timestamp : Float
+    name : String
+    parameter : String
+}
+

+ 27 - 7
models/SCCD_execute.alc

@@ -193,11 +193,15 @@ String function start_class(model : Element, data : Element, class : String, par
 
 	execute_actions(model, init, set_copy(class_handle["states"]), data, "")
 
+	// Notify this class itself of its ID
+	Element lst
+	lst = list_create()
+	list_append(lst, identifier)
+	set_add_node(data["current_class_handle"]["new_events"], create_tuple("instance_created", lst))
+
 	dict_overwrite(data, "current_class", prev_class)
 	dict_overwrite(data, "current_class_handle", data["classes"][prev_class])
 
-	log("Created class with identifier: " + cast_v2s(identifier))
-
 	return identifier!
 
 Element function get_enabled_transitions(model : Element, state : String, data : Element):
@@ -304,6 +308,8 @@ Void function process_raised_event(model : Element, event : Element, parameter_a
 			class = set_pop(filter(model, allInstances(model, "SCCD/Class"), "name", list_read(parameter_action, 1)))
 			parameters = list_read(parameter_action, 2)
 			identifier = start_class(model, data, class, parameters)
+
+			// Notify the creator of the ID of the created instance
 			Element lst
 			lst = list_create()
 			list_append(lst, identifier)
@@ -315,23 +321,32 @@ Void function process_raised_event(model : Element, event : Element, parameter_a
 			identifier = list_read(parameter_action, 0)
 			delete_class(model, data, identifier)
 			set_add_node(data["current_class_handle"]["new_events"], create_tuple("instance_deleted", parameter_action))
+
 	elif (scope == "broad"):
 		// Send to all classes
 		Element classes
 		classes = dict_keys(data["classes"])
 		while(set_len(classes) > 0):
 			set_add_node(data["classes"][set_pop(classes)]["new_events"], create_tuple(read_attribute(model, event, "event"), parameter_action))
+
 	elif (scope == "narrow"):
 		// Send to the specified class only
 		Element func
 		func = resolve_function(read_attribute(model, event, "target"), data)
 		String dest
 		dest = func(data["current_class_handle"]["attributes"])
-		log("Narrowcasting to " + cast_v2s(dest))
 		set_add_node(data["classes"][dest]["new_events"], create_tuple(read_attribute(model, event, "event"), parameter_action))
+
+	elif (scope == "output"):
+		// Store it in the output event model
+		String evt
+		evt = instantiate_node(model, "trace/Event", "")
+		instantiate_attribute(model, evt, "timestamp", data["time_sim"])
+		instantiate_attribute(model, evt, "name", read_attribute(model, event, "event"))
+		instantiate_attribute(model, evt, "parameter", parameter_action)
+
 	else:
 		// Same as local
-		log("Unknown scope, assuming local: " + scope)
 		set_add_node(data["current_class_handle"]["new_events"], create_tuple(read_attribute(model, event, "event"), parameter_action))
 
 	return !
@@ -721,6 +736,10 @@ Boolean function main(model : Element):
 	time_sim = 0.0
 	dict_add(data, "time_sim", 0.0)
 
+	Boolean afap
+	// NOTE this can be changed to True for testing
+	afap = False
+
 	// Prepare for input
 	output("Ready for input!")
 
@@ -735,6 +754,8 @@ Boolean function main(model : Element):
 	Element interrupt
 	timeout = 0.0
 	while (True):
+		if (afap):
+			timeout = 0.0
 		interrupt = input_timeout(timeout)
 
 		if (value_eq(interrupt, "#EXIT#")):
@@ -776,7 +797,6 @@ Boolean function main(model : Element):
 
 		time_wallclock = time() - time_0
 		timeout = time_sim - time_wallclock
-		log("Pause for: " + cast_v2s(timeout))
 
-	// We should never get here!
-	return False!
+	// We have finished without error
+	return True!

+ 0 - 1
models/bfs.alc

@@ -36,7 +36,6 @@ Boolean function bfs(model : Element):
 			// Found an error path!
 			log("Found error path!")
 			log(list_to_string(path))
-			output("Found error path:")
 			output(list_to_string(path))
 			break!
 

+ 40 - 40
models/pm_req_analyse.mvc

@@ -2,118 +2,118 @@ Start start {}
 Finish finish {}
 
 Exec revise_req {
-    name = "revise_req"
+    name = "models/revise_req"
 }
 Exec make_initial_models {
-    name = "make_initial_models"
+    name = "models/make_initial_models"
 }
 
 Fork fork1 {}
 
 Exec revise_plant {
-    name = "revise_plant"
+    name = "models/revise_plant"
 }
 Exec revise_environment {
-    name = "revise_environment"
+    name = "models/revise_environment"
 }
 Exec revise_control {
-    name = "revise_control"
+    name = "models/revise_control"
 }
 Exec revise_query {
-    name = "revise_query"
+    name = "models/revise_query"
 }
 Exec revise_architecture {
-    name = "revise_architecture"
+    name = "models/revise_architecture"
 }
 
 Exec plant_to_EPN {
-    name = "plant_to_EPN"
+    name = "models/plant_to_EPN"
 }
 Exec environment_to_EPN {
-    name = "environment_to_EPN"
+    name = "models/environment_to_EPN"
 }
 
 Exec control_to_EPN {
-    name = "control_to_EPN"
+    name = "models/control_to_EPN"
 }
 
 Join join2 {}
 
 Exec merge_EPN {
-    name = "merge_EPN"
+    name = "models/merge_EPN"
 }
 
 Exec combine_EPN {
-    name = "combine_EPN"
+    name = "models/combine_EPN"
 }
 
 Exec EPN_to_PN {
-    name = "EPN_to_PN"
+    name = "models/EPN_to_PN"
 }
 
 Exec analyse {
-    name = "reachability"
+    name = "models/reachability"
 }
 
 Join join3 {}
 
 Exec match {
-    name = "match"
+    name = "models/match"
 }
 
 Exec bfs {
-    name = "bfs"
+    name = "models/bfs"
 }
 
 Decision found {}
 
 Data req {
-    name = "requirements"
-    type = "Requirements"
+    name = "models/requirements"
+    type = "formalisms/Requirements"
 }
 Data plant_model {
-    name = "plant_model"
-    type = "PW_Plant"
+    name = "models/plant_model"
+    type = "formalisms/PW_Plant"
 }
 Data environment_model {
-    name = "environment_model"
-    type = "PW_Environment"
+    name = "models/environment_model"
+    type = "formalisms/PW_Environment"
 }
 Data control_model {
-    name = "control_model"
-    type = "PW_Control"
+    name = "models/control_model"
+    type = "formalisms/PW_Control"
 }
 Data plant_EPN {
-    name = "plant_EPN"
-    type = "Encapsulated_PetriNet"
+    name = "models/plant_EPN"
+    type = "formalisms/Encapsulated_PetriNet"
 }
 Data control_EPN {
-    name = "control_EPN"
-    type = "Encapsulated_PetriNet"
+    name = "models/control_EPN"
+    type = "formalisms/Encapsulated_PetriNet"
 }
 Data environment_EPN {
-    name = "environment_EPN"
-    type = "Encapsulated_PetriNet"
+    name = "models/environment_EPN"
+    type = "formalisms/Encapsulated_PetriNet"
 }
 Data pn {
-    name = "pn"
-    type = "PetriNet"
+    name = "models/pn"
+    type = "formalisms/PetriNet"
 }
 Data reachability_graph {
-    name = "reachability"
-    type = "ReachabilityGraph"
+    name = "models/reachability"
+    type = "formalisms/ReachabilityGraph"
 }
 Data query {
-    name = "query"
-    type = "Query"
+    name = "models/query"
+    type = "formalisms/Query"
 }
 Data architecture {
-    name = "architecture"
-    type = "Architecture"
+    name = "models/architecture"
+    type = "formalisms/Architecture"
 }
 Data merged_EPN {
-    name = "merged_EPN"
-    type = "Encapsulated_PetriNet"
+    name = "models/merged_EPN"
+    type = "formalisms/Encapsulated_PetriNet"
 }
 
 Next (start, make_initial_models) {}

+ 46 - 46
models/pm_req_analyse_debug.mvc

@@ -2,135 +2,135 @@ Start start {}
 Finish finish {}
 
 Exec revise_req {
-    name = "revise_req"
+    name = "models/revise_req"
 }
 Exec make_initial_models {
-    name = "make_initial_models"
+    name = "models/make_initial_models"
 }
 
 Fork fork1 {}
 
 Exec revise_plant {
-    name = "revise_plant"
+    name = "models/revise_plant"
 }
 Exec revise_environment {
-    name = "revise_environment"
+    name = "models/revise_environment"
 }
 Exec revise_control {
-    name = "revise_control"
+    name = "models/revise_control"
 }
 Exec revise_query {
-    name = "revise_query"
+    name = "models/revise_query"
 }
 Exec revise_architecture {
-    name = "revise_architecture"
+    name = "models/revise_architecture"
 }
 
 Exec plant_to_EPN {
-    name = "plant_to_EPN"
+    name = "models/plant_to_EPN"
 }
 Exec print_plant_EPN{
-    name = "epn_print"
+    name = "models/epn_print"
 }
 Exec environment_to_EPN {
-    name = "environment_to_EPN"
+    name = "models/environment_to_EPN"
 }
 Exec print_env_EPN{
-    name = "epn_print"
+    name = "models/epn_print"
 }
 
 Exec control_to_EPN {
-    name = "control_to_EPN"
+    name = "models/control_to_EPN"
 }
 Exec print_control_EPN{
-    name = "epn_print"
+    name = "models/epn_print"
 }
 
 Join join2 {}
 
 Exec merge_EPN {
-    name = "merge_EPN"
+    name = "models/merge_EPN"
 }
 
 Exec combine_EPN {
-    name = "combine_EPN"
+    name = "models/combine_EPN"
 }
 
 Exec print_pn {
-    name = "pn_print"
+    name = "models/pn_print"
 }
 
 Exec EPN_to_PN {
-    name = "EPN_to_PN"
+    name = "models/EPN_to_PN"
 }
 
 Exec analyse {
-    name = "reachability"
+    name = "models/reachability"
 }
 
 Join join3 {}
 
 Exec match {
-    name = "match"
+    name = "models/match"
 }
 
 Exec bfs {
-    name = "bfs"
+    name = "models/bfs"
 }
 
 Decision found {}
 
 Data req {
-    name = "requirements"
-    type = "Requirements"
+    name = "models/requirements"
+    type = "formalisms/Requirements"
 }
 Data plant_model {
-    name = "plant_model"
-    type = "PW_Plant"
+    name = "models/plant_model"
+    type = "formalisms/PW_Plant"
 }
 Data environment_model {
-    name = "environment_model"
-    type = "PW_Environment"
+    name = "models/environment_model"
+    type = "formalisms/PW_Environment"
 }
 Data control_model {
-    name = "control_model"
-    type = "PW_Control"
+    name = "models/control_model"
+    type = "formalisms/PW_Control"
 }
 Data plant_EPN {
-    name = "plant_EPN"
-    type = "Encapsulated_PetriNet"
+    name = "models/plant_EPN"
+    type = "formalisms/Encapsulated_PetriNet"
 }
 Data control_EPN {
-    name = "control_EPN"
-    type = "Encapsulated_PetriNet"
+    name = "models/control_EPN"
+    type = "formalisms/Encapsulated_PetriNet"
 }
 Data environment_EPN {
-    name = "environment_EPN"
-    type = "Encapsulated_PetriNet"
+    name = "models/environment_EPN"
+    type = "formalisms/Encapsulated_PetriNet"
 }
 Data merged_EPN {
-    name = "merged_EPN"
-    type = "Encapsulated_PetriNet"
+    name = "models/merged_EPN"
+    type = "formalisms/Encapsulated_PetriNet"
 }
 Data pn {
-    name = "pn"
-    type = "PetriNet"
+    name = "models/pn"
+    type = "formalisms/PetriNet"
 }
 Data reachability_graph {
-    name = "reachability"
-    type = "ReachabilityGraph"
+    name = "models/reachability"
+    type = "formalisms/ReachabilityGraph"
 }
 Data query {
-    name = "query"
-    type = "Query"
+    name = "models/query"
+    type = "formalisms/Query"
 }
 Data architecture {
-    name = "architecture"
-    type = "Architecture"
+    name = "models/architecture"
+    type = "formalisms/Architecture"
 }
 Data merged_EPN {
-    name = "merged_EPN"
-    type = "Encapsulated_PetriNet"
+    name = "models/merged_EPN"
+    type = "formalisms/Encapsulated_PetriNet"
 }
 
 Next (start, make_initial_models) {}

+ 7 - 1
scripts/run_fast_tests.py

@@ -1,10 +1,16 @@
+from flush_compiler_caches import flush_all
+from generate_bootstrap import bootstrap
 import subprocess
 import sys
 
+flush_all()
+
+bootstrap()
+
 subprocess.check_call([sys.executable, "-m", "pytest"], cwd="state")
 
 subprocess.check_call([sys.executable, "-m", "pytest"], cwd="kernel")
 
 subprocess.check_call([sys.executable, "-m", "pytest"], cwd="interface/HUTN")
 
-subprocess.check_call([sys.executable, "-m", "pytest", "integration", "-v"])
+subprocess.check_call([sys.executable, "-m", "pytest", "unit", "-x", "-v"])

+ 3 - 1
scripts/run_tests.py

@@ -15,4 +15,6 @@ flush_all()
 
 subprocess.check_call([sys.executable, "-m", "pytest"], cwd="interface/HUTN")
 
-subprocess.check_call([sys.executable, "-m", "pytest", "integration", "--runslow", "-s", "-v"])
+subprocess.check_call([sys.executable, "-m", "pytest", "unit", "-x"])
+
+subprocess.check_call([sys.executable, "-m", "pytest", "integration", "--runslow", "-s", "-v", "-x"])

+ 8 - 11
state/modelverse_state/main.py

@@ -215,13 +215,16 @@ class ModelverseState(object):
             first = self.cache[node][value]
             # Got hit, so validate
             if (self.edges[first][0] == node) and \
-                (value in [self.values[self.edges[i][1]] for i in self.outgoing[first]]):
+                (value in [self.values[self.edges[i][1]] for i in self.outgoing[first] if self.edges[i][1] in self.values]):
                 return self.edges[first][1]
             # Hit but invalid now
             del self.cache[node][value]
         except KeyError:
             # Didn't exist
             pass
+        except:
+            print(locals())
+            raise
         return None
 
     def read_dict_keys(self, node):
@@ -229,12 +232,12 @@ class ModelverseState(object):
             return None
 
         result = []
+        #NOTE cannot just use the cache here, as some keys in the cache might not actually exist; we would have to check all of them anyway
         if node in self.outgoing:
             for e1 in self.outgoing[node]:
                 if e1 in self.outgoing:
                     for e2 in self.outgoing[e1]:
                         result.append(self.edges[e2][1])
-        #NOTE cannot just use the cache here, as some keys in the cache might not actually exist; we would have to check all of them anyway
         return result
 
     def read_dict_edge(self, node, value):
@@ -242,7 +245,7 @@ class ModelverseState(object):
             first = self.cache[node][value]
             # Got hit, so validate
             if (self.edges[first][0] == node) and \
-                (value in [self.values[self.edges[i][1]] for i in self.outgoing[first]]):
+                (value in [self.values[self.edges[i][1]] for i in self.outgoing[first] if self.edges[i][1] in self.values]):
                 return first
             # Hit but invalid now
             del self.cache[node][value]
@@ -293,14 +296,8 @@ class ModelverseState(object):
                         # And access its value
                         if target in self.values and self.values[target] == value:
                             # Found a match
-                            if len(self.outgoing[e1]) > 1:
-                                return None
-                            else:
-                                matches.append(e1)
-        if len(matches) == 0:
-            return None
-        else:
-            return [self.edges[e][0] for e in matches]
+                            matches.append(e1)
+        return [self.edges[e][0] for e in matches]
 
     def delete_node(self, node):
         if node == self.root:

+ 8 - 12
state/test/test_read_reverse_dict.py

@@ -11,11 +11,11 @@ class TestReadReverseDict(unittest.TestCase):
 
     def test_read_reverse_dict_not_found_node(self):
         a = self.mvs.create_node()
-        assert a != None
+        assert a != []
 
         # Passing data is not enforced, as the data will be interpreted if necessary
         l = self.mvs.read_reverse_dict(a, "abc")
-        assert l == None
+        assert l == []
 
     def test_read_reverse_dict_not_found_nodevalue(self):
         a = self.mvs.create_nodevalue(1)
@@ -23,7 +23,7 @@ class TestReadReverseDict(unittest.TestCase):
 
         # Passing data is not enforced, as the data will be interpreted if necessary
         l = self.mvs.read_reverse_dict(a, "abc")
-        assert l == None
+        assert l == []
 
     def test_read_reverse_dict_not_found_edge(self):
         a = self.mvs.create_node()
@@ -35,7 +35,7 @@ class TestReadReverseDict(unittest.TestCase):
 
         # Passing data is not enforced, as the data will be interpreted if necessary
         l = self.mvs.read_reverse_dict(c, "abc")
-        assert l == None
+        assert l == []
 
     def test_read_reverse_dict_no_primitive(self):
         a = self.mvs.create_node()
@@ -43,7 +43,7 @@ class TestReadReverseDict(unittest.TestCase):
 
         # Passing data is not enforced, as the data will be interpreted if necessary
         l = self.mvs.read_reverse_dict(a, a)
-        assert l == None
+        assert l == []
 
     def test_read_reverse_dict_node_simple(self):
         a = self.mvs.create_node()
@@ -58,7 +58,6 @@ class TestReadReverseDict(unittest.TestCase):
         assert e != None
 
         l = self.mvs.read_reverse_dict(b, "f")
-        assert l != None
         assert set(l) == set([a])
 
     def test_read_reverse_dict_no_match(self):
@@ -74,7 +73,7 @@ class TestReadReverseDict(unittest.TestCase):
         assert e != None
 
         l = self.mvs.read_reverse_dict(b, "f")
-        assert l == None
+        assert l == []
 
     def test_read_reverse_dict_node_multi(self):
         a = self.mvs.create_node()
@@ -98,15 +97,13 @@ class TestReadReverseDict(unittest.TestCase):
         assert j != None
 
         l = self.mvs.read_reverse_dict(b, "f")
-        assert l != None
         assert set(l) == set([a])
 
         l = self.mvs.read_reverse_dict(g, "k")
-        assert l != None
         assert set(l) == set([a])
 
         l = self.mvs.read_reverse_dict(a, "l")
-        assert l == None
+        assert l == []
 
     def test_read_reverse_dict_node_multi_ambiguous(self):
         a = self.mvs.create_node()
@@ -130,7 +127,6 @@ class TestReadReverseDict(unittest.TestCase):
         assert j != None
 
         l = self.mvs.read_reverse_dict(a, "f")
-        assert l != None
         assert set(l) == set([b, g])
 
     def test_read_reverse_dict_node_uncertain(self):
@@ -151,4 +147,4 @@ class TestReadReverseDict(unittest.TestCase):
         assert i != None
 
         l = self.mvs.read_reverse_dict(b, "f")
-        assert l == None
+        assert set(l) == set([a])

+ 429 - 0
unit/test_all.py

@@ -0,0 +1,429 @@
+import unittest
+
+import sys
+from utils import *
+
+sys.path.append("wrappers")
+from modelverse import *
+
+model_hierarchy = \
+            {"formalisms/": {"SimpleClassDiagrams": {},
+                             "TypeMapping": {},
+                             "Tracability": {},
+                             "ProcessModel": {},
+                             "ActionLanguage": {},
+                             "ManualOperation": {},
+                             "Bottom": {},
+                            },
+            "models/": {"conformance_mv": {},
+                        },
+            "administration/": {"core": {},
+                                "CoreFormalism": {},
+                               },
+            "users/": {"admin/": {}
+                      },
+            "type mappings/": {"formalisms/": {"SimpleClassDiagrams": {},
+                                               "TypeMapping": {},
+                                               "Tracability": {},
+                                               "ProcessModel": {},
+                                               "ActionLanguage": {},
+                                               "ManualOperation": {},
+                                               "Bottom": {},
+                                              },
+                               "models/": {"conformance_mv": {},
+                                          },
+                               "administration/": {"core": {},
+                                                   "CoreFormalism": {},
+                                                  },
+                               "users/": {"admin/": {}
+                                         },
+                              },
+            }
+
+def verify_clean():
+    compare_locations("", set())
+    compare_locations("models", set())
+    compare_locations("formalisms", set())
+    compare_locations("administration", set())
+    compare_locations("type mappings", set())
+    compare_locations("type mappings/models", set())
+    compare_locations("type mappings/formalisms", set())
+    compare_locations("type mappings/administration", set())
+
+def get_model_list(location):
+    try:
+        location_parts = location.split("/")
+        current = model_hierarchy
+        while location_parts:
+            l = location_parts.pop(0)
+            if l != "":
+                current = current[l + "/"]
+        return set(current.keys())
+    except:
+        return set([])
+
+def compare_locations(location, extra_to_default):
+    assert model_list(location) == get_model_list(location) | set(extra_to_default)
+
+def compare_unordered_lists(got, expected):
+    assert len(got) == len(expected)
+    for i in got:
+        assert i in expected
+    for i in expected:
+        assert i in got
+
+class TestModelverse(unittest.TestCase):
+    proc = None
+
+    @classmethod
+    def setUpClass(self):
+        TestModelverse.proc, address = start_mvc()
+        init(address)
+        login("admin", "admin")
+
+    @classmethod
+    def tearDownClass(self):
+        try:
+            kill(TestModelverse.proc)
+        except:
+            print("Got exception during teardown.")
+
+    def setUp(self):
+        verify_clean()
+        folder_create("test")
+        folder_create("type mappings/test")
+
+    def tearDown(self):
+        model_delete("test")
+        model_delete("type mappings/test")
+        verify_clean()
+
+    def test_list_full(self):
+        assert model_list_full("") == set([("formalisms/", "admin", "admin", "221"),
+                                           ("models/", "admin", "admin", "221"),
+                                           ("administration/", "admin", "admin", "110"),
+                                           ("type mappings/", "admin", "admin", "221"),
+                                           ("users/", "admin", "admin", "221"),
+                                           ("test/", "admin", "nobody", "200"),
+                                          ])
+
+    def test_modelling(self):
+        # Add a model
+        model_add("test/Empty", "formalisms/SimpleClassDiagrams")
+
+        # Check that it exists
+        compare_locations("test", set(["Empty"]))
+        compare_locations("type mappings/test", set(["Empty"]))
+
+        # Check that it conforms
+        assert verify("test/Empty", "formalisms/SimpleClassDiagrams") == "OK"
+
+        # Check for permissions
+        assert model_list_full("test") == set([(name, "admin", "admin", "221") for name in get_model_list("test")]) | \
+                                                set([("Empty", "admin", "nobody", "200")])
+        assert model_list_full("type mappings/test") == set([(name, "admin", "admin", "221") for name in get_model_list("type mappings/test")]) | \
+                                                              set([("Empty", "admin", "nobody", "200")])
+
+        # Instantiate it further
+        model_add("test/my_empty", "test/Empty")
+        assert verify("test/my_empty", "test/Empty") == "OK"
+
+        # Check that it exists
+        compare_locations("test", set(["my_empty", "Empty"]))
+        compare_locations("type mappings/test", set(["my_empty", "Empty"]))
+
+        # Check that an instantiate of "A" fails
+        try:
+            instantiate("test/my_empty", "A")
+            assert False
+        except UnknownIdentifier:
+            assert verify("test/Empty", "formalisms/SimpleClassDiagrams") == "OK"
+
+        # Create something in the formalism
+        instantiate("test/Empty", "Class", ID="A")
+        assert verify("test/Empty", "formalisms/SimpleClassDiagrams") == "OK"
+
+        # Now instantiate that in the model as well, which now works
+        instantiate("test/my_empty", "A")
+        assert verify("test/my_empty", "test/Empty") == "OK"
+
+    def test_overwrite(self):
+        model_add("test/Empty", "formalisms/SimpleClassDiagrams")
+        assert element_list("test/Empty") == set([])
+
+        instantiate("test/Empty", "Class", ID="A")
+        assert element_list("test/Empty") == set([("A", "Class")])
+
+        model_overwrite("test/Empty")
+        assert element_list("test/Empty") == set([])
+        compare_locations("test", set(["Empty"]))
+        compare_locations("type mappings/test", set(["Empty"]))
+        
+        assert element_list("test/Empty") == set([])
+
+        instantiate("test/Empty", "Class", ID="B")
+        compare_locations("test", set(["Empty"]))
+        compare_locations("type mappings/test", set(["Empty"]))
+
+    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)
+
+        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_R2D():
+            instantiate(None, "Association", ("PetriNet_Runtime/Place", "PetriNet/Place"), ID="R2D_PlaceLink")
+            instantiate(None, "Association", ("PetriNet_Runtime/Transition", "PetriNet/Transition"), ID="R2D_TransitionLink")
+
+        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)
+        transformation_add_AL({"PetriNet_Runtime": "test/PetriNet_Runtime"}, {"PetriNet_Runtime": "test/PetriNet_Runtime"}, "test/pn_simulate", open("integration/code/pn_simulate.alc").read())
+        transformation_add_MT({"PetriNet_Runtime": "test/PetriNet_Runtime"}, {"PetriNet": "test/PetriNet"}, "test/pn_runtime_to_design", open("integration/code/pn_runtime_to_design.mvc").read(), add_tracability_R2D)
+
+        log = []
+        assert transformation_execute_MT("test/print_pn", {"PetriNet": "test/my_pn"}, {}, callback) == True
+        assert set(log) == set(['"p1" --> 1',
+                                '"p2" --> 2',
+                                '"p3" --> 3'])
+
+        assert transformation_execute_MANUAL("test/pn_design_to_runtime", {"PetriNet": "test/my_pn"}, {"PetriNet_Runtime": "test/my_pn_RT"}, manual_callback) == True
+        assert transformation_execute_AL("test/pn_simulate", {"PetriNet_Runtime": "test/my_pn_RT"}, {"PetriNet_Runtime": "test/my_pn_RT"}) == True
+        assert transformation_execute_MT("test/pn_runtime_to_design", {"PetriNet_Runtime": "test/my_pn_RT"}, {"PetriNet": "test/my_pn"}) == True
+
+        log = []
+        assert transformation_execute_MT("test/print_pn", {"PetriNet": "test/my_pn"}, {}, callback) == True
+        assert set(log) == set(['"p1" --> 0',
+                                '"p2" --> 1',
+                                '"p3" --> 5'])
+
+        model_delete("RAMified")
+        model_delete("merged")
+        model_delete("type mappings/RAMified")
+        model_delete("type mappings/merged")
+
+    def test_process_model_trivial_pn_subfunction(self):
+        model_add("test/PetriNet", "formalisms/SimpleClassDiagrams", open("integration/code/pn_design.mvc", "r").read())
+        model_add("test/ReachabilityGraph", "formalisms/SimpleClassDiagrams", open("integration/code/reachability_graph.mvc", "r").read())
+        model_add("test/pn_reachability", "formalisms/ProcessModel", open("integration/code/pm_pn_reachability.mvc", "r").read())
+        transformation_add_MT({}, {"PetriNet": "test/PetriNet"}, "test/initialize_PN", open("integration/code/initialize_PN.mvc", "r").read())
+        transformation_add_MANUAL({"PetriNet": "test/PetriNet"}, {"PetriNet": "test/PetriNet"}, "test/refine_PN")
+        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)
+
+            t1 = instantiate(None, "PetriNet/Transition")
+            attr_assign(None, t1, "name", "t1")
+
+            p2t = instantiate(None, "PetriNet/P2T", (p1, t1))
+            attr_assign(None, p2t, "weight", 1)
+
+        log = set([])
+        def callback_print(value):
+            log.add(value)
+
+        process_execute("test/pn_reachability", "", {"test/refine_PN": callback_refine_PN, "test/reachability_print": callback_print})
+
+        assert log == set(['"0": {"p1": 1, }',
+                           '"1": {"p1": 0, }',
+                           '"0" --["t1"]--> "1"'])
+
+        model_delete("RAMified")
+        model_delete("merged")
+        model_delete("type mappings/RAMified")
+        model_delete("type mappings/merged")
+
+    def test_render(self):
+        model_add("test/CausalBlockDiagrams", "formalisms/SimpleClassDiagrams", open("integration/code/cbd_design.mvc", 'r').read())
+        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")
+
+        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")
+        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):
+        model_add("test/SCCD", "formalisms/SimpleClassDiagrams", open("models/SCCD.mvc", 'r').read())
+        model_add("test/SCCD_Trace", "formalisms/SimpleClassDiagrams", open("models/SCCD_Trace.mvc", 'r').read())
+
+        model_add("test/my_SCCD", "test/SCCD", open("integration/code/SCCD_all.mvc", 'r').read())
+
+        transformation_add_AL({"SCCD": "test/SCCD"}, {"trace": "test/SCCD_Trace"}, "test/SCCD_execute_afap", open("models/SCCD_execute.alc", 'r').read().replace("afap = False", "afap = True"))
+        transformation_execute_AL("test/SCCD_execute_afap", {"SCCD": "test/my_SCCD"}, {"trace": "test/my_SCCD_trace"})
+
+        alter_context("test/my_SCCD_trace", "test/SCCD_Trace")
+        lst = element_list_nice("test/my_SCCD_trace")
+
+        model_delete("merged")
+        model_delete("type mappings/merged")
+
+        lst.sort(key=lambda i: (i["timestamp"], i["name"]))
+        result = [(i["timestamp"], str(i["name"])) for i in lst if i["name"] not in ["updateTimerValue", "updateTimerColour", "resetTimer"]]
+        print(result)
+
+        assert result == [(5.0, "displayRed"),
+                          (20.0, "displayYellow"),
+                          (20.5, "displayNone"),
+                          (21.0, "displayYellow"),
+                          (21.5, "displayNone"),
+                          (22.0, "displayYellow"),
+                          (22.5, "displayNone"),
+                          (23.0, "displayYellow"),
+                          (23.5, "displayNone"),
+                          (24.0, "displayYellow"),
+                          (24.5, "displayNone"),
+                          (25.0, "displayYellow"),
+                          (25.5, "displayNone"),
+                          (26.0, "displayYellow"),
+                          (26.5, "displayNone"),
+                          (27.0, "displayYellow"),
+                          (27.5, "displayNone"),
+                          (28.0, "displayYellow"),
+                          (28.5, "displayNone"),
+                          (29.0, "displayYellow"),
+                          (29.4, "displayNone"),
+                          (29.4, "displayRed"),
+                          (89.4, "displayGreen"),
+                          (129.4, "displayNone"),
+                          (129.4, "displayRed"),
+                          (139.4, "displayYellow"),
+                          (139.9, "displayNone"),
+                          (140.4, "displayYellow"),
+                          (140.9, "displayNone"),
+                          (141.4, "displayYellow"),
+                          (141.9, "displayNone"),
+                          (142.4, "displayYellow"),
+                          (142.9, "displayNone"),
+                          (143.4, "displayYellow"),
+                          (143.9, "displayNone"),
+                          (144.4, "displayYellow"),
+                          (144.9, "displayNone"),
+                          (145.4, "displayYellow"),
+                          (145.9, "displayNone"),
+                          (146.4, "displayYellow"),
+                          (146.9, "displayNone"),
+                          (147.4, "displayYellow"),
+                          (147.9, "displayNone"),
+                          (148.4, "displayYellow"),
+                         ]
+
+    def test_switch_MM(self):
+        model_add("test/PetriNet", "formalisms/SimpleClassDiagrams", open("integration/code/pn_design.mvc", "r").read())
+        model_add("test/my_pn", "test/PetriNet", open("integration/code/pn_design_model.mvc", "r").read())
+
+        alter_context("test/PetriNet", "formalisms/SimpleClassDiagrams")
+        alter_context("test/my_pn", "test/PetriNet")
+        
+        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': '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_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_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_weight', 'type': 'AttributeLink', '__source': 'T2P', '__target': 'Natural', 'name': 'weight', 'optional': False, 'constraint': None}
+            ]
+        compare_unordered_lists(got, expected)
+
+        got = element_list_nice("test/my_pn")
+        expected = \
+            [{'id': 'p1', 'type': 'Place', 'tokens': 1, 'name': 'p1'},
+             {'id': 'p2', 'type': 'Place', 'tokens': 2, 'name': 'p2'},
+             {'id': 'p3', 'type': 'Place', 'tokens': 3, 'name': 'p3'},
+             {'id': 't1', 'type': 'Transition', 'name': 't1'},
+             {'id': '__0', 'type': 'P2T', '__source': 'p1', '__target': 't1', 'weight': 1},
+             {'id': '__1', 'type': 'P2T', '__source': 'p2', '__target': 't1', 'weight': 1},
+             {'id': '__2', 'type': 'T2P', '__source': 't1', '__target': 'p3', 'weight': 2}
+            ]
+        compare_unordered_lists(got, expected)
+
+        alter_context("test/PetriNet", "formalisms/Bottom")
+        alter_context("test/my_pn", "formalisms/Bottom")
+
+        count_nodes = 0
+        count_edges = 0
+        for entry in element_list_nice("test/PetriNet"):
+            assert entry["type"] in ["Node", "Edge"]
+            if entry["type"] == "Node":
+                assert len(entry) == 2
+                count_nodes += 1
+            else:
+                assert len(entry) == 4
+                count_edges += 1
+        assert count_nodes == 14
+        assert count_edges == 17
+
+        count_nodes = 0
+        count_edges = 0
+        for entry in element_list_nice("test/my_pn"):
+            assert entry["type"] in ["Node", "Edge"]
+            if entry["type"] == "Node":
+                assert len(entry) == 2
+                count_nodes += 1
+            else:
+                assert len(entry) == 4
+                count_edges += 1
+        assert count_nodes == 14
+        assert count_edges == 13
+
+        alter_context("test/PetriNet", "test/PetriNet")
+        alter_context("test/my_pn", "formalisms/SimpleClassDiagrams")
+        try:
+            element_list_nice("test/PetriNet")
+            self.fail()
+        except:
+            pass
+        try:
+            element_list_nice("test/my_pn")
+            self.fail()
+        except:
+            pass

+ 82 - 0
unit/utils.py

@@ -0,0 +1,82 @@
+import unittest
+import sys
+import os
+
+import sys
+import time
+import json
+import urllib
+import urllib2
+import subprocess
+import signal
+import random
+
+sys.path.append("interface/HUTN")
+sys.path.append("scripts")
+from hutn_compiler.compiler import main as do_compile
+
+taskname = "test_task"
+INIT_TIMEOUT = 30
+
+try:
+    import pytest
+    slow = pytest.mark.skipif(
+        not pytest.config.getoption("--runslow"),
+            reason="need --runslow option to run"
+    )
+except:
+    slow = lambda i:i
+
+ports = set()
+
+def getFreePort():
+    """Gets a unique new port."""
+    while 1:
+        port = random.randint(10000, 20000)
+        # Check if this port is in the set of ports.
+        if port not in ports:
+            # We have found a unique port. Add it to the set and return.
+            ports.add(port)
+            return port
+
+def execute(scriptname, parameters=[], wait=False):
+    if os.name not in ["nt", "posix"]:
+        # Stop now, as we would have no clue on how to kill its subtree
+        raise Exception("Unknown OS version: " + str(os.name))
+
+    command = [sys.executable, "-u", "scripts/%s.py" % scriptname] + parameters
+
+    if wait:
+        return subprocess.call(command, shell=False)
+    else:
+        return subprocess.Popen(command, shell=False)
+
+def child_kill(pid):
+    subprocess.call(["pkill", "-P", "%i" % pid])
+    start = time.time()
+    with open(os.devnull, 'w') as null:
+        while subprocess.call(["pgrep", "-P", "%i" % pid], stdout=null) != 1:
+            time.sleep(0.1)
+            if time.time() > start + 4:
+                subprocess.call(["pkill", "-9", "-P", "%i" % pid])
+
+def kill(process):
+    if os.name == "nt":
+        #TODO this is Windows and I don't care too much about that...
+        subprocess.call(["taskkill", "/F", "/T", "/PID", "%i" % process.pid])
+    elif os.name == "posix":
+        child_kill(process.pid)
+        process.send_signal(signal.SIGINT)
+        process.wait()
+        child_kill(os.getpid())
+
+def flush_data(address, data):
+    if data:
+        urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "set_input", "data": json.dumps(data), "taskname": taskname})), timeout=INIT_TIMEOUT).read()
+    return []
+
+def start_mvc():
+    port = getFreePort()
+    address = "http://127.0.0.1:%s" % port
+    proc = execute("run_local_modelverse", [str(port)], wait=False)
+    return proc, address

+ 24 - 24
wrappers/modelverse.py

@@ -341,28 +341,16 @@ def model_delete(model_name):
     _input(["model_delete", model_name])
     _handle_output("Success")
 
-def model_list():
+def model_list(location):
     """List all models."""
     _goto_mode(MODE_MODELLING)
-    _input("model_list")
-    output = _handle_output("Success: ", split=" ")
-    if output == "":
-        return set([])
-
-    lst = set([])
-    value = output.strip().split("\n")
-    for v in value:
-        m, mm = v.split(":")
-        m = m.strip()
-        mm = mm.strip()
-        lst.add((m, mm))
+    _input(["model_list", location])
+    return set(_handle_output("Success: ", split=" ").split("\n"))
 
-    return lst
-
-def model_list_full():
+def model_list_full(location):
     """List full information on all models."""
     _goto_mode(MODE_MODELLING)
-    _input("model_list_full")
+    _input(["model_list_full", location])
     output = _handle_output("Success: ", split=" ")
     if output == "":
         return set([])
@@ -370,11 +358,9 @@ def model_list_full():
     lst = set([])
     value = output.strip().split("\n")
     for v in value:
-        m, mm = v.split(":")
-        m = m.strip()
-        mm = mm.strip()
-        perm, own, grp, m = m.split(" ")
-        lst.add((m, mm, own, grp, perm))
+        m = v.strip()
+        perm, own, grp, m = m.split(" ", 3)
+        lst.add((m, own, grp, perm))
 
     return lst
 
@@ -617,7 +603,9 @@ def process_execute(process_name, prefix, callbacks):
     _input(["process_execute", process_name, prefix])
     _handle_output("Success")
 
-    while _output() != "Success":
+    reuse = False
+    while reuse or _output() != "Success":
+        reuse = False
         output = _last_output()
         if output.startswith("Enacting "):
             # Next activity!
@@ -632,6 +620,8 @@ def process_execute(process_name, prefix, callbacks):
                         mode = MODE_MODELLING
                         if reply is not None:
                             _input(reply)
+                    if _last_output().startswith("Enacting "):
+                        reuse = True
                 elif t == "ManualOperation":
                     _handle_output("Please perform manual operation ")
                     _output("Model loaded, ready for commands!")
@@ -948,7 +938,12 @@ def element_list_nice(model_name):
 
     _input(["element_list_nice", model_name, _get_metamodel(model_name)])
 
-    return json.loads(_handle_output("Success: ", split=" "))
+    data = _handle_output("Success: ", split=" ")
+    try:
+        return json.loads(data)
+    except:
+        print(data)
+        raise
 
 def connections_between(model_name, source_element, target_element):
     """Gets a list of all allowed connections between the source and target element in the model."""
@@ -999,3 +994,8 @@ def add_conformance(model_name, metamodel_name, partial_type_mapping=None):
     _goto_mode(MODE_MODELLING)
     _input(["add_conformance", model_name, metamodel_name])
     _handle_output("Success")
+
+def folder_create(folder_name):
+    _goto_mode(MODE_MODELLING)
+    _input(["folder_create", folder_name])
+    _handle_output("Success")