使用Xtext/Xtend 实现域专用语言DSL(2)

在前面的几篇博文中,我们已经介绍了如何用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...ENDIFIF...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++代码。

猜你喜欢

转载自blog.csdn.net/yaojiawan/article/details/115909742
今日推荐