スコープ
カスタム言語要素のスコープを定義する 2 つの方法、つまり継承された(階層的)アプローチと参照アプローチについて見ていきます。実験のテストベッドとして計算機のチュートリアル言語を選択しました。MPS ディストリビューションに付属するサンプルプロジェクトのセットに含まれている電卓チュートリアルプロジェクトを見つけることができます。
2 つの方法
すべての参照は許可されたターゲットのセットを知る必要があります。これにより、ユーザーが参照の値を入力しようとしているときはいつでも MPS が完了メニューに値を入力することができます。既存の参照がそのセットに対して検証され、範囲外の要素を参照している場合は無効としてマークされます。デフォルトでは、参照に対して有効範囲が定義されていない場合、現在のモデル内およびインポートされたモデル内のすべてのターゲットが有効範囲内にあるため、参照に使用できます。
MPS はスコープを定義する 2 つの方法を提供します。
継承スコープ
参照スコープ
継承スコープではモデル内のノードの階層に従ってスコープを徐々に構築することができますが、参照スコープでは低いセレモニーが提供されます。
継承スコープ
最初に、スコープ解決の新しい階層(継承)メカニズムについて説明します。 このメカニズムは、スコープ解決を ScopeProvider を実装する祖先に委譲します。
MPS は、ScopeProvider を実装し、現在の種類のスコープを提供できる参照ノードに最も近い祖先の検索を開始します。
ScopeProvider が null を返す場合、MPS はより遠い祖先の検索を続行します。
各 ScopeProvider は次のことができます
スコープ実装をビルドして返す (これらの詳細)
親スコープに委譲する
独自の要素を親スコープに追加する
親スコープから要素を隠す (スコープを操作する方法の詳細については後で説明します)
親スコープを取得するための呼び出しは、ScopeProvider 内から明示的に行う必要があります。
私たちは、このように InputFieldReference の InputField のための検索は、ノードとそれらのリストを構築するためにその祖先に依存しています。
の InputField を検索 InputFieldReference ためのスコープが継承されることを指定したら、電卓が ScopeProvider であることを示す必要があります。これにより、Calculator は、その子孫として配置されるすべての InputFieldReferences のスコープを構築する際に発言権を持つようになります。
ここで、Calculator の構造に移動し、ScopeProvider コンセプトインターフェースコンセプトが実装されていることを確認します。
この場合の電卓 は、InputField のスコープを照会するたびに、すべての InputField のリストを返す必要があります。電卓 の動作の側面では、getScope() メソッドをオーバーライド(Control + O)します。
スコープが未解決のままの場合は、スコープ(jetbrains.mps.scope)を含むモデル(Control + R)をインポートする必要があります。
getScope() メソッドは 2 つのパラメーターを取ります。
kind - 参照の可能なターゲットの概念
child - 要求の送信元である現在の(this)ScopeProvider の子ノード。実際の参照は子ノードの子孫の中にあります。
一部の機能をエンコードする必要があるため、BaseLanguage も必要です。jetbrains.mps.lang。ノードをクエリするには、smodel 言語をインポートする必要があります。これらの言語は自動的にインポートされているはずです。そうでない場合は、Control + L ショートカットを使用してインポートできます。
これで、スコープ定義コードを完成させることができます。これは、本質的に、計算機内 からすべての入力フィールドを返します(親スコープを使用できるようにするには、jetbrains.mps.lang.Scopes 言語をインポートします)。
簡単なヒント: SimpleRoleScope クラスの使用に注意してください。これは、独自のカスタムスコープの構築に役立ついくつかのヘルパークラスの 1 つです。SimpleRoleScope(Control/Cmd + N)に移動し、含まれているパッケージ構造 Alt+F1 を開いて、確認してください。
スコープヘルパーの実装
MPS には、考えられる多くのシナリオをカバーするいくつかのヘルパー Scope 実装が付属しており、使用してスコープを定義するタスクを簡単にすることができます。
ListScope- コンストラクターに渡されるノードを表します
DelegatingScope- コンストラクターに渡された Scope インスタンスに委譲します。通常、既存のスコープの周囲に機能を追加する必要があるスコープ(LazyScope など)によって拡張されます。
CompositeScope- (ラップされた)スコープインスタンスのグループに委譲します
FilteringScope- 単一の Scope インスタンスに委譲し、そのノードを述語でフィルタリングします (isExcluded メソッド)
FilteringByNameScope- 単一の Scope インスタンスに委譲し、コンストラクターパラメーターとして取得する名前ブラックリストでノードをフィルタリングします
EmptyScope- ノードのないスコープ
SimpleRoleScope- 特定のロールに一致するノードのすべての子ノードを提供するスコープ
ModelsScope- 提供されたモデルのセットに含まれる特定の概念のすべてのノードを含むスコープ
ModelPlusImportedScope -ModelsScope と同様ですが、特定のモデルによってインポートされたすべてのモデルが含まれます
例: getScope() メソッドは、次のように ListScope を使用して書き直すことができます。
VariableReference
少し高度な例が BaseLanguage にあります。VariableReference は、その variableDeclaration 参照に継承されたスコープを使用します。
ForStatement、LocalVariableDeclaration、BaseMethodDeclaration、Classifier などの概念は、スコープに変数宣言を追加して、ScopeProvider を実装します。
例: ForStatement は Scopes.forVariables ヘルパー関数を使用して、for ループで宣言されたすべての変数で親スコープを強化し、親スコープで同じ名前の変数を非表示にする可能性のあるスコープを構築します。come from 式は、現在スコープを解決している参照がサブツリーの指定された部分にあるかどうかを検出します。
参照スコープを使用する
スコープは、参照スコープを使用して、高速ではありますがスケーラビリティの低い方法で実装することもできます。
ScopeProvider タイプの祖先に委譲して解決を行う代わりに、スコープ解決コードを制約定義に直接挿入できます。
元々電卓の getScope() メソッド内にあったコードの代わりに、スコープを定義するのは InputFieldReference 自体になりました。 参照スコープの関数は、ScopeProvider.getScope() メソッドと同様に、Scope インスタンスを返すことになっています。スコープは基本的に、潜在的な参照ターゲットと、これらのターゲットをテキスト値で解決するためのロジックのリストです。
念のために言っておきますが、いくつかの事前定義された Scope 実装と関連するヘルパーファクトリメソッドをすぐに使用できます。
SimpleRoleScope- 指定されたノードに接続され、指定されたロールにあるすべてのノードを追加するだけです。
ListScope- コンストラクターで提供されるノードのリスト(シーケンス)の周りにスコープ実装を構築します。
ModelPlusImportedScope- インポートされたモデルからの参照ターゲットを提供します。ユーザーが ctrl + R/cmd + R (インポートを含むモデル)でスコープにターゲットを追加できるようにします。
FilteringScope- 一部の要素を別のスコープから除外できます。FilteringScope のサブクラスは、isExcluded() メソッドをオーバーライドします。
DelegatingScope- 別のスコープに委譲します。元のスコープの動作をカスタマイズするためにオーバーライドすることを意味します。
スコープモデルで自分の周囲を見回すこともできます。
言語構造のキャッシュ
スコープを一度計算し、指定されたキーにキャッシュする参照スコープ関数には、いくつかの式が用意されています。モデルがこのようなキャッシュされたスコープを要求すると、スコープは一度だけ計算され、後続のリクエスト用にキャッシュされます。
モデル [ ファクトリ、キー ] - 「factory」関数は提供されたモデルのスコープを計算し、そのスコープは「key」にキャッシュされます。
可視ルート [ concept declaration ] - 現在のモデルから見える特定の概念のすべてのルートのスコープを作成し、それをキャッシュします。
可視ノード [ concept declaration ] - 現在のモデルから見える特定の概念のすべてのノードのスコープを作成し、それをキャッシュします。
関連ページ:
MPS 電卓言語チュートリアル
このチュートリアル導入、テンプレートの実装、導入このチュートリアルでは、MPS での言語デザインのさまざまな分野について説明します。単純なスタンドアロン言語の抽象的な構造を定義し、そのためのエディターを設計し、タイプを制限し、スコープを作成し、最後に Java コードを生成するジェネレーターを準備します。以前の MPS への露出に応じて、このチュートリアルでは完了までに約 1 日かかります。このチュートリアルでは、主に MPS を評価する必要のある言語デザイナーを対象とし、ベアボーンの例以外のも...
MPS 電卓言語チュートリアル
導入:このチュートリアルでは、MPS での言語デザインのさまざまな分野について説明します。単純なスタンドアロン言語の抽象的な構造を定義し、そのためのエディターを設計し、タイプを制限し、スコープを作成し、最後に Java コードを生成するジェネレーターを準備します。以前の MPS への露出に応じて、このチュートリアルでは完了までに約 1 日かかります。このチュートリアルでは、主に MPS を評価する必要のある言語デザイナーを対象とし、ベアボーンの例以外のものを見たいと思っています。Java の基...
説明コメント
ノードに属性を追加する機能を活用する方法を学びます。この機能は、構造の章の属性のセクションで詳しく説明されています。このクックブックでは、電卓チュートリアル言語に簡単な追加機能を作成して、電卓定義の入力フィールドと出力フィールドに説明的なコメントを付けることができます。これらの説明コメントは、入力フィールドと出力フィールドに対応する Swing テキストフィールドに添付された「ポップアップツールチップ」の形式で、生成された Java コードに伝達されます。計算機の言語:実験のテストベッドとして...