在前面的幾篇博文中,我們已經介紹了如何用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) [email protected]:~$ java -jar ST-compiler.jar ./GenericFB.ST
Code generation finished.
(base) [email protected]:~$
成功地生成了cpp 代碼。
小結
帶着問題去學習總是效率最高的,不過掌握的知識并不全面。我學習xtend 的目的是實作語言,模型的翻譯。比如 PLC 的Structure Text 翻譯成 C++代碼。