まっつんです。今回は JetBrainsMeta Programming System を使って、DSL エディタの実装を行い、最後に Meta Programming SystemTMF の比較を行いたいと思います。

MATSUFUJI Hideharu

MPS とは?

Meta Programming System (MPS) は、IntelliJ IDEATeamCity といったプロダクトで知られる JetBrains によって、Apache License, Version 2.0 で提供されているオープンソースソフトウェアで、その名が示す通り メタプログラミング のためのシステムです。

書籍 ジェネレーティブプログラミング (IT Architects'Archive CLASSIC MODER) によると、メタプログラミング とは 「他のプログラムや自分自身を記述したり、操作するプログラムを記述する」 プログラミングを指します。

MPS は 2003 年に研究プロジェクトとして開始されました。2004 年に発表された論文「Language Oriented Programming:The Next Programming Paradigm」ではそのコンセプトの詳細を知ることができます。2006 年からは内部のいくつかのプロダクトの開発に使われ始め、それらで得られた知見とフィードバックをもとにさらに開発が進められ、ついに先日バージョン 1.0 がリリースされました。

MPS の中核にあるのは 言語指向プログラミング (LOP: Language Oriented Programming) という聞き慣れないパラダイムです。

言語指向プログラミング

言語指向プログラミング とは、ドメインに特化した言語を設計し、その言語を使用してアプリケーションを構築するプログラミングスタイルのことを指します。既に数多くのプログラミング言語が存在しているいま、なぜ新しい言語を設計する必要があるのでしょうか?

プログラマはある問題に対して、解決策を頭の中の言語で考えます。そして、その解決策をプログラミング言語に変換 (プログラミング) します。頭の中の言語は非常に豊かな表現力を持っています。それに比べてプログラミング言語の表現力はいかにも乏しいものです。これはプログラミング言語に汎用の表現が求められることに起因しています。よって、頭の中の言語をプログラミング言語に変換するためには多くの時間と労力が必要になります。

この問題は既存のプログラムのコードを読み理解しようとするときにも表れます。プログラマは、プログラミング言語で書かれた表現力の乏しいコードから、プログラムの意図を発掘する必要があります。プログラムのコードは書かれる時間よりも読まれる時間の方が圧倒的に多いため、このことはプログラムの保守コストを増大させる原因となっています。

ノート: プログラミング言語で書かれたコードから当初の意図を完全に復元することは常に可能でしょうか?ジェネレーティブプログラミング (IT Architects'Archive CLASSIC MODER) には、プログラミング言語で書かれたコードから当初の設計情報が失われることについての記述があります。

問題空間を表現するための言語である DSL の場合、プログラミング言語で書かれたコードでは失われるであろう多くの設計情報をそのまま保存できる可能性が高くなります。上手く設計された DSL は意図を的確に表現することができます。これは新しい言語を設計することに対する大きな根拠となります。

DSL の仕様

今回の DSL の仕様は以下の通りです。これは「まっつんチャレンジ 6: 言語ワークベンチによる DSL エディタ の実装 TMF 編」とまったく同じものです。

  • Windows の INI ファイルなどで利用されている name = value 形式のテキストフォーマットであること
  • value には文字列または他の name の参照を記述できること
  • + 演算子によって複数の要素を連結できること

この DSL の使用例は以下のようになります。

foo = "AAA"
bar = "BBB"
baz = "CCC"
qux = foo + bar + baz   // "AAABBBCCC"

MPS のインストール

そろそろ DSL エディタの実装に入りましょう。最初に、以下のサイトから MPS のダウンロードを行います。

http://www.jetbrains.com/mps/download/index.html

次に、ダウンロードしたアーカイブを適当なディレクトリに展開しましょう。

tar xvzf MPS-linux-1.0.1.tar.gz

プロジェクトの作成

次に、プロジェクトを作成します。[File] -> [New Project...] をクリックし、プロジェクトの情報を以下のように入力します。

項目 説明
Namemydslプロジェクトの名称
Language Namespacejp.iteman.mydsl言語のネームスペース

mps2-project.png

Logical View から見える部分ツリー毎に以下のような役割があります。

"S" アイコン DSL を利用したソリューション
"L" アイコン DSL の定義
Modules Pool 再利用可能なソリューションや DSL

DSL の構造を定義する

MPS では、コンセプト を定義し、それらを組み合わせることによって DSL 全体の構造を定義します。コンセプトは拡張したり、プロパティを保持したりできるので、オブジェクト指向言語における振る舞い (メソッド) を持たないクラスと考えると理解しやすいでしょう。

それでは DSL の構造を定義していきましょう。最初にトップレベルのコンセプトとなる Ini コンセプト を定義します。Ini コンセプト は複数の Config コンセプト を保持します。

mps2-concept-ini.png

Config コンセプト はひとつの Expression コンセプト を保持します。MPS が提供するコンセプトである Expression コンセプト は「2 + 3」のような式表現をサポートします。

mps2-concept-config.png

Expression コンセプト が定義しているのは数値や文字列といったプリミティブな型の式表現であり、この場合他のコンセプトを参照することはできません。Expression コンセプト を継承した ConfigReference コンセプト を定義することで、Config コンセプト を式から参照できるようになります。

mps2-concept-config-reference.png

ConfigReference コンセプト はひとつの Config コンセプト の参照を保持します。

これで「foo = "AAA" + bar」のような Config コンセプト への参照を含んだ表現が可能になります。

DSL エディタを定義する

次にコンセプト毎に DSL エディタ上での表現を定義します。

Ini コンセプト については、最初に名称を入力し、次に空行を挿入したあと、Config コンセプト を入力するように定義を行います。

mps2-editor-ini.png

Config コンセプト については、name = value 形式の文字列を入力するように定義を行います。

mps2-editor-config.png

ConfigReference コンセプト については、参照された Config コンセプト の名称を表示するように定義を行います。

mps2-editor-config-reference.png

以上で DSL エディタの定義は完了です。

DSL エディタを実行する

jp.iteman.mydsl を右クリックし、[Generate Language] をクリックすると、DSL エディタが生成されます。

次にソリューションの sandbox を右クリックし、[Create Root Node] -> [jp.iteman.mydsl] -> [Ini] をクリックすると DSL エディタが起動し、DSL を編集することができるようになります。

mps2-execution.png

MPS と TMF の比較

今回と前回のチャレンジで MPSTMFそれぞれによる DSL エディタの実装が完了しましたので、両者を比較してみたいと思います。

保存形

MPSTMF の最も大きな違いは、目に見える部分ではなく DSL の保存形にあります。MPS が抽象構文木 (AST: Abstract Syntax Tree) を保存するのに対して、TMFDSL のテキスト表現をそのまま保存します。

プログラムがテキスト表現で保存されることに、筆者を含めて多くの開発者は違和感を感じないのではないでしょうか。しかし、コンパイラやエディタを実装する立場からすると、テキスト表現から AST に変換する必要があります。パーサやレキサが技術的に確立されているとはいえ、これは手間のかかる処理であることに変わりありません。

このような観点から、AST を保存形として用いることは非常に効率的であると言えるでしょう。

ノート: AST ではなくランタイムのオブジェクト表現 (セマンティックモデル) そのものを保存する方法もあります。例えば、GMF の保存形は EMF モデルです。これは AST よりもさらに効率的な方法といえる可能性があります。

DSL の定義

DSL の定義についても両者の間に大きな違いが見られます。

MPS では、言語を構成するコンセプトを定義し、それらを組み合わせることで DSL の構造 (抽象形) を定義します。次にテキスト表現の DSL のグラマーを、コンセプト毎に定義します。

これに対して、TMF では、テキスト表現の DSL のグラマー (文法) をグラマー言語を使って定義します。

つまり、MPS では DSL の構造と表現を個別に定義しますが、TMF では構造と表現の定義が一体となっています。

この構造と表現が一体となっていることにより、TMF では DSL の抽象形として EMF モデルを使用することができません。そのため、GMF と抽象形を共有することができず、同一の抽象形をグラフィカルなエディタとテキストエディタの双方から同時に操作するといった機能の実装が困難となっています。

DSL エディタ

一見すると、どちらもテキストエディタであり大きな違いはないように見えますが、使用してみると大きな違いがわかります。

TMF のエディタは Eclipse のテキストエディタを拡張したものです。使い勝手は通常のテキストエディタと変わりありません。

しかし、MPS のエディタでは、DSL の構造に基づいた セル 単位で編集を行います。そのため、固定の文字列として定義されている Ini= を編集することはできません。コンセプトやエディタを定義するためのエディタも同じ振る舞いを示します。また、先述のとおり保存形として AST が採用されているので、エディタで表示されているテキスト表現はどのファイルにも保存されることはありません。今回のソースコード表示にテキストではなくスクリーンショットを使ったのはこのためです。

おわりに

今回と前回のチャレンジで MPSTMF という 2 つの言語ワークベンチを見てきました。

ご覧いただいたように、現時点の完成度は MPS の方がかなり上といえるでしょう。一方の TMF は、Eclipse に寄贈されて 1 年程度ということもあり、まだまだこれからという感じがしますが、EMFGMF と連携できる要素は持っているので、大きな可能性を秘めていると言えるでしょう。

ところで、今回と前回のチャレンジででは DSL の保存形を使った処理系については紹介しませんでした。TMFMPS のサンプルには、テンプレート言語を利用して静的にプログラミング言語のソースコードを生成する方法がみられますが、動的 (実行時) に保存形を読み込みセマンティックモデルを生成する方法もあります。処理系についてはまたの機会にご紹介したいと思います。

使用したソフトウェアのバージョン

MPS1.0

参考文献

トラックバック(0)
  • このブログ記事のトラックバックURL:
コメント