epsilon icon indicating copy to clipboard operation
epsilon copied to clipboard

[ETL] Native support for 1:m..n rules

Open arcanefoam opened this issue 4 months ago • 9 comments

ETL natively supports 1:n transformation rules. However, in some cases we can need 1:m..n rules, which I call runtime multiplicity, in which the actual number of output elements can not be determined until runtime.

An example is a port -> implements relation between Ports and Interfaces, where the interface needs to be transformed m times, where m is the number of ports that use the interface. Dimitris has provided an alternative:

rule OperationInInterface
	transform sOp:SysML!Operation
	to runbls: Sequence {
	
	var aClntSrvOp = sOp.equivalents().selectOne(eq | ...));
	for (p in portsUsingTheInterface(sOp.owner)) {
		var aSrvComSpec = new AUTOSAR!ServerComSpec();
		aSrvComSpec.`operation` = aClntSrvOp;
		p.equivalent().providedComSpec.add(aSrvComSpec);
		runbls.add(aSrvComSpec);	
	}
	
}

This workaround works, but using the equivalents operation is uselss unless you define the rule as lazy. The execution time of the lazy algorithm is much worse than the base one.

My prosal is to support 1:m..n rules natively. The porposed syntax change adds a foreach keyword in which you specify a sequence statement. The size of the returned sequence (m) will be be used to instantiate m Tuples, each containg on set of target parameters.

CST Addiiton

(@abstract)?
(@lazy)?
(@primary)?
rule <name>
    transform <sourceParameterName>:<sourceParameterType>
    to (<targetParameterName>:<targetParameterType>
        ,<targetParameterName>:<targetParameterType>)*
	  (foreach <sequence>)?
    (extends <ruleName> (, <ruleName>*)? {

    (guard (:expression)|({statementBlock}))?

    statement+
	
}

With this the previous example would be

rule OperationInInterface
	transform sOp:SysML!Operation
	to runbls: aSrvComSpec: AUTOSAR!ServerComSpec()
			foreach portsUsingTheInterface(sOp.owner)
	{
	
	var aClntSrvOp = sOp.equivalents().selectOne(eq | ...));
	
	aSrvComSpec.`operation` = aClntSrvOp;
	loopVar.equivalent().providedComSpec.add(aSrvComSpec);
	runbls.add(aSrvComSpec);	
	
	
}

Note that the use of the for each keyword would result in a stack variable called loopVar that can be used in the rule.

When using the equivalent(s) operation you would get a List<Tuple<>> . The tuple will contain all the targetPrams + the loopVar.

In the ETL implementation

Since ETL already creates the output elements in a first pass, I think implementing this shuold not be to complex. We would execute the foreach statement and create the list of tuples. When we get to the body we repeat it m times, replacing the stack variables as needed.

For more complex scenarios, the foreach could be a block, instead of a keyword:

rule <name>
    transform <sourceParameterName>:<sourceParameterType>
    to (<targetParameterName>:<targetParameterType>
        ,<targetParameterName>:<targetParameterType>)*
    (foreach (:expression)|({statementBlock}))?

This would allow to breakdown the sequence statement or collect elements from various places.

If something of interest, we can discuss more details and I can work on this (if @agarciadom is not too eager to jump and do it :) ).

arcanefoam avatar Sep 27 '24 21:09 arcanefoam