我不太确定您要的是什么。经过思考,我可以得出以下可能的问题:
1.) 您想将脚本拆分为两个文件。文件 a 将仅包含您的声明,而文件 b 将仅包含声明。但是任何 'what' 属性都会引用 File a 的声明。
这适用于您的语法。
2.) 您有任何 Java 源代码提供了定义的类,例如“声明接口”,并且您希望“什么”属性引用此接口或实现此接口的类。
更新答案您应该在您的语言中使用 Xbase。在那里,您可以使用 Xtypes 规则“JvmTypeReference”定义您的“what”属性对任何 Java 类型的引用。您必须在语法中进行的修改并不难,我认为它可能看起来像这样:
// Grammar now inherits from the Xbase grammar
// instead of the common terminals grammar
grammar org.xtext.example.mydsl.MyDsl with org.eclipse.xtext.xbase.Xbase
generate myDsl "http://www.xtext.org/example/mydsl/MyDsl"
Model:
declarator=Declarator?
statements+=Statement*;
Declarator:
'Declare' name=ID;
Statement:
'Execute' what=JvmTypeReference;
您可以通过使用它们的限定名称对它们进行寻址来引用任何 Java 类型(Java API、任何链接的 API、用户定义的类型)。它看起来像这样:
Referring to JVM types look like this in an Xtext language. (Screenshot)
您还可以验证引用的 JVM 类型是否有效,例如实现了一个所需的接口,我将在模型中使用一个可选的声明符来定义它。
Referenced JVM type is checked whether it is a valid type. (Screenshot)
使用 Xbase 很容易为这个模型元素推断 Java 接口。使用生成的存根 '...mydsl.MyDslJvmModelInferrer':
class MyDslJvmModelInferrer extends AbstractModelInferrer {
@Inject extension JvmTypesBuilder
@Inject extension TypeReferences
def dispatch void infer(Model element, IJvmDeclaredTypeAcceptor acceptor, boolean isPreIndexingPhase) {
acceptor.accept(
element.declaration.toInterface('declarator.' + element.declaration.name) [
members += it.toMethod("execute", TypesFactory.eINSTANCE.createJvmVoid.createTypeRef)[]
])
}
}
它派生一个接口,单独命名,只有一个方法'execute()'。
然后,像这样实现静态检查,您应该使用生成的存根 '...mydsl.validation.MyDslValidator' 在我的示例中它非常快速和肮脏,但您应该了解它:
class MyDslValidator extends AbstractMyDslValidator {
@Check
def checkReferredType(Statement s) {
val declarator = (s.eContainer as Model).declaration.name
for (st : (s.what.type as JvmDeclaredType).superTypes) {
if (st.qualifiedName.equals('declarator.' + declarator)) {
return
}
}
(s.what.simpleName + " doesn't implement the declarator interface " + declarator).
warning(MyDslPackage.eINSTANCE.statement_What)
}
}
(我使用首选的 Xtend 编程语言来实现静态检查!)静态检查确定给定的 JvmTypeReference(它是您项目中的 Java 类)是否实现了声明的接口。否则,它会在您的 dsl 文档中引入警告。
希望这能回答你的问题。
下一次更新:你的想法不会那么好用!你可以简单地用 Xtend 编写一个模板而不使用 Xbase,但我无法想象如何以一种好的方式使用它。问题是,我假设您不会生成孔类“移动”和孔执行过程。我玩了一点尝试生成可用的代码,似乎很hacky!尽管如此,这是我的解决方案:
Xtext 使用“void doGenerate”方法为您生成了存根“...mydsl.generator.MyDslGenerator”。您必须填写此方法。我的想法如下:首先,您生成一个带有两个泛型参数 T 和 U 的抽象和泛型 Executor 类。然后我的 executor 类有一个返回值 T 的抽象方法“executeMoves()”。如果这应该是无效的,请使用非原始的“无效”类。它包含您的 List,但属于通用类型 u,它被定义为 Move 类的子类。
Move 类也将生成,但只有一个用于存储字符串的字段。然后它必须被导出。我的“MyDslGenerator”看起来像这样:
class MyDslGenerator implements IGenerator {
static var cnt = 0
override void doGenerate(Resource resource, IFileSystemAccess fsa) {
cnt = 0
resource.allContents.filter(typeof(Model)).forEach [ m |
fsa.generateFile('mydsl/execution/Move.java', generateMove)
fsa.generateFile('mydsl/execution/Executor' + cnt++ + '.java', m.generateExecutor)
]
}
def generateMove() '''
package mydsl.execution;
public class Move {
protected String s;
public Move(String s) {
this.s = s;
}
}
'''
def generateExecutor(Model m) '''
package mydsl.execution;
import java.util.List;
import java.util.Arrays;
/**
* The class Executor is abstract because the execution has to implemented somewhere else.
* The class Executor is generic because one does not know if the execution has a return
* value. If it has no return value, use the not primitive type 'Void':
* public class MyExecutor extends Executor_i<Void> {...}
*/
public abstract class Executor«cnt - 1»<T, U extends Move> {
@SuppressWarnings("unchecked")
private List<U> declarations = Arrays.<U>asList(
«FOR Statement s : m.statements»
(U) new Move("«s.what.name»")«IF !m.statements.get(m.statements.size - 1).equals(s)»,«ENDIF»
«ENDFOR»
);
/**
* This method return list of moves.
*/
public List<U> getMoves() {
return declarations;
}
/**
* The executor class has to be extended and the extending class has to implement this
* method.
*/
public abstract T executeMoves();
}'''
}