2011年8月20日土曜日

Xtextグラマーのポイント(2)

さらに 15 Minutes Tutorial のサンプルから、Xtextグラマーのポイントを追ってみます。
今回は、クロスリファレンスなどのXtextの面白い機能も見てみましょう。

grammar org.example.domainmodel.DomainModel with
                                      org.eclipse.xtext.common.Terminals

generate domainmodel "http://www.example.org/domainmodel/Domainmodel"

Domainmodel :
  elements += Type*
;

Type:
  DataType | Entity
;

DataType:
  'datatype' name = ID
;

Entity:
  'entity' name = ID ('extends' superType = [Entity])? '{'
     features += Feature*
  '}'
;

Feature:
  many?='many'? name = ID ':' type = [Type]
;

選択は継承としてモデル化される
Type:  DataType | Entity;
この場合、Typeルールが適用される場所では、DataTypeまたはEntityのいずれかのルールが適用されるというのがグラマー的な理解ですが、モデル的にはTypeはDataTypeとEntityの汎化として扱われます。つまり、基底型Typeを継承したDataType型とEntity型が生成されます。
DataTypeとEntityのいずれが選択されるかに曖昧さが生じないよう注意が必要です。DataTypeとEntityルールには、どちらも先頭にキーワード('datatype'や'entity')が定義されており、出現するキーワードによってルール選択の曖昧さが生じないようになっています。
もし構文に曖昧さがある場合、グラマーコンパイル時のエラーでそれが分かります。

オプション構文とキーワード
Entity:
  'entity' name = ID ('extends' superType = [Entity])? '{'
     features += Feature*
  '}'
;
この場合、'extends'キーワードとそれに続くsuperType=[Entity]の項はオプションです。
「?」は正規表現同様、0〜1回の出現、すなわちオプションであることを意味しています。キーワードを含む割当構文全体がオプションとなります。
この場合はオプション構文が1つしか無いので、'extends'キーワードが無くても曖昧さは生じませんが、複数のオプション構文がある場合は、何が省略されているのかが曖昧になるので、多くの場合オプション構文にキーワードが含まれる必要があります。

キーワードの有無自体のオプション構文

Feature:
  many?='many'? name = ID ':' type = [Type]
;
 'many'キーワードの有無自体がモデル上の情報になるオプション構文です。
「?=」オペレータは、右辺の構文が現れた場合のみ左辺が真になる割当構文です。
この時、Featureモデルのmany属性はboolean型(isMany())としてモデル化されます。

クロスリファレンス
クロスリファレンスの仕組みは、Xtextの面白い強力な機能の一つです。DSL内でユーザが定義した識別子を、DSL内の別な箇所で参照する機能です。

例えば、プログラミング言語の型名や変数名のように、ユーザがDSL内のどこかで定義/宣言した識別子を、他所で指定することが多くありますが、このような場合に使用できるのが、このクロスリファレンスの機能です。
生成されるDSLエディタ上でも、他所で定義した識別子が、参照時にコンテンツアシストされるようになり、とても強力です。
'extends' superType = [Entity] 
type = [Type]
このように右辺が [ルール] となっている割当構文は、クロスリファレンスになります。
このサンプルの例では、ユーザがDSL内で「型」を定義したあと、別な場所でその定義済みの「型名」を指定できるようにしています。

通常の割当構文では右辺でルール呼び出しした結果がフィールドに格納されるため包含参照になりますが、クロスリファレンスの場合のフィールドは非包含参照になります(他所で定義された値を参照するのですから当然ですが)。
(ここで言う包含参照とは、EMFにおけるcontainment=trueな参照モデルです)

クロスリファレンスの参照先の指定には、若干の注意が必要です。
  • DSL上の参照は、参照先モデルの「name」属性と同じ値を、「ID」終端ルールにより記述することで行われます。
    このルールに従わないと、クロスリファレンスは機能しません。
    (変更することは可能です)
  • 参照先名称は、厳密にはルール名ではなくて、モデルのEClass名です。
    Xtextではルール名とモデル名を別定義できるので、その場合は要注意です。


これらのサンプルテクニックを覚えると、ちょっとしたDSLをすぐに作れそうですよね。
どの構文も簡単ですし、どのようにモデル化されるかも分かりやすいと思います。

0 件のコメント:

コメントを投稿