MPS 2019.1ヘルプ

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

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

"value"演算を含むドット式をLValue 式として扱うので、Box_ValueOperationの動作の側面で IOperation#lvalue()メソッドをオーバーライドします。このメソッドをオーバーライドすると、"value"演算を含むドット式を代入の左側で使用できます。さらに、この操作は、複合代入(+ =、-=),、および増減操作など)で使用できます。最終的なゴールは、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

"一般的な左辺値式"はtype参照. の2つの必須ロールで構成されています。前者のロールは元のLValue 式のタイプを指定し、後者のロールはjetbrains.mps.references.Reference <T>。タイプの式を指定します。#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年4月12日