MPS 2019.1ヘルプ

共通言語パターン

この章では、MPSを学ぶ初心者がよく遭遇するであろう共通言語設計パターンについて説明します。私達はMPSユーザーがオンラインフォーラムで確認する最も頻繁な質問としてこれらの時間を経て特定したため私達は容易な参照のために一箇所に答えをまとめることにした。

言語パターン付きのサンプルプロジェクト

MPSはサンプルプロジェクトにバンドルされています。ここで説明したパターンの多くは、languagePatternsサンプルプロジェクトに実装されています。MPSで開くことができ(MPSのようこそ画面でサンプル・プロジェクトを開くをクリック)、サンプルプロジェクトを参照しているこの章のパターンの実際の実装を確認することができます。

mps patterns1

パターンは言語定義とサンドボックスソリューションの両方で仮想パッケージにまとめられ、個々のパターンを形成する要素を識別できます。

初期のヒント

命名の概念

多くの概念では、ノードの名前を保持するための文字列プロパティーを提供する必要があります。概念の名前には特別な特質があり、特別な方法でMPSによって認識され処理される必要があるため(たとえば、コード補完メニューやプロジェクトビュー、ノードエクスプローラー、デバッガツールウィンドウなどでノードにラベルを付けるため) INamedConceptコンセプトインターフェースからプロパティーを継承することをお勧めします。あなたの概念の多くとほとんどすべての根本的な概念はこのようにINamedConcept実装することを宣言するべきです。

有効な識別子

BaseLanguageを拡張していて概念の名前が直接Java IDに変換される場合は、INamedConceptを拡張するIValidIdentifier概念インターフェースからnameプロパティーを継承することを検討してください。それは、制約を通して、その名前が有効なJava識別子の基準を満たすことを保証します。

固有の名前

同じ概念のノードの特定のグループに対して、名前の一意性を保証したいことがよくあります。制約または非型システム規則のどちらかを使うことができます。Not-typesystemルールはエラーメッセージをカスタマイズするオプションを提供し、自動的な問題解決のためにユーザーに迅速な修正を提供することさえあります。計算機チュートリアルの電卓にあるInputFieldの名前の一意性チェックのサンプルは、次のようになります。

checking rule check_InputField {   applicable for concept = InputField as inputField   overrides false   do {     if (inputField.parent : Calculator.inputField.any({~it => it.name :eq: inputField.name && it :ne: inputField; })) {   error "Duplicate name " + inputField.name -> inputField; }   }                                                                                                                                                                                    }                                                                                                                                                                                     

constraintで指定されたものと同じ制約は、次のようになります。

concepts constraints InputField {    can be child <none>        can be parent <none>        can be ancestor <none>            property {name}      get:<default>      set:<default>      is valid:(propertyValue, node)->boolean {        node.parent : Calculator.inputField.where({~it => it.name :eq: propertyValue; }).size <= 1;      }            <<referent constraints>>        default scope      <no default scope>      }

いくつかの理由から、ここでは制約はおそらくあまり最適ではないことに注意してください。

  • クイックフィックスを指定することはできません

  • エラーメッセージはカスタマイズできません

  • 非型システムルールではエラーを示すために重複した名前にのみ下線を引いていますが、制約はモデルに無効な値が挿入されるのを防ぎ、無効な値を赤いフォントで表示します。

型システムエラーが表示されない

型システムはバックグラウンドで実行され、結果をエディターに配信するのが遅くなることがあります。エディターのすべてのエラーと警告メッセージを更新するためにF5を押してください。省電力モード設定がオフになっていることも確認してください。パワーセーフモードでは、型システムはユーザーからの明示的な要求に応じてのみ動作します(F5)。

正規表現

デフォルトでは、プロパティーは、整数ブール値文字列の3つのタイプのいずれかになります。よりカスタマイズされたデータ型が必要な場合は、それを定義して正規表現で許容値を制限する必要があります。BaseLanguageはこのようにfloat型を定義しています。例: FloatingPointConstantの概念(コントロール / Cmd + N)を開き、プロパティーを確認します - それは_FPNumber_Stringの型を持ちます。_FPNumber_String(コントロール / Cmd + B)の定義に移動して、制約付きデータ型の例を見ます。

constrained string datatype: _FPNumber_String                                                                                           matching regexp: -?[0-9]+\\.[0-9]*([Ee][\\+\\-]?[0-9]+)?[dD]?

安全なオペレータと操作

BaseLanguageとそのコア拡張機能には、Javaで正しく動作させるためにより多くの努力を必要とする一般的な操作のための便利なショートカットがいくつか用意されています。たとえば、比較する前にnullをチェックする、のリストとnullリストを区別する、「==」の代わりにequalsを呼び出す、などです。

  • :式: - null-safeが等しい

  • ねえ: - nullセーフではない

  • 無効です

  • isNotNull

  • サイズ

  • isEmpty

  • isNotEmpty

基本パターン

コンテナー - コンポーネント

languagePatternsサンプルではcontainer-component仮想パッケージとして示されてます。

コンテナー(この場合はFruitPlate)に複数の種類(リンゴ、オレンジ)の要素(果物)が含まれる一般的なシナリオ。抽象概念(Fruit)がコンテナーのプレースホルダーとして使用され、具体的な下位概念(Apples、Oranges)が言語ユーザーによって明示的に提供されます。サブコンセプトは完全に異なる外観を持つことがあります。

mps patterns2

また、seamless-substitutionパターンを参照して、ユーザーがリンゴをオレンジに切り替えたり、元の状態に戻したりできるようにする方法についても説明します。現在の実装では、ユーザーが最初にノード全体(AppleまたはOrange)を選択する必要があり、それからコード補完が代替案を提供します。

patterns3

カスタマイズプレゼンテーション

languagePatternsサンプルではcustom-presentation仮想パッケージとして示されてます。

http://forum.jetbrains.com/thread/Meta-Programming-System-2138?message=Meta-Programming-System-2138-3(英語)での議論に動機付けられて

コンテナー内で保持されているコンポーネントはComponentUsagesから参照されています。ただし、参照には、補完メニューとエディターの両方に、コンポーネントの名前と一緒に、コンテナー名前を含めてください。

patterns4

patterns6

さらに、スコーピングルールは、コンポーネントへの参照が1つだけ存在し、完了メニューがフィルタ処理され、すでに参照されているコンポーネントが提供されないようにする必要があります。

patterns5

宣言 - 参照

languagePatternsサンプルではdeclaration-references仮想パッケージとして示されてます。

patterns8

宣言とそれらへの参照の典型的なパターン - イベントでの歌手は、それらのパフォーマンスを使って議題にまとめることができます。さまざまなタイプのパフォーマンスがあります。

スコープ規則はそれを確実にします:

  • 現在のイベントにリストされている歌手だけがアジェンダに追加できます

  • 各歌手は、組み合わせられたパフォーマンスで一度だけリストされることができます

  • パフォーマンスで入力された文字列から歌手を作成するのに便利なインテンション(歌う)があります。(「使用方法から作成」または「変数の導入」リファクタリングとしてよく知られています)。

流暢な編集

languagePatternsサンプルではfluent-editing仮想パッケージとして示されてます。

patterns7

テキスト風の編集環境を作成し、Editorクックブック(エディタークックブック)に記載されている推奨事項の多くを実装する例。描画コマンド(線、長方形)を指定するための簡単な言語を実装しています。

  • Enterキーを押すと空行が挿入されます: 子コレクションに指定されたデフォルトノードファクトリのおかげで空行はどこにでも配置できます

  • IDontSubstituteByDefaultコンセプトインターフェースを実装しているため、空の行が補完メニューのオプションとして表示されない

  • 空行に入力すると目的の項目が挿入されます (線または長方形)

  • コードブロックの本体が空の場合、カーソルは最初の行(ヘッダーの隣)に置かれ、この最初の行から編集を開始できます。

  • ノードが別のノードに置き換えられた場合、NodeFactoriesは新しいノードに値を伝達します。

  • 左側の変換により、描画コマンドの左側にオプションの「線種」子を指定できます。

  • ラッパーを使用すると、空の行に目的の線種を入力でき、中間の "IncompleteCommand"が自動的に作成されます。

  • IncompleteCommandは線か長方形のどちらかがタイプされることを期待しており、即座に希望の描画コマンドに置き換えられるでしょう

  • drawコマンドはエイリアスとエディターを定義し、それらが同じ単語で始まるようにします。

  • drawコマンドの抽象スーパーコンセプトのエディターは、具象サブコンセプトによって再利用されます。

  • コマンドの左側に入力すると、許可されている接頭辞が表示されます。

  • 接頭辞(実線、点線)を削除しても、接頭辞のみが正しく削除されます。

オーバーライドエディターコンポーネント

languagePatternsサンプルではoverride-editor-component仮想パッケージとして示されてます。

サブコンセプトでオーバーライドされるエディターコンポーネントの使用例(トラックCarのエディターは、Carでも定義されているエディターコンポーネント車のプロパティーを使用します。サブコンセプト(トラック)は、車のプロパティーエディターコンポーネントをTruckPropertiesエディターコンポーネントでオーバーライドして、独自のプロパティーを含みます。Carのエディターは、トラック用のエディターコンポーネントのトラックバリアントと用のCarバリアントを使用します。

patterns9

シームレス置換

languagePatternsサンプルではseamless-substitution仮想パッケージとして示されてます。

patterns10

異なる関連サブ概念をシームレスに切り替える(置換する)例。リクエストには「説明」が含まれています。これは、文字列単純形式、または複合形式のいずれかです。補完メニューを使用すると、ユーザーは要求された説明タイプを選択できます。ユーザーが単にテキストを入力すると、"PickTheRightDescriptionType"置換アクションで "string" -based説明が自動的に選択されます。それぞれの記述概念のためのエディター最初のセルは置換の影響を受けやすく(セルの "menu"プロパティーを通して設定される)、それで補完メニューで記述タイプを切り替えるオプションを提供します。"コンバーター"ノードファクトリには、記述情報の一部を保存し、それを新しくインスタンス化された記述概念に伝播するコードが含まれています。

patterns11

patterns12

階層スコープ

何らかの種類の参照を定義している場合は、通常、参照の範囲を制限して、参照が特定の場所にあるノード、または参照からの「距離」を指すことができるようにします。さらに、定義によって、参照から遠く離れた同じ名前の他の参照を隠すことができます。たとえば、Javaのローカル変数がパラメータの宣言やフィールドを隠しているとします。MPSでは、参照を定義するときに、言語定義の制約の側面で参照の範囲を指定します。階層スコープでは、継承されたスコープ型を参照に使用してから、コードコンテナー(別名クラス、メソッド、ブロックステートメントなど)にScopeProvider概念インターフェースを実装させます。そのgetScope()メソッドでは、指し示したい参照のために特定の種類の候補ターゲットを取得するロジックを実装します。詳しい使い方はスコープドキュメントをチェックしてください。

自分の参照用にDotExpressionを拡張する

languagePatternsサンプルでdotexpression仮想パッケージとして示されてます(MPS 3.3以降にバンドルされています)。

ドット表記による要素の参照解除は、多くの言語で非常に一般的な方法です。BaseLanguageは、Javaの "ドット"演算子を模倣するドット式の概念を提供します。BaseLanguageの式を拡張するのであれば、あなたの言語でそれを利用することができます。検証式で検証する必要があるいくつかのアドレスを持つ単純な形式を想定しましょう。検証式に値を含めるには、式で各住所の番地と郵便番号を参照する必要があります。

patterns13

各アドレスはアドレスコンセプトのノードで表されます。また、コード補完メニューに「kind」プロパティーが表示されるようにプレゼンテーションもカスタマイズされています。

patterns14

patterns15

AddressReferenceの概念では、検証セクションのBaseLanguage式の中からアドレスを参照できます。

patterns16

すべてのBaseLanguage式は、「ドット」を追加するとすぐにドット式に変換されます。左側のオペランドは元の式です。残っているのはドット式の右側、操作と呼ばれるものです。オペレーションは操作コンセプトインターフェースを実装しています。1つはストリート用、もう1つは郵便番号用の2つの操作が必要になります。これらの共通の抽象スーパーコンセプトから始めます。

patterns17

具体的な操作では、意味のある別名と型システム規則のみを指定して、周囲の式全体に正しく参加するようにします。

patterns18
最終更新日: 2019年6月7日