MPS 2020.1 ヘルプ

スコープ

カスタム言語要素のスコープを定義する 2 つの方法、継承 (階層)参照アプローチを見ていきます。実験用のテストベッドとして計算機のチュートリアル言語を選択しました。MPS ディストリビューションに付属のサンプルプロジェクトのセットに calculator-tutorial プロジェクトが含まれています。

2 つの方法

すべての参照は許可されたターゲットのセットを知る必要があります。これにより、ユーザーが参照の値を入力しようとしているときはいつでも MPS が完了メニューに値を入力することができます。既存の参照がそのセットに対して検証され、範囲外の要素を参照している場合は無効としてマークされます。デフォルトでは、参照に対して有効範囲が定義されていない場合、現在のモデル内およびインポートされたモデル内のすべてのターゲットが有効範囲内にあるため、参照に使用できます。

MPS はスコープを定義する 2 つの方法を提供します。

  • 継承スコープ

  • 参照スコープ

継承スコープではモデル内のノードの階層に従ってスコープを徐々に構築することができますが、参照スコープでは低いセレモニーが提供されます。

継承スコープ

最初に、スコープ解決の新しい階層(継承)メカニズムについて説明します。このメカニズムは ScopeProvider を実装する先祖にスコープ解決を委譲します。

  1. MPS は、ScopeProvider を実装し、現在の種類のスコープを提供できる参照ノードに最も近い先祖を探し始めます。

  2. ScopeProvidernull を返した場合、MPS はより遠い先祖の検索を続けます

  3. ScopeProvider はできます
    • スコープ実装を構築して返す (これらの詳細)

    • 親スコープに委譲する

    • 独自の要素を親スコープに追加する

    • 親スコープから要素を隠す (スコープを操作する方法の詳細については後で説明します)

親スコープを取得するための呼び出しは、ScopeProvider 内から明示的に行わなければなりません。

InputFieldReferenceInputField ノードを検索し、それらのリストを構築するためにその先祖に頼ります。

Sc1

Sc2

InputField を検索するときの InputFieldReference のスコープが継承されるように指定したら、計算機ScopeProvider であることを示す必要があります。これは、計算機がその子孫として配置されているすべての InputFieldReferences のスコープを構築することを宣言していることを保証する

Sc3

このケースの計算機は、InputFields の範囲を照会するたびに、その InputField のすべてのリストを返す必要があります。振る舞い計算機の面では、Control + O の方法(getScope())をオーバーライドする

Co1111

スコープが未解決のまま残っている場合は、それを含むモデル(Control + R)をインポートする必要があります(jetbrains.mps.scope)。

Sc5

getScope() メソッドは 2 つのパラメーターを取ります。

  • kind - 参照の可能なターゲットの概念

  • child - 要求の送信元である現在の(this)ScopeProvider の子ノード。実際の参照はノードの子孫の中にあります。

機能をエンコードする必要があるため、BaseLanguage も必要です。ノードを照会するには、jetbrains.mps.lang smodel 言語をインポートする必要があります。これらの言語は自動的にインポートされているはずです。そうでない場合は、Control + L ショートカットを使用してインポートできます。

Sc6

これで、スコープ定義コードを完成させることができます。スコープ定義コードは、本質的に、電卓内からすべての入力フィールドを返する

Sc8

簡単なヒント:SimpleRoleScope クラスの使用に注意してください。これは、独自のカスタムスコープの構築に役立ついくつかのヘルパークラスの 1 つです。SimpleRoleScope N/A に移動し、含まれるパッケージ構造 Alt+F1 を開いて、確認します

スコープヘルパーの実装

MPS には、考えられる多くのシナリオをカバーするヘルパースコープ実装がいくつか付属しており、使用してスコープを定義する作業を容易にすることができます。

  • ListScope - コンストラクターに渡されたノードを表します

  • DelegatingScope - コンストラクターに渡された Scope インスタンスへのデリゲート。通常は既存のスコープの周囲に機能を追加する必要があるスコープによって拡張されます。LazyScope

  • CompositeScope - (ラップされた)Scope インスタンスのグループに委譲する

  • FilteringScope - 述語を使ってノードをフィルタリングしながら、単一の Scope インスタンスに委譲する (isExcluded メソッド)

  • FilteringByNameScope - 単一の Scope インスタンスに委譲し、そのノードを名前ブラックリストでフィルタリングします。これはコンストラクターパラメーターとして取得されます。

  • EmptyScope - ノードを持たないスコープ

  • SimpleRoleScope - 特定のロールに一致する、ノードのすべての子ノードを提供するスコープ

  • ModelsScope - 提供されたモデルのセットに含まれる特定の概念のすべてのノードを含むスコープ

  • ModelPlusImportedScope - ModelsScope と似ていますが、指定されたモデルによってインポートされたすべてのモデルを含みます。

例:getScope() メソッドは、ListScope を使用して次のように書き直すことができます。

Scopex1001

VariableReference

もう少し高度な例が BaseLanguage VariableReference で見つけることができます。variableDeclaration 参照に継承されたスコープを使用します。

scp1

ForStatementLocalVariableDeclarationBaseMethodDeclaration分類子などの概念やその他の概念では、スコープに変数宣言を追加して ScopeProvider を実装しています。

scp2

例:ForStatement は、Scopes.forVariables ヘルパー関数を使用して、for スコープで宣言されたすべての変数で親スコープを強化するスコープを構築します。親スコープ内で同じ名前の変数を隠す可能性があります。from from 式は、現在スコープを解決している参照がサブツリーの特定の部分にあるかどうかを検出する

参照スコープを使用する

スコープは、参照スコープを使用して、高速ではあるがスケーラビリティの低い方法で実装することもできます。

rsc1

解決を行うために ScopeProvider 型の祖先に委譲する代わりに、スコープ解決コードを制約定義に挿入することができます。


rsc2

元々電卓の getScope() メソッド内にあったコードの代わりに、スコープを定義するのは InputFieldReference 自体になりました。: 参照スコープの関数は、ScopeProvider.getScope() メソッドと同様に、スコープインスタンスを返すことになっています。: スコープは基本的に、潜在的な参照ターゲットと、これらのターゲットをテキスト値で解決するためのロジックのリストです。

あなたを思い出させるために、使用する準備ができているいくつかの定義済みスコープ実装と関連するヘルパーファクトリメソッドがあります:

  • SimpleRoleScope - 指定されたロールに属している、指定されたノードに接続されているすべてのノードを単純に追加します

  • ModelPlusImportedScope - インポートされたモデルから参照ターゲットを提供します。ユーザーが Ctrl + R / cmd + R(モデルを含むインポート)によってスコープにターゲットを追加することを許可します。

  • FilteringScope - 他のスコープからいくつかの要素を除外することを許可します。FilteringScope のサブクラスは isExcluded() メソッドをオーバーライドします。

  • DelegatingScope - 別のスコープに委譲します。元のスコープの動作をカスタマイズするためにオーバーライドされることを意味します。

スコープモデルで自分を見回すこともできます。

scp3