在前面的几篇博文中,我们已经介绍了如何用Xtext 来描述一个领域特定语言(DSL)的方法,你通常要对已经语法分析的EMF 模型(也就是语法抽象树AST)进行某种处理,比如转换成为另一种语言(例如Java,C++),配置文件,XML 文档等等。这就是代码生成。与之对应的,使用Xtend 语言来编写代码生成程序非常适合。Xtend在模型驱动的软件工程中使用了很多(用于模型到模型以及模型到文本的转换)。
Xtend是和xtext 相伴的程序设计语言,使用它实现语言翻译十分方便。xtend 的最大特点是它具有模板的功能,这项功能类似与php 产生动态web 网页的方式。用起来十分的方便。
Xtend本身是一种特定于领域的语言,它可以编译为Java,因此可以与现有Java程序无缝集成。Xtend提供了强大的功能,例如模板字符串,扩展方法,以及内置功能,例如过滤器,映射和归约。能够简洁地表达代码生成的程序。
你好,世界
您要用任何一种语言查看的第一件事都是Hello World示例。在Xtend中,其读为
class HelloWorld {
def static void main(String[] args) {
println("Hello World")
}
}
您会看到Xtend看起来与Java非常相似。乍一看,主要的区别似乎是def
用于声明方法的关键字。就像在Java中一样,必须将类和main方法定义为应用程序的入口点。
Xtend类位于简单的Eclipse Java项目中。一旦安装了SDK,Eclipse将自动将所有类转换为Java源代码。默认情况下,您将在源文件夹xtend-gen中找到它。hello world示例已翻译为以下Java代码:
// Generated Java Source Code
import org.eclipse.xtext.xbase.lib.InputOutput;
public class HelloWorld {
public static void main(final String[] args) {
InputOutput.<String>println("Hello World");
}
}
表达式
非常奇怪的事情是在Xtend中不存在语句,所有内容都是表达式,并且具有返回类型。这使您能够以有趣的方式编写代码。例如,您可以try catch
在作业的右侧有一个表达式:
val data = try {
fileContentsToString('data.txt')
} catch (IOException e) {
'dummy data'
}
模板表达式(Template Expressions)
在DSL 代码生成过程中,使用最方便的就是模板表达式。利用模板可以生成一段目标代码的字符串,这个功能有点类似web 程序设计中的php语言。
模板被三重单引号('''
)包围。模板表达式可以跨越多行,并且可以嵌套对表达式进行求值,然后将其toString()
表示形式自动插入该位置。
例如:
def someHTML(String content) '''
<html>
<body>
«content»
</body>
</html>
'''
«content» 中的内容是修改模板的脚本。它可以有IF,WHILE,FOR 等语句,也可以调用方法。这种方式在HTML 动态网页生成中经常会用到。
注意:使用CTRL + < 和 CTRL + > 输入
«
和 » 符号。
例如 : 我们可以编写一个产生content 的方法
def content(){
'''
<h1>Hello the world</h1>
<p> this is generated by xtend</P>
'''
}
模板中的注释
普通的单行注释//...
和多行注释/* ... */
将无法在模板表达式中使用。但是,您可以使用注释掉模板表达式内的完整行««« .....
。
def someHTML(String body) '''
<html>
<body>
««« this will not be visible in the result
««« nor will this: «body»
</body>
</html>
'''
模板中的条件
IF
模板中有一个特殊的用途:
def someHTML(Paragraph p) '''
<html>
<body>
«IF p.headLine != null»
<h1>«p.headline»</h1>
«ENDIF»
<p>
«p.text»
</p>
</body>
</html>
'''
您也可以使用IF...ELSE...ENDIF
或IF...ELSEIF...ENDIF
表达式:
def someHTML(Paragraph p) '''
<html>
<body>
«IF p.headLine != null»
<h1>«p.headline»</h1>
«ELSE»
<h1>«p.standartHeadline»</h1>
«ENDIF»
<p>
«p.text»
</p>
</body>
</html>
'''
模板中的循环
还有一个FOR
表达式可用:
def someHTML(List<Paragraph> paragraphs) '''
<html>
<body>
«FOR p : paragraphs»
«IF p.headLine != null»
<h1>«p.headline»</h1>
«ENDIF»
<p>
«p.text»
</p>
«ENDFOR»
</body>
</html>
'''
实例:DSL 代码生成
Xtend 的一个重要的应用是实现DSL 语言的代码生成。而DSL 语言是通过Xtext 来定义的。当你建立一个 DSL 的语法文件Test.mydsl后,你可以生成一个具有你的DSL 插件的Eclipse 编辑器。然后在这个编辑器上你可以输入符合你的DSL 语法的文本文件,并且会自动生成对应的代码。
编写代码生成程序
xtext 自动建立了一个代码生成的代码
package org.xtext.example.st.generator
import org.eclipse.emf.ecore.resource.Resource
import org.eclipse.xtext.generator.AbstractGenerator
/**
* Generates code from your model files on save.
*
* See https://www.eclipse.org/Xtext/documentation/303_runtime_concepts.html#code-generation
*/
class STGenerator extends AbstractGenerator {
override void doGenerate(Resource resource, IFileSystemAccess2 fsa, IGeneratorContext context) {
for (e : resource.allContents.toIterable.filter(typeof(StructuredTextAlgorithm))){
fsa.generateFile('GenericFB.h', "hello the world" )
}
}
运行
要运行生成的编辑器,必须启动一个启用了DSL插件的新Eclipse工作台
- 选择运行->运行配置...
- 出现“运行配置”对话框。从Eclipse应用程序的上下文菜单中选择“新建”。
- 从Eclipse Application的上下文菜单中选择New。
- 转到“参数”选项卡,然后在“ VM参数”字段中输入 -Xmx256m以增加新Eclipse工作台的最大堆大小。
- 通过单击“运行”按钮启动新的Eclipse工作台。
注意:在Arguments VM Arguments 添加 -Xmx256m 很重要,我搞了两天Code 始终不能产生。添加了这个参数后成功了。
当Eclipse 编辑器启动后,你新建一个java 项目,然后在src 目录中新建一个符合你的DSL 语法的mydsl 文件,这时候编辑器会弹出一个对话框问你是否要转化成为xtext 项目,你选择yes。在mydsl 文件中输入你的dsl 程序或模型,当你编写完成,没有语法错误后,按save 按钮后,eclipse 编辑自动会在src-gen 目录中产生一个目标代码的程序。
下面是本人将PLC 的ST语言翻译成为C 的样子。很棒吧?你也是可以的。
独立的命令行编译器
除了在eclipse IDE 架构下能够生成代码之外,也可以生存一个独立的java 应用程序实现语言的翻译。下面介绍具体的过程
1 在MWE2的文件中添加下面的内容:
language = StandardLanguage {
name = "org.example.entities.Entities"
fileExtensions = "entities"
...
generator = {
generateXtendMain = true
}
}
注意:《Implementing Domain-Specific Languages with Xtext and Xtend》书的第五章好像是不对的,按照我这样的写。
2 运行MWE2 后你会发现在src/org.xtext.example.st.generator 目录下多了一个main.xtend 文件,
3 击右键 Run As | Java Application 这时在控制台下会出现“Aborting: no path to EMF resource provided!" 不要担心,因为你没有带任何命令参数。
4 文件菜单中选择 Export... | Java | Runnable JAR File,然后点击 Next.
5.按如下设参数
这时在你的目录中出现了ST-compiler.jar
6 运行
(base) yao@minipc:~$ java -jar ST-compiler.jar ./GenericFB.ST
Code generation finished.
(base) yao@minipc:~$
成功地生成了cpp 代码。
小结
带着问题去学习总是效率最高的,不过掌握的知识并不全面。我学习xtend 的目的是实现语言,模型的翻译。比如 PLC 的Structure Text 翻译成 C++代码。