MPS 2020.1 ヘルプ

軽量 DSL

MPS コア言語の長年の進化により、IDE 統合用の言語記述型 DSL の繰り返しパターンが認識されるようになりました。概念的には、それらはいくつかのより高いレベルのインターフェースを実装するプレーンなクラスによって表現されるより高いレベルの構造のように見え、ふるまいました。言語指向プログラミングの優れた伝統の中で、これらのパターンを言語に反映し、プレーンな BaseLanguage クラスの上に薄い抽象として再実装することを決めました。そのような抽象化を可能にするために、新しい軽量 DSL 言語が作成されました。

jetbrains.mps.baselanguage.lightweightdsl 言語により、内部 DSL を BaseLanguage クラス内に埋め込むことができます。一般的に内部 DSL は、本格的な外部 DSL よりも開発が簡単かつ迅速です。通常、ホスト DSL の構文を再利用し、周囲の非 DSL コードと緊密に統合されています。同様に、MPS の軽量 DSL は、単一のノードを定義し、そのノードを BaseLanguage ClassConcept またはそのサブコンセプトに織り込むことによって作成できます。

MPS 自体がいくつかの場所でこのメカニズムを利用しています。

  • 単なる BaseLanguage クラスである MigrationScript の概念は、マイグレーション DSLDescriptor によって強化されています。これは、いくつかの追加のプロパティ、メンバー、カスタムメンバーを追加します。

  • 使用箇所の検索

  • インテンション

  • カスタム言語の側面

軽量の DSL をプレーンな BaseLanguage クラスまたは ClassConcept を拡張した独自の概念に織り込むことができます。

普通のクラスを拡張する

Lightweight DSL 言語の背後にある基本的な考え方は、DSL デザイナーが、オプションのメソッドやプロパティ、制約に応じた型、オプションのメソッドパラメーター、カスタムクラスメンバーなど、制約付きのインターフェースを定義できるようにすることです。DSLDescriptor の概念は、クラスの実装に関して強制されるべきそのような制約のあるインターフェースを体系化するノードを表します。例:以下の DSLDescriptor インスタンスがクラスに numberOfFrames プロパティを織るだけでなく、calculateFoo() 方法とカスタム BuilderMember ノードます:

dslclass SwingBuilder for 概念 ClassConcept {
プロパティ numberOfFrames:整数。プレースホルダー <1 フレーム>
メソッド calculateFoo(int value):int; 必須
カスタムメンバー BuilderMember。<修飾子>

イニシャライザ:
{node <ClassConcept> ノード、モデル model =>}
}

特定の DSLDescriptor をクラスに適用するには、DSL 注釈で注釈を付ける必要があります。これはインテンションを介して行われます。

ldsl1

ldsl2

注釈が追加されるとすぐに、DSLDescriptor で定義された必須要素、またはプレースホルダが指定された要素がクラスに追加されます。必要な要素を削除すると、クラスにエラーが表示されます。

ldsl3

インテンションを使用して、必要な要素とプレースホルダーを 1 つのステップで追加し直すことができます。

ldsl4

標準のメソッドの実装アクション Ctrl+I は、必要なメソッドでも機能する

DSLDescriptor を定義する

DSLDescriptor がクラスに追加できる要素は 4 つあります。

  • プロパティ

  • メソッド

  • カスタムメンバー

  • イニシャライザ

これらはそれぞれ必須であり、欠落しているメンバーを表すように定義されたプレースホルダーを持つことができ、カスタムメンバー複数としてマークされてこの種の複数のノードがクラスのメンバーになることができることを示します。

Properties

プロパティには名前があり、文字列型、int 型、または boolean 型にすることができます。プロパティは編まれたクラスの本格的なメンバーになり、これまたは他の DSLDescriptor を通して編まれたものを含む他のメンバーからアクセスすることができます。

メソッド

メソッドはあなたの軽量 DSL のユーザがコードをインジェクションすることを可能にする非常に便利な方法を提供します - DSLDescriptor は強化されたクラスが実装することができるか実装しなければならないメソッドのメソッドシグネチャーを指定します。通常のメソッドとは異なり、これらのウィーブメソッドの戻り値の型およびパラメーターの型は、ホスト(拡張)クラスでの実際の使用状況に基づいて決定できます。

依存型

依存型は、メソッドの戻り型またはパラメーター型に指定されている場合、特定のクラスのメソッドの実際の型を計算する方法を指定します。

ldsl5

条件付きメソッドパラメーター

メソッドは、条件付きとしてマークされたいくつかのパラメーターを持つことができるため、それらは提供された条件が満たされたときにだけユーザコードで見えるようになります。パラメーターを条件付きとしてマークするには、対応するインテンションを使用できます。

ldsl19

実際の条件はインスペクターで指定されています。

ldsl20

カスタムメンバー

プロパティもメソッドも必要な抽象化レベルを提供しない場合は、カスタムメンバーを使用して、わずかな追加作業で任意の概念を織り込むことができます。

ldsl6

CustomMemberDescriptor は、新しいメンバーとしてクラスに組み込まれる概念(この場合は BuilderMember)を指する: これを可能にするには、コンセプトは MemberInstance および ClassifierMember コンセプトインターフェースを実装し、オーバーライドする getDeclaration() メソッドから CustomMemberDescriptor を指す必要があります。

ldsl7

ldsl8

指定された概念のノード(または CustomMemberDescriptor複数として定義されている場合は複数のノード)をホストクラスのメンバーとしてインスタンス化し、その中で直接編集することができます。

ldsl9

イニシャライザー

イニシャライザ関数は、ホストクラスが作成または DSL 注釈でアノテートされるとすぐに、プログラム的にホストクラスを拡張する機会を得ます。イニシャライザが呼び出されたときにノード(ホストクラス)自体がモデルに追加されていない可能性があるため、モデルパラメーターはノードと同様に提供されます。smodel 言語をインポートすると、クラスとモデルを操作することができます。通常、イニシャライザはインポートと使用言語を設定するか、必要に応じて、メンバーに織り込まれたユーザにデフォルト値と実装を提供します。

ldsl10

カスタム ClassConcept サブコンセプトの強化

BaseLanguage クラスを直接拡張するのではなく、ClassConcept クラスの特定のサブコンセプトを拡張する場合の詳細がいくつかあります。これにより、外部 DSL と内部 DSL の利点を組み合わせることができます。目的の DSL にカスタマイズされた編集経験、専用のジェネレーター、または特定の型システム規則が必要になった場合は、通常、単純な BaseLanguage クラスを使用する代わりに ClassConcept サブコンセプトを作成します。そこで、ClassConcept のサブコンセプトを作成し、それに AutoInitDSLClass を実装させます。

ldsl11

AutoInitDSLClass を実装することで、あなたの概念のノードが作成されるときはいつでも、それらの DSLDescriptor で定義されたイニシャライザを呼び出すことによって、DSL に織り込まれたものが正しく初期されることを確実にする

ldsl12

AutoInitDSLClass インターフェースはまた、特定の DSLDescriptor インスタンスを織り込むべき getDescriptor() メソッドをオーバーライドするように実装の概念を強制する

サンプル

Lightweight DSL の基本的な使い方を説明するサンプルは、MPS ディストリビューションにバンドルされている lightweightDSL サンプルプロジェクトにあります。