Commit some outstanding changes. Add TODO for cleanup.

This commit is contained in:
Joeri Exelmans 2025-01-27 16:03:45 +01:00
parent 200f2a3ede
commit 8fe7b0ea04
12 changed files with 114 additions and 35 deletions

44
TODO.txt Normal file
View file

@ -0,0 +1,44 @@
Things that need to be cleaned up:
- At several places in the code, it is assumed that from the root node, there is an edge labeled 'SCD' containing the self-conforming meta-meta-model. It would be better for parts of the code that need the meta-meta-model to receive this model as a (function) parameter.
- The whole 'ModelRef'-construct does not work as originally foreseen. It is currently only used for attributes of primitive types, where it unnecessarily complicates things. Better to get rid of it.
Known bugs:
- Cannot parse negative numbers
- When merging models, the model element names must not overlap. Maybe allow some kind of prefixing of the overlapping names? Difficulty porting existing models to the merged models if the type names have changed...
Merging (meta-)models is a nightmare:
- Prefixing the type names (to avoid naming collisions) is not an option:
(*) constraints (and transformation rules) already contain API calls that mention type names -> all of these would break
(*) don't want to prefix primitive types like "Integer", "String", ... because the existing code already assumes these exact names
- Not prefixing the type names leads to naming collisions, even if names are carefully chosen:
(*) anonymous names, e.g., Inheritance-links still result in naming collisions (requiring auto-renaming?)
Feature requests:
- Support custom functions in 'conditions'
- When matching edge, match 'any' src/tgt
- Support 'return'-statement in conditions?
- Separate script for running LHS (+NAC) on any model, and visualizing the match.
- Syntax highlighting:
most students use:
- VS Code
- PyCharm
i use:
- Sublime Text
nobody uses:
- Eclipse

View file

@ -1,18 +1,23 @@
{% macro render_name(name) %}{{ name if not hide_names or name.startswith("__") else "" }}{% endmacro %} {% macro render_name(name) %}{{ name if not hide_names or name.startswith("__") else "" }}{% endmacro %}
{% macro render_attributes(obj) %} { {% macro render_attributes(obj) %}
{% if len(odapi.get_slots(obj)) > 0 %} {
{% for attr_name in odapi.get_slots(obj) %} {% for attr_name in odapi.get_slots(obj) %}
{{ attr_name}} = {{ display_value( {{ attr_name}} = {{ display_value(
val=odapi.get_slot_value(obj, attr_name), val=odapi.get_slot_value(obj, attr_name),
type_name=odapi.get_type_name(odapi.get_slot(obj, attr_name)), type_name=odapi.get_type_name(odapi.get_slot(obj, attr_name)),
indentation=4) }}; indentation=4) }};
{% endfor %} {% endfor -%}
}{% endmacro %} }
{% endif -%}
{%- endmacro %}
{% for obj_name, obj in objects %} {%- for obj_name, obj in objects %}
{{ render_name(obj_name) }}:{{ odapi.get_type_name(obj) }}{{ render_attributes(obj) }} {{ render_name(obj_name) }}:{{ odapi.get_type_name(obj) }}
{% endfor %} {{- render_attributes(obj) }}
{% endfor -%}
{% for lnk_name, lnk in links %} {%- for lnk_name, lnk in links %}
{{ render_name(obj_name) }}:{{ odapi.get_type_name(lnk) }} ({{odapi.get_name(odapi.get_source(lnk))}} -> {{odapi.get_name(odapi.get_target(lnk))}}){{ render_attributes(lnk) }} {{ render_name(obj_name) }}:{{ odapi.get_type_name(lnk) }} ({{odapi.get_name(odapi.get_source(lnk))}} -> {{odapi.get_name(odapi.get_target(lnk))}})
{% endfor %} {{- render_attributes(lnk) }}
{% endfor -%}

View file

@ -4,6 +4,7 @@ from framework.conformance import Conformance, render_conformance_check_result
from concrete_syntax.textual_od import parser, renderer from concrete_syntax.textual_od import parser, renderer
from concrete_syntax.common import indent from concrete_syntax.common import indent
from concrete_syntax.plantuml import renderer as plantuml from concrete_syntax.plantuml import renderer as plantuml
from concrete_syntax.plantuml.make_url import make_url
from util.prompt import yes_no, pause from util.prompt import yes_no, pause
state = DevState() state = DevState()
@ -153,6 +154,7 @@ woods_m_cs = """
bear2:Bear bear2:Bear
:afraidOf (george -> bear1) :afraidOf (george -> bear1)
:afraidOf (george -> bear2) :afraidOf (george -> bear2)
:afraidOf (billy -> george)
""" """
print() print()
@ -194,7 +196,7 @@ if yes_no("Print PlantUML?"):
uml += plantuml.render_trace_conformance(state, woods_m, woods_mm) uml += plantuml.render_trace_conformance(state, woods_m, woods_mm)
print("==================================") print("==================================")
print(uml) print(make_url(uml))
print("==================================") print("==================================")
print("Go to either:") print("Go to either:")
print(" ▸ https://www.plantuml.com/plantuml/uml") print(" ▸ https://www.plantuml.com/plantuml/uml")

View file

@ -113,9 +113,7 @@ def main():
# object to match # object to match
man:{prefix}Man {{ man:{prefix}Man {{
# match only men heavy enough # match only men heavy enough
{prefix}weight = ``` {prefix}weight = `get_value(this) > 60`;
get_value(this) > 60
```;
}} }}
# object to delete # object to delete
@ -142,6 +140,7 @@ def main():
# object to create # object to create
bill:{prefix}Man {{ bill:{prefix}Man {{
# name = `"billie"+str(get_slot_value(matched("man"), "weight"))`;
{prefix}weight = `100`; {prefix}weight = `100`;
}} }}
@ -208,6 +207,7 @@ def main():
generator = match_od(state, dsl_m_id, dsl_mm_id, lhs_id, ramified_mm_id) generator = match_od(state, dsl_m_id, dsl_mm_id, lhs_id, ramified_mm_id)
for i, (match, color) in enumerate(zip(generator, ["red", "orange"])): for i, (match, color) in enumerate(zip(generator, ["red", "orange"])):
print("\nMATCH:\n", match)
uml += plantuml.render_trace_match(state, match, lhs_id, dsl_m_id, color) uml += plantuml.render_trace_match(state, match, lhs_id, dsl_m_id, color)
# rewrite happens in-place (which sucks), so we will only modify a clone: # rewrite happens in-place (which sucks), so we will only modify a clone:

View file

@ -1,7 +1,7 @@
from state.devstate import DevState from state.devstate import DevState
from api.od import ODAPI from api.od import ODAPI
from concrete_syntax.textual_od.renderer import render_od from concrete_syntax.textual_od.renderer import render_od
# from concrete_syntax.textual_od.renderer_jinja2 import render_od_jinja2 from concrete_syntax.textual_od.renderer_jinja2 import render_od_jinja2
from bootstrap.scd import bootstrap_scd from bootstrap.scd import bootstrap_scd
from util import loader from util import loader
from transformation.rule import RuleMatcherRewriter, ActionGenerator from transformation.rule import RuleMatcherRewriter, ActionGenerator
@ -28,10 +28,10 @@ if __name__ == "__main__":
mm_rt_cs = mm_cs + read_file('metamodels/mm_runtime.od') mm_rt_cs = mm_cs + read_file('metamodels/mm_runtime.od')
# m_cs = read_file('models/m_example_simple.od') # m_cs = read_file('models/m_example_simple.od')
# m_rt_initial_cs = m_cs + read_file('models/m_example_simple_rt_initial.od') # m_rt_initial_cs = m_cs + read_file('models/m_example_simple_rt_initial.od')
# m_cs = read_file('models/m_example_mutex.od') m_cs = read_file('models/m_example_mutex.od')
# m_rt_initial_cs = m_cs + read_file('models/m_example_mutex_rt_initial.od') m_rt_initial_cs = m_cs + read_file('models/m_example_mutex_rt_initial.od')
m_cs = read_file('models/m_example_inharc.od') # m_cs = read_file('models/m_example_inharc.od')
m_rt_initial_cs = m_cs + read_file('models/m_example_inharc_rt_initial.od') # m_rt_initial_cs = m_cs + read_file('models/m_example_inharc_rt_initial.od')
# Parse them # Parse them
mm = loader.parse_and_check(state, mm_cs, scd_mmm, "Petri-Net Design meta-model") mm = loader.parse_and_check(state, mm_cs, scd_mmm, "Petri-Net Design meta-model")
@ -51,7 +51,8 @@ if __name__ == "__main__":
def render_callback(od): def render_callback(od):
show_petri_net(od) show_petri_net(od)
return render_od(state, od.m, od.mm) # return render_od(state, od.m, od.mm)
return render_od_jinja2(state, od.m, od.mm)
sim = simulator.Simulator( sim = simulator.Simulator(
action_generator=action_generator, action_generator=action_generator,

View file

@ -12,11 +12,22 @@
nameOffsetY="0" nameOffsetY="0"
positionX="{{ i * 100 + 100 }}" positionX="{{ i * 100 + 100 }}"
positionY="100" positionY="100"
/> />
{% endfor %} {% endfor %}
{% for i, (transition_name, transition) in enumerate(odapi.get_all_instances("PNTransition")) %} {% for i, (transition_name, transition) in enumerate(odapi.get_all_instances("PNTransition")) %}
<transition angle="0" displayName="true" id="{{ transition_name }}" infiniteServer="false" name="{{ transition_name }}" nameOffsetX="0" nameOffsetY="0" player="0" positionX="{{ i * 100 + 100 }}" positionY="300" priority="0" urgent="false"/> <transition angle="0"
displayName="true"
id="{{ transition_name }}"
infiniteServer="false"
name="{{ transition_name }}"
nameOffsetX="0"
nameOffsetY="0"
player="0"
positionX="{{ i * 100 + 100 }}"
positionY="300"
priority="0"
urgent="false"/>
{% endfor %} {% endfor %}
{% for arc_name, arc in odapi.get_all_instances("arc") %} {% for arc_name, arc in odapi.get_all_instances("arc") %}

View file

@ -270,7 +270,7 @@ port_rt_m_cs = port_m_cs + """
time = 0; time = 0;
} }
waitingState:PlaceState { numShips = 0; } :of (waitingState -> waiting) waitingState:PlaceState { numShips = 2; } :of (waitingState -> waiting)
inboundPassageState:PlaceState { numShips = 0; } :of (inboundPassageState -> inboundPassage) inboundPassageState:PlaceState { numShips = 0; } :of (inboundPassageState -> inboundPassage)
outboundPassageState:PlaceState { numShips = 0; } :of (outboundPassageState -> outboundPassage) outboundPassageState:PlaceState { numShips = 0; } :of (outboundPassageState -> outboundPassage)
@ -282,7 +282,7 @@ port_rt_m_cs = port_m_cs + """
berth1State:BerthState { status = "empty"; numShips = 0; } :of (berth1State -> berth1) berth1State:BerthState { status = "empty"; numShips = 0; } :of (berth1State -> berth1)
berth2State:BerthState { status = "empty"; numShips = 0; } :of (berth2State -> berth2) berth2State:BerthState { status = "empty"; numShips = 0; } :of (berth2State -> berth2)
servedState:PlaceState { numShips = 0; } :of (servedState -> served) servedState:PlaceState { numShips = 1; } :of (servedState -> served)
workersState:WorkerSetState :of (workersState -> workers) workersState:WorkerSetState :of (workersState -> workers)
@ -396,12 +396,12 @@ smaller_model2_rt_cs = smaller_model2_cs + """
} }
waitingState:PlaceState { numShips = 1; } :of (waitingState -> waiting) waitingState:PlaceState { numShips = 1; } :of (waitingState -> waiting)
berthState:BerthState { numShips = 0; status = "empty"; } :of (berthState -> berth) berthState:BerthState { numShips = 1; status = "served"; } :of (berthState -> berth)
servedState:PlaceState { numShips = 0; } :of (servedState -> served) servedState:PlaceState { numShips = 1; } :of (servedState -> served)
gen2waitState:ConnectionState { moved = False; } :of (gen2waitState -> gen2wait) gen2waitState:ConnectionState { moved = False; } :of (gen2waitState -> gen2wait)
wait2berthState:ConnectionState { moved = False; } :of (wait2berthState -> wait2berth) wait2berthState:ConnectionState { moved = False; } :of (wait2berthState -> wait2berth)
berth2servedState:ConnectionState { moved = False; } :of (berth2servedState -> berth2served) berth2servedState:ConnectionState { moved = True; } :of (berth2servedState -> berth2served)
workersState:WorkerSetState :of (workersState -> workers) workersState:WorkerSetState :of (workersState -> workers)
""" """

View file

@ -53,7 +53,7 @@ sim = Simulator(
termination_condition=termination_condition, termination_condition=termination_condition,
check_conformance=True, check_conformance=True,
verbose=True, verbose=True,
renderer=render_port_textual, # renderer=render_port_textual,
# renderer=render_port_graphviz, # renderer=render_port_graphviz,
) )

View file

@ -9,7 +9,7 @@
pn_place:RAM_PNPlace { pn_place:RAM_PNPlace {
# new feature: you can control the name of the object to be created: # new feature: you can control the name of the object to be created:
name = `f"pn_{get_name(matched("port_place"))}"`; name = `f"ships_{get_name(matched("port_place"))}"`;
} }
place2place:RAM_generic_link (pn_place -> port_place) place2place:RAM_generic_link (pn_place -> port_place)
@ -19,4 +19,4 @@
pn_place_state:RAM_PNPlaceState { pn_place_state:RAM_PNPlaceState {
RAM_numTokens = `get_slot_value(matched('port_place_state'), "numShips")`; RAM_numTokens = `get_slot_value(matched('port_place_state'), "numShips")`;
} }
:RAM_pn_of(pn_place_state -> pn_place) :RAM_pn_of(pn_place_state -> pn_place)

View file

@ -1,5 +1,7 @@
# Just look for a connection: # Just look for a connection and its state:
port_src:RAM_Source port_src:RAM_Source
port_snk:RAM_Sink port_snk:RAM_Sink
port_conn:RAM_connection (port_src -> port_snk) port_conn:RAM_connection (port_src -> port_snk)
port_conn_state:RAM_ConnectionState
port_of:RAM_of (port_conn_state -> port_conn)

View file

@ -3,12 +3,26 @@
port_src:RAM_Source port_src:RAM_Source
port_snk:RAM_Sink port_snk:RAM_Sink
port_conn:RAM_connection (port_src -> port_snk) port_conn:RAM_connection (port_src -> port_snk)
port_conn_state:RAM_ConnectionState
port_of:RAM_of (port_conn_state -> port_conn)
# Create a Petri Net transition, and link it to our port-connection: # Create a Petri Net transition, and link it to our port-connection:
pn_transition:RAM_PNTransition { move_transition:RAM_PNTransition {
name = `f"pn_{get_name(matched("port_conn"))}"`; name = `f"move_{get_name(matched("port_conn"))}"`;
} }
trans2conn:RAM_generic_link (pn_transition -> port_conn)
moved_place:RAM_PNPlace {
name = `f" moved_{get_name(matched("port_conn"))}"`;
}
moved_place_state:RAM_PNPlaceState {
RAM_numTokens = `1 if get_slot_value(matched('port_conn_state'), "moved") else 0`;
}
:RAM_pn_of (moved_place_state -> moved_place)
# when firing a 'move', put a token in the 'moved'-place
:RAM_arc (move_transition -> moved_place)
trans2conn:RAM_generic_link (move_transition -> port_conn)
moved2conn:RAM_generic_link (moved_place -> port_conn)
# Note that we are not yet creating any incoming/outgoing petri net arcs! This will be done in another rule. # Note that we are not yet creating any incoming/outgoing petri net arcs! This will be done in another rule.

View file

@ -62,9 +62,9 @@ if __name__ == "__main__":
print('loading model...') print('loading model...')
port_m_rt_initial = loader.parse_and_check(state, port_m_rt_initial = loader.parse_and_check(state,
m_cs=models.port_rt_m_cs, # <-- your final solution should work with the full model # m_cs=models.port_rt_m_cs, # <-- your final solution should work with the full model
# m_cs=models.smaller_model_rt_cs, # <-- simpler model to try first # m_cs=models.smaller_model_rt_cs, # <-- simpler model to try first
# m_cs=models.smaller_model2_rt_cs, # <-- simpler model to try first m_cs=models.smaller_model2_rt_cs, # <-- simpler model to try first
mm=merged_mm, mm=merged_mm,
descr="initial model", descr="initial model",
check_conformance=False, # no need to check conformance every time check_conformance=False, # no need to check conformance every time