From e046f2f972362a55d78b7419af447b4de1d8f0d8 Mon Sep 17 00:00:00 2001 From: Joeri Exelmans Date: Wed, 23 Jul 2025 13:54:32 +0200 Subject: [PATCH] nice visualization --- tutorial/00_metamodeling.py | 13 +++++++++---- tutorial/02_inheritance.py | 3 --- tutorial/03_api.py | 1 - tutorial/05_advanced_transformation.py | 24 +++++++++++++++++++++++- util/loader.py | 3 +++ 5 files changed, 35 insertions(+), 9 deletions(-) diff --git a/tutorial/00_metamodeling.py b/tutorial/00_metamodeling.py index c1664d4..6b12fe0 100644 --- a/tutorial/00_metamodeling.py +++ b/tutorial/00_metamodeling.py @@ -27,7 +27,10 @@ m_cs = """ myLnk:a2b (myA -> myB) """ -# So far we've only created text strings. To parse the models, we first create our 'state', which is a mutable graph that will contain our models and meta-models: +# Notice that the syntax for meta-model and model is the same: We always declare a named object/link, followed by a colon (:) and the name of the type. The type name refers to the name of an object/link in the meta-model of our model. + + +# So far we've only created text strings in Python. To parse them as models, we first create our 'state', which is a mutable graph that will contain our models and meta-models: from state.devstate import DevState @@ -37,7 +40,9 @@ state = DevState() # Next, we must load the Simple Class Diagrams (SCD) meta-meta-model into our 'state'. The SCD meta-meta-model is a meta-model for our meta-model, and it is also a meta-model for itself. -# The meta-meta-model is not specified in textual syntax because it is typed by itself, and the parser cannot resolve circular dependencies. Therefore, we load the meta-meta-model by mutating the 'state' directly at a very low level: +# The meta-meta-model is not specified in textual syntax because it is typed by itself. In textual syntax, it would contain things like: +# Class:Class +# which is an object typed by itself. The parser cannot handle this (or circular dependencies in general). Therefore, we load the meta-meta-model by mutating the 'state' directly at a very low level: from bootstrap.scd import bootstrap_scd @@ -50,7 +55,7 @@ print("OK") from concrete_syntax.textual_od import parser print() -print("Parsing 'woods' meta-model...") +print("Parsing meta-model...") mm = parser.parse_od( state, m_text=mm_cs, # the string of text to parse @@ -62,7 +67,7 @@ print("OK") # And we can parse our model, the same way: print() -print("Parsing 'woods' model...") +print("Parsing model...") m = parser.parse_od( state, m_text=m_cs, diff --git a/tutorial/02_inheritance.py b/tutorial/02_inheritance.py index a8f6062..9de434f 100644 --- a/tutorial/02_inheritance.py +++ b/tutorial/02_inheritance.py @@ -1,4 +1,3 @@ - # The following meta-model has an inheritance relation: mm_cs = """ @@ -33,8 +32,6 @@ m_nonconform_cs = """ from state.devstate import DevState from bootstrap.scd import bootstrap_scd -# from concrete_syntax.textual_od import parser -# from framework.conformance import Conformance, render_conformance_check_result from util import loader state = DevState() diff --git a/tutorial/03_api.py b/tutorial/03_api.py index f30ce6d..7cdd3ea 100644 --- a/tutorial/03_api.py +++ b/tutorial/03_api.py @@ -14,7 +14,6 @@ mm_cs = """ myZ:Association (MyAbstractClass -> Z) { target_lower_cardinality = 1; } - """ m_cs = """ diff --git a/tutorial/05_advanced_transformation.py b/tutorial/05_advanced_transformation.py index 02d9d30..194a381 100644 --- a/tutorial/05_advanced_transformation.py +++ b/tutorial/05_advanced_transformation.py @@ -61,6 +61,7 @@ from transformation.cloner import clone_od from transformation import rewriter from concrete_syntax.textual_od.renderer import render_od from concrete_syntax.common import indent +from api.od import ODAPI state = DevState() mmm = bootstrap_scd(state) @@ -166,11 +167,32 @@ def fire_transition(m, transition_match): for match_outgoing in match_od(state, m, mm, lhs_outgoing, mm_ramified, pivot=transition_match): rewriter.rewrite(state, lhs_outgoing, rhs_outgoing, mm_ramified, match_outgoing, m, mm) +def show_petri_net(m): + odapi = ODAPI(state, m, mm) + p1 = odapi.get_slot_value(odapi.get("p1"), "tokens") + p2 = odapi.get_slot_value(odapi.get("p2"), "tokens") + cp1 = odapi.get_slot_value(odapi.get("cp1"), "tokens") + cp2 = odapi.get_slot_value(odapi.get("cp2"), "tokens") + return f""" + t1 t2 t3 + ┌─┐ p1 ┌─┐ p2 ┌─┐ + │ │ --- │ │ --- │ │ + │ ├─────► ( {p1} )─────►│ │─────► ( {p2} )─────►│ │ + └─┘ --- └─┘ --- └─┘ + ▲ │ ▲ │ + │ │ │ │ + │ │ │ │ + │ │ │ │ + │ --- │ │ --- │ + └───────( {cp1} )◄──────┘ └──────( {cp2} )◄───────┘ + --- --- + cp1 cp2 """ # Let's see if it works: while len(enabled) > 0: + print(show_petri_net(m)) + print("\nenabled PN transitions:", enabled) print("press ENTER to fire", enabled[0]['t']) input() fire_transition(m, enabled[0]) enabled = list(find_enabled_transitions(m)) - print("\nenabled PN transitions:", enabled) diff --git a/util/loader.py b/util/loader.py index 4a29d63..3b5112b 100644 --- a/util/loader.py +++ b/util/loader.py @@ -39,8 +39,11 @@ KINDS = ["nac", "lhs", "rhs"] # Phony name generator that raises an error if you try to use it :) class LHSNameGenerator: def __call__(self, type_name): + if type_name == "GlobalCondition": + return parser.DefaultNameGenerator()(type_name) raise Exception(f"Error: Object or link of type '{type_name}' does not have a name.\nAnonymous objects/links are not allowed in the LHS of a rule, because they can have unintended consequences. Please give all of the elements in the LHS explicit names.") + # load model transformation rules def load_rules(state, get_filename, rt_mm_ramified, rule_names, check_conformance=True): rules = {}