从Xtext中的外部源获取声明

时间:2014-11-07 09:30:44

标签: xtext

我有以下语法:

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

1 个答案:

答案 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();
        }'''
}