十五分钟入门
本章将实现一个小型的特定领域的语言来对实体和属性进行建模,类似于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]
;
Domainmodel :
elements += Type*
;
Type:
DataType | Entity
;
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 {