Integration with Xbase - reference to model elements and java-like methodscalls [message #1784965] |
Fri, 06 April 2018 10:55 |
Fabian G. Messages: 60 Registered: May 2010 Location: Christchurch (NZ) |
Member |
|
|
HI,
I tried to create a simple language (for pedagogical purposes) integrated with Xbase. I took some inspiration from the domain model and wrote this grammar
grammar nz.ac.canterbury.seng441.Component with org.eclipse.xtext.xbase.Xbase
generate component "http://www.ac.nz/canterbury/seng441/Component"
ComponentModel:
importSection=XImportSection?
elements += AbstractElement+;
AbstractElement:
PackageDeclaration | Component;
PackageDeclaration:
'package' name=QualifiedName '{'
elements+=AbstractElement+
'}';
Component:
'component' name=ValidID ('extends' superType=JvmTypeReference)? '{'
content += Content*
'}';
Content:
Declaration | Provide | Require ;
Declaration:
'var' type=JvmTypeReference name=ValidID ';';
Provide:
'provides' name=ValidID ('('(parameters+=FullJvmFormalParameter("," parameters+=FullJvmFormalParameter)*)?')')?
(':' type=JvmTypeReference)?
(isEmpty?=';' | body=XBlockExpression);
Require:
'requires' type=JvmTypeReference '::' method=JAVALIKECALL;
The (partial) inferrer:
@Inject extension JvmTypesBuilder
@Inject extension IQualifiedNameProvider
// working on the component level as we want to create one class per component
def dispatch void infer(Component component, IJvmDeclaredTypeAcceptor acceptor, boolean isPreIndexingPhase) {
acceptor.accept(component.toClass(component.fullyQualifiedName)) [
documentation = component.documentation
if (component.superType !== null) {
superTypes += component.superType.cloneWithProxies
}
// add a default constructor
members += component.toConstructor []
for (content : component.content) {
switch (content) {
Declaration : {
members += content.toField(content.name, content.type)
}
Provide : {
var type = typeRef(Void::TYPE);
if (content.type !== null) {
type = content.type
}
members += content.toMethod(content.name, type) [
content.parameters.forEach[parameter | parameters += parameter.toParameter(parameter.name, parameter.parameterType)]
if (content.isIsEmpty) {
body = '''/* TODO implement me */'''
} else {
body = content.body
}
]
}
}
}
]
}
I have two questions:
1. I'm not able to reference components that I just declared until they are generated at save time. Do I have to add some configuration somewhere to be able to reference them on the fly without saving the model (and trigger the java code generation)? I tried to replicate the domainmodel example, but it seems I missed something. Strangely, I can call the methods declared as provides inside XblockExpression before saving (but only using their simple names, but it should just be a matter of qualifiedname, I beleve).
2. As you can see, I'd like to simply create provides clauses where a method is declared (possibly with its implementation, this is working) and requires clauses that must refer to them. In the XBlockExpression part, I can call the method by its name, but I can't figure out what I have to put in place of the JAVALIKECALL. I've seen/tried things with XFeatures, or alike, but with no success.
Any help will be appreciated,
Thanks,
Cheers,
Fabian
|
|
|
Re: Integration with Xbase - reference to model elements and java-like methodscalls [message #1785647 is a reply to message #1784965] |
Tue, 17 April 2018 22:43 |
Fabian G. Messages: 60 Registered: May 2010 Location: Christchurch (NZ) |
Member |
|
|
Hi,
I found some time to progress on this, but I still have one strange behaviour. I combined some answers from previous posts to have something like this for the Require rule (to be scoped correctly, thanks to https://www.eclipse.org/forums/index.php/m/1723598/#msg_1723598
Require:
'requires' declaration=[Declaration] '::' method=XRestrictedFeatureCall;
XRestrictedFeatureCall returns xbase::XExpression:
{xbase::XFeatureCall}
('<' typeArguments+=JvmArgumentTypeReference (',' typeArguments+=JvmArgumentTypeReference)* '>')?
feature=[jvmTypes::JvmIdentifiableElement|IdOrSuper]
(=>explicitOperationCall?='('
(
featureCallArguments+=XShortClosure
| featureCallArguments+=XExpression (',' featureCallArguments+=XExpression)*
)?
')');
The relevant inferrer part:
Require : {
members += content.toMethod("require" + content.declaration.type.simpleName, inferredType) [
visibility = JvmVisibility.PUBLIC
parameters += content.toParameter("it", content.declaration.type.cloneWithProxies) => []
body = content.method
]
}
The strange thing is that whatever I use instead of "it" as the parameter name, makes the generated plugin issue an error saying the method is undefined. But with that "magic it", it works. An idea? The strange stuff is that my scoping works and it proposes only the methods defined inside the component.
A sample model
component First {
provides first(int param1);
}
component Second {
First myFirst
requires myFirst::first(10) // got "the method first(int) is undefined if the inferrer uses any other parameter name than "it"
}
[Updated on: Wed, 18 April 2018 03:55] Report message to a moderator
|
|
|
Re: Integration with Xbase - reference to model elements and java-like methodscalls [message #1785650 is a reply to message #1785647] |
Wed, 18 April 2018 04:43 |
|
From your inferrer I don't see how First myFirst in Second gets translated.
You automatically get all methods on parameters callled it offered so that you can call them without explicit Naming the parameter
It.xxxx or xxxxx would be both possible (not in your case since you just want to have restricted feature calls) so at your place there is only xxxxx
You can customize this behaviour in the feature scopes class (or by inferrring an additional @Extension annotation to the parameter ( that needs to be tested because I am not 1000 percent sure)
Twitter : @chrdietrich
Blog : https://www.dietrich-it.de
|
|
|
Re: Integration with Xbase - reference to model elements and java-like methodscalls [message #1785652 is a reply to message #1785650] |
Wed, 18 April 2018 05:34 |
Fabian G. Messages: 60 Registered: May 2010 Location: Christchurch (NZ) |
Member |
|
|
I finally got rid of the "declaration" part in the grammar (which was actually annoying and redundant) and I don't quite understand your answer, maybe I wasn't clear enough. I put all the simplified code:
ComponentModel:
// magic import resolution mechanism (works out of the box)
importSection=XImportSection?
elements += AbstractElement+;
AbstractElement:
PackageDeclaration | Component;
PackageDeclaration:
'package' name=QualifiedName '{'
elements+=AbstractElement+
'}';
Component:
'component' name=ValidID ('extends' superType=JvmTypeReference)? '{'
content += Content*
'}';
Content:
Provide | Require ;
Provide:
'provides' name=ValidID ('('(parameters+=FullJvmFormalParameter("," parameters+=FullJvmFormalParameter)*)?')')?
(':' type=JvmTypeReference)?
(isEmpty?=';' | body=XBlockExpression);
Require:
'requires' declaration=JvmTypeReference '::' method=XRestrictedFeatureCall ";";
XRestrictedFeatureCall returns xbase::XExpression:
{xbase::XFeatureCall}
('<' typeArguments+=JvmArgumentTypeReference (',' typeArguments+=JvmArgumentTypeReference)* '>')?
feature=[jvmTypes::JvmIdentifiableElement|IdOrSuper]
(=>explicitOperationCall?='('
(
featureCallArguments+=XShortClosure
| featureCallArguments+=XExpression (',' featureCallArguments+=XExpression)*
)?
')');
and the complete inferrer (where the "it" parameter in plain string is confusing me in the Require translation code),
def dispatch void infer(Component component, IJvmDeclaredTypeAcceptor acceptor, boolean isPreIndexingPhase) {
acceptor.accept(component.toClass(component.fullyQualifiedName)) [
documentation = component.documentation
if (component.superType !== null) {
superTypes += component.superType.cloneWithProxies
}
members += component.toConstructor []
for (content : component.content) {
switch (content) {
Provide : {
var type = typeRef(Void::TYPE);
if (content.type !== null) {
type = content.type
}
members += content.toMethod(content.name, type) [
content.parameters.forEach[parameter | parameters += parameter.toParameter(parameter.name, parameter.parameterType)]
if (content.isIsEmpty) {
body = '''/* TODO implement me */'''
} else {
body = content.body
}
]
}
Require : {
members += content.toMethod("require" + content.declaration.simpleName, inferredType) [
visibility = JvmVisibility.PUBLIC
// if I use "content.declaration.simpleName.toLowerCase" instead of "it", I got the method is undefined error in the DSL model
// using "it" as parameter here makes the trick => what am I missing to have the freedom on the name of that parameter ?
parameters += content.toParameter("it", content.declaration.cloneWithProxies)
body = content.method
]
}
}
}
]
}
The simplified models looks like
component First {
provides first(int param1);
}
component Second {
requires First::first(10);
}
[Updated on: Wed, 18 April 2018 05:36] Report message to a moderator
|
|
|
|
|
|
Powered by
FUDForum. Page generated in 0.04901 seconds