Xtext practice
-
Upload
shintaro-hosoai -
Category
Engineering
-
view
598 -
download
7
description
Transcript of Xtext practice
©2014 Shintaro Hosoai
Xtext Tutorial細合 晋太郎
2023/04/11 SEA 関西プロセス分科会
©2014 Shintaro Hosoai 2
Step0:
サンプルプロジェクトを実行してみよう
04/11/2023 SEA 関西プロセス分科会
Sample ProjectState Machine DSL
©2014 Shintaro Hosoai 3
Step0-1: Sample Project の作成• Eclipse を起動します• New > Example →ダイアログ表示• Xtext Examples/Xtext State-Machine Example を選
択し Next• 次ページの設定は変えずに Finish
• Workspace に以下の二つのプロジェクトが作成されるorg.eclipse.xtext.example.fowlerdslorg.eclipse.xtext.example.fowlerdsl.ui
04/11/2023 SEA 関西プロセス分科会
©2014 Shintaro Hosoai 4
Step0-2: 言語定義の確認• org.eclipse.xtext.example.fowlerdsl
src/org.eclipse.xtext.example.fowlerdsl/ Statemachine.xtext• ステートマシン DSL の言語定義
• src/org.eclipse.xtext.example.fowlerdsl/ GenerateStatemachine.mwe2 • 言語環境生成のためのワークフローと各種設定が
入っています.• ・ fileExtensions = “statemachine” : DSL の拡張子
04/11/2023 SEA 関西プロセス分科会
©2014 Shintaro Hosoai 5
Step0-3: 言語環境の生成• org.eclipse.xtext.example.fowlerdsl を開き,
src/org.eclipse.xtext.example.fowlerdsl/ GenerateStatemachine.mwe2を右クリック > Run As > MWE2 Workflow• 言語定義に基づいて,言語モデル,エディタプ
ラグイン等が生成されます.
• ( Example では実はすでに環境生成されています)
04/11/2023 SEA 関西プロセス分科会
©2014 Shintaro Hosoai 6
Step0-4 : 作成した DSL プラグインの実行• org.eclipse.xtext.example.fowlerdsl を右クリック >
Run As > Eclipse Application• 新しく作成した Plugin を含む Eclipse が起動します.
• 起動した Eclipse の Package Explorer で右クリック> New > Java Project ,適当なプロジェクト名を設定し Finish• 作成したプロジェクトの src フォルダを右クリッ
ク> New > File ,適当な名前 .statemachine として作成初回はプロジェクトに Xtext の機能を追加するか聞かれるため, Yes
04/11/2023 SEA 関西プロセス分科会
©2014 Shintaro Hosoai 7
Step0-5: DSL を書いてみる• 右図のような信号のステートマシンを書いてみましょう. DSL 構成は
以下の通りevent event 名 EventID ...endcommands command 名 CommadID ...end
state state 名 actions { command 名 } event 名 => 次 state 名end...
04/11/2023 SEA 関西プロセス分科会
DSL イメージ
blueentry/lightBlue
yellowentry/lightYellow
redentry/lightRed
toBlue
toYellow
toRed
EventID, CommandID は任意
©2014 Shintaro Hosoai
Step0-6: 記述例events toRed E001 toBlue E002 toYellow E003end
commands lightRed C001 lightYellow C002 lightBlue C003end
state blue actions { lightBlue } toYellow => yellowend
state yellow actions { lightYellow } toRed=>redend
state red actions { lightRed } toBlue => blueend
804/11/2023 SEA 関西プロセス分科会
©2014 Shintaro Hosoai
Step0-7 : 生成コードの確認と実行• src-gen フォルダ下に入力した DSL から Java
コードが生成されています.• src-gen フォルダを右クリック> Build Path >
Use as Source Folder
• 生成された Java コードを右クリック> Run As > Java Application
• コマンドラインからイベントを入力し,ステートマシンの動作を確認できます.
904/11/2023 SEA 関西プロセス分科会
©2014 Shintaro Hosoai 10
Step1 :
独自プロジェクトを作成してみよう
04/11/2023 SEA 関西プロセス分科会
プロジェクトの作成
©2014 Shintaro Hosoai 11
Step1-0 :どんな DSL?
• TDD するときに,クラスのひな形とテストクラス作るのめんどいよね!→ DSL で作ってみよう
04/11/2023 SEA 関西プロセス分科会
UnitDSL
MockClassMock
ClassMockClass
TestClassTest
ClassTestClass
クラス定義メソッド定義テスト定義
©2014 Shintaro Hosoai 12
Step1-1 : Project の作成• New > Project > Xtext Project• Dialog
project name : jp.sea.kansai.example.unitdsl☑Use default locationLanguage Name : jp.sea.kansai.example.UnitDsl Extensions : unitLayout □ Create SDK feature projectWorking sets □ Add Project to working sets
04/11/2023 SEA 関西プロセス分科会
今回は利用しない
任意
DSL のファイル拡張子(任意)
言語名, Project 名と合わせておくのが無難.
クラスになるので Camelで
プロジェクト名(任意)
©2014 Shintaro Hosoai
Step1-2 : 言語環境の生成• jp.sea.kansai.example.unitdsl プロジェクト
src/jp.sea.kansai.example/GenerateUnitDsl.mwe2を開き,内容を確認.• 上記を右クリック > Run as > MWE2 Workflow• 各種ファイルが生成される• unitdsl プロジェクト
• src-gen/• xtend-gen/• model
• unitdsl.tests/• src-gen
• unitdsl.ui• /src-gen• /xtend-gen
1304/11/2023 SEA 関西プロセス分科会
©2014 Shintaro Hosoai
Step1-3 : 生成環境の実行• jp.sea.kansai.example.unitdsl プロジェクトを右クリッ
ク > Run As > Eclipse Application• 新しい Eclipse が起動する.→一旦終了• jp.sea.kansai.example.unitdsl プロジェクトを右クリッ
ク > Run As > Run Configuration• 左のツリーから Eclipse Application/Launch Runtime
Eclipse (名称が異なる場合もあり)を選択• Arguments タブの VM arguments に以下を追加
-XX:MaxPermSize=128m (64 以上であれば十分)
Xtext はかなり Heap を食います,実行時にメモリエラーが出る場合は,上記オプションをご確認ください.( Java 8 では Heap 問題が解決されたっぽい?
1404/11/2023 SEA 関西プロセス分科会
©2014 Shintaro Hosoai
Step1-4 : Greeting DSL を試す.• 再度,生成環境を実行• 新しく起動した Eclipse 上に新しく Java Project
を作成• DSL ファイルの作成: New > File ,適当な場所
に~ .unit ファイルを作成• 初回はダイアログが表示されるので
Yes ( Xtext Nature の追加)• コンテンツアシストやシンタックスカラーリン
グが有効か確認してみましょう.• 本プロジェクトは現状,コード生成は行われま
せん.
1504/11/2023 SEA 関西プロセス分科会
©2014 Shintaro Hosoai
Step1-5 : Xtext を書き換える• まずは,クラス構造を定義する部分を作る(型
を扱うとややこしいので,とりあえず以下のレベルで)
unit name { methodName
...
}
1604/11/2023 SEA 関西プロセス分科会
クラス名
メソッドは複数個指定
Unit : “ unit” name=ID “{” methods+=Method* “}”;
Method: name=ID;
Model: units+=Unit*;
©2014 Shintaro Hosoai
Step1-6 : 再生成・実行・確認
1704/11/2023 SEA 関西プロセス分科会
• 言語定義を変更した場合は,再度 Generate* .mwe2 を実行して,言語環境を再生成する必要があります.• 再生成後,実行してみましょう.以下のように
Unit が記述できるようになっているはずです.• unit Hogehoge{• foo• bar• }
©2014 Shintaro Hosoai
Step1-7 : コード生成の定義(1)
1804/11/2023 SEA 関西プロセス分科会
• jp.sea.kansai.example.unitdsl プロジェクト/src/jp.sea.kansai.example.generator/UnitDslGenerator.xtend を開く
class UnitDslGenerator implements IGenerator {
override void doGenerate(Resource resource, IFileSystemAccess fsa) { for(unit : resource.allContents.filter(typeof(Unit)).toIterable){ fsa.generateFile(unit.name+".java", genClass(unit)); fsa.generateFile(unit.name+"Test.java", genTestClass(unit)); } }
次ページに続く
©2014 Shintaro Hosoai
Step1-7 : コード生成の定義(1)
1904/11/2023 SEA 関西プロセス分科会
def genClass(Unit unit)''' public class «unit.name» { «FOR method:unit.methods»«genMethod(method)»«ENDFOR» } '''
def genMethod(Method method)''' void «method.name»(){} '''
def genTestClass(Unit unit)''' public class «unit.name»Test { } '''}再度,実行,確認してみてください.
(コードテンプレート定義の際には再生成は必要ありません.)src-gen フォルダの中に, DSL に応じた Java と Test コードが生成されているはず.src-gen フォルダをソースフォルダに指定すると,生成したコードがコンパイルされます
©2014 Shintaro Hosoai 20
Step2 :
言語定義,コードテンプレートの拡張, Type Reference
04/11/2023 SEA 関西プロセス分科会
プロジェクトの拡張
©2014 Shintaro Hosoai 21
Step2-1 : Xtext の拡張(1)• 前回作成した, Unit DSL を拡張していきましょ
う.• 型周りを実装してみましょう• unit className extends superClass {
retType methodName(paramType param)}
04/11/2023 SEA 関西プロセス分科会
Unit: "unit" name=ID ("extends" parent=[Unit])?"{" methods+=Method* "}";
Method: retType=[Unit] name=ID ("("params+=Param (","params+=Param)*")")?;
Param: paramType=[Unit] param=ID;
[ ] で型を囲むと,インスタンスの参照が行えます.
カンマ区切りの要素を作る時は,このような記述をします.
Model :は変更なし
引数がない場合は省略可にしてみました
©2014 Shintaro Hosoai
Step2-2 : Xtext の拡張(2)• Primitive も書けるようにしてみます.
2204/11/2023 SEA 関西プロセス分科会
Unit: "unit" name=ID ("extends" parent=[Unit])?"{" methods+=Method* "}";
Method: retType=([Unit]|PredefTypes) name=ID ("("params+=Param (","params+=Param)*")")?;
Param: paramType=([Unit]|PredefTypes) param=ID;
PredefTypes: type="int"| type="String" | type="Void";
再生成・実行してみましょう
©2014 Shintaro Hosoai
Step2-3: テンプレートの拡張 (1)
2304/11/2023 SEA 関西プロセス分科会
def genClass(Unit unit)''' public class «unit.name» «IF unit.parent!=null»extends «unit.parent.name»«ENDIF»{ «FOR method:unit.methods»«genMethod(method)»«ENDFOR» }'''
def genMethod(Method method)''' «getTypeName(method.retType)» «method.name»(«FOR param:method.params SEPARATOR","»«genParam(param)»«ENDFOR»){
}'''
def genParam(Param param)''' «getTypeName(param.paramType)» «param.param»'''
©2014 Shintaro Hosoai
Step2-3: テンプレートの拡張 (2)
• 型に応じて名前を返す補助メソッドです.• type=[Unit]|PredefTypes としているため
type は EObject ( EMF の Object )型になっています→• 通常このような場合,オーバーロードできませ
んが dispatch を指定することで,型に応じたメソッドが呼ばれます.
2404/11/2023 SEA 関西プロセス分科会
def dispatch getTypeName(Unit unit){ unit.name}def dispatch getTypeName(PredefTypes predef){ predef.type} EObject
Unit PredefTypes
©2014 Shintaro Hosoai 25
Step3 :
エディタにバリデーション機能をつけよう
04/11/2023 SEA 関西プロセス分科会
Validation
©2014 Shintaro Hosoai 2604/11/2023
Step3-1 : Validation
• エディタにバリデーション(入力規則チェック)を付けてみましょう↓
• Validation 機能は以下クラスで定義します. src/jp.sea.kansai.example.validation/UnitDslValidator.xtend
©2014 Shintaro Hosoai 27
Step3-2 : Validation の定義(1)
• @Check を付けたメソッドが自動的に呼ばれます.• 型ごとのチェックメソッドを列挙するだけでよ
い.04/11/2023 SEA 関西プロセス分科会
class UnitDslValidator extends AbstractUnitDslValidator { public static val INVALID_NAME = 'invalidName'
@Check def checkGreetingStartsWithCapital(Unit unit) { if (!Character.isUpperCase(unit.name.charAt(0))) { warning(‘Name should start with a capital’, UnitDslPackage::Literals.UNIT__NAME, INVALID_NAME) } }}
保存後,実行してみましょう
©2014 Shintaro Hosoai
Step3-2: Validation の定義(2)• Validator 内で使える主要なメソッド
info () : 情報warning () : 警告error () : エラー
メソッドの使い方 info (メッセージ: String, 要素: EStructuralFeature )モデルで警告等を出す場合,モデルのどの要素なのかを指定します.DSL 名 Package というクラスにモデル構造の定数が定義されており,これを参照します.例えば, Unit の name属性の場合 UnitDslPackge::Literals.UNIT__NAMEとなります.
2804/11/2023 SEA 関西プロセス分科会
©2014 Shintaro Hosoai
Step3-Ex : Xtext のビルドエラー対策
• Xtext の言語定義に不具合があった際のエラーは,エラーメッセージを読んでも,どこに不具合があるのか分かり辛いです.• 言語定義は,内部的に Antlr という Java の Parser
ジェネレータのファイルに変換されます.• 生成された g ( Antlr )ファイルのエラー行の付近
を確認し, // Rule ~ 等を探して該当するルールを判別します.
•他に良い方法があれば教えてください^^;2904/11/2023 SEA 関西プロセス分科会
©2014 Shintaro Hosoai 04/11/2023 30
Step4 :
DSL に Java の要素を取り込むJVM統合
©2014 Shintaro Hosoai 3104/11/2023
Step4-1 : Java 要素の取り込み• Xtext では, Java のライブラリを利用するよう
な DSL も作成できます.
• Java 要素を用いるには,以下の作業が必要です.• 言語定義に Xbase を Mixin する• 言語定義を Jvm に適したものに変更する• クラスとして扱う要素を Xtext に伝える
©2014 Shintaro Hosoai
Step4-2 : Xtext の変更(1)
3204/11/2023 SEA 関西プロセス分科会
grammar jp.sea.kansai.example.UnitDsl with org.eclipse.xtext.xbase.Xbasegenerate unitDsl "http://www.sea.jp/kansai/example/UnitDsl"
Model: units+=Unit*;
Unit: "unit" name=ValidID ("extends" parent=JvmTypeReference)?"{" methods+=Method* "}";
Method: retType=JvmTypeReference name=ValidID (“(”params+=FullJvmFormalParameter (","params+=FullJvmFormalParameter )*")")?;
PredefTypes: type="int"| type="String" | type="Void";
Value : INT|STRING;
©2014 Shintaro Hosoai
Step4-2 : Xtext の変更(2)• ベース言語の変更grammar jp.sea.kansai.example.UnitDsl with org.eclipse.xtext.xbase.Xbaseベースとなる言語を Xbase に変更します.Xbase は, Xtend のコア部のようなもので, Jvm統合に必要な言語要素が定義されています.• Reference の変更
型を参照している箇所を, JvmTypeReference に変更します. [Unit] 等は削除します.• Parameter の変更FullJvmFormalParameter という便利そうな型があるので利用します• ID の変更ID の部分を ValidID に変更します.
3304/11/2023 SEA 関西プロセス分科会
©2014 Shintaro Hosoai
Step4-Ex : 左再帰• この変更の過程で, Method 定義が左再帰になってし
まっているようです.( JvmTypeReference の中でMethod を参照しているのでしょうか・・)• 正攻法でいくのであれば,左再帰を除去するのです
が,,今回は時間の都合上(というか作者の能力上・・) Back Track を有効にします.生成時間が多少伸びるのと・・もしかしたら言語に少し悪影響があるかも.• GenerateUnitDsl.mwe2 を開き, backtrack を検索.コメ
ントを外すfragment = parser.antlr.XtextAntlrGeneratorFragment auto-inject {
options = {
backtrack = true
}
}3404/11/2023 SEA 関西プロセス分科会
©2014 Shintaro Hosoai
Step4-3 : Xtext にクラス要素を伝える• src/jp.sea.kansai.example.jvmmodel/
UnitDslJvmModelInferrer.xtendを開き以下のように変更
3504/11/2023 SEA 関西プロセス分科会
def dispatch void infer(Unit unit, IJvmDeclaredTypeAcceptor acceptor, boolean isPreIndexingPhase) { acceptor.accept(unit.toClass(unit.name)) }
infer を書き換え,型として扱いたい要素を
指定
©2014 Shintaro Hosoai
Step4-4 : Xtend の変更
3604/11/2023 SEA 関西プロセス分科会
def genClass(Unit unit)'''public class «unit.name» «IF unit.parent!=null»extends «unit.parent.qualifiedName»«ENDIF»{ «FOR method:unit.methods»«genMethod(method)»«ENDFOR»}'''
def genMethod(Method method)'''«method.retType.qualifiedName» «method.name»(«FOR param:method.params SEPARATOR","»«genParam(param)»«ENDFOR»){
}'''
def genParam(JvmFormalParameter param)''' «param.parameterType.qualifiedName» «param.name»'''
赤文字周辺が変更箇所.灰の文字背景色は意味はありません(なぜか消せませんでした..)
©2014 Shintaro Hosoai 37
Step5Xtext Project をエクスポートして,単独で動作させる
04/11/2023 SEA 関西プロセス分科会
Project の Export
©2014 Shintaro Hosoai
Step5-1: Xtext Project の Plugin化• 作成した 3 つのプロジェクトを選択し右クリック
> Export• jp.sea.kansai.xtext.unitdsl , jp.sea.kansai.xtext.unitdsl.test
, jp.sea.kansai.xtext.unitdsl.ui
• ダイアログから,以下を選択Plugin Development/ Deployable plug-ins and fragments• 次画面の Directry で eclipse をインストールしたフォ
ルダの dropins を選択する.~\eclipse\dropins\• エクスポート終了後, Eclipse を再起動すると,作
成した DSL エディタが利用できるようになる.• また, dropins に生成された jar ファイルを配布す
れば,他の Eclipse 上でも利用できるようになる( Xtext のインストールは必要かも・・) 3804/11/2023