Xtext:在DSL文件中使用生成的类,缺少类型的问题
Xtext: use generated classes in DSL file, problem of missing type
问题:我如何配置 Xtext 和 Xbase 以便在我的 DSL 文件(带有 DSL 扩展名“.myx”的文件)中使用 classJvmModelInferrer 尚未生成的文件?
这是语言语法:
grammar org.xtext.example.mydsl.MyX with org.eclipse.xtext.xbase.Xbase
generate myX "http://www.xtext.org/example/mydsl/MyX"
import "http://www.eclipse.org/xtext/xbase/Xbase" as xbase
Model:
expressions+=CommonExpression*;
CommonExpression:
Anime | AnimeResource
;
AnimeResource:
'AnimeRes' name=ID '{'
(args+=FullJvmFormalParameter)*
'}'
;
Anime:
'watch' name=ID body=XBlockExpression
;
这是我想要实现的 (test.myx):
AnimeRes Resource {
}
watch Watcher {
val someStub = Resource.create()
}
所以 dsl 文件看起来像是为 Resource class 定义了一个静态方法。但实际上,必须有额外的参数应该传递给资源,在我的例子中它们纯粹是样板,这就是为什么我不想每次都将它们传递给“创建”。
我希望生成的文件看起来如何实现:
package test;
public class Model {
private int id= 0;
public static class Resource {
private int id;
public Resource(final int id) {
this.id = id;
}
}
public class ResourceCreator {
public Resource create() {
return new Resource(id /* the creator is inner non-static class */));
}
}
public ResourceCreator Resource = new ResourceCreator();
}
那样我有点作弊。我有一个名称为 class 的变量,在客户端代码中,当他们实际上只是在使用名为 class 的构建器时,它们看起来像是使用静态方法。这是制作类似文件的 JvmModelInferrer:
import org.eclipse.xtext.xbase.jvmmodel.AbstractModelInferrer
import org.xtext.example.mydsl.myX.Model
import org.eclipse.xtext.xbase.jvmmodel.IJvmDeclaredTypeAcceptor
import org.eclipse.xtext.naming.QualifiedName
import com.google.inject.Inject
import org.eclipse.xtext.xbase.jvmmodel.JvmTypesBuilder
import org.xtext.example.mydsl.myX.AnimeResource
import org.eclipse.xtext.common.types.JvmVisibility
import org.xtext.example.mydsl.myX.Anime
class MyXJvmModelInferrer extends AbstractModelInferrer {
@Inject extension JvmTypesBuilder
def dispatch void infer(Model element, IJvmDeclaredTypeAcceptor acceptor, boolean isPreIndexingPhase) {
acceptor.accept(element.toClass(QualifiedName.create("test", "Model"))) [
for (expression : element.expressions) {
switch (expression ) {
AnimeResource: {
members += expression.toClass(expression.name) [
static = true
visibility = JvmVisibility.PUBLIC
val _members = members
expression.args.forEach [
_members += expression.toField(name, parameterType) [
static = false
visibility = JvmVisibility.PUBLIC
]
]
members += expression.toField("id", typeRef(int))
members += expression.toConstructor [
val _parameters = parameters
expression.args.forEach [
_parameters += it.toParameter(name, parameterType)
]
_parameters += expression.toParameter("id", typeRef(int))
body = '''
«FOR param : parameters»this.«param.name» = «param.name»;
«ENDFOR»
'''
]
]
members += expression.toField("id", typeRef(int))
members += element.toClass(expression.name + "Creator") [
static = false
visibility = JvmVisibility.PUBLIC
members += element.toMethod("create", typeRef(expression.name)) [
val parameters = parameters
expression.args.forEach [
parameters += it.toParameter(name, parameterType)
]
body = '''
return new «expression.name»(«FOR param : parameters»«IF parameters.indexOf(param) < parameters.size - 1», «ENDIF»«ENDFOR»id);
'''
]
]
members += expression.toField(expression.name, typeRef(expression.name + "Creator")) [
visibility = JvmVisibility.PUBLIC
initializer = '''
new «expression.name + "Creator"»()
'''
]
}
Anime: {
members += expression.toMethod(expression.name, typeRef(void)) [
body = expression.body
]
}
}
}
]
}
}
我使用这种方法遇到的问题:
所以似乎有些链接失败了,但我不明白我应该做什么来解决这个问题,我应该覆盖哪些绑定以及如何覆盖。
如有任何帮助,我们将不胜感激。
更新。使用可编译的 ModelInferrer 更新了描述(抱歉)。当我尝试使用 watch 块的 XBlockExpression 为模型 class 内部的方法生成 Java 代码时,就会出现问题。所以如果我有这样的 DSL 文件:
AnimeRes Resource {
}
watch Watcher {
val some = Resource.create()
}
并且还在 Inferrer 中使用 Anime 分支,出现了所描述的问题。
如果我有相同的文件并且不使用动漫分支(这样注释掉):
// Anime: {
// members += expression.toMethod(expression.name, typeRef(void)) [
// body = expression.body
// ]
// }
那就没问题了,但我需要生成那个方法。
您需要为内部类型使用专有名称
members += element.toClass(expression.name + "Creator") [
static = false
visibility = JvmVisibility.PUBLIC
members += element.toMethod("create", typeRef("test.Model$"+expression.name)) [
val parameters = parameters
expression.args.forEach [
parameters += it.toParameter(name, parameterType)
]
body = '''
return new «expression.name»(«FOR param : parameters»«IF parameters.indexOf(param) < parameters.size - 1», «ENDIF»«ENDFOR»id);
'''
]
]
members += expression.toField(expression.name, typeRef("test.Model$"+expression.name + "Creator")) [
visibility = JvmVisibility.PUBLIC
initializer = '''
new «expression.name + "Creator"»()
'''
]
问题:我如何配置 Xtext 和 Xbase 以便在我的 DSL 文件(带有 DSL 扩展名“.myx”的文件)中使用 classJvmModelInferrer 尚未生成的文件?
这是语言语法:
grammar org.xtext.example.mydsl.MyX with org.eclipse.xtext.xbase.Xbase
generate myX "http://www.xtext.org/example/mydsl/MyX"
import "http://www.eclipse.org/xtext/xbase/Xbase" as xbase
Model:
expressions+=CommonExpression*;
CommonExpression:
Anime | AnimeResource
;
AnimeResource:
'AnimeRes' name=ID '{'
(args+=FullJvmFormalParameter)*
'}'
;
Anime:
'watch' name=ID body=XBlockExpression
;
这是我想要实现的 (test.myx):
AnimeRes Resource {
}
watch Watcher {
val someStub = Resource.create()
}
所以 dsl 文件看起来像是为 Resource class 定义了一个静态方法。但实际上,必须有额外的参数应该传递给资源,在我的例子中它们纯粹是样板,这就是为什么我不想每次都将它们传递给“创建”。 我希望生成的文件看起来如何实现:
package test;
public class Model {
private int id= 0;
public static class Resource {
private int id;
public Resource(final int id) {
this.id = id;
}
}
public class ResourceCreator {
public Resource create() {
return new Resource(id /* the creator is inner non-static class */));
}
}
public ResourceCreator Resource = new ResourceCreator();
}
那样我有点作弊。我有一个名称为 class 的变量,在客户端代码中,当他们实际上只是在使用名为 class 的构建器时,它们看起来像是使用静态方法。这是制作类似文件的 JvmModelInferrer:
import org.eclipse.xtext.xbase.jvmmodel.AbstractModelInferrer
import org.xtext.example.mydsl.myX.Model
import org.eclipse.xtext.xbase.jvmmodel.IJvmDeclaredTypeAcceptor
import org.eclipse.xtext.naming.QualifiedName
import com.google.inject.Inject
import org.eclipse.xtext.xbase.jvmmodel.JvmTypesBuilder
import org.xtext.example.mydsl.myX.AnimeResource
import org.eclipse.xtext.common.types.JvmVisibility
import org.xtext.example.mydsl.myX.Anime
class MyXJvmModelInferrer extends AbstractModelInferrer {
@Inject extension JvmTypesBuilder
def dispatch void infer(Model element, IJvmDeclaredTypeAcceptor acceptor, boolean isPreIndexingPhase) {
acceptor.accept(element.toClass(QualifiedName.create("test", "Model"))) [
for (expression : element.expressions) {
switch (expression ) {
AnimeResource: {
members += expression.toClass(expression.name) [
static = true
visibility = JvmVisibility.PUBLIC
val _members = members
expression.args.forEach [
_members += expression.toField(name, parameterType) [
static = false
visibility = JvmVisibility.PUBLIC
]
]
members += expression.toField("id", typeRef(int))
members += expression.toConstructor [
val _parameters = parameters
expression.args.forEach [
_parameters += it.toParameter(name, parameterType)
]
_parameters += expression.toParameter("id", typeRef(int))
body = '''
«FOR param : parameters»this.«param.name» = «param.name»;
«ENDFOR»
'''
]
]
members += expression.toField("id", typeRef(int))
members += element.toClass(expression.name + "Creator") [
static = false
visibility = JvmVisibility.PUBLIC
members += element.toMethod("create", typeRef(expression.name)) [
val parameters = parameters
expression.args.forEach [
parameters += it.toParameter(name, parameterType)
]
body = '''
return new «expression.name»(«FOR param : parameters»«IF parameters.indexOf(param) < parameters.size - 1», «ENDIF»«ENDFOR»id);
'''
]
]
members += expression.toField(expression.name, typeRef(expression.name + "Creator")) [
visibility = JvmVisibility.PUBLIC
initializer = '''
new «expression.name + "Creator"»()
'''
]
}
Anime: {
members += expression.toMethod(expression.name, typeRef(void)) [
body = expression.body
]
}
}
}
]
}
}
我使用这种方法遇到的问题:
所以似乎有些链接失败了,但我不明白我应该做什么来解决这个问题,我应该覆盖哪些绑定以及如何覆盖。
如有任何帮助,我们将不胜感激。
更新。使用可编译的 ModelInferrer 更新了描述(抱歉)。当我尝试使用 watch 块的 XBlockExpression 为模型 class 内部的方法生成 Java 代码时,就会出现问题。所以如果我有这样的 DSL 文件:
AnimeRes Resource {
}
watch Watcher {
val some = Resource.create()
}
并且还在 Inferrer 中使用 Anime 分支,出现了所描述的问题。 如果我有相同的文件并且不使用动漫分支(这样注释掉):
// Anime: {
// members += expression.toMethod(expression.name, typeRef(void)) [
// body = expression.body
// ]
// }
那就没问题了,但我需要生成那个方法。
您需要为内部类型使用专有名称
members += element.toClass(expression.name + "Creator") [
static = false
visibility = JvmVisibility.PUBLIC
members += element.toMethod("create", typeRef("test.Model$"+expression.name)) [
val parameters = parameters
expression.args.forEach [
parameters += it.toParameter(name, parameterType)
]
body = '''
return new «expression.name»(«FOR param : parameters»«IF parameters.indexOf(param) < parameters.size - 1», «ENDIF»«ENDFOR»id);
'''
]
]
members += expression.toField(expression.name, typeRef("test.Model$"+expression.name + "Creator")) [
visibility = JvmVisibility.PUBLIC
initializer = '''
new «expression.name + "Creator"»()
'''
]