BT

最新技術を追い求めるデベロッパのための情報コミュニティ

寄稿

Topics

地域を選ぶ

InfoQ ホームページ アーティクル 「OSLO」を使用してテキストDSLを記述する

「OSLO」を使用してテキストDSLを記述する

ブックマーク

はじめに

Microsoftは、ロサンゼルスで開催されたPDCイベントで「OSLO」ビジョンの構成要素を発表しました。Osloには、次の3つの主要コンポーネントがあります。

  • テキストDSL用のモデリング言語M
  • グラフィカルDSL用デザインサーフェスQuadrant
  • これらのモデルを格納するリレーショナル・データベース・リポジトリ

テキスト言語開発は3つのコア言語で構成され、その2つは技術的にどんな開発者でも作成できます。

  • MGrammar: Syntax Directed Translation(直構文変換)の文法を定義する。
  • MSchema: モデル認識ランタイムが使用できる、Semantic Model(意味論モデル)のスキーマを定義する言語。
  • MGraph: MGrammarを使用して定義したパーサーに対する所定のテキスト入力の変換に関するオブジェクトグラフを示す。

すべての内部DSL(リンク)の「hello world」であるDate DSLは、Mgを使用して日付のDSLを記述し、生成されたMGraphのパーサーを記述して任意の自然言語入力の限定サブセットを有効な日付に変換可能にする良い練習になるかもしれないと、私は気付きました。この記事は、MGrammar言語(リンク)について基本的に精通していることを前提としています。

問題

目標は、日付を自然言語で表す小さいDSLを記述することです。日付を解析する最良の自然言語DSLを記述することではなく、MGrammar言語の能力を探ることです。少なくともこの具体化でサポートしたいものは、次のとおりです。

  • “Today(今日)”、“tomorrow(明日)”、“day after tomorrow(明後日)”
  • “Next Monday(次の月曜日)”、“next year(来年)”、“last month(先月)”、“last week(先週)”
  • “5 days later(5日後)”、“1 year ago(1年前)”、“a year later(1年後)”、“5 months before(5カ月前)”
  • “5 days from today(今日から5日後)”、“10 days before yesterday(昨日から10日前)”
  • そしてもちろん、日付リテラルも受け入れる必要があります!

問題の克服

日付表現を含むセンテンスを、MGraphに変換できる品詞に分解する必要があります。これは後で、推定日付をもたらす関数/演算セットとして解釈できます。分類の目標は、主語目的語述語形式のセンテンスを、黙示的な日付の算出に使用可能な演算子とオペランドの数学的表記に置き換えることです。

それを念頭に置いて、日付表現を次のいずれかに分類します。

  • 絶対日付表現 - 絶対日付は当然ながら説明不要です。これらは絶対日付ですが、簡単にするために、ここでは、たとえば11/20/2008などの「mm/dd/yyyy」形式の日付のみを含めましょう。
  • 日付プリミティブ表現 ? 日付プリミティブは、today(今日)、tomorrow(明日)、yesterday(昨日)など、ある種の定数であるが絶対日付ではない単語または単語の組み合わせと見なすものです。
  • 単項日付表現 ? 1つの演算子と1つのみのオペランドを伴うものです。
  • 二項日付表現 ? 1つの演算子と2つのオペランドを伴うものです。

単項表現と二項表現についてさらに詳しく見てみましょう。

単項日付項表現

二項日付表現

接尾形式の演算子

5 days later”,
1 year ago”,
a year later”,
5 months before

5 days from today”,
10 days before yesterday

接頭形式の演算子

next Monday”,
next year”,
last month”,
last week

 

上の表は、日付フィールドに値を入力しようとするときに使用すると考えられる特定のセンテンスの例を挙げています。センテンスが各カテゴリに分類されている理由と、また、センテンスが、これらのセンテンスで着目するデータを強調する目的で色分けされている理由がこれで明らかになればと思います。黄色いハイライトは、私が演算子と見なすものです()。それらはオペランドに適用します()。オペランドは、演算子が特定の単位数を適用する際の基準となる日付を示します()。単項表現にもオペランドはあり、それは単に黙示されるだけです。オペランドが存在しない場合、演算子は今日が基準になると仮定されます。単項演算子は、センテンスが接尾演算子のように見える演算と、接頭表現がより自然に見える演算の、2種類があります。

これらの考えを形式化する目的で、いくつかの中心概念を定義します。

  • 日付プリミティブ– これを基礎値として考えることができます。ほとんどの場合、結局使用することになる日付プリミティブは「Tday (今日)」の概念です。このDate DSLにおけるすべてが、この1つの時点を基準とします。
  • 絶対日付– mm/dd/yyyyこれは日付リテラルです。簡単にするため、ここでは、mm/dd/yyyyの形式の日付しか気に掛ける必要はありません。
  • 算出日付– これは基礎値を基準に算出される日付です。オペランド、演算子で構成されます。つまり、加算または減算のいずれか、および「どのくらいの量か」を示す単位/相対単位コンポーネントです。
    • オペランド - 計算式において、オペランドは基礎値になります。基礎値は、絶対日付、プリミティブ日付のいずれかです。算出日付は、“yesterday(昨日)”および“tomorrow(明日)”を除き、基礎値として許可されません。
    • 単位 – これは、日、週、月、年の絶対数字です。単位は、NumberOf DaysWeeksMonthsYearsで単位を示す測定単位に関連付けることができます。日、週などの値は、絶対量を示します。
    • 相対単位– 相対単位は、next Monday(次の月曜日)やlast January(この前の1月)などの、「today(今日)」を基準とした特定の曜日や月まで/からの相対日数である日付表現を示すために使用します。相対単位は、NumberOfDaysToOrFromまたはMonthsToOrFromで示されます。したがって、“next Monday”は、NumberOf DaysToOrFrom “Monday”に変換されます。
    • 演算子 - これは加算(+)または減算(-)のいずれかになります。

これを形式化する取り組みの大半は、自然言語からの変換が、MGraphに変換される際にも(DSLを認識するアプリケーションにとって)読みやすいようにするためです。どのようにパーサー出力を消費し、そこから言語を作成したいかの例をいくつか挙げます。下記の例証は、実際は(Intellipad(リンク)ツールを使用した)解析に対する所定の入力の変換結果のMGraphであるという事実は別として、私達が目指して取り組んでいる最終状態を表しています。

Main[
  DatePrimitive[
    "today"
  ]
] 
これは、入力が“today”であるときに見たいものです。today(今日)は基準値として機能する時点を示しているため、この値をプリミティブとして扱います。“today”には特別の意味があるため、これをプリミティブとして扱います。
Main[
  AbsoluteDate[
    Month[
      "11"
    ],
    "/",
    Day[
      "01"
    ],
    "/",
    Year[
      "2008"
    ]
  ]
] 

日付が常にmm/dd/yyyyであると仮定して、絶対日付を示すために、Day、Month、Yearプロパティを持つ絶対日付を示すグラフを作成します。上のグラフは入力が11/01/2008である場合の出力を示しています。

Main[
  CalculatedDate[
    NumberOf[
      DaysToOrFrom[
        "Monday"
      ]
    ],
    Operator[
      "-"
    ],
    DatePrimitive[
      "today"
    ]
  ]
] 

入力が“last Monday” のとき、“today” より前の月曜日を得る分だけ今日から日数を引いた算出日付としてグラフを表します。

Main[
  CalculatedDate[
    NumberOf[
      Days[
        1
      ]
    ],
    Operator[
      "+"
    ],
    DatePrimitive[
      "today"
    ]
  ]
] 

その他すべてが、“today” を基準とした、Number Of Days、Weeks、Months、Yearsを持つ算出日付として示されます。上のグラフは、入力が“tomorrow”の場合の出力を示しています。

解決策

MGrammar言語仕様(リンク)について詳しく説明せずに、基本トークンをミニ言語での定義と仮定すると、言語のトップレベルの文法は下記に抜粋されます。

module MyDSL
{
    @{CaseSensitive[false]}
    language Dates
    {
        syntax Main = d:DateExpression;
        syntax DateExpression =  a:AbsoluteOrPrimitiveDateExpression => a|
                                u:UnaryDateExpression => u|
                                b:BinaryDateExpression => b;
        syntax BinaryDateExpression = q:UnitOrUnits o:Operator a:AbsoluteOrPrimitiveDateExpression
                                        => CalculatedDate[NumberOf[valuesof(q)],
                                                          Operator[o],
                                                          valuesof(a)];
        syntax UnaryDateExpression =
                                o:PrefixUnaryOperator p:Unit
                                    => CalculatedDate[NumberOf[p],
                                                      o,
                                                      DatePrimitive["today"]] |
                                q:UnitOrUnits o:PostfixUnaryOperator
                                    => CalculatedDate[NumberOf[valuesof(q)],
                                                      o,
                                                      DatePrimitive["today"]];
        syntax AbsoluteOrPrimitiveDateExpression = a:AbsoluteDate => a |
                                                   d:DatePrimitive => d;
        syntax DatePrimitive = Yesterday =>  CalculatedDate[NumberOf[ Days[1]],
                                                                      Operator["-"],
                                                                      DatePrimitive["today"]] |
                               Today => DatePrimitive["today"] |
                               Tomorrow =>  CalculatedDate[NumberOf[ Days[1]],
                                                            Operator["+"],
                                                            DatePrimitive["today"]];
        syntax AbsoluteDate = MM "/" DD "/" YY;
        syntax PrefixUnaryOperator = "Next" => Operator["+"] |
                                ("Previous" | "Last") => Operator["-"];
        syntax PostfixUnaryOperator = ("Later" | "After") => Operator["+"] |
                                ("Before"|"Ago"|"Back") => Operator["-"];

        syntax Operator = a:AfterToken => "+" |
                          b:BeforeToken => "-";
        syntax UnitOrUnits = Unit | Units;
        syntax Unit = OneToken? Day => Days[1] |
                        OneToken? Year => Year[1] |
                        OneToken? Month => Month[1] |
                        OneToken? Week => Week[1] |
                        d:DayOfTheWeek => DaysToOrFrom[d] |
                        d:MonthsOfTheYear => MonthsToOrFrom[d];
        syntax Units = n:NumberOf Days => Days[n] |
                             n:NumberOf Months => Months[n] |
                             n:NumberOf Years => Years[n] |
                             n:NumberOf Weeks => Weeks[n];

        ...

        //Excluded for brevity

        ...

        interleave Whitespace = " " | "\r" | "\n";
    }
}

文法を定義したので、生成されたグラフを消費することはごく些細なことです。Mgツールを使用して、文法を「コンパイル」する(ことに似ている)Mgxファイルを生成します。これにより、パーサーが生成されます。生成されたパーサーを、入力の解析に使用できるアプリケーションにロードします。解析された入力は、私達が着目するデータのオブジェクトグラフを生成します。OSLO SDKツールチェーンを使用する開発者ワークフローは、次のとおりです。

ワークフローのステップ1では、Mg.exe文法コンパイラを使用して作成したMGrammar定義のイメージファイルを作成します。イメージファイルは、Xaml形式にシリアル化されOpen Packaging Convention OPC(リンク)フォーマットでパッケージ化されたパーサーにすぎません。 これにより、Xamlサービスを使用して動的なパーサーをランタイム時にロードすることが可能になります。

ワークフローのステップ2では、MGrammarコンパイラは、イメージファイル(1)をロードして、ランタイム時にパーサー(2)を作成します。このパーサーは、テキスト入力(3)を取り、それをMGraph(4)に変換します。

生成されたMGraphのトラバースを行うには、IGraphBuilderの具体的な実装を使用します。かなり平凡なタスクですが、一般化することはそれほど容易ではないと思います。しかしながら、巧みなXAMLトリック(リンク)により、それはどちらも困難ではなくなるはずです。

結論

Mgは驚くほどシンプルなメタ言語です。Mgにはいくつかの制限があり、主に広範囲にわたる文書の欠如が原因です。私が気付いたものをいくつか次に示します。

  • id(“SomeNode”)ノードとSomeNode {…}と呼ばれる静的に定義されたノードの違いが分かりませんでした。Id関数(?)は、ノードのラベルが動的に必要なときに使用されますが、リテラルに対して使用するときに、それに関連付けされた使用イディオム/コンベンションの一部を収集できませんでした。
  • 言語自体にはプリミティブな.netタイプでグラフを表すサポートがありますが、日付リテラルを投影する方法を見つけることはできませんでした。
  • たとえば、年を"yy"形式から"yyyy"形式に標準化するルールが必要な場合、id関数(?)に類似した投影を操作する方法がありませんでした。つまり、入力が‘08'の場合、投影において年に‘20'の接頭辞を付けて‘2008'に変換するようにジェネレータに伝える方法が分かりませんでした。

OSLOは未成熟なプラットフォーム(依然としてCTP)であり、リリースされるまでにMicrosoftは問題点を解決するだろうと私は確信しています。OSLOは確かに、さまざまなドメイン表現を標準の構造化されたデータフォームに変換可能にすることで、とても簡単にドメイン駆動設計(リンク)の可能性を切り開きます。これは、ビジュアルダイアレクトなど、DSLの独自のダイアレクト(方言)で互いの異なるモデル認識/支援システムのドメイン相互作用を可能にします。この例において、中国語で書かれた日付表現の文法があることを恐らく誰もが想像でき、日付表現の同じ構造化表現が作成され、ダウンストリームでモデル駆動アプリケーションによって使用可能なものになります。

参考URL

Oslo Developer Center (リンク) 
 

Introducing "Oslo" - Service Station, by Aaron Skonnard (リンク)

OSLO: Building Textual DSLs (リンク)

Martin Fowler on OSLO (リンク)

First in-depth look at Microsoft’s Oslo and the “M” modeling language (リンク)

 

原文はこちらです:http://www.infoq.com/articles/smalltalk-comeback-schwartz
(このArticleは2008年12月8日に原文が掲載されました)

この記事に星をつける

おすすめ度
スタイル

BT