CBD model is computing Fibonacci numbers! :)
This commit is contained in:
parent
9c68b288c1
commit
80cba4b9f8
27 changed files with 429 additions and 269 deletions
|
|
@ -150,14 +150,18 @@ def render_trace_ramifies(state, mm, ramified_mm, render_attributes=True, prefix
|
||||||
|
|
||||||
# Render RAMifies-edges between classes
|
# Render RAMifies-edges between classes
|
||||||
for ram_name, ram_class_node in ramified_mm_scd.get_classes().items():
|
for ram_name, ram_class_node in ramified_mm_scd.get_classes().items():
|
||||||
original_class, = bottom.read_outgoing_elements(ram_class_node, ramify.RAMIFIES_LABEL)
|
original_class = ramify.get_original_type(bottom, ram_class_node)
|
||||||
|
if original_class == None:
|
||||||
|
continue # not all classes come from original (e.g., 'GlobalCondition')
|
||||||
original_name = mm_scd.get_class_name(original_class)
|
original_name = mm_scd.get_class_name(original_class)
|
||||||
output += f"\n{make_ram_id(ram_class_node)} ..> {make_orig_id(original_class)} #line:green;text:green : RAMifies"
|
output += f"\n{make_ram_id(ram_class_node)} ..> {make_orig_id(original_class)} #line:green;text:green : RAMifies"
|
||||||
|
|
||||||
if render_attributes:
|
if render_attributes:
|
||||||
# and between attributes
|
# and between attributes
|
||||||
for (ram_attr_name, ram_attr_edge) in od.get_attributes(bottom, ram_class_node):
|
for (ram_attr_name, ram_attr_edge) in od.get_attributes(bottom, ram_class_node):
|
||||||
orig_attr_edge, = bottom.read_outgoing_elements(ram_attr_edge, ramify.RAMIFIES_LABEL)
|
orig_attr_edge = ramify.get_original_type(bottom, ram_attr_edge)
|
||||||
|
if orig_attr_edge == None:
|
||||||
|
continue # not all attributes come from original (e.g., 'condition')
|
||||||
orig_class_node = bottom.read_edge_source(orig_attr_edge)
|
orig_class_node = bottom.read_edge_source(orig_attr_edge)
|
||||||
# dirty AF:
|
# dirty AF:
|
||||||
orig_attr_name = mm_scd.get_class_name(orig_attr_edge)[len(original_name)+1:]
|
orig_attr_name = mm_scd.get_class_name(orig_attr_edge)[len(original_name)+1:]
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,7 @@ def get_fibonacci(state, scd_mmm):
|
||||||
|
|
||||||
return (mm, mm_rt, m, m_rt_initial)
|
return (mm, mm_rt, m, m_rt_initial)
|
||||||
|
|
||||||
RULE_NAMES = ["delay"]
|
RULE_NAMES = ["delay_out", "function_out", "delay_in", "advance_time"]
|
||||||
KINDS = ["nac", "lhs", "rhs"]
|
KINDS = ["nac", "lhs", "rhs"]
|
||||||
|
|
||||||
def get_rules(state, rt_mm):
|
def get_rules(state, rt_mm):
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
# Adder, two inputs, one output
|
# Adder, two inputs, one output
|
||||||
adder:Function {
|
adder:Function {
|
||||||
func = ```
|
func = ```
|
||||||
n2_out = n0_in + n1_in
|
read('n0_in') + read('n1_in')
|
||||||
```;
|
```;
|
||||||
}
|
}
|
||||||
n0_in:InPort
|
n0_in:InPort
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,13 @@
|
||||||
# Initial state for both delay blocks:
|
# Initial state for both delay blocks:
|
||||||
d0s:State {
|
d0s:State {
|
||||||
state = 0;
|
x = 0;
|
||||||
}
|
}
|
||||||
d1s:State {
|
d1s:State {
|
||||||
state = 1;
|
x = 1;
|
||||||
}
|
}
|
||||||
:delay2State (d0 -> d0s)
|
:delay2State (d0 -> d0s)
|
||||||
:delay2State (d1 -> d1s)
|
:delay2State (d1 -> d1s)
|
||||||
|
|
||||||
|
clock:Clock {
|
||||||
|
time = 0;
|
||||||
|
}
|
||||||
|
|
@ -2,12 +2,8 @@ Block:Class {
|
||||||
abstract = True;
|
abstract = True;
|
||||||
}
|
}
|
||||||
|
|
||||||
InPort:Class {
|
InPort:Class
|
||||||
# abstract = True;
|
OutPort:Class
|
||||||
}
|
|
||||||
OutPort:Class {
|
|
||||||
# abstract = True;
|
|
||||||
}
|
|
||||||
|
|
||||||
hasInPort:Association (Block -> InPort) {
|
hasInPort:Association (Block -> InPort) {
|
||||||
# Every Port contained by exactly one Block:
|
# Every Port contained by exactly one Block:
|
||||||
|
|
@ -21,27 +17,12 @@ hasOutPort:Association (Block -> OutPort) {
|
||||||
}
|
}
|
||||||
|
|
||||||
link:Association (OutPort -> InPort) {
|
link:Association (OutPort -> InPort) {
|
||||||
#abstract = True;
|
|
||||||
|
|
||||||
# Every InPort connected to exactly one OutPort
|
# Every InPort connected to exactly one OutPort
|
||||||
source_lower_cardinality = 1;
|
source_lower_cardinality = 1;
|
||||||
source_upper_cardinality = 1;
|
source_upper_cardinality = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
# In- and Out-Ports are labeled:
|
|
||||||
|
|
||||||
# hasInPort_label:AttributeLink (hasInPort -> String) {
|
|
||||||
# name = "label";
|
|
||||||
# optional = False;
|
|
||||||
# }
|
|
||||||
# hasOutPort_label:AttributeLink (hasOutPort -> String) {
|
|
||||||
# name = "label";
|
|
||||||
# optional = False;
|
|
||||||
# }
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Function Block: pure function that computes outputs based on inputs
|
# Function Block: pure function that computes outputs based on inputs
|
||||||
|
|
||||||
Function:Class
|
Function:Class
|
||||||
|
|
@ -69,61 +50,9 @@ Delay:Class {
|
||||||
num_outports = len(get_outgoing(this, "hasOutPort"))
|
num_outports = len(get_outgoing(this, "hasOutPort"))
|
||||||
if num_inports != 1:
|
if num_inports != 1:
|
||||||
errors.append(f"Delay block must have one inport, instead got {num_inports}")
|
errors.append(f"Delay block must have one inport, instead got {num_inports}")
|
||||||
in_type = None
|
|
||||||
else:
|
|
||||||
in_type = get_type_name(get_target(get_outgoing(this, "hasInPort")[0]))
|
|
||||||
if num_outports != 1:
|
if num_outports != 1:
|
||||||
errors.append(f"Delay block must have one inport, instead got {num_outports}")
|
errors.append(f"Delay block must have one inport, instead got {num_outports}")
|
||||||
out_type = None
|
|
||||||
else:
|
|
||||||
out_type = get_type_name(get_target(get_outgoing(this, "hasOutPort")[0]))
|
|
||||||
# if in_type != None and out_type != None and in_type[0:3] != out_type[0:3]:
|
|
||||||
# errors.append(f"Inport type ({in_type}) differs from outport type ({out_type})")
|
|
||||||
errors
|
errors
|
||||||
```;
|
```;
|
||||||
}
|
}
|
||||||
:Inheritance (Delay -> Block)
|
:Inheritance (Delay -> Block)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Object Diagrams are statically typed, so we must create in/out-ports, and MemorySlots for all primitive types:
|
|
||||||
|
|
||||||
|
|
||||||
# # Port types
|
|
||||||
|
|
||||||
# BoolInPort:Class
|
|
||||||
# IntInPort:Class
|
|
||||||
# StrInPort:Class
|
|
||||||
|
|
||||||
# BoolOutPort:Class
|
|
||||||
# IntOutPort:Class
|
|
||||||
# StrOutPort:Class
|
|
||||||
|
|
||||||
# :Inheritance (BoolInPort -> InPort)
|
|
||||||
# :Inheritance (IntInPort -> InPort)
|
|
||||||
# :Inheritance (StrInPort -> InPort)
|
|
||||||
|
|
||||||
# :Inheritance (BoolOutPort -> OutPort)
|
|
||||||
# :Inheritance (IntOutPort -> OutPort)
|
|
||||||
# :Inheritance (StrOutPort -> OutPort)
|
|
||||||
|
|
||||||
# # Link types
|
|
||||||
|
|
||||||
# boolLink:Association (BoolOutPort -> BoolInPort)
|
|
||||||
# intLink:Association (IntOutPort -> IntInPort)
|
|
||||||
# strLink:Association (StrOutPort -> StrInPort)
|
|
||||||
|
|
||||||
# :Inheritance (boolLink -> link)
|
|
||||||
# :Inheritance (intLink -> link)
|
|
||||||
# :Inheritance (strLink -> link)
|
|
||||||
|
|
||||||
# # Delay block types
|
|
||||||
|
|
||||||
# BoolDelay:Class
|
|
||||||
# IntDelay:Class
|
|
||||||
# StrDelay:Class
|
|
||||||
|
|
||||||
# :Inheritance (BoolDelay -> Delay)
|
|
||||||
# :Inheritance (IntDelay -> Delay)
|
|
||||||
# :Inheritance (StrDelay -> Delay)
|
|
||||||
|
|
|
||||||
|
|
@ -1,85 +1,47 @@
|
||||||
# Link state ("signal")
|
# Link state ("signal")
|
||||||
|
|
||||||
Signal:Class {
|
Signal:Class
|
||||||
# abstract = True;
|
|
||||||
}
|
|
||||||
|
|
||||||
Signal_signal:AttributeLink (Signal -> Integer) {
|
Signal_x:AttributeLink (Signal -> Integer) {
|
||||||
name = "signal";
|
name = "x";
|
||||||
optional = False;
|
optional = False;
|
||||||
}
|
}
|
||||||
|
|
||||||
hasSignal:Association (link -> Signal) {
|
hasSignal:Association (OutPort -> Signal) {
|
||||||
# every Signal has 1 link
|
# every Signal has 1 link
|
||||||
source_lower_cardinality = 1;
|
source_lower_cardinality = 1;
|
||||||
source_upper_cardinality = 1;
|
source_upper_cardinality = 1;
|
||||||
|
|
||||||
# every link has 0..1 Signals:
|
# every link has 0..1 Signals:
|
||||||
target_upper_cardinality = 1;
|
target_upper_cardinality = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
# BoolSignal:Class
|
|
||||||
# IntSignal:Class
|
|
||||||
# StrSignal:Class
|
|
||||||
|
|
||||||
# :Inheritance (BoolSignal -> Signal)
|
|
||||||
# :Inheritance (IntSignal -> Signal)
|
|
||||||
# :Inheritance (StrSignal -> Signal)
|
|
||||||
|
|
||||||
# BoolSignal_signal:AttributeLink (BoolSignal -> Boolean) {
|
|
||||||
# name = "signal";
|
|
||||||
# optional = False;
|
|
||||||
# }
|
|
||||||
# IntSignal_signal:AttributeLink (IntSignal -> Integer) {
|
|
||||||
# name = "signal";
|
|
||||||
# optional = False;
|
|
||||||
# }
|
|
||||||
# StrSignal_signal:AttributeLink (StrSignal -> String) {
|
|
||||||
# name = "signal";
|
|
||||||
# optional = False;
|
|
||||||
# }
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Delay block state
|
# Delay block state
|
||||||
# mandatory - otherwise we cannot determine the output signal of a delay block
|
# mandatory - otherwise we cannot determine the output signal of a delay block
|
||||||
|
|
||||||
State:Class {
|
State:Class
|
||||||
# abstract = True;
|
|
||||||
}
|
|
||||||
|
|
||||||
State_state:AttributeLink (State -> Integer) {
|
State_x:AttributeLink (State -> Integer) {
|
||||||
name = "state";
|
name = "x";
|
||||||
optional = False;
|
optional = False;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
delay2State:Association (Delay -> State) {
|
delay2State:Association (Delay -> State) {
|
||||||
|
# one-to-one
|
||||||
source_lower_cardinality = 1;
|
source_lower_cardinality = 1;
|
||||||
source_upper_cardinality = 1;
|
source_upper_cardinality = 1;
|
||||||
target_lower_cardinality = 1;
|
target_lower_cardinality = 1;
|
||||||
target_upper_cardinality = 1;
|
target_upper_cardinality = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
# BoolState:Class
|
Clock:Class {
|
||||||
# IntState:Class
|
lower_cardinality = 1;
|
||||||
# StrState:Class
|
upper_cardinality = 1;
|
||||||
|
}
|
||||||
|
|
||||||
# :Inheritance (BoolState -> State)
|
Clock_time:AttributeLink (Clock -> Integer) {
|
||||||
# :Inheritance (IntState -> State)
|
name = "time";
|
||||||
# :Inheritance (StrState -> State)
|
optional = False;
|
||||||
|
}
|
||||||
|
|
||||||
# BoolState_state:AttributeLink (BoolState -> Boolean) {
|
|
||||||
# name = "state";
|
|
||||||
# optional = False;
|
|
||||||
# }
|
|
||||||
|
|
||||||
# IntState_state:AttributeLink (IntState -> Integer) {
|
|
||||||
# name = "state";
|
|
||||||
# optional = False;
|
|
||||||
# }
|
|
||||||
|
|
||||||
# StrState_state:AttributeLink (StrState -> String) {
|
|
||||||
# name = "state";
|
|
||||||
# optional = False;
|
|
||||||
# }
|
|
||||||
3
examples/cbd/models/r_advance_time_lhs.od
Normal file
3
examples/cbd/models/r_advance_time_lhs.od
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
clock:RAM_Clock {
|
||||||
|
RAM_time = `True`;
|
||||||
|
}
|
||||||
36
examples/cbd/models/r_advance_time_nac.od
Normal file
36
examples/cbd/models/r_advance_time_nac.od
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
|
||||||
|
# If there is a Delay-block whose input signal differs from its state, we cannot yet advance time:
|
||||||
|
|
||||||
|
delay:RAM_Delay
|
||||||
|
|
||||||
|
delay_in:RAM_InPort
|
||||||
|
|
||||||
|
delay_has_input:RAM_hasInPort (delay -> delay_in)
|
||||||
|
|
||||||
|
some_outport:RAM_OutPort
|
||||||
|
|
||||||
|
delay_in_conn:RAM_link (some_outport -> delay_in)
|
||||||
|
|
||||||
|
in_signal:RAM_Signal
|
||||||
|
|
||||||
|
port_has_signal:RAM_hasSignal (some_outport -> in_signal)
|
||||||
|
|
||||||
|
state:RAM_State {
|
||||||
|
RAM_x = `get_slot_value(matched('in_signal'), 'x') != get_value(this)`;
|
||||||
|
}
|
||||||
|
|
||||||
|
delay_to_state:RAM_delay2State (delay -> state)
|
||||||
|
|
||||||
|
|
||||||
|
# Also, we cannot advance time until all outports have signals:
|
||||||
|
|
||||||
|
:GlobalCondition {
|
||||||
|
condition = ```
|
||||||
|
missing_signals = False
|
||||||
|
for _, outport in get_all_instances("OutPort"):
|
||||||
|
if len(get_outgoing(outport, 'hasSignal')) == 0:
|
||||||
|
missing_signals = True
|
||||||
|
break
|
||||||
|
missing_signals
|
||||||
|
```;
|
||||||
|
}
|
||||||
10
examples/cbd/models/r_advance_time_rhs.od
Normal file
10
examples/cbd/models/r_advance_time_rhs.od
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
clock:RAM_Clock {
|
||||||
|
RAM_time = `get_value(this) + 1`;
|
||||||
|
}
|
||||||
|
|
||||||
|
:GlobalCondition {
|
||||||
|
condition = ```
|
||||||
|
for _, signal in get_all_instances("Signal"):
|
||||||
|
delete(signal)
|
||||||
|
```;
|
||||||
|
}
|
||||||
27
examples/cbd/models/r_delay_in_lhs.od
Normal file
27
examples/cbd/models/r_delay_in_lhs.od
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
# We look for a Delay-block, its inport connected to an outport that has a signal
|
||||||
|
|
||||||
|
delay:RAM_Delay
|
||||||
|
|
||||||
|
delay_in:RAM_InPort
|
||||||
|
|
||||||
|
delay_has_input:RAM_hasInPort (delay -> delay_in)
|
||||||
|
|
||||||
|
some_outport:RAM_OutPort
|
||||||
|
|
||||||
|
delay_in_conn:RAM_link (some_outport -> delay_in)
|
||||||
|
|
||||||
|
in_signal:RAM_Signal
|
||||||
|
|
||||||
|
port_has_signal:RAM_hasSignal (some_outport -> in_signal)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# State of Delay block... (will be updated in RHS)
|
||||||
|
|
||||||
|
state:RAM_State {
|
||||||
|
# Attention: you MUST match the existing attribute, in order to force an UDPATE of the attribute, rather than CREATION
|
||||||
|
|
||||||
|
RAM_x = `True`;
|
||||||
|
}
|
||||||
|
|
||||||
|
delay_to_state:RAM_delay2State (delay -> state)
|
||||||
6
examples/cbd/models/r_delay_in_nac.od
Normal file
6
examples/cbd/models/r_delay_in_nac.od
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
state:RAM_State # <- must repeat elements from LHS that we refer to
|
||||||
|
|
||||||
|
in_signal:RAM_Signal {
|
||||||
|
# If the signal is already equal to the state, the NAC holds:
|
||||||
|
RAM_x = `get_value(this) == get_slot_value(matched('state'), 'x')`;
|
||||||
|
}
|
||||||
26
examples/cbd/models/r_delay_in_rhs.od
Normal file
26
examples/cbd/models/r_delay_in_rhs.od
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
# Everything from our LHS (don't delete anything)
|
||||||
|
|
||||||
|
delay:RAM_Delay
|
||||||
|
|
||||||
|
delay_in:RAM_InPort
|
||||||
|
|
||||||
|
delay_has_input:RAM_hasOutPort (delay -> delay_in)
|
||||||
|
|
||||||
|
some_outport:RAM_OutPort
|
||||||
|
|
||||||
|
delay_in_conn:RAM_link (some_outport -> delay_in)
|
||||||
|
|
||||||
|
in_signal:RAM_Signal
|
||||||
|
|
||||||
|
port_has_signal:RAM_hasSignal (some_outport -> in_signal)
|
||||||
|
|
||||||
|
state:RAM_State {
|
||||||
|
# Update:
|
||||||
|
RAM_x = ```
|
||||||
|
new_state = get_slot_value(matched('in_signal'), 'x')
|
||||||
|
print(f"Updating delay {get_name(matched('delay'))} state: {new_state}")
|
||||||
|
new_state
|
||||||
|
```;
|
||||||
|
}
|
||||||
|
|
||||||
|
delay_to_state:RAM_delay2State (delay -> state)
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
# We look for a Delay-block, its outgoing connection, and its State
|
|
||||||
|
|
||||||
delay:RAM_Delay
|
|
||||||
|
|
||||||
delay_out:RAM_OutPort # abstract
|
|
||||||
|
|
||||||
delay_has_output:RAM_hasOutPort (delay -> delay_out)
|
|
||||||
|
|
||||||
some_inport:RAM_InPort # abstract
|
|
||||||
|
|
||||||
delay_out_conn:RAM_link (delay_out -> some_inport)
|
|
||||||
|
|
||||||
|
|
||||||
state:RAM_State
|
|
||||||
|
|
||||||
delay_to_state:RAM_delay2State (delay -> state)
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
# From our LHS:
|
|
||||||
|
|
||||||
delay_out:RAM_OutPort # abstract
|
|
||||||
|
|
||||||
some_inport:RAM_InPort # abstract
|
|
||||||
|
|
||||||
delay_out_conn:RAM_link (delay_out -> some_inport) # abstract
|
|
||||||
|
|
||||||
|
|
||||||
# The delay block's outgoing connection already has a signal:
|
|
||||||
|
|
||||||
some_signal:RAM_Signal
|
|
||||||
|
|
||||||
:RAM_hasSignal (delay_out_conn -> some_signal)
|
|
||||||
14
examples/cbd/models/r_delay_out_lhs.od
Normal file
14
examples/cbd/models/r_delay_out_lhs.od
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
# We look for a Delay-block, its outport, and its State
|
||||||
|
|
||||||
|
delay:RAM_Delay
|
||||||
|
|
||||||
|
delay_out:RAM_OutPort
|
||||||
|
|
||||||
|
delay_has_output:RAM_hasOutPort (delay -> delay_out)
|
||||||
|
|
||||||
|
|
||||||
|
state:RAM_State
|
||||||
|
|
||||||
|
delay_to_state:RAM_delay2State (delay -> state)
|
||||||
|
|
||||||
|
clock:RAM_Clock
|
||||||
10
examples/cbd/models/r_delay_out_nac.od
Normal file
10
examples/cbd/models/r_delay_out_nac.od
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
# From our LHS:
|
||||||
|
|
||||||
|
delay_out:RAM_OutPort
|
||||||
|
|
||||||
|
|
||||||
|
# We don't want to see the delay block's outport already having a signal:
|
||||||
|
|
||||||
|
some_signal:RAM_Signal
|
||||||
|
|
||||||
|
:RAM_hasSignal (delay_out -> some_signal)
|
||||||
20
examples/cbd/models/r_delay_out_rhs.od
Normal file
20
examples/cbd/models/r_delay_out_rhs.od
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
# Our entire LHS (don't delete anything):
|
||||||
|
|
||||||
|
delay:RAM_Delay
|
||||||
|
|
||||||
|
delay_out:RAM_OutPort
|
||||||
|
|
||||||
|
delay_has_output:RAM_hasOutPort (delay -> delay_out)
|
||||||
|
|
||||||
|
state:RAM_State
|
||||||
|
|
||||||
|
delay_to_state:RAM_delay2State (delay -> state)
|
||||||
|
|
||||||
|
clock:RAM_Clock
|
||||||
|
|
||||||
|
# To create:
|
||||||
|
|
||||||
|
new_signal:RAM_Signal {
|
||||||
|
RAM_x = `get_slot_value(matched('state'), 'x')`;
|
||||||
|
}
|
||||||
|
:RAM_hasSignal (delay_out -> new_signal)
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
# Our entire LHS (don't delete anything):
|
|
||||||
|
|
||||||
delay:RAM_Delay
|
|
||||||
|
|
||||||
delay_out:RAM_OutPort # abstract
|
|
||||||
|
|
||||||
delay_has_output:RAM_hasOutPort (delay -> delay_out)
|
|
||||||
|
|
||||||
some_inport:RAM_InPort # abstract
|
|
||||||
|
|
||||||
delay_out_conn:RAM_link (delay_out -> some_inport) # abstract
|
|
||||||
|
|
||||||
state:RAM_State
|
|
||||||
|
|
||||||
delay_to_state:RAM_delay2State (delay -> state)
|
|
||||||
|
|
||||||
|
|
||||||
# To create:
|
|
||||||
|
|
||||||
new_signal:RAM_Signal {
|
|
||||||
RAM_signal = `get_slot_value(match('state'), 'state')`;
|
|
||||||
}
|
|
||||||
:RAM_hasSignal (delay_out_conn -> new_signal)
|
|
||||||
22
examples/cbd/models/r_function_out_lhs.od
Normal file
22
examples/cbd/models/r_function_out_lhs.od
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
# Match function and its out-connection
|
||||||
|
|
||||||
|
f:RAM_Function {
|
||||||
|
# Only match if the signal of all inputs has been computed:
|
||||||
|
condition = ```
|
||||||
|
ok = True
|
||||||
|
for o in get_outgoing(this, "hasInPort"):
|
||||||
|
inport = get_target(o)
|
||||||
|
in_conn = get_incoming(inport, "link")[0]
|
||||||
|
some_outport = get_source(in_conn)
|
||||||
|
if len(get_outgoing(some_outport, "hasSignal")) == 0:
|
||||||
|
ok = False
|
||||||
|
break
|
||||||
|
ok
|
||||||
|
```;
|
||||||
|
}
|
||||||
|
|
||||||
|
f_outport:RAM_OutPort
|
||||||
|
|
||||||
|
f_has_outport:RAM_hasOutPort (f -> f_outport)
|
||||||
|
|
||||||
|
clock:RAM_Clock
|
||||||
14
examples/cbd/models/r_function_out_nac.od
Normal file
14
examples/cbd/models/r_function_out_nac.od
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
# From our LHS:
|
||||||
|
|
||||||
|
f:RAM_Function
|
||||||
|
|
||||||
|
f_outport:RAM_OutPort
|
||||||
|
|
||||||
|
f_has_outport:RAM_hasOutPort (f -> f_outport)
|
||||||
|
|
||||||
|
|
||||||
|
# We don't want to see the function's out-connection already having a signal:
|
||||||
|
|
||||||
|
some_signal:RAM_Signal
|
||||||
|
|
||||||
|
:RAM_hasSignal (f_outport -> some_signal)
|
||||||
26
examples/cbd/models/r_function_out_rhs.od
Normal file
26
examples/cbd/models/r_function_out_rhs.od
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
# Our entire LHS (don't delete anything):
|
||||||
|
|
||||||
|
f:RAM_Function
|
||||||
|
|
||||||
|
f_outport:RAM_OutPort
|
||||||
|
|
||||||
|
f_has_outport:RAM_hasOutPort (f -> f_outport)
|
||||||
|
|
||||||
|
clock:RAM_Clock
|
||||||
|
|
||||||
|
# To create:
|
||||||
|
|
||||||
|
f_out_signal:RAM_Signal {
|
||||||
|
RAM_x = ```
|
||||||
|
def read(inport_name):
|
||||||
|
inport = get(inport_name)
|
||||||
|
outport = get_source(get_incoming(inport, "link")[0])
|
||||||
|
signal = get_target(get_outgoing(outport, "hasSignal")[0])
|
||||||
|
return get_slot_value(signal, "x")
|
||||||
|
|
||||||
|
code = get_slot_value(matched('f'), 'func')
|
||||||
|
|
||||||
|
eval(code, {}, { 'read': read })
|
||||||
|
```;
|
||||||
|
}
|
||||||
|
:RAM_hasSignal (f_outport -> f_out_signal)
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
from state.devstate import DevState
|
from state.devstate import DevState
|
||||||
from bootstrap.scd import bootstrap_scd
|
from bootstrap.scd import bootstrap_scd
|
||||||
|
|
||||||
|
from framework.conformance import Conformance, render_conformance_check_result
|
||||||
|
|
||||||
from concrete_syntax.common import indent
|
from concrete_syntax.common import indent
|
||||||
from concrete_syntax.textual_od import renderer as od_renderer
|
from concrete_syntax.textual_od import renderer as od_renderer
|
||||||
from concrete_syntax.plantuml import renderer as plantuml
|
from concrete_syntax.plantuml import renderer as plantuml
|
||||||
|
|
@ -17,12 +19,10 @@ import models
|
||||||
state = DevState()
|
state = DevState()
|
||||||
scd_mmm = bootstrap_scd(state)
|
scd_mmm = bootstrap_scd(state)
|
||||||
|
|
||||||
|
print("Parsing models...")
|
||||||
mm, mm_rt, m, m_rt_initial = models.get_fibonacci(state, scd_mmm)
|
mm, mm_rt, m, m_rt_initial = models.get_fibonacci(state, scd_mmm)
|
||||||
|
|
||||||
mm_rt_ram, rules = models.get_rules(state, mm_rt)
|
mm_rt_ram, rules = models.get_rules(state, mm_rt)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# print("RT-MM")
|
# print("RT-MM")
|
||||||
# print(make_plantuml_url(plantuml.render_class_diagram(state, mm_rt)))
|
# print(make_plantuml_url(plantuml.render_class_diagram(state, mm_rt)))
|
||||||
|
|
||||||
|
|
@ -42,28 +42,40 @@ def get_matches():
|
||||||
pattern_m=lhs,
|
pattern_m=lhs,
|
||||||
pattern_mm=mm_rt_ram)
|
pattern_mm=mm_rt_ram)
|
||||||
|
|
||||||
for i, lhs_match in enumerate(lhs_matcher):
|
try:
|
||||||
|
for i, lhs_match in enumerate(lhs_matcher):
|
||||||
|
nac_matcher = match_od(state,
|
||||||
|
host_m=m_rt,
|
||||||
|
host_mm=mm_rt,
|
||||||
|
pattern_m=rule["nac"],
|
||||||
|
pattern_mm=mm_rt_ram,
|
||||||
|
pivot=lhs_match)
|
||||||
|
|
||||||
nac_matcher = match_od(state,
|
try:
|
||||||
host_m=m_rt,
|
for j, nac_match in enumerate(nac_matcher):
|
||||||
host_mm=mm_rt,
|
break # there may be more NAC-matches, but we already now enough
|
||||||
pattern_m=rule["nac"],
|
else:
|
||||||
pattern_mm=mm_rt_ram,
|
# We got a match!
|
||||||
pivot=lhs_match)
|
yield (rule_name, lhs, rule["rhs"], lhs_match)
|
||||||
|
except Exception as e:
|
||||||
|
# Make exceptions raised in eval'ed code easier to trace:
|
||||||
|
e.add_note(f"while matching NAC of '{rule_name}'")
|
||||||
|
raise
|
||||||
|
|
||||||
for j, nac_match in enumerate(nac_matcher):
|
except Exception as e:
|
||||||
break # there may be more NAC-matches, but we already now enough
|
# Make exceptions raised in eval'ed code easier to trace:
|
||||||
else:
|
e.add_note(f"while matching LHS of '{rule_name}'")
|
||||||
# We got a match!
|
raise
|
||||||
yield (rule_name, lhs, rule["rhs"], lhs_match)
|
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
# print(make_graphviz_url(graphviz.render_object_diagram(state, m_rt, mm_rt)))
|
# print(make_graphviz_url(graphviz.render_object_diagram(state, m_rt, mm_rt)))
|
||||||
cs = od_renderer.render_od(state, m_rt, mm_rt, hide_names=False)
|
cs = od_renderer.render_od(state, m_rt, mm_rt, hide_names=False)
|
||||||
print(indent(cs, 6))
|
print(indent(cs, 6))
|
||||||
|
conf = Conformance(state, m_rt, mm_rt)
|
||||||
|
print(render_conformance_check_result(conf.check_nominal()))
|
||||||
|
|
||||||
matches = list(get_matches())
|
matches = list(get_matches())
|
||||||
|
|
||||||
print(f"There are {len(matches)} matches.")
|
print(f"There are {len(matches)} matches.")
|
||||||
if len(matches) == 0:
|
if len(matches) == 0:
|
||||||
break
|
break
|
||||||
|
|
@ -75,21 +87,23 @@ while True:
|
||||||
# txt += graphviz.render_trace_match(state, lhs_match, lhs, m_rt, color="orange")
|
# txt += graphviz.render_trace_match(state, lhs_match, lhs, m_rt, color="orange")
|
||||||
# match_urls.append(make_graphviz_url(txt))
|
# match_urls.append(make_graphviz_url(txt))
|
||||||
|
|
||||||
print('picking', lhs_match)
|
print(f"executing rule '{rule_name}' ", lhs_match)
|
||||||
|
|
||||||
print('rewriting')
|
|
||||||
|
|
||||||
# copy or will be overwritten in-place
|
# copy or will be overwritten in-place
|
||||||
m_rt = clone_od(state, m_rt, mm_rt)
|
m_rt = clone_od(state, m_rt, mm_rt)
|
||||||
rhs_match = dict(lhs_match)
|
rhs_match = dict(lhs_match)
|
||||||
|
try:
|
||||||
rewrite(state,
|
rewrite(state,
|
||||||
lhs_m=lhs,
|
lhs_m=lhs,
|
||||||
rhs_m=rhs,
|
rhs_m=rhs,
|
||||||
pattern_mm=mm_rt_ram,
|
pattern_mm=mm_rt_ram,
|
||||||
name_mapping=rhs_match,
|
name_mapping=rhs_match,
|
||||||
host_m=m_rt,
|
host_m=m_rt,
|
||||||
mm=mm_rt)
|
host_mm=mm_rt)
|
||||||
|
except Exception as e:
|
||||||
|
# Make exceptions raised in eval'ed code easier to trace:
|
||||||
|
e.add_note(f"while executing RHS of '{rule_name}'")
|
||||||
|
raise
|
||||||
|
|
||||||
# import subprocess
|
# import subprocess
|
||||||
# subprocess.run(["firefox", "--new-window", *match_urls])
|
# subprocess.run(["firefox", "--new-window", *match_urls])
|
||||||
|
|
|
||||||
|
|
@ -254,7 +254,10 @@ def is_typed_by(bottom, el: UUID, typ: UUID):
|
||||||
def get_typed_by(bottom, model, type_node: UUID):
|
def get_typed_by(bottom, model, type_node: UUID):
|
||||||
name_to_instance = {}
|
name_to_instance = {}
|
||||||
for key in bottom.read_keys(model):
|
for key in bottom.read_keys(model):
|
||||||
element, = bottom.read_outgoing_elements(model, key)
|
els = bottom.read_outgoing_elements(model, key)
|
||||||
|
if len(els) > 1:
|
||||||
|
raise Exception(f"Assertion failed: Model contains more than one object named '{key}'!")
|
||||||
|
element = els[0]
|
||||||
element_types = bottom.read_outgoing_elements(element, "Morphism")
|
element_types = bottom.read_outgoing_elements(element, "Morphism")
|
||||||
if type_node in element_types:
|
if type_node in element_types:
|
||||||
name_to_instance[key] = element
|
name_to_instance[key] = element
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,8 @@ UUID_REGEX = re.compile(r"[0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-
|
||||||
|
|
||||||
# Converts an object diagram in MVS state to the pattern matcher graph type
|
# Converts an object diagram in MVS state to the pattern matcher graph type
|
||||||
# ModelRefs are flattened
|
# ModelRefs are flattened
|
||||||
def model_to_graph(state: State, model: UUID, metamodel: UUID, prefix=""):
|
def model_to_graph(state: State, model: UUID, metamodel: UUID,
|
||||||
|
_filter=lambda node: True, prefix=""):
|
||||||
# with Timer("model_to_graph"):
|
# with Timer("model_to_graph"):
|
||||||
od = services_od.OD(model, metamodel, state)
|
od = services_od.OD(model, metamodel, state)
|
||||||
scd = SCD(model, state)
|
scd = SCD(model, state)
|
||||||
|
|
@ -121,7 +122,7 @@ def model_to_graph(state: State, model: UUID, metamodel: UUID, prefix=""):
|
||||||
return node
|
return node
|
||||||
|
|
||||||
# Objects and Links become vertices
|
# Objects and Links become vertices
|
||||||
uuid_to_vtx = { node: to_vtx(node, prefix+key) for key in bottom.read_keys(model) for node in bottom.read_outgoing_elements(model, key) }
|
uuid_to_vtx = { node: to_vtx(node, prefix+key) for key in bottom.read_keys(model) for node in bottom.read_outgoing_elements(model, key) if _filter(node) }
|
||||||
graph.vtxs = [ vtx for vtx in uuid_to_vtx.values() ]
|
graph.vtxs = [ vtx for vtx in uuid_to_vtx.values() ]
|
||||||
|
|
||||||
# For every Link, two edges are created (for src and tgt)
|
# For every Link, two edges are created (for src and tgt)
|
||||||
|
|
@ -183,7 +184,6 @@ def model_to_graph(state: State, model: UUID, metamodel: UUID, prefix=""):
|
||||||
# # print(type_edge)
|
# # print(type_edge)
|
||||||
# graph.edges.append(type_edge)
|
# graph.edges.append(type_edge)
|
||||||
|
|
||||||
|
|
||||||
# Add typing information for:
|
# Add typing information for:
|
||||||
# - classes
|
# - classes
|
||||||
# - attributes
|
# - attributes
|
||||||
|
|
@ -192,25 +192,31 @@ def model_to_graph(state: State, model: UUID, metamodel: UUID, prefix=""):
|
||||||
objects = scd.get_typed_by(class_node)
|
objects = scd.get_typed_by(class_node)
|
||||||
# print("typed by:", class_name, objects)
|
# print("typed by:", class_name, objects)
|
||||||
for obj_name, obj_node in objects.items():
|
for obj_name, obj_node in objects.items():
|
||||||
add_types(obj_node)
|
if _filter(obj_node):
|
||||||
|
add_types(obj_node)
|
||||||
for attr_name, attr_node in scd_mm.get_attributes(class_name).items():
|
for attr_name, attr_node in scd_mm.get_attributes(class_name).items():
|
||||||
attrs = scd.get_typed_by(attr_node)
|
attrs = scd.get_typed_by(attr_node)
|
||||||
for slot_name, slot_node in attrs.items():
|
for slot_name, slot_node in attrs.items():
|
||||||
add_types(slot_node)
|
if _filter(slot_node):
|
||||||
|
add_types(slot_node)
|
||||||
for assoc_name, assoc_node in scd_mm.get_associations().items():
|
for assoc_name, assoc_node in scd_mm.get_associations().items():
|
||||||
objects = scd.get_typed_by(assoc_node)
|
objects = scd.get_typed_by(assoc_node)
|
||||||
# print("typed by:", assoc_name, objects)
|
# print("typed by:", assoc_name, objects)
|
||||||
for link_name, link_node in objects.items():
|
for link_name, link_node in objects.items():
|
||||||
add_types(link_node)
|
if _filter(link_node):
|
||||||
|
add_types(link_node)
|
||||||
|
|
||||||
return names, graph
|
return names, graph
|
||||||
|
|
||||||
|
|
||||||
def match_od(state, host_m, host_mm, pattern_m, pattern_mm, pivot={}):
|
def match_od(state, host_m, host_mm, pattern_m, pattern_mm, pivot={}):
|
||||||
|
bottom = Bottom(state)
|
||||||
|
|
||||||
# compute subtype relations and such:
|
# compute subtype relations and such:
|
||||||
cdapi = CDAPI(state, host_mm)
|
cdapi = CDAPI(state, host_mm)
|
||||||
odapi = ODAPI(state, host_m, host_mm)
|
odapi = ODAPI(state, host_m, host_mm)
|
||||||
|
pattern_odapi = ODAPI(state, pattern_m, pattern_mm)
|
||||||
|
pattern_mm_odapi = ODAPI(state, pattern_mm, cdapi.mm)
|
||||||
|
|
||||||
# Function object for pattern matching. Decides whether to match host and guest vertices, where guest is a RAMified instance (e.g., the attributes are all strings with Python expressions), and the host is an instance (=object diagram) of the original model (=class diagram)
|
# Function object for pattern matching. Decides whether to match host and guest vertices, where guest is a RAMified instance (e.g., the attributes are all strings with Python expressions), and the host is an instance (=object diagram) of the original model (=class diagram)
|
||||||
class RAMCompare:
|
class RAMCompare:
|
||||||
|
|
@ -221,6 +227,9 @@ def match_od(state, host_m, host_mm, pattern_m, pattern_mm, pivot={}):
|
||||||
type_model_id = bottom.state.read_dict(bottom.state.read_root(), "SCD")
|
type_model_id = bottom.state.read_dict(bottom.state.read_root(), "SCD")
|
||||||
self.scd_model = UUID(bottom.state.read_value(type_model_id))
|
self.scd_model = UUID(bottom.state.read_value(type_model_id))
|
||||||
|
|
||||||
|
# constraints need to be checked at the very end, after a complete match is established, because constraint code may refer to matched elements by their name
|
||||||
|
self.conditions_to_check = []
|
||||||
|
|
||||||
def match_types(self, g_vtx_type, h_vtx_type):
|
def match_types(self, g_vtx_type, h_vtx_type):
|
||||||
# types only match with their supertypes
|
# types only match with their supertypes
|
||||||
# we assume that 'RAMifies'-traceability links have been created between guest and host types
|
# we assume that 'RAMifies'-traceability links have been created between guest and host types
|
||||||
|
|
@ -256,26 +265,10 @@ def match_od(state, host_m, host_mm, pattern_m, pattern_mm, pivot={}):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
python_code = services_od.read_primitive_value(self.bottom, g_vtx.node_id, pattern_mm)[0]
|
python_code = services_od.read_primitive_value(self.bottom, g_vtx.node_id, pattern_mm)[0]
|
||||||
return exec_then_eval(python_code,
|
|
||||||
_globals=bind_api_readonly(odapi),
|
|
||||||
_locals={'this': h_vtx.node_id})
|
|
||||||
|
|
||||||
# nested_matches = [m for m in match_od(state, h_ref_m, h_ref_mm, g_ref_m, g_ref_mm)]
|
self.conditions_to_check.append((python_code, h_vtx.node_id))
|
||||||
|
|
||||||
|
return True # do be determined later, if it's actually a match
|
||||||
# print('begin recurse')
|
|
||||||
# g_ref_m, g_ref_mm = g_vtx.modelref
|
|
||||||
# h_ref_m, h_ref_mm = h_vtx.modelref
|
|
||||||
# print('nested_matches:', nested_matches)
|
|
||||||
# if len(nested_matches) == 0:
|
|
||||||
# return False
|
|
||||||
# elif len(nested_matches) == 1:
|
|
||||||
# return True
|
|
||||||
# else:
|
|
||||||
# raise Exception("We have a problem: there is more than 1 match in the nested models.")
|
|
||||||
# print('end recurse')
|
|
||||||
|
|
||||||
# Then, match by value
|
|
||||||
|
|
||||||
if g_vtx.value == None:
|
if g_vtx.value == None:
|
||||||
return h_vtx.value == None
|
return h_vtx.value == None
|
||||||
|
|
@ -293,20 +286,26 @@ def match_od(state, host_m, host_mm, pattern_m, pattern_mm, pivot={}):
|
||||||
if h_vtx.value == IS_MODELREF:
|
if h_vtx.value == IS_MODELREF:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# python_code = g_vtx.value
|
|
||||||
# try:
|
|
||||||
# return exec_then_eval(python_code,
|
|
||||||
# _globals=bind_api_readonly(odapi),
|
|
||||||
# _locals={'this': h_vtx.node_id})
|
|
||||||
# except Exception as e:
|
|
||||||
# print(e)
|
|
||||||
# return False
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
# Convert to format understood by matching algorithm
|
# Convert to format understood by matching algorithm
|
||||||
h_names, host = model_to_graph(state, host_m, host_mm)
|
h_names, host = model_to_graph(state, host_m, host_mm)
|
||||||
g_names, guest = model_to_graph(state, pattern_m, pattern_mm)
|
|
||||||
|
|
||||||
|
# Only match matchable pattern elements
|
||||||
|
# E.g., the 'condition'-attribute that is added to every class, cannot be matched with anything
|
||||||
|
def is_matchable(pattern_el):
|
||||||
|
pattern_el_name = pattern_odapi.get_name(pattern_el)
|
||||||
|
if pattern_odapi.get_type_name(pattern_el) == "GlobalCondition":
|
||||||
|
return False
|
||||||
|
# Super-cheap and unreliable way of filtering out the 'condition'-attribute, added to every class:
|
||||||
|
return not (pattern_el_name.endswith("condition")
|
||||||
|
# as an extra safety measure, if the user defined her own 'condition' attribute, RAMification turned this into 'RAM_condition', and we can detect this
|
||||||
|
# of course this breaks if the class name already ended with 'RAM', but let's hope that never happens
|
||||||
|
# also, we are assuming the default "RAM_" prefix is used, but the user can change this...
|
||||||
|
and not pattern_el_name.endswith("RAM_condition"))
|
||||||
|
|
||||||
|
g_names, guest = model_to_graph(state, pattern_m, pattern_mm,
|
||||||
|
_filter=is_matchable)
|
||||||
|
|
||||||
graph_pivot = {
|
graph_pivot = {
|
||||||
g_names[guest_name] : h_names[host_name]
|
g_names[guest_name] : h_names[host_name]
|
||||||
|
|
@ -314,7 +313,46 @@ def match_od(state, host_m, host_mm, pattern_m, pattern_mm, pivot={}):
|
||||||
if guest_name in g_names
|
if guest_name in g_names
|
||||||
}
|
}
|
||||||
|
|
||||||
matcher = MatcherVF2(host, guest, RAMCompare(Bottom(state), services_od.OD(host_mm, host_m, state)))
|
obj_conditions = []
|
||||||
|
for class_name, class_node in pattern_mm_odapi.get_all_instances("Class"):
|
||||||
|
for obj_name, obj_node in pattern_odapi.get_all_instances(class_name):
|
||||||
|
python_code = pattern_odapi.get_slot_value_default(obj_node, "condition", 'True')
|
||||||
|
if class_name == "GlobalCondition":
|
||||||
|
obj_conditions.append((python_code, None))
|
||||||
|
else:
|
||||||
|
obj_conditions.append((python_code, obj_name))
|
||||||
|
|
||||||
|
|
||||||
|
def check_conditions(name_mapping):
|
||||||
|
def check(python_code: str, loc):
|
||||||
|
return exec_then_eval(python_code,
|
||||||
|
_globals={
|
||||||
|
**bind_api_readonly(odapi),
|
||||||
|
'matched': lambda name: bottom.read_outgoing_elements(host_m, name_mapping[name])[0],
|
||||||
|
},
|
||||||
|
_locals=loc)
|
||||||
|
|
||||||
|
# Attribute conditions
|
||||||
|
for python_code, host_node in compare.conditions_to_check:
|
||||||
|
if not check(python_code, {'this': host_node}):
|
||||||
|
return False
|
||||||
|
|
||||||
|
for python_code, pattern_el_name in obj_conditions:
|
||||||
|
if pattern_el_name == None:
|
||||||
|
# GlobalCondition
|
||||||
|
if not check(python_code, {}):
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
# object-lvl condition
|
||||||
|
host_el_name = name_mapping[pattern_el_name]
|
||||||
|
host_node = odapi.get(host_el_name)
|
||||||
|
if not check(python_code, {'this': host_node}):
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
compare = RAMCompare(bottom, services_od.OD(host_mm, host_m, state))
|
||||||
|
matcher = MatcherVF2(host, guest, compare)
|
||||||
for m in matcher.match(graph_pivot):
|
for m in matcher.match(graph_pivot):
|
||||||
# print("\nMATCH:\n", m)
|
# print("\nMATCH:\n", m)
|
||||||
# Convert mapping
|
# Convert mapping
|
||||||
|
|
@ -322,4 +360,8 @@ def match_od(state, host_m, host_mm, pattern_m, pattern_mm, pivot={}):
|
||||||
for guest_vtx, host_vtx in m.mapping_vtxs.items():
|
for guest_vtx, host_vtx in m.mapping_vtxs.items():
|
||||||
if isinstance(guest_vtx, NamedNode) and isinstance(host_vtx, NamedNode):
|
if isinstance(guest_vtx, NamedNode) and isinstance(host_vtx, NamedNode):
|
||||||
name_mapping[guest_vtx.name] = host_vtx.name
|
name_mapping[guest_vtx.name] = host_vtx.name
|
||||||
|
|
||||||
|
if not check_conditions(name_mapping):
|
||||||
|
continue # not a match after all...
|
||||||
|
|
||||||
yield name_mapping
|
yield name_mapping
|
||||||
|
|
|
||||||
|
|
@ -54,8 +54,13 @@ def ramify(state: State, model: UUID, prefix = "RAM_") -> UUID:
|
||||||
# create traceability link
|
# create traceability link
|
||||||
bottom.create_edge(ramified_attr_link, attr_edge, RAMIFIES_LABEL)
|
bottom.create_edge(ramified_attr_link, attr_edge, RAMIFIES_LABEL)
|
||||||
|
|
||||||
|
# Additional constraint that can be specified
|
||||||
|
ramified_scd._create_attribute_link(prefix+class_name, actioncode_modelref, "condition", optional=True)
|
||||||
|
|
||||||
already_ramified.add(class_name)
|
already_ramified.add(class_name)
|
||||||
|
|
||||||
|
glob_cond = ramified_scd.create_class("GlobalCondition", abstract=None)
|
||||||
|
ramified_scd._create_attribute_link("GlobalCondition", actioncode_modelref, "condition", optional=False)
|
||||||
|
|
||||||
assocs_to_ramify = m_scd.get_associations()
|
assocs_to_ramify = m_scd.get_associations()
|
||||||
|
|
||||||
|
|
@ -90,6 +95,9 @@ def ramify(state: State, model: UUID, prefix = "RAM_") -> UUID:
|
||||||
# create traceability link
|
# create traceability link
|
||||||
bottom.create_edge(ramified_assoc, assoc_node, RAMIFIES_LABEL)
|
bottom.create_edge(ramified_assoc, assoc_node, RAMIFIES_LABEL)
|
||||||
|
|
||||||
|
# Additional constraint that can be specified
|
||||||
|
ramified_scd._create_attribute_link(prefix+assoc_name, actioncode_modelref, "condition", optional=True)
|
||||||
|
|
||||||
already_ramified.add(assoc_name)
|
already_ramified.add(assoc_name)
|
||||||
|
|
||||||
assocs_to_ramify = ramify_later
|
assocs_to_ramify = ramify_later
|
||||||
|
|
|
||||||
|
|
@ -11,25 +11,34 @@ from services import od
|
||||||
from services.primitives.string_type import String
|
from services.primitives.string_type import String
|
||||||
from services.primitives.actioncode_type import ActionCode
|
from services.primitives.actioncode_type import ActionCode
|
||||||
from services.primitives.integer_type import Integer
|
from services.primitives.integer_type import Integer
|
||||||
from util.eval import exec_then_eval
|
from util.eval import exec_then_eval, simply_exec
|
||||||
|
|
||||||
def process_rule(state, lhs: UUID, rhs: UUID):
|
def preprocess_rule(state, lhs: UUID, rhs: UUID, name_mapping):
|
||||||
bottom = Bottom(state)
|
bottom = Bottom(state)
|
||||||
|
|
||||||
to_delete = { name for name in bottom.read_keys(lhs) if name not in bottom.read_keys(rhs) }
|
to_delete = { name for name in bottom.read_keys(lhs) if name not in bottom.read_keys(rhs) and name in name_mapping }
|
||||||
to_create = { name for name in bottom.read_keys(rhs) if name not in bottom.read_keys(lhs) }
|
to_create = { name for name in bottom.read_keys(rhs) if name not in bottom.read_keys(lhs)
|
||||||
common = { name for name in bottom.read_keys(lhs) if name in bottom.read_keys(rhs) }
|
# extremely dirty:
|
||||||
|
and "GlobalCondition" not in name }
|
||||||
|
common = { name for name in bottom.read_keys(lhs) if name in bottom.read_keys(rhs) and name in name_mapping }
|
||||||
|
|
||||||
# print("to_delete:", to_delete)
|
print("to_delete:", to_delete)
|
||||||
# print("to_create:", to_create)
|
print("to_create:", to_create)
|
||||||
|
|
||||||
return to_delete, to_create, common
|
return to_delete, to_create, common
|
||||||
|
|
||||||
# Rewrite is performed in-place (modifying `host_m`)
|
# Rewrite is performed in-place (modifying `host_m`)
|
||||||
# Also updates the `mapping` in-place, to become RHS -> host
|
# Also updates the `mapping` in-place, to become RHS -> host
|
||||||
def rewrite(state, lhs_m: UUID, rhs_m: UUID, pattern_mm: UUID, name_mapping: dict, host_m: UUID, mm: UUID):
|
def rewrite(state, lhs_m: UUID, rhs_m: UUID, pattern_mm: UUID, name_mapping: dict, host_m: UUID, host_mm: UUID):
|
||||||
bottom = Bottom(state)
|
bottom = Bottom(state)
|
||||||
|
|
||||||
|
orig_name_mapping = dict(name_mapping)
|
||||||
|
|
||||||
|
# function that can be called from within RHS action code
|
||||||
|
def matched_callback(pattern_name: str):
|
||||||
|
host_name = orig_name_mapping[pattern_name]
|
||||||
|
return bottom.read_outgoing_elements(host_m, host_name)[0]
|
||||||
|
|
||||||
scd_metamodel_id = state.read_dict(state.read_root(), "SCD")
|
scd_metamodel_id = state.read_dict(state.read_root(), "SCD")
|
||||||
scd_metamodel = UUID(state.read_value(scd_metamodel_id))
|
scd_metamodel = UUID(state.read_value(scd_metamodel_id))
|
||||||
|
|
||||||
|
|
@ -38,12 +47,12 @@ def rewrite(state, lhs_m: UUID, rhs_m: UUID, pattern_mm: UUID, name_mapping: dic
|
||||||
assoc_type = od.get_scd_mm_assoc_node(bottom)
|
assoc_type = od.get_scd_mm_assoc_node(bottom)
|
||||||
modelref_type = od.get_scd_mm_modelref_node(bottom)
|
modelref_type = od.get_scd_mm_modelref_node(bottom)
|
||||||
|
|
||||||
m_od = od.OD(mm, host_m, bottom.state)
|
m_od = od.OD(host_mm, host_m, bottom.state)
|
||||||
rhs_od = od.OD(pattern_mm, rhs_m, bottom.state)
|
rhs_od = od.OD(pattern_mm, rhs_m, bottom.state)
|
||||||
|
|
||||||
to_delete, to_create, common = process_rule(state, lhs_m, rhs_m)
|
to_delete, to_create, common = preprocess_rule(state, lhs_m, rhs_m, orig_name_mapping)
|
||||||
|
|
||||||
odapi = ODAPI(state, host_m, mm)
|
odapi = ODAPI(state, host_m, host_mm)
|
||||||
|
|
||||||
# Perform deletions
|
# Perform deletions
|
||||||
for pattern_name_to_delete in to_delete:
|
for pattern_name_to_delete in to_delete:
|
||||||
|
|
@ -89,7 +98,7 @@ def rewrite(state, lhs_m: UUID, rhs_m: UUID, pattern_mm: UUID, name_mapping: dic
|
||||||
# print(' -> postpone (is link)')
|
# print(' -> postpone (is link)')
|
||||||
edges_to_create.append((pattern_name_to_create, rhs_el_to_create, original_type, 'link', model_el_name_to_create))
|
edges_to_create.append((pattern_name_to_create, rhs_el_to_create, original_type, 'link', model_el_name_to_create))
|
||||||
else:
|
else:
|
||||||
original_type_name = od.get_object_name(bottom, mm, original_type)
|
original_type_name = od.get_object_name(bottom, host_mm, original_type)
|
||||||
print(" -> warning: don't know about", original_type_name)
|
print(" -> warning: don't know about", original_type_name)
|
||||||
else:
|
else:
|
||||||
# print(" -> no original (un-RAMified) type")
|
# print(" -> no original (un-RAMified) type")
|
||||||
|
|
@ -100,7 +109,11 @@ def rewrite(state, lhs_m: UUID, rhs_m: UUID, pattern_mm: UUID, name_mapping: dic
|
||||||
# Assume the string is a Python expression to evaluate
|
# Assume the string is a Python expression to evaluate
|
||||||
python_expr = ActionCode(UUID(bottom.read_value(rhs_el_to_create)), bottom.state).read()
|
python_expr = ActionCode(UUID(bottom.read_value(rhs_el_to_create)), bottom.state).read()
|
||||||
|
|
||||||
result = exec_then_eval(python_expr, _globals=bind_api(odapi))
|
result = exec_then_eval(python_expr, _globals={
|
||||||
|
**bind_api(odapi),
|
||||||
|
'matched': matched_callback,
|
||||||
|
})
|
||||||
|
|
||||||
# Write the result into the host model.
|
# Write the result into the host model.
|
||||||
# This will be the *value* of an attribute. The attribute-link (connecting an object to the attribute) will be created as an edge later.
|
# This will be the *value* of an attribute. The attribute-link (connecting an object to the attribute) will be created as an edge later.
|
||||||
if isinstance(result, int):
|
if isinstance(result, int):
|
||||||
|
|
@ -109,7 +122,8 @@ def rewrite(state, lhs_m: UUID, rhs_m: UUID, pattern_mm: UUID, name_mapping: dic
|
||||||
m_od.create_string_value(model_el_name_to_create, result)
|
m_od.create_string_value(model_el_name_to_create, result)
|
||||||
name_mapping[pattern_name_to_create] = model_el_name_to_create
|
name_mapping[pattern_name_to_create] = model_el_name_to_create
|
||||||
else:
|
else:
|
||||||
raise Exception(f"RHS element '{pattern_name_to_create}' needs to be created in host, but has no un-RAMified type, and I don't know what to do with it. It's type is '{type_name}'")
|
msg = f"RHS element '{pattern_name_to_create}' needs to be created in host, but has no un-RAMified type, and I don't know what to do with it. It's type is '{type_name}'"
|
||||||
|
raise Exception(msg)
|
||||||
|
|
||||||
# print("create edges....")
|
# print("create edges....")
|
||||||
for pattern_name_to_create, rhs_el_to_create, original_type, original_type_name, model_el_name_to_create in edges_to_create:
|
for pattern_name_to_create, rhs_el_to_create, original_type, original_type_name, model_el_name_to_create in edges_to_create:
|
||||||
|
|
@ -130,7 +144,7 @@ def rewrite(state, lhs_m: UUID, rhs_m: UUID, pattern_mm: UUID, name_mapping: dic
|
||||||
tgt = bottom.read_edge_target(rhs_el_to_create)
|
tgt = bottom.read_edge_target(rhs_el_to_create)
|
||||||
tgt_name = od.get_object_name(bottom, rhs_m, tgt)
|
tgt_name = od.get_object_name(bottom, rhs_m, tgt)
|
||||||
obj_name = name_mapping[src_name] # name of object in host graph to create slot for
|
obj_name = name_mapping[src_name] # name of object in host graph to create slot for
|
||||||
attr_name = od.get_object_name(bottom, mm, original_type)
|
attr_name = od.get_object_name(bottom, host_mm, original_type)
|
||||||
m_od.create_link(model_el_name_to_create, attr_name, obj_name, name_mapping[tgt_name])
|
m_od.create_link(model_el_name_to_create, attr_name, obj_name, name_mapping[tgt_name])
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -146,7 +160,7 @@ def rewrite(state, lhs_m: UUID, rhs_m: UUID, pattern_mm: UUID, name_mapping: dic
|
||||||
# nothing to do
|
# nothing to do
|
||||||
pass
|
pass
|
||||||
elif od.is_typed_by(bottom, host_type, assoc_type):
|
elif od.is_typed_by(bottom, host_type, assoc_type):
|
||||||
print(' -> is association')
|
# print(' -> is association')
|
||||||
# nothing to do
|
# nothing to do
|
||||||
pass
|
pass
|
||||||
elif od.is_typed_by(bottom, host_type, attr_link_type):
|
elif od.is_typed_by(bottom, host_type, attr_link_type):
|
||||||
|
|
@ -154,12 +168,15 @@ def rewrite(state, lhs_m: UUID, rhs_m: UUID, pattern_mm: UUID, name_mapping: dic
|
||||||
# nothing to do
|
# nothing to do
|
||||||
pass
|
pass
|
||||||
elif od.is_typed_by(bottom, host_type, modelref_type):
|
elif od.is_typed_by(bottom, host_type, modelref_type):
|
||||||
print(' -> is modelref')
|
# print(' -> is modelref')
|
||||||
old_value, _ = od.read_primitive_value(bottom, host_el, mm)
|
old_value, _ = od.read_primitive_value(bottom, host_el, host_mm)
|
||||||
rhs_el, = bottom.read_outgoing_elements(rhs_m, pattern_el_name)
|
rhs_el, = bottom.read_outgoing_elements(rhs_m, pattern_el_name)
|
||||||
python_expr, _ = od.read_primitive_value(bottom, rhs_el, pattern_mm)
|
python_expr, _ = od.read_primitive_value(bottom, rhs_el, pattern_mm)
|
||||||
result = exec_then_eval(python_expr,
|
result = exec_then_eval(python_expr,
|
||||||
_globals=bind_api(odapi),
|
_globals={
|
||||||
|
**bind_api(odapi),
|
||||||
|
'matched': matched_callback,
|
||||||
|
},
|
||||||
_locals={'this': host_el})
|
_locals={'this': host_el})
|
||||||
# print('eval result=', result)
|
# print('eval result=', result)
|
||||||
if isinstance(result, int):
|
if isinstance(result, int):
|
||||||
|
|
@ -172,3 +189,11 @@ def rewrite(state, lhs_m: UUID, rhs_m: UUID, pattern_mm: UUID, name_mapping: dic
|
||||||
msg = f"Don't know what to do with element '{pattern_el_name}'->'{host_el_name}' of type ({host_type})"
|
msg = f"Don't know what to do with element '{pattern_el_name}'->'{host_el_name}' of type ({host_type})"
|
||||||
# print(msg)
|
# print(msg)
|
||||||
raise Exception(msg)
|
raise Exception(msg)
|
||||||
|
|
||||||
|
rhs_odapi = ODAPI(state, rhs_m, pattern_mm)
|
||||||
|
for cond_name, cond in rhs_odapi.get_all_instances("GlobalCondition"):
|
||||||
|
python_code = rhs_odapi.get_slot_value(cond, "condition")
|
||||||
|
simply_exec(python_code, _globals={
|
||||||
|
**bind_api(odapi),
|
||||||
|
'matched': matched_callback,
|
||||||
|
})
|
||||||
|
|
|
||||||
10
util/eval.py
10
util/eval.py
|
|
@ -6,8 +6,16 @@ def exec_then_eval(code, _globals={}, _locals={}):
|
||||||
# assumes last node is an expression
|
# assumes last node is an expression
|
||||||
last = ast.Expression(block.body.pop().value)
|
last = ast.Expression(block.body.pop().value)
|
||||||
extended_globals = {
|
extended_globals = {
|
||||||
'__builtins__': {'isinstance': isinstance, 'print': print, 'int': int, 'float': float, 'bool': bool, 'str': str, 'tuple': tuple, 'len': len, 'set': set, 'dict': dict },
|
'__builtins__': {'isinstance': isinstance, 'print': print, 'int': int, 'float': float, 'bool': bool, 'str': str, 'tuple': tuple, 'len': len, 'set': set, 'dict': dict, 'eval': eval },
|
||||||
**_globals,
|
**_globals,
|
||||||
}
|
}
|
||||||
exec(compile(block, '<string>', mode='exec'), extended_globals, _locals)
|
exec(compile(block, '<string>', mode='exec'), extended_globals, _locals)
|
||||||
return eval(compile(last, '<string>', mode='eval'), extended_globals, _locals)
|
return eval(compile(last, '<string>', mode='eval'), extended_globals, _locals)
|
||||||
|
|
||||||
|
def simply_exec(code, _globals={}, _locals={}):
|
||||||
|
block = ast.parse(code, mode='exec')
|
||||||
|
extended_globals = {
|
||||||
|
'__builtins__': {'isinstance': isinstance, 'print': print, 'int': int, 'float': float, 'bool': bool, 'str': str, 'tuple': tuple, 'len': len, 'set': set, 'dict': dict, 'eval': eval },
|
||||||
|
**_globals,
|
||||||
|
}
|
||||||
|
exec(compile(block, '<string>', mode='exec'), extended_globals, _locals)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue