MPS 2019.2ヘルプ

BaseLanguageの拡張機能用のジェネレータを実装する

この記事では、LValue 式で動作するBaseLanguageエクステンションのジェネレータを書くための慣用的な方法を紹介します。代入式の左側で使用できる場合、BaseLanguage式は左辺値であると言います。このような式を導入する概念は、動作の側面でインスタンスメソッドExpression#isLValue() または静的メソッドExpression#lvalue() をオーバーライドする必要があります。

この調査は2つの独立した言語で構成されており、どちらもBaseLanguageの小さな拡張です。ソースコードは、MPSディストリビューションのBlReferenceサンプルプロジェクトにあります。

jetbrains.mps.baseLanguage.box

この言語では、新しいLValue 式を導入したカスタムBaseLanguage拡張を生成する方法を示します。提示された技術は、smodelプロパティーアクセスやカスタムコレクションのインデックスによる要素アクセスなどの式を生成するために使用できます。

BoxLanguageの拡張機能は、変数をラップし、それを読み書きする操作を提供する新しい "box" 型を導入します。言語全体は3つの概念で構成されています。

  • モデル内の「ボックス」タイプを表すBoxType

  • "box" インスタンスを作成するための新しい式で使用できるBoxCreator

  • オペランドがボックス型のときにドット式の演算に使用できるBox_ValueOperation

image2018 11 12 10 25 36(英語)

「値」操作を含むドット式をLValue 式として扱うため、Box_ValueOperationの動作の側面で IOperation#lvalue()メソッドをオーバーライドします。このメソッドをオーバーライドすると、割り当ての左側で「値」操作を使用したドット式を使用できます。さらに、この操作は、複合割り当て(+ =、-=など)、および増分操作と減分操作で使用できます。最終的なゴールは、Box_ValueOperation コンセプト用のジェネレーターを作成することです。

素朴なアプローチは、"value" 演算を含むドット式が集約され、それぞれのケースに対して適切なJavaコードを生成するすべてのケースを手動で処理することです。ただし、処理するケースが多すぎるため、この方法では不十分です。また、このアプローチでは、式がBaseLanguageの独立した拡張からの式と集約されている場合は処理できません。

別のアプローチは、私たちの表現をもう一つの、すでに存在するLValue 式に変換することです。式を変換できるLValue 式は、Java言語でいくつかあります。変数参照、フィールドアクセス操作、またはインデックス操作による配列の要素アクセスです。残念ながら、すべてのカスタムLValue 式をJava LValue 式に簡単に変換できるわけではありません。

たとえば、実行時に "box"型が2つのメソッド( getValuesetValue)を提供するボックスインターフェースで表される場合、"value"操作をJava LValue 式に変換するのは困難になります。この課題を解決するためにBaseLanguageの生成時間を使用できます。式を「一般的な左辺値式」に変換します。

genxx1

image2018 11 12 11 36 58(英語)

「汎用左辺値式」は二つの必須のロール、型と参照。から構成されています。前者のロールは作成者らのオリジナルLValue 式のを指定し、後者のロールはjetbrains.mps.references.Reference <T>。型の表現を指定します。この型は二つの方法、評価された変数から値を読み込むための#get()、評価された変数に新しい値を割り当てるための#set()を提供します。

「総称左辺値式」には、さらに、値の 取得と値の 割り当てという 2つのオプションのロールがあります。これらの式は、生成されたコードを単純化し、実行中に参照<T>オブジェクトのメモリ割り当てを少なくするために使用されます。最初の式は左辺値ではなく生成された式が使用される場合に使用され、2番目の式は生成された式が代入の左側のロールを果たす場合にのみ使用されます。値の割り当て式には特別な "value"キーワードがあり、このキーワードは式の中で一度だけ使用する必要があります。

jetbrains.mps.baseLanguage.date

このサンプル言語では、新しい式を導入したり、LValue 式を何らかのロールで集約した既存の式をカスタマイズしたりするカスタムBaseLanguage拡張を生成する方法を示します。

日付BaseLanguageの拡張は、新しい "date" 型と "date" インスタンスに対するいくつかの操作を紹介します。

image2018 11 12 10 45 48(英語)

実行時には、java.time.LocalDate:で "日付"型を表現します。

"date"エクステンションは "date"および "int"インスタンスに対するプラス演算子をオーバーロードしているため、この演算は左の部分式から評価される日付に日数(右の部分式から評価される量)を追加します。生成時に、このプラス演算子を単純な静的メソッド呼び出しに変換します。

image2018 11 12 10 59 49(英語)

image2018 11 12 11 31 48(英語)

拡張を一貫性のあるものにするために、同様にプラス演算子をオーバーロードしたオーバーロードプラス代入式が欲しいです。正の演算子正の代入式の大きな違いは、正の 代入では左の部分式が左辺値でなければならないため、この式は計算値を代入できる変数に評価される必要があるということです。しかし、Javaでは、メソッド呼び出しを通じて変数を渡すことができないため、式を単なるメソッド呼び出しに変換することはできません。

この問題に取り組むために、MPSは "@byRef"生成時間概念を提供します。この式はLValue 式をラップし、生成中にそれをjetbrains.mps.references.Reference <T> 型の式に変換します。このタイプのインスタンスが与えられると、ラップされたLValue 式から評価される変数に対してget&setオペレーションを生成することができます。

"@byRef"式を使用すると、プラス代入式は次のようにして簡単に生成できます。

image2018 11 12 11 33 15(英語)

image2018 11 12 11 34 3(英語)

独立した拡張の構成

上記の2つの拡張子は互いに依存しません。どちらのジェネレータも、BaseLanguageが提供する生成時概念を使用して設計されています。これらの生成時機能により、ジェネレータはより一貫性のあるものになり、定型コードが少なくなります。それに加えて、示された方法でそのジェネレーターが設計されているエクステンションは、互いに依存関係を持たずに安全に互いに集約することができます。

image2018 11 12 11 51 17(英語)

最終更新日: 2019年8月30日