MPS 2020.1 ヘルプ

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

We treat a dot-expression with "value" operation as lvalue-expression, so we override IOperation#lvalue() method in Box_ValueOperation 's behaviour aspect: Once we've overridden this method, the dot-expression with "value" operation can be used at the left-hand side of an assignment: Additionally, this operation can be used in compound assignments (such as +=, -=), as well as the increment and decrement operations: Our final goal is to write a generator for Box_ValueOperation concept.

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

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

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

genxx1

image2018 11 12 11 36 58

The "generic lvalue expression" consists of two obligatory roles: type and 参照。 The former role specifies the type of our original lvalue-expression while the latter role specifies the expression of type jetbrains.mps.references.Reference <T>。 This type provides two methods: #get() in order to read value from evaluated variable and #set() - to assign new value to evaluated variable.

「総称左辺値式」には、さらに、値の 取得と値の 割り当てという 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

To make our extension consistent, we also want overload plus assignment expression likewise we overloaded the plus operator: A significant difference between the plus operator and the plus assignment expression is that in plus assignment a left sub-expression should be an lvalue so this expression has to be evaluated to a variable to which we can assign computed values: However, in Java, we cannot pass variables through method invocations so we can not transform our expression into just a method call.

この問題に取り組むために、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