Skip to main content


Eclipse Community Forums
Forum Search:

Search      Help    Register    Login    Home
Home » Modeling » ATL » OCL closure operation
OCL closure operation [message #1852704] Wed, 01 June 2022 14:22 Go to next message
Yves BERNARD is currently offline Yves BERNARDFriend
Messages: 173
Registered: July 2014
Senior Member
Hello,
I didn't succeed in using the OCL closure operation in my ATL rule. Do I do something wrong or is it simply not implemented by SimpleOCL?

Thanks.


Yves
Re: OCL closure operation [message #1852706 is a reply to message #1852704] Wed, 01 June 2022 16:36 Go to previous messageGo to next message
Dennis Wagelaar is currently offline Dennis WagelaarFriend
Messages: 606
Registered: September 2012
Location: Belgium
Senior Member

Not sure what you're trying to do: SimpleOCL is a separate language/plugin that compiles to EMFTVM bytecode. You can import SimpleOCL modules in ATL, and invoke the helpers you defined in SimpleOCL, including those that take lambda parameters. See also https://github.com/dwagelaar/simpleocl/blob/master/org.eclipselabs.simpleocl.tests/test-data/iterators.simpleocl

Cheers,
Dennis
Re: OCL closure operation [message #1852712 is a reply to message #1852706] Thu, 02 June 2022 06:02 Go to previous messageGo to next message
Yves BERNARD is currently offline Yves BERNARDFriend
Messages: 173
Registered: July 2014
Senior Member
Hello Dennis,
Basically I just want to use the standard OCL "closure" operation in ATL rules. I was speaking about SimpleOCL because my understanding was that the OCL engine used by ATL is SimpleOCL. That said, I was not aware of this ability to import SimpleOCL modules. I suppose it relies on the same "uses" directive than that for importing ATL modules?


Yves
Re: OCL closure operation [message #1852716 is a reply to message #1852712] Thu, 02 June 2022 06:54 Go to previous messageGo to next message
Dennis Wagelaar is currently offline Dennis WagelaarFriend
Messages: 606
Registered: September 2012
Location: Belgium
Senior Member

Yves BERNARD wrote on Thu, 02 June 2022 08:02
Hello Dennis,
I suppose it relies on the same "uses" directive than that for importing ATL modules?

Yes, it does!

Note that ATL/EMFTVM itself is based on OCL 2.2: see also https://wiki.eclipse.org/ATL/EMFTVM#Additional_API


Cheers,
Dennis
Re: OCL closure operation [message #1852733 is a reply to message #1852716] Thu, 02 June 2022 12:13 Go to previous messageGo to next message
Dennis Wagelaar is currently offline Dennis WagelaarFriend
Messages: 606
Registered: September 2012
Location: Belgium
Senior Member

I've added an example here: https://github.com/dwagelaar/atl.examples/tree/main/ATLWithSimpleOCL

Cheers,
Dennis
Re: OCL closure operation [message #1852737 is a reply to message #1852733] Thu, 02 June 2022 13:08 Go to previous messageGo to next message
Yves BERNARD is currently offline Yves BERNARDFriend
Messages: 173
Registered: July 2014
Senior Member
This is very helpful.

Thank you Dennis ! :-)


Yves
Re: OCL closure operation [message #1854509 is a reply to message #1852704] Tue, 23 August 2022 07:11 Go to previous messageGo to next message
Yves BERNARD is currently offline Yves BERNARDFriend
Messages: 173
Registered: July 2014
Senior Member
Sorry for the late follow up on this, I had to switch to another task. I'm back on this topic now.

Actually the sample example does not run on my computer. Instead, I get the following error message:

org.eclipse.m2m.atl.emftvm.util.VMException: Error during module loading: Error during module loading: Module Helpers not found

I confirm that simpleOCL is installed (from Eclipse "Installed Software" dialog):
SimpleOCL 2.1.0.202001021418 org.eclipselabs.simpleocl.feature.feature.group EclipseLabs


Yves

[Updated on: Tue, 23 August 2022 07:11]

Report message to a moderator

Re: OCL closure operation [message #1854550 is a reply to message #1854509] Wed, 24 August 2022 20:11 Go to previous messageGo to next message
Dennis Wagelaar is currently offline Dennis WagelaarFriend
Messages: 606
Registered: September 2012
Location: Belgium
Senior Member

The SimpleOCL builder is a bit primitive, and does not kick in without editing the .simpleocl file first. If the Helpers.emftvm file is missing, you can try to edit the Helpers.simpleocl file a bit, and save the file to trigger the compiler as a workaround.

Cheers,
Dennis
Re: OCL closure operation [message #1854768 is a reply to message #1854550] Fri, 09 September 2022 06:17 Go to previous messageGo to next message
Yves BERNARD is currently offline Yves BERNARDFriend
Messages: 173
Registered: July 2014
Senior Member
Ok! Now it works! :-)

Thank you Dennis.


Yves
Re: OCL closure operation [message #1866006 is a reply to message #1854768] Fri, 17 May 2024 10:16 Go to previous messageGo to next message
Yves BERNARD is currently offline Yves BERNARDFriend
Messages: 173
Registered: July 2014
Senior Member
Let's come back on this thread that had drift a bit from its initial topic (i.e. the implementation of the transitive closure) toward the question of including SimpleOCL utilities in our ATL code.

So I'm trying to implement the OCL "closure" operation, using the "Lambda" type that SimpleOCL provide. I wrote the code below:
module Extensions;

context Bag(OclAny) def : closure(f : Lambda(OclAny): OclAny) : Bag(OclAny) = 
	let buffer: Bag(OclAny) = self->iterate(e; acc: Bag(OclAny) = Bag{} |
		let result: OclAny = f(e) in 
		if result.oclIsUndefined() then 
			acc
		else
			if result.oclIsKindOf(Bag(OclAny)) then
				acc->union(result.oclAsType(Bag(OclAny)))
			else
				acc->including(result)
			endif
		endif
		) in
			
	if buffer->isEmpty() then
		self
	else
		self->union(buffer->closure(f))
	endif;

But it generates those two errors on the 4th line:
Syntax error on token "-> (->)", ";" expected
Syntax error on token "-> (->)", "in" expected


However the syntax looks correct to me. Do I miss something?




Yves
Re: OCL closure operation [message #1866033 is a reply to message #1866006] Sun, 19 May 2024 18:40 Go to previous messageGo to next message
Dennis Wagelaar is currently offline Dennis WagelaarFriend
Messages: 606
Registered: September 2012
Location: Belgium
Senior Member

Your code is correct, but there seems to be a bug in the SimpleOCL parser that prevents you from passing primitive types as function parameters, e.g.
result.oclIsKindOf(Bag(OclAny))
.


Cheers,
Dennis
Re: OCL closure operation [message #1866090 is a reply to message #1866033] Tue, 21 May 2024 15:55 Go to previous messageGo to next message
Yves BERNARD is currently offline Yves BERNARDFriend
Messages: 173
Registered: July 2014
Senior Member
Hum... The point is that I need a way to test whether the result is a collection or a not. Any idea?

Yves
Re: OCL closure operation [message #1866093 is a reply to message #1866090] Tue, 21 May 2024 16:07 Go to previous messageGo to next message
Yves BERNARD is currently offline Yves BERNARDFriend
Messages: 173
Registered: July 2014
Senior Member
I've modified the code so that I assume that the lambda function always returns a collection
context Bag(OclAny) def: closure(f : Lambda(OclAny): Bag(OclAny)) : Bag(OclAny) = 
	let buffer: Bag(OclAny) = self->iterate(e: OclAny; acc: Bag(OclAny) = Bag{} |
		let result: Bag(OclAny) = f(e) in 
		if result.oclIsUndefined() then 
			acc
		else
			acc->union(result)
		endif
		) in
			
	if buffer->isEmpty() then
		self
	else
		self->union(buffer)->union(buffer->closure(f))
	endif;

With this version the errors disappear but I get an exception when I compile it:
org.eclipse.m2m.atl.emftvm.util.VMException: java.lang.NoSuchFieldException: Field OCL!IterateExp::source not found
at OCL!OclExpression::getUpD() : Sequence#59(platform:/plugin/org.eclipselabs.simpleocl.resource.simpleocl/transformations/SimpleOCLWFR.atl#[117:6-117:22])
	Local variables: [self: OCL!OclExpression = 334aa4df:OCL!LetExp, container: OCL!Element = 6362ba6c:OCL!IterateExp]
at OCL!VariableDeclaration::getDeclarations() : Sequence#12(platform:/plugin/org.eclipselabs.simpleocl.resource.simpleocl/transformations/SimpleOCLWFR.atl#[53:4-53:22])
	Local variables: [self: OCL!VariableDeclaration = 21f488d4:OCL!LocalVariable, container: Object = 334aa4df:OCL!LetExp]
at rule VariableNameIsUniqueInContainer@matcher#1(platform:/plugin/org.eclipselabs.simpleocl.resource.simpleocl/transformations/SimpleOCLWFR.atl#[359:4-359:23])
	Local variables: [s: OCL!VariableDeclaration = 21f488d4:OCL!LocalVariable]
at static EMFTVM!ExecEnv::main() : Object(platform:/plugin/org.eclipselabs.simpleocl.resource.simpleocl/transformations/SimpleOCLWFR.atl)
	Local variables: []
	at org.eclipse.m2m.atl.emftvm.impl.CodeBlockImpl.internalExecute(CodeBlockImpl.java:987)
	at org.eclipse.m2m.atl.emftvm.impl.CodeBlockImpl.execute(CodeBlockImpl.java:726)
	at org.eclipse.m2m.atl.emftvm.impl.CodeBlockImpl.invoke(CodeBlockImpl.java:2305)
	at org.eclipse.m2m.atl.emftvm.impl.CodeBlockImpl.internalExecute(CodeBlockImpl.java:854)
	at org.eclipse.m2m.atl.emftvm.impl.CodeBlockImpl.execute(CodeBlockImpl.java:726)
	at org.eclipse.m2m.atl.emftvm.impl.CodeBlockImpl.invoke(CodeBlockImpl.java:2305)
	at org.eclipse.m2m.atl.emftvm.impl.CodeBlockImpl.internalExecute(CodeBlockImpl.java:854)
	at org.eclipse.m2m.atl.emftvm.impl.CodeBlockImpl.execute(CodeBlockImpl.java:726)
	at org.eclipse.m2m.atl.emftvm.impl.RuleImpl$WithMatcherCbState.matchFor(RuleImpl.java:1144)
	at org.eclipse.m2m.atl.emftvm.impl.RuleImpl$UniqueOnState.matchFor(RuleImpl.java:306)
	at org.eclipse.m2m.atl.emftvm.impl.RuleImpl.matchFor(RuleImpl.java:3121)
	at org.eclipse.m2m.atl.emftvm.impl.RuleImpl.matchFor(RuleImpl.java:3117)
	at org.eclipse.m2m.atl.emftvm.impl.RuleImpl.matchFor(RuleImpl.java:3061)
	at org.eclipse.m2m.atl.emftvm.impl.RuleImpl.matchFor(RuleImpl.java:3055)
	at org.eclipse.m2m.atl.emftvm.impl.RuleImpl.access$0(RuleImpl.java:3038)
	at org.eclipse.m2m.atl.emftvm.impl.RuleImpl$WithoutSuperRulesState.match(RuleImpl.java:486)
	at org.eclipse.m2m.atl.emftvm.impl.RuleImpl$LeafState.matchSingle(RuleImpl.java:831)
	at org.eclipse.m2m.atl.emftvm.impl.RuleImpl$AutomaticSingleState.matchSingle(RuleImpl.java:760)
	at org.eclipse.m2m.atl.emftvm.impl.RuleImpl.matchSingle(RuleImpl.java:2126)
	at org.eclipse.m2m.atl.emftvm.impl.ExecEnvImpl.matchAllSingle(ExecEnvImpl.java:2247)
	at org.eclipse.m2m.atl.emftvm.impl.ExecEnvImpl.run(ExecEnvImpl.java:2108)
	at org.eclipselabs.simpleocl.resource.simpleocl.mopp.SimpleoclBuilder.build(SimpleoclBuilder.java:144)
	at org.eclipselabs.simpleocl.resource.simpleocl.mopp.SimpleoclBuilderAdapter.buildResource(SimpleoclBuilderAdapter.java:104)
	at org.eclipselabs.simpleocl.resource.simpleocl.mopp.SimpleoclBuilderAdapter$1.visit(SimpleoclBuilderAdapter.java:81)
	at org.eclipse.core.internal.events.ResourceDelta.accept(ResourceDelta.java:77)
	at org.eclipse.core.internal.events.ResourceDelta.accept(ResourceDelta.java:87)
	at org.eclipse.core.internal.events.ResourceDelta.accept(ResourceDelta.java:61)
	at org.eclipselabs.simpleocl.resource.simpleocl.mopp.SimpleoclBuilderAdapter.build(SimpleoclBuilderAdapter.java:76)
	at org.eclipselabs.simpleocl.resource.simpleocl.mopp.SimpleoclBuilderAdapter.build(SimpleoclBuilderAdapter.java:58)
	at org.eclipse.core.internal.events.BuildManager$2.run(BuildManager.java:1079)
	at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:47)
	at org.eclipse.core.internal.events.BuildManager.basicBuild(BuildManager.java:296)
	at org.eclipse.core.internal.events.BuildManager.basicBuild(BuildManager.java:352)
	at org.eclipse.core.internal.events.BuildManager$1.run(BuildManager.java:441)
	at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:47)
	at org.eclipse.core.internal.events.BuildManager.basicBuild(BuildManager.java:444)
	at org.eclipse.core.internal.events.BuildManager.basicBuildLoop(BuildManager.java:555)
	at org.eclipse.core.internal.events.BuildManager.basicBuildLoop(BuildManager.java:503)
	at org.eclipse.core.internal.events.BuildManager.build(BuildManager.java:585)
	at org.eclipse.core.internal.resources.Workspace.buildInternal(Workspace.java:594)
	at org.eclipse.core.internal.resources.Workspace.build(Workspace.java:490)
	at org.eclipse.ui.actions.BuildAction$1.runInWorkspace(BuildAction.java:291)
	at org.eclipse.core.internal.resources.InternalWorkspaceJob.run(InternalWorkspaceJob.java:43)
	at org.eclipse.core.internal.jobs.Worker.run(Worker.java:63)
Caused by: java.lang.NoSuchFieldException: Field OCL!IterateExp::source not found
	at org.eclipse.m2m.atl.emftvm.impl.CodeBlockImpl.get(CodeBlockImpl.java:1932)
	at org.eclipse.m2m.atl.emftvm.impl.CodeBlockImpl.internalExecute(CodeBlockImpl.java:781)
	... 43 more


Yves
Re: OCL closure operation [message #1866126 is a reply to message #1866093] Tue, 21 May 2024 20:14 Go to previous messageGo to next message
Dennis Wagelaar is currently offline Dennis WagelaarFriend
Messages: 606
Registered: September 2012
Location: Belgium
Senior Member

Indeed, I bumped into that one as well: there's another bug in the SimpleOCL compiler for ->iterate() expressions :-(.

Luckily, you can make it work without using iterate in this case (if I understood your code correctly):
module Extensions2;

context Bag(OclAny) def : closure(f : Lambda(OclAny): OclAny) : Bag(OclAny) = 
	let buffer: Bag(OclAny) = self->collect(e | f(e))->reject(e | e.oclIsUndefined())->flatten() in
	if buffer->isEmpty() then
		self
	else
		self->union(buffer->closure(f))
	endif;

context Integer def : minusOne : Integer =
	if self = 0 then
		OclUndefined
	else
		self - 1
	endif;

context Integer def : minusOnes : Bag(Integer) =
	if self = 0 then
		OclUndefined
	else
		let this : Integer = self - 1 in
		Bag{this, this.minusOnes}->reject(e | e.oclIsUndefined())->flatten()
	endif;

static def : test() : OclAny =
	Bag{5,4,3,2,1}->closure(e | e.minusOne);

static def : test2() : OclAny =
	Bag{5,4,3,2,1}->closure(e | e.minusOnes);

static def : main() : OclAny =
	Sequence{Env::test(), Env::test2()}.debug();


Of course, this would not just run without triggering yet another bug, this time in the JIT compiler for EMFTVM when trying to compile closure invocations (unique to SimpleOCL) :-((. If you disable the JIT in the launch config, this will run...


Cheers,
Dennis
Re: OCL closure operation [message #1866129 is a reply to message #1866126] Tue, 21 May 2024 20:31 Go to previous messageGo to next message
Dennis Wagelaar is currently offline Dennis WagelaarFriend
Messages: 606
Registered: September 2012
Location: Belgium
Senior Member

I started with logging the most prudent bug, which is in ATL/EMFTVM itself: https://github.com/eclipse-atl/atl/issues/1

Cheers,
Dennis
Re: OCL closure operation [message #1866174 is a reply to message #1866129] Wed, 22 May 2024 13:11 Go to previous messageGo to next message
Yves BERNARD is currently offline Yves BERNARDFriend
Messages: 173
Registered: July 2014
Senior Member
Hi Dennis,
Thank you for your interest in this topic.

Unfortunately, there is no way around using iterate in the specific case where I want to use this closure operation. Indeed, the input collection is actually a Bag of Sequences and it is expected to get also a Bag of Sequences as a result. The point is that the flatten() operation will "smatch" everything on not only the first level, conversely to what the iterate can do.
However, based on your idea, I found a way to use the iterate without activating the bug. Unfortunately, it works in my specific case only, or more precisely in cases where the expected out put is a collection of collections, but not with a simple collection. Here is the code for those who are interested:

context Bag(OclAny) def: closure(f : Lambda(OclAny): Bag(OclAny)) : Bag(OclAny) = 
	let buffer1: Bag(OclAny) = self->collect(e | f(e))->reject(e | e.oclIsUndefined()) in 

	let buffer2: Bag(OclAny) = buffer1->iterate(e: OclAny; acc: Bag(OclAny) = Bag{} | acc->union(e->asBag())) in 
			
	if buffer2->isEmpty() then
		self
	else
		self->union(buffer2->closure(f))
	endif;


In order to make it fully generic (i.e. usable with any kind of collection), I need a way to test whether an element (i.e. OclAny) is a collection or not, but I didn't find how to do that so far.


Yves
Re: OCL closure operation [message #1867119 is a reply to message #1866174] Sun, 16 June 2024 16:22 Go to previous messageGo to next message
Dennis Wagelaar is currently offline Dennis WagelaarFriend
Messages: 606
Registered: September 2012
Location: Belgium
Senior Member

Hmm, perhaps this could work:
context OclAny def : isCollection : Boolean = false;
context Collection def : isCollection : Boolean = true;


Cheers,
Dennis
Re: OCL closure operation [message #1867302 is a reply to message #1867119] Fri, 21 June 2024 06:30 Go to previous message
Yves BERNARD is currently offline Yves BERNARDFriend
Messages: 173
Registered: July 2014
Senior Member
So simple ... Shame on me! :-D

Thanks Dennis!


Yves
Previous Topic:Working with profiles as metamodels
Next Topic:Listing inputs models
Goto Forum:
  


Current Time: Mon Sep 23 08:59:31 GMT 2024

Powered by FUDForum. Page generated in 0.05064 seconds
.:: Contact :: Home ::.

Powered by: FUDforum 3.0.2.
Copyright ©2001-2010 FUDforum Bulletin Board Software

Back to the top