Browse Source

WIP: FTG parser

Joeri Exelmans 2 years ago
parent
commit
d85914f9ea

+ 24 - 17
drawio2oml/convert.py

@@ -7,6 +7,9 @@ from drawio2oml.pm import oml_generator as pm_oml_generator
 from drawio2oml.ftg import parser as ftg_parser
 from drawio2oml.ftg import oml_generator as ftg_oml_generator
 
+import re
+PAGENAME_REGEX = re.compile(r"([^:]+):([^:]+)")
+
 PARSERS = {
     "pm": {
         "parser": pm_parser,
@@ -38,41 +41,45 @@ if __name__ == "__main__":
     for page in dio_asyntax.pages:
         names = dio_oml_generator.assign_names(page)
 
-        dio_oml_file = args.outdir + "/" + page.name + "_drawio.oml"
+        match = PAGENAME_REGEX.fullmatch(page.name)
+        if match != None:
+            # If this regex matches, then the groups are key,value:
+            model_name, model_type = match.groups()
+            print("Model", model_name, "is a", model_type)
+        else:
+            model_name = page.name
+            model_type = ""
+
+
+        dio_oml_file = args.outdir + "/" + model_name + "_drawio.oml"
         print("Writing", dio_oml_file)
         with open(dio_oml_file, 'wt') as file:
             dio_oml_generator.write_oml(
                 filename=args.inputfile,
                 drawio_page=page,
+                model_name=model_name,
                 names=names,
                 ostream=file)
 
-        for parser_name in args.parsers:
+        if model_type in PARSERS:
             layer_to_parse = page.root.children[0] # just parse the first layer
-            try:
-                (parsed_as, tracelinks) = PARSERS[parser_name]["parser"].parse(layer_to_parse)
-            except Exception as e:
-                print("Skipping parser", parser_name, "due to error:", e)
-                continue
-
-            model_names = PARSERS[parser_name]["oml_generator"].assign_names(parsed_as)
-
-            model_oml_file = args.outdir + "/" + page.name + "_" + parser_name + ".oml"
+            (parsed_as, tracelinks) = PARSERS[model_type]["parser"].parse(layer_to_parse)
+            model_names = PARSERS[model_type]["oml_generator"].assign_names(parsed_as)
+            model_oml_file = args.outdir + "/" + model_name + "_" + model_type + ".oml"
             print("Writing", model_oml_file)
             with open(model_oml_file, 'wt') as file:
-                PARSERS[parser_name]["oml_generator"].write_oml(
+                PARSERS[model_type]["oml_generator"].write_oml(
                     input_filename=args.inputfile,
-                    input_page=page,
+                    model_name=model_name,
                     asyntax=parsed_as,
                     names=model_names,
                     ostream=file)
-
-            corr_oml_file = args.outdir + "/" + page.name + "_corr.oml"
+            corr_oml_file = args.outdir + "/" + model_name + "_corr.oml"
             print("Writing", corr_oml_file)
             with open(corr_oml_file, 'wt') as file:
-                PARSERS[parser_name]["oml_generator"].write_corr_oml(
+                PARSERS[model_type]["oml_generator"].write_corr_oml(
                     input_filename=args.inputfile,
-                    input_page=page,
+                    model_name=model_name,
                     traceability_links=tracelinks,
                     drawio_names=names,
                     model_names=model_names,

+ 3 - 2
drawio2oml/drawio/oml_generator.py

@@ -20,6 +20,7 @@ def assign_names(drawio_page: abstract_syntax.Page) -> typing.Dict[abstract_synt
 def write_oml(
     filename: str, # only for traceability/documentation purposes
     drawio_page: abstract_syntax.Page,
+    model_name: str,
     names: typing.Dict[abstract_syntax.Element, str],
     ostream: io.TextIOBase,
     namespaces=util.DTDESIGN_NAMESPACES):
@@ -44,7 +45,7 @@ def write_oml(
             page=drawio_page,
             names=names,
             enumerate=enumerate,
-            output_namespace=namespaces['artifacts']+drawio_page.name+"_drawio",
-            shorthand=drawio_page.name+"_drawio",
+            output_namespace=namespaces['artifacts']+model_name+"_drawio",
+            shorthand=model_name+"_drawio",
             namespaces=namespaces):
         ostream.write(piece)

+ 4 - 3
drawio2oml/ftg/abstract_syntax.py

@@ -1,14 +1,15 @@
 from dataclasses import dataclass, field
 from typing import List, Tuple
 
-@dataclass
+@dataclass(eq=False)
 class Element:
     pass
 
-@dataclass
+@dataclass(eq=False)
 class Formalism(Element):
     name: str
 
+@dataclass(eq=False)
 class Transformation(Element):
     name: str
     ctrl_in: List[str] # names of control input ports
@@ -17,7 +18,7 @@ class Transformation(Element):
     data_out: List[Tuple[str, Formalism]] # named data output ports
     automated: bool
 
-@dataclass
+@dataclass(eq=False)
 class FTG(Element):
     formalisms: List[Formalism]
     transformations: List[Transformation]

+ 51 - 0
drawio2oml/ftg/oml_generator.py

@@ -0,0 +1,51 @@
+import io
+import os
+import typing
+import jinja2
+from drawio2py import abstract_syntax as dio_as
+from drawio2oml.ftg import abstract_syntax as ftg_as
+from drawio2oml.ftg import trace_abstract_syntax as trace_as
+from drawio2oml import util
+
+def assign_names(ftg):
+    names = {}
+    for t in ftg.transformations:
+        names[t] = "t_" + t.name
+    for f in ftg.formalisms:
+        names[f] = "f_" + f.name
+    return names
+
+def write_oml(
+    input_filename: str,
+    model_name: str,
+    asyntax: ftg_as.FTG,
+    names: typing.Dict[ftg_as.Element, str],
+    ostream: io.TextIOBase,
+    namespaces=util.DTDESIGN_NAMESPACES):
+
+    environment = jinja2.Environment(
+        loader=jinja2.FileSystemLoader(os.path.dirname(__file__)))
+    environment.filters['to_oml_string_literal'] = util.to_oml_string_literal
+    template = environment.get_template("oml_template_ftg.j2")
+
+    for piece in template.generate(
+        asyntax=asyntax,
+        input_filename=input_filename,
+        model_name=model_name,
+        enumerate=enumerate,
+        concat=util.concat,
+        names=names,
+        output_namespace=namespaces['artifacts']+model_name+"_ftg",
+        shorthand=model_name+"_ftg",
+        namespaces=namespaces):
+        ostream.write(piece)
+
+def write_corr_oml(
+    input_filename: str,
+    model_name: dio_as.Page,
+    traceability_links: typing.List[trace_as.TraceabilityLink],
+    drawio_names: typing.Dict[dio_as.Element, str],
+    model_names: typing.Dict[ftg_as.Element, str],
+    ostream: io.TextIOBase,
+    namespaces=util.DTDESIGN_NAMESPACES):
+    pass

+ 44 - 0
drawio2oml/ftg/oml_template_ftg.j2

@@ -0,0 +1,44 @@
+// Warning: Generated Code! Do not edit!
+// Input file: {{input_filename}}
+// Page: {{model_name}}
+// Generator: https://msdl.uantwerpen.be/git/jexelmans/drawio2oml
+
+description <{{output_namespace}}#> as {{shorthand}} {
+
+  uses <{{namespaces.ftg}}#> as ftg
+  uses <{{namespaces.base}}#> as base
+
+  {%- for f in asyntax.formalisms %}
+
+  ci {{names[f]}} : ftg:Formalism [
+    base:hasGUID {{f.name|to_oml_string_literal}}
+  ]
+  {%- endfor %}
+
+  {%- for t in asyntax.transformations %}
+
+  ci {{names[t]}} : ftg:Transformation [
+    base:hasGUID {{t.name|to_oml_string_literal}}
+    {%- for ctrl_in in t.ctrl_in %}
+    ftg:hasCtrlInput {{ctrl_in|to_oml_string_literal}}
+    {%- endfor -%}
+    {%- for ctrl_out in t.ctrl_out %}
+    ftg:hasCtrlOutput {{ctrl_out|to_oml_string_literal}}
+    {%- endfor %}
+  ]
+  {%- for (portname, f) in t.data_in %}
+  ri {{names[t]}}_input{{loop.index0}} : ftg:HasInput [
+    from {{names[t]}}
+    to {{names[f]}}
+    ftg:hasName {{portname|to_oml_string_literal}}
+  ]
+  {%- endfor %}
+  {%- for (portname, f) in t.data_out %}
+  ri {{names[t]}}_output{{loop.index0}} : ftg:HasOutput [
+    from {{names[t]}}
+    to {{names[f]}}
+    ftg:hasName {{portname|to_oml_string_literal}}
+  ]
+  {%- endfor %}
+  {%- endfor %}
+}

+ 9 - 9
drawio2oml/ftg/parser.py

@@ -6,6 +6,7 @@ from typing import List
 import pprint
 
 def parse(parent: dio_as.Cell) -> (ftg_as.FTG, List[trace_as.TraceabilityLink]):
+    tracelinks = []
     formalism_cells = [cell for cell in parent.children if "pmRole" in cell.properties and cell.properties["pmRole"] == "formalism"]
 
     transformation_cells = [cell for cell in parent.children if "pmRole" in cell.properties and cell.properties["pmRole"] == "transformation"]
@@ -13,18 +14,17 @@ def parse(parent: dio_as.Cell) -> (ftg_as.FTG, List[trace_as.TraceabilityLink]):
     formalisms = [ftg_as.Formalism(f.properties["name"]) for f in formalism_cells]
     name_to_formalism = { f.name : f for f in formalisms }
 
-    pprint.pprint(formalisms)
 
     transformations = []
     for t in transformation_cells:
-        Transformation(
+        transformations.append(ftg_as.Transformation(
             name=t.properties["name"],
-            ctrl_in=[port.properties["name"] for port in t.children if port.properties["pmRole"] == "ctrl_in"],
-            ctrl_out=[port.properties["name"] for port in t.children if port.properties["pmRole"] == "ctrl_out"],
-            data_in=[(port.properties["name"]) for port in t.children if port.properties["pmRole"] == "data_in"],
-            data_out=data_out,
-            automated="automated" in t.properties and t.properties["automated"]=="true")
+            ctrl_in=[port.properties["portname"] for port in t.children if port.properties["pmRole"] == "ctrl_in"],
+            ctrl_out=[port.properties["portname"] for port in t.children if port.properties["pmRole"] == "ctrl_out"],
+            data_in=[(port.properties["portname"], name_to_formalism[port.incoming[0].source.properties["name"]]) for port in t.children if port.properties["pmRole"] == "data_in"],
+            data_out=[(port.properties["portname"], name_to_formalism[port.outgoing[0].target.properties["name"]]) for port in t.children if port.properties["pmRole"] == "data_out"],
+            automated="automated" in t.properties and t.properties["automated"]=="true"))
 
-    return ftg_as.FTG(
+    return (ftg_as.FTG(
         formalisms=formalisms,
-        transformations=transformations)
+        transformations=transformations), tracelinks)

+ 12 - 12
drawio2oml/pm/oml_generator.py

@@ -55,7 +55,7 @@ def assign_names(pm_model: pm_as.ProcessModel) -> typing.Dict[pm_as.Element, str
 
 def write_oml(
     input_filename: str,
-    input_page: dio_as.Page,
+    model_name: str,
     asyntax: pm_as.ProcessModel,
     names: typing.Dict[pm_as.Element, str],
     ostream: io.TextIOBase,
@@ -64,7 +64,7 @@ def write_oml(
     Generate an OML description of a process model.
     Parameters:
       input_filename: has no precise semantics - only written to the output in a comment
-      input_page: only used to determine the name of the model
+      model_name: only used to determine the name of the model
       pm_model: the parsed process model to write as an OML description
       pm_names: mapping from every element in the process model to a unique name
       ostream: stream to write OML output to (e.g., stdout, a file, ...)
@@ -80,20 +80,20 @@ def write_oml(
     for piece in template.generate(
             model=asyntax,
             input_filename=input_filename,
-            input_page=input_page,
+            model_name=model_name,
             enumerate=enumerate,
             concat=util.concat,
             pm_names=names,
             types=TYPENAMES,
-            output_namespace=namespaces['artifacts']+input_page.name+"_pm",
-            shorthand=input_page.name+"_pm",
+            output_namespace=namespaces['artifacts']+model_name+"_pm",
+            shorthand=model_name+"_pm",
             namespaces=namespaces):
         ostream.write(piece)
 
 
 def write_corr_oml(
     input_filename: str,
-    input_page: dio_as.Page,
+    model_name: dio_as.Page,
     traceability_links: typing.List[trace_as.TraceabilityLink],
     drawio_names: typing.Dict[dio_as.Element, str],
     model_names: typing.Dict[pm_as.Element, str],
@@ -103,7 +103,7 @@ def write_corr_oml(
     Generate an OML description of the traceability links between a Drawio-description and a PM-description.
     Parameters:
       input_filename: has no precise semantics - only written to the output in a comment
-      input_page: only used to determine the name of the model
+      model_name: only used to determine the name of the model
       traceability_links: list of traceability links to write as an OML description
       ostream: stream to write OML output to (e.g., stdout, a file, ...)
       output_namespace: namespace for the to-be-generated OML description
@@ -119,13 +119,13 @@ def write_corr_oml(
             pm_names=model_names,
             drawio_names=drawio_names,
             input_filename=input_filename,
-            input_page=input_page,
+            model_name=model_name,
             enumerate=enumerate,
             concat=util.concat,
             types=TYPENAMES,
-            drawio_descr_namespace=namespaces['artifacts']+input_page.name+"_drawio",
-            pm_descr_namespace=namespaces['artifacts']+input_page.name+"_pm",
-            output_namespace=namespaces['artifacts']+input_page.name+"_corr",
-            shorthand=input_page.name+"_corr",
+            drawio_descr_namespace=namespaces['artifacts']+model_name+"_drawio",
+            pm_descr_namespace=namespaces['artifacts']+model_name+"_pm",
+            output_namespace=namespaces['artifacts']+model_name+"_corr",
+            shorthand=model_name+"_corr",
             namespaces=namespaces):
         ostream.write(piece)

+ 2 - 2
drawio2oml/pm/oml_template_corr.j2

@@ -1,7 +1,7 @@
 // Warning: Generated Code! Do not edit!
 // This file contains the correspondence links between concrete syntax (Drawio) and abstract syntax (Process Model).
 // Input file: {{input_filename}}
-// Page: {{input_page.name}}
+// Page: {{model_name}}
 // Generator: https://msdl.uantwerpen.be/git/jexelmans/drawio2oml
 
 description <{{output_namespace}}#> as {{shorthand}} {
@@ -13,7 +13,7 @@ description <{{output_namespace}}#> as {{shorthand}} {
   extends <{{pm_descr_namespace}}#> as my_pm
 
   ci model : cs_as:CorrespondenceModel [
-    object_diagram:hasName {{(input_page.name+"_corr")|to_oml_string_literal}}
+    object_diagram:hasName {{(model_name+"_corr")|to_oml_string_literal}}
   ]
 
   {%- for trace_link in traceability_links %}

+ 2 - 2
drawio2oml/pm/oml_template_pm.j2

@@ -22,7 +22,7 @@ pm:dataTo {{pm_names[outgoing]}} // for some reason, we have to explicitly creat
 
 // Warning: Generated Code! Do not edit!
 // Input file: {{input_filename}}
-// Page: {{input_page.name}}
+// Page: {{model_name}}
 // Generator: https://msdl.uantwerpen.be/git/jexelmans/drawio2oml
 
 description <{{output_namespace}}#> as {{shorthand}} {
@@ -31,7 +31,7 @@ description <{{output_namespace}}#> as {{shorthand}} {
   uses <{{namespaces.object_diagram}}#> as object_diagram
 
   ci {{pm_names[model]}} : {{types[model.__class__]}} [
-    object_diagram:hasName {{(input_page.name+"_pm")|to_oml_string_literal}}
+    object_diagram:hasName {{(model_name+"_pm")|to_oml_string_literal}}
   ]
 
   ci {{pm_names[model.initial]}} : {{types[model.initial.__class__]}} [

+ 2 - 0
drawio2oml/util.py

@@ -28,4 +28,6 @@ DTDESIGN_NAMESPACES = {
     "object_diagram": "http://ua.be/sdo2l/vocabulary/formalisms/object_diagram",
     "pm": "http://ua.be/sdo2l/vocabulary/formalisms/pm",
     "artifacts": "http://ua.be/sdo2l/description/artifacts/",
+    "base": "http://ua.be/sdo2l/vocabulary/base/base",
+    "ftg": "http://ua.be/sdo2l/vocabulary/base/ftg",
 }

File diff suppressed because it is too large
+ 1 - 1
test/data/ftg/FTG.drawio


File diff suppressed because it is too large
+ 1 - 0
test/data/pm/MyFTGPM.drawio