我有以下语法:
Model: declarations += Declaration* statements += Statement*;
Declaration: 'Declare' name=ID;
Statement: 'Execute' what=[Declaration];
我可以编写简单的脚本,如:
Declare step_forward
Declare turn_right
Declare turn_left
Execute step_forward
Execute turn_left
Execute step_forward
现在我希望java程序提供所有声明,以便脚本只包含Execute
语句。我读到IGlobalScopeProvider
似乎是正确的工具,但我不知道如何将数据添加到它,以及如何让Xtext使用它。
那么,我如何从外部语法提供声明?
更新
我的目标有点不清楚,所以我试着让它更具体。我想将声明保持为简单的java对象,例如:
List<Move> declarations = Arrays.asList(
new Move("step_forward"),
new Move("turn_right"),
new Move("turn_left"));
并且脚本应该是:
Execute step_forward
Execute turn_left
Execute step_forward
答案 0 :(得分:1)
我不确定你要求的是什么。在考虑之后,我可以得出以下可能的问题:
1。)您想将脚本分成两个文件。文件a只包含您的声明,文件b只包含语句。但任何&#39;什么&#39; attribute将保存对File a。
的声明的引用这与你的语法开箱即用。
2。)你有任何Java源代码,它提供了一个定义了一个类,例如一个声明接口&#39;,你想要什么&#39;什么&#39;属性以引用此接口或实现此接口的类。
更新的答案您应该在您的语言中使用Xbase。在那里你可以定义你的&#39;什么&#39;使用Xtypes规则&#39; JvmTypeReference&#39;属性引用任何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接口。使用生成的存根&#39; ... mydsl.MyDslJvmModelInferrer&#39;:
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)[]
])
}
}
它派生出一个单独的界面,只用一种方法单独命名&#39; execute()&#39;。
然后,实现这样的静态检查,你应该使用生成的存根&#39; ... mydsl.validation.MyDslValidator&#39;在我的例子中,它非常快速和肮脏,但你应该明白它:
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! Neverthess,这是我的解决方案:
Xtext生成了存根&#39; ... mydsl.generator.MyDslGenerator&#39;使用方法&#39; void doGenerate&#39;。你必须填写这个方法。我的想法如下:首先,你生成一个抽象的和通用的Executor类,它有两个通用参数T和U.我的executor类有一个抽象的方法&#39; executeMoves()&#39;如果这应该是无效的,那么使用非原始的&#39; Void&#39;类。它包含您的List,但是包含泛型类型u,它被定义为Move类的子类。
也将生成Move类,但仅限于存储String的字段。然后必须得出它。我的MyDslGenerator&#39;看起来像那样:
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();
}'''
}