Xtext——3. 十五分钟入门

来源:互联网 发布:一手淘宝买家数据 编辑:程序博客网 时间:2024/06/04 19:57

十五分钟入门

 本章将实现一个小型的特定领域的语言来对实体和属性进行建模,类似于Rails、Grails、或Spring Roo。下面的语法还是很有吸引力的:

datatype String

entity Blog {
    title: String
    many posts: Post
}

entity HasAuthor {
    author: String
}

entity Post extends HasAuthor {
    title: String
    content: String
    many comments: Comment
}

entity Comment extends HasAuthor {
    content: String
}

    通过第1章的介绍安装好Xtext之后,启动Eclipse,创建一个新的工作区。

创建一个新的Xtext项目

    首先需要创建几个Eclipse项目,通过Eclipse向导:

            File->New->Project...->Xtext->Xtext project

    选择一个有意义的项目名称、语言名称,以及文件扩展名。例如:

    Main Project Name: org.example.domainmodel

    Language Name: org.example.domainmodel.Domainmodel

    DSL-File Extension: dmodel

    通过向导完成项目的创建之后,在工作区中可以看到三个新项目,其中:

        org.example.domainmodel包含了语法的定义和所有运行时组建(此法语法分析解析、链接、验证等)

        org.example.domainmodel.tests为单元测试

        org.example.domainmodel.ui是Eclipse编辑器,以及所有其他工作台相关的功能


自己制定语法

    向导会自动在编辑器中打开语法文件Domainmodel.xtext,如下所示,其中含有简单的Hello World语法:

grammar org.example.domainmodel.Domainmodel with
                                      org.eclipse.xtext.common.Terminals

generate domainmodel "http://www.example.org/domainmodel/Domainmodel"

Model:
    greetings+=Greeting*;
    
Greeting:
    'Hello' name=ID '!';

  

  现在,将上边的语法替换为下面我们定义的域建模语言:

grammar org.example.domainmodel.DomainModel with
                                      org.eclipse.xtext.common.Terminals

generate domainmodel "http://www.example.org/domainmodel/Domainmodel"

Domainmodel :
  elements += Type*
;
  
Type:
  DataType | Entity
;
  
DataType:
  'datatype' name = ID
;
 
Entity:
  'entity' name = ID ('extends' superType = [Entity])? '{'
     features += Feature*
  '}'
;
 
Feature:
  many?='many'? name = ID ':' type = [Type]
;

       现在详细看一下不同的语法规则所代表的意思:
 1. 语法中的第一个规则通常作为入口开始的规则。

Domainmodel :
  elements += Type*
;

     上面的意思是,Donmainmodel包含任意数量(*)的Type,且该Type会被加到(+=)一个名为elements的特性中。
2. Type的规则表明Type是规则DataType或(|)规则Entrity

Type:
  DataType | Entity
;

3. 规则DataType以关键字'datatype'开始,后边跟着一个标示符,该标示符被解析为规则ID,其中规则ID的定义在语法超集org.eclipse.xtext.common.Terminals中,规则ID是一个单词,即标示符。可以通过F3定位到ID的定义,通过调用ID返回的值被付给了特性name

DataType:
  'datatype' name = ID
;

4. 规则Entity也是以一个关键字开头,后边跟着一个名称(name)。

Entity :
  'entity' name = ID ('extends' superType = [Entity])? '{'
    features += Feature*
  '}'
;

    然后是一个带有括号和可选项(?)的extends从句。因为名为superType的特性采用交叉引用(注意其中的中括弧),不会中总括弧中的规则Entity的进行解析,而仅仅对标示符进行解析(ID规则)。在链接过程中才会对Entity进行解析。最后,大括弧中可以有任意数量的Features,其采用下面的规则5。

5. 最后一个(并不是不重要),规则Feature的定义如下:

Feature:
  (many ?= 'many')? name = ID ':' type = [Type] 
;

    关键字many用来对域建模DSL中对一个多值特性进行建模,赋值操作符(?=)表明特性many的类型为boolean,目前 已经了解了解析规则中的其他语法元素。
    域建模语法是Xtext的语法语言中最为重要的概念之一,已经了解到,关键字为一个字符串,然后通过简单的等于号(=)进行赋值,通过加等于(+=)进行多值赋值,通过文号等于(?=)进行布尔赋值。此外,了解了如何声明交叉引用,以及不同的赋值(?=表示可选,=任意数目,+=至少一次)。在第7节中会有更为详细的描述。现在来看一下这样的语言描述能做哪些事情。

生成语言

    我们已经定义了语法,现在需要执行代码生成器来生成各种语言组建,在项目中选中文件GenerateDomainmodel.mwe2,点击右键 Run As -> MWE2 Workow,将会触发Xtext语言生成器,会生成解析器和序列化器,以及一些其他基础结构代码。可以在控制台的日志消息中看到相应的过程。


运行生成的IDE插件

    现在,可以对IDE集成进行测试了。如果从Eclipse的菜单中选择Run -> Run Con gurations...,可以创建一个新的Eclipse应用,在左边树状列表中选择节点Eclipse Application并添加一个新的,给一个有意义的名称,然后到右边的Arguments那里配置VM arguments:

-XX:MaxPermSize=128m
-Xmx512m

    需要确保所配置的内存大小足够运行一个新的Eclipse实力。现在可以通过点击Run来创建一个新的进程。

    新启动的Eclipse工作台已经安装了刚才开发的插件。在新的工作太重,创建一个新的项目,例如File -> New ->Project... -> Java Project,然后添加一个新的文件,文件的扩展名为前面步骤中指定的扩展名(*.dmodel ),此时会打开所生成的编辑器,来试一下代码自动完成、语法高亮、语法验证、链接错误、突出显示、引用等功能。

第二次迭代:添加Package和Import

    创建了第一个DSL,在编辑器看一下效果,现在对其进行进一步的改进。域建模语言应支持包(Packages),用来防止命名冲突,这样可以更好的适用于目标环境(Java)。一个Package可能含有Types和其他包。为了能够应用命名,还需要添加import声明。 

    最后,我们想要将前边所使用的模型和新的模型需求放到不同的文件中。

// datatypes.dmodel

datatype String

// commons.dmodel

package my.company.common {
    
    entity HasAuthor {
        author: String
    }
}

// blogs.dmodel

package my.company.blog {
  
    import my.company.common.*
      
    entity Blog {
        title: String
        many posts: Post
    }
    
    entity Post extends my.company.common.HasAuthor {
        title: String
        content: String
        many comments: Comment
    }
    
    entity Comment extends HasAuthor {
        content: String
    }
}


    现在开始对语法进行改进。
1. 由于Domainmodel不在包含类型,而是包含包,因此,需要对入口规则进行修改。此外,需要定义通用的超类型PackagesTypesAbstractElement

Domainmodel:
  (elements += AbstractElement)*
;

AbstractElement:
  PackageDeclaration | Type
;

2. PackageDeclaration正如所期待的,含有一系列的ImportsAbstractElements,因为Imports可以作为根Domainmodel,所以讲Imprt添加到AbstractElement中。

PackageDeclaration:
  'package' name = QualifiedName '{'
    (elements += AbstractElement)*
  '}'
;

AbstractElement:
  PackageDeclaration | Type | Import
;

QualifiedName:
  ID ('.' ID)*
;

    QualifiedName有一点特殊,其不包含任何赋值,因此仅作为数据类型规则,返回一个字符串。因此,Package的特性nameString类型。

3. 使用Xtext,可以很方便的定义Imports。如果在解析规则中使用importedNamespace,基础结构将会将其视为import,甚至支持通配符:

Import:
  'import' importedNamespace = QualifiedNameWithWildcard
;

QualifiedNameWithWildcard:
  QualifiedName '.*'?
;

    同QualifiedName类似,QualifiedNameWithWildcard返回一个空字符串。

4. 最后一步是允许使用完整的命名来进行交叉引用。否则,只有import之后,才能进行引用。

Entity:
  'entity' name = ID 
              ('extends' superType = [Entity | QualifiedName])?
  '{'
    (features += Feature)*
  '}'
;
 
Feature:
  (many ?= 'many')? name = ID ':' type = [Type | QualifiedName]
;

    需要注意的是,在交叉引用的上线稳重,竖线(|)不是可选的意思,而是用来规定被解析的字符串的语法。

这就是全部的语法,应该是下面的样子:

grammar org.example.domainmodel.Domainmodel with
                                      org.eclipse.xtext.common.Terminals

generate domainmodel "http://www.example.org/domainmodel/Domainmodel"

Domainmodel:
  (elements += AbstractElement)*
;

PackageDeclaration:
  'package' name = QualifiedName '{'
    (elements += AbstractElement)*
  '}'
;

AbstractElement:
  PackageDeclaration | Type | Import
;

QualifiedName:
  ID ('.' ID)*
;

Import:
  'import' importedNamespace = QualifiedNameWithWildcard
;
  
QualifiedNameWithWildcard:
  QualifiedName '.*'?
;
  
Type:
  DataType | Entity
;
  
DataType:
  'datatype' name=ID
;
 
Entity:
  'entity' name = ID 
              ('extends' superType = [Entity | QualifiedName])?
  '{'
    (features += Feature)*
  '}'
;
 
Feature:
  (many ?= 'many')? name = ID ':' type = [Type | QualifiedName]
;

根据前边所介绍的方法,重新生成语言基础结构,然后再次试一下编辑器。你甚至可以在多个文件之间进行交叉引用。

[转载请标明出处http://blog.csdn.net/donhao/article/details/7182792]

英文原文地址:http://www.eclipse.org/Xtext/documentation/2_1_0/020-domainmodel-step-by-step.php

原创粉丝点击