Implicitly add method call to generated interface. [message #1754650] |
Tue, 21 February 2017 05:15  |
Eclipse User |
|
|
|
Hi,
I'am developing a DSL which relies on Xbase and allows some simple implementations for methods. In my JVMModelInferrer I created a player attribute of type I<RoleName>Player. Thus, I can refer to it without explicit declaration.
However, whenever I invoke a method on this player Attribute I would like to add it to the generated Interface implicitly. Unfortunately, I've no idea how to hook into Xbase inside the JvmModelInferrer in order to retrieve method names and attributes.
CollSpec:
importSection=XImportSection?
module = Module;
Module:
'module' name=QualifiedName
collaboration = Collaboration;
Collaboration:
'collaboration' name=ValidID '{'
(
(roles+=CoordinatorRole) &
(roles+=NonCoordinatorRole+)
)
'}'
;
CoordinatorRole:
'coordinator' 'role' Role
;
NonCoordinatorRole:
'role' Role
;
fragment Role:
name=ValidID '{'
features+=Feature*
'}'
;
...
Feature:
Property | Operation;
Property:
name=ValidID ':' type=JvmTypeReference;
Operation:
(isPlayerQualifierSet?='playable') 'op' name=ValidID
'('(params+=FullJvmFormalParameter
(',' params+=FullJvmFormalParameter)*)?')'
':' type=JvmTypeReference
body=XBlockExpression?;
In the following example I'd like to add the doSomething Method of role B to IBPlayer interface.
collaboration Test {
coordinator role A {
}
role B {
test:String
playable op bar() :String {
player.doSomething(String test)
}
}
}
|
|
|
|
Re: Implicitly add method call to generated interface. [message #1754656 is a reply to message #1754651] |
Tue, 21 February 2017 06:21   |
Eclipse User |
|
|
|
Thanks for your reply!
Christian Dietrich wrote on Tue, 21 February 2017 10:20can you please post the expected java code as well.
For each Role a class and an interface is generated.
The class gets the following method:
public String bar() {
player.performBRoleDoSomething() // something like getPlayer().doSomething() would be preferred actually.
return null;
}
and the interface gets the performBRoleDoSomething() method.
Christian Dietrich wrote on Tue, 21 February 2017 10:20is the interface inferred or is it a existing class?
both, here an excerpt of my Inferer:
def dispatch void infer(NonCoordinatorRole role, IJvmDeclaredTypeAcceptor acceptor, boolean isPreIndexingPhase, CollSpec collSpec){
acceptor.accept(role.toClass('''«collSpec.module.fullyQualifiedName».«role.name»Role''')) [
documentation = role.documentation
if(!isPreIndexingPhase) {
superTypes += AbstractRole.typeRef
var playerRef = typesFactory.createJvmField()
playerRef.type = typeRef('''«collSpec.module.fullyQualifiedName».I«role.name»RolePlayer''')
playerRef.simpleName = 'player'
playerRef.visibility = JvmVisibility.PRIVATE
playerRef.type
members += playerRef
}
for(feature : role.features) {
switch feature {
Property : {
members += feature.toField(feature.name, feature.type)
members += feature.toGetter(feature.name, feature.type)
members += feature.toSetter(feature.name, feature.type)
}
Operation : {
members += feature.toMethod(feature.name, feature.type) [
documentation = feature.documentation
for(p : feature.params) {
parameters += p.toParameter(p.name, p.parameterType)
}
body = feature.body
]
}
}
}
]
}
def void inferPlayerInterface(Role role, IJvmDeclaredTypeAcceptor acceptor, boolean isPreIndexingPhase, CollSpec collSpec) {
acceptor.accept(role.toClass('''«collSpec.module.fullyQualifiedName».I«role.name»RolePlayer''')) [
setInterface = true
for (feature : role.features) {
if (feature instanceof Operation) {
if (feature.isPlayerQualifierSet) {
members += feature.toMethod('''perform«role.name»Role«feature.name.toFirstUpper»''', feature.type) [
documentation = feature.documentation
for(p : feature.params) {
parameters += p.toParameter(p.name, p.parameterType)
}
// body = feature.body
]
}
}
}
]
}
Christian Dietrich wrote on Tue, 21 February 2017 10:20you want to be ablte to call a method that does not exist? so let it drop from the sky?
Actually yes, it shall be dispatched at run time. So it does not drop from the sky but the complementing implementation is provided somewhere else. In the end there should be no fixed player attribute but for the moment this would be ok.
[Updated on: Tue, 21 February 2017 06:24] by Moderator
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Re: Implicitly add method call to generated interface. [message #1754937 is a reply to message #1754935] |
Fri, 24 February 2017 04:34   |
Eclipse User |
|
|
|
Sure.
In the JvmInferrer I start with inferring the root element.
def dispatch void infer(CollSpec collSpec, IJvmDeclaredTypeAcceptor acceptor, boolean isPreIndexingPhase) {
infer(collSpec.module.collaboration,acceptor,isPreIndexingPhase,collSpec)
collSpec.module.collaboration.roles.forEach [
infer(it,acceptor,isPreIndexingPhase,collSpec)
]
collSpec.module.collaboration.roles.forEach [
inferPlayerInterface(it,acceptor,isPreIndexingPhase,collSpec)
]
}
The interface is generated like this:
def void inferPlayerInterface(Role role, IJvmDeclaredTypeAcceptor acceptor, boolean isPreIndexingPhase, CollSpec collSpec) {
acceptor.accept(role.toClass('''«collSpec.module.fullyQualifiedName».I«role.name»RolePlayer''')) [
setInterface = true
for(feature : role.features) {
if(feature instanceof Operation) {
if(feature.isPlayerQualifierSet) {
members += feature.toMethod('''perform«role.name»Role«feature.name.toFirstUpper»''', feature.type) [
setDefault = false
documentation = feature.documentation
for(p : feature.params) {
parameters += p.toParameter(p.name, p.parameterType)
}
]
}
var playerReferences = feature?.body?.findPlayerReferences
for(featureCall : playerReferences) {
println(featureCall)
var op = typesFactory.createJvmOperation
members += featureCall.toMethod('''perform«role.name»Role«feature.name.toFirstUpper»«featureCall.feature.simpleName.toFirstUpper»''',if(featureCall.explicitReturnType) featureCall.returnType else Void.typeRef) [
setDefault = false
setAbstract = true
]
}
}
}
]
}
The method findPlayerReferences returns the correct references - I added them as a specific modeling element to my grammar:
PlayerFeatureCall returns xBase::XExpression:
(=>'player' '.' {PlayerFeatureCall}
//('<' typeArguments+=JvmArgumentTypeReference (',' typeArguments+=JvmArgumentTypeReference)* '>')?
feature=[types::JvmIdentifiableElement|IdOrSuper] (
=>explicitOperationCall?='('
(
//memberCallArguments+=XShortClosure
//|
memberCallArguments+=XExpression (',' memberCallArguments+=XExpression)*
)?
')')?
(=>explicitReturnType?='returns' returnType=JvmTypeReference)?
//memberCallArguments+=XClosure?
);
XPrimaryExpression returns xBase::XExpression:
XConstructorCall |
XBlockExpression |
XSwitchExpression |
XSynchronizedExpression |
XFeatureCall |
XLiteral |
XIfExpression |
XForLoopExpression |
XBasicForLoopExpression |
XWhileExpression |
XDoWhileExpression |
XThrowExpression |
XReturnExpression |
XTryCatchFinallyExpression |
XParenthesizedExpression |
PlayerFeatureCall
;
|
|
|
|
|
|
Re: Implicitly add method call to generated interface. [message #1754942 is a reply to message #1754941] |
Fri, 24 February 2017 05:39   |
Eclipse User |
|
|
|
p.s.
you might start traversing the ast as you do,
but you may never follow cross references but use NodeModelUtils to get the node and text e.g
class MyDslJvmModelInferrer extends AbstractModelInferrer {
@Inject extension JvmTypesBuilder
@Inject extension MyDslGrammarAccess
def dispatch void infer(Element element, IJvmDeclaredTypeAcceptor acceptor, boolean isPreIndexingPhase) {
acceptor.accept(element.toInterface("demo.I" + element.name + "Player", [])) [
if (element.operation !== null) {
for (XMemberFeatureCall c : EcoreUtil2.getAllContentsOfType(element.operation.body,
XMemberFeatureCall)) {
val node = NodeModelUtils.findNodesForFeature(c,
XbasePackage.Literals.XMEMBER_FEATURE_CALL__MEMBER_CALL_TARGET).head
if (node !== null) {
val text = node.text.trim
if ("player" == text) {
val node2 = NodeModelUtils.findNodesForFeature(c,
XbasePackage.Literals.XABSTRACT_FEATURE_CALL__FEATURE).head
if (node2 !== null) {
val text2 = node2.text.trim
members+= element.toMethod(text2, Void.TYPE.typeRef)[]
}
}
}
}
}
]
acceptor.accept(element.toClass("demo." + element.name)) [
members += element.toField("player", ("demo.I" + element.name + "Player").typeRef)[]
if (element.operation !== null) {
members += element.operation.toMethod(element.operation.name, Void.TYPE.typeRef) [
body = element.operation.body
]
}
]
}
}
|
|
|
|
|
Powered by
FUDForum. Page generated in 0.07314 seconds