Integration with Xbase - reference to model elements and java-like methodscalls [message #1784965] |
Fri, 06 April 2018 06:55  |
Eclipse User |
|
|
|
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 18:43   |
Eclipse User |
|
|
|
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: Tue, 17 April 2018 23:55] by Moderator
|
|
|
|
Re: Integration with Xbase - reference to model elements and java-like methodscalls [message #1785652 is a reply to message #1785650] |
Wed, 18 April 2018 01:34   |
Eclipse User |
|
|
|
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 01:36] by Moderator
|
|
|
|
|
|
Powered by
FUDForum. Page generated in 0.08328 seconds