天天看點

使用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...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 的樣子。很棒吧?你也是可以的。

使用Xtext/Xtend 實作域專用語言DSL(2)你好,世界

獨立的指令行編譯器

        除了在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.按如下設參數

使用Xtext/Xtend 實作域專用語言DSL(2)你好,世界

這時在你的目錄中出現了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++代碼。