MPS 2020.3 ヘルプ

正規表現

正規表現言語の概要

導入

正規表現言語(テキスト操作を大幅に簡素化する言語)は、現在最も使用されているドメイン固有の言語の 1 つです。ほとんどすべての開発者が少なくとも 1 回は使用しています。Perl や Python などの一部の言語には、サポートが組み込まれています。Java などの一部は、ライブラリを介して使用します。MPS の実装に使用する言語である Java には、正規表現の言語レベルのサポートがないため、DSL を実装するのが自然であったため、正規表現ライブラリの代わりに DSL を使用できます。この言語は、MPS 言語の良い例です。この概要を読んで、MPS で言語を作成および使用する方法を理解できるようになります。

サンプル

MPS がすでにインストールされていることを前提としています。
このドキュメントでは多くの例を使用しています。それらは、jetbrains.mps.regexp.examples ソリューションの正規表現言語プロジェクト(%MPS_HOME%/platform/regexp)で見つけることができます。

Worddav54fd8999f157b75ecce5d51ca9bff681

言語の概要

簡単な正規表現アプリケーションを見てみましょう。メールアドレスからユーザー名とドメイン名を取得するとします。正規表現を使用してメールアドレスを分析することにより、ユーザー名とドメイン名を出力するコードを次に示します(この例は EmailExample クラスにあります)。

Worddav6357b7cf6d2db641dc49ad95d2b20ce6

この matchregexp ステートメントで使用される正規表現は、次のことを行います。まず、1 つ以上の単語文字(\ w +)を読み取り、「ユーザー」変数に保存します。その後、「@」文字を読み取ります。次に、ピリオド( "。" 文字)で区切られた単語のリストを読み取り、ドメイン変数(\ w +(。\ w +))に保存します。一致するものが見つかった場合、プログラムはユーザーとドメインを System.out に出力します。
この例の正規表現の構文ツリーは次のとおりです。
Worddave83ccb1d63a90d30c3ac013f61241197

言語構造

MPS で言語を作成するとき、通常、その抽象構文を定義することから始めます。MPS の抽象構文は、言語構造と呼ばれます。これを行うには、構造言語を使用します。構造言語は、XML 言語に対応する XML スキーマ、または SQL に対応する DDL です。正規表現の言語構造を見てみましょう。

概要

MPS 正規表現言語には、いくつかの部分が含まれています。

  • 正規表現: 正規表現を指定するために使用される概念。これらには、文字列リテラル、シンボルクラス、「or」および「sequence」正規表現の概念が含まれます。

  • BaseLanguage(BaseLanguage は Java に似た言語であり、ジェネレーターのターゲット言語として MPS によって内部的に使用されます)統合: この部分には、正規表現関連のコードを BaseLanguage に埋め込むために使用される概念が含まれます。例: それは MatchStatement、ReplaceStatement、および SplitStatement が含まれています。

  • 正規表現ライブラリのサポート。正規表現で作業するとき、再利用したいため、このタスクのための特別な概念を作成しました。

正規表現

言語のすべての正規表現の概念は、その構造モデルの「Regexp」フォルダーに配置されます。undefined

Worddavc3eea13ac43a8310f50d2743e3c07435

詳しく考えてみましょう。それらすべてに単一の基本概念があります。正規表現:
Worddavb1c8d5e9a555626d4a7c6f326b4391ba

これは、BaseConcept の概念から派生しています。すべての MPS の概念はそれから派生しています。この概念には、抽象概念プロパティもあります。これは、正規表現を定義するための言語では使用されない、概念階層を形成するために作成されることを意味します。これは、Java クラスの「abstract」修飾子に似ています。
それから派生した概念を考えてみましょう。それらは階層ビューで確認できます。コンセプト宣言で Ctrl + H を押すと、このビューを表示できます。正規表現の概念については、次のようになります。
Worddav641bbd1e055f8a1257d7e09e8545a4b5

StringRegexp は、テキストと照合できる任意の文字列を表します(このセクションで検討する正規表現のすべての例は、Regexps ルートノードにあります)。
Worddav60b32b2dae9e635eaec0472fce900e33

その概念宣言を見てみましょう(エディターで概念のインスタンスが選択されているときに Ctrl + Shift + S を押すと、概念宣言にすばやく移動できます)。
Worddavc394a4e587c8986c177d7a7e08981894

その宣言には、エディターに表示されるテキストを格納するために使用される文字列型のプロパティテキストが表示されます。また、この概念は、概念プロパティ「エイリアス」を宣言します。コンセプトプロパティは単純なプロパティとは異なります。単純なプロパティは Java インスタンスフィールドに対応し、概念プロパティは Java 静的フィールドに対応します。Ctrl + Space を押すと、コンセプトプロパティエイリアスの値が完了メニューに表示されます。
Worddavf9496c8f18b0eb7b37640d05e82a8bf6

バイナリ正規表現は、2 つの異なる正規表現を 1 つに結合する正規表現を表すために作成されます。BinaryRegexp の概念は抽象として宣言されており、OrRegexpSeqRegexp の 2 つの具体的なサブ概念があります。それらのインスタンスの例を次に示します。
Worddav293aaafcd17556c2b1f82ba50ef99148
Worddava8acebee034eb28ab80b57e485b5aebd

その概念宣言は次のとおりです。
Worddav37de03cf815e63b6dab3fbf5daf43f86

2 つのリンクを定義します。1 つは左側の部分を格納するためのもので、もう 1 つは右側の部分を格納するためのものです。「集約」という言葉は、このリンクの正規表現が宣言された概念インスタンスの一部になることを意味します。つまり、構文ツリーを見ると、親 BinaryRegexp の下に子正規表現があります。
Worddav4ad0a0fbd213cbdb7725a1851aaf03bf

ドット正規表現は、任意の文字に一致する正規表現を表します。LineEndRegexp は、行の終わりでのみ一致します。LineStartRegexp は、行の先頭でのみ一致します。ParensRegexp は、囲んでいる正規表現を読みやすくするために、他の正規表現をグループ化するために使用されます。
Worddav24e21f44c149abcb3659dfd9d06e62aa
Worddav642ced80a263561d8c469346025b4836
Worddav94705c1fc94ecb5145b425d14f42afcf

よく使われる記号のセットはたくさんありますが、入力するのは非常に冗長です。(A | B | CZ)の代わりに [AZ] を入力できる文字クラスがあります。ネガティブとポジティブの 2 種類があります。それらは両方とも抽象 SymbolClassRegexp を拡張します:
Worddav802b499d6a7435f7a89accadba5698b3
Worddava11126417379b10b2d63b7b458305019

これらの文字クラスの多くはいくつかの場所で使用されているため、PredefinedSymbolClassRegexp を使用して簡単に参照できます。[AZ] の代わりに「\ w」と書くことができます:
Worddav133f90af8072c5fb9dfe57c7158c29a9

この概念は次のように宣言されます。
Worddav622ddc5598027e3c18f2fbd3f4bf8d24

ここに、参照ステレオタイプを持つ symbolClass リンク宣言があります(前述の集約もリンクステレオタイプです)。参照ステレオタイプとは、この概念のインスタンスに、参照されるノードが子として含まれないことを意味します。代わりに、参照されるノードをモデルの任意の場所に保存できます。
また、抽象的な概念 UnaryRegexp から派生したさまざまな UnaryRegexp があります。+、* およびその他の正規表現操作が含まれます。
Worddav302fe26fe89dcd25ad86915228449271
Worddav196774bebcf4832840530e8a598b777a

テキストを操作するときは、一致するものを覚えて、後で参照すると便利なことがよくあります。この作業を容易にするために、言及する前にマッチした文字列こと、それが一致する文字列を覚えて MatchParensRegexp、および MatchVariableReferenceRegexp を持っています。次のコードは、同じ xml タグのペアとその中にテキストが含まれているものと一致します。
Worddav480b88108c470d3412e1c3ca338bf95d

BaseLanguage 統合

正規表現は、BaseLanguage コードに統合できない場合、少し役に立ちます。正規表現言語には、BaseLanguage で記述されたプログラムで正規表現関連の構造を記述できるようにする特別な概念があります。
BaseLanguage に新しい構造を追加する場合は、通常、BaseLanguage から Expression または Statement の概念を拡張します。式の概念は、「1 + 2」、「a == b」などの式を表します。ステートメントの概念は、「if() {}」、「while() {}」などの制御構造を表します。正規表現言語では、新しい式とステートメントの両方を作成します。
まず、式ではなくステートメントを見てみましょう。
MatchRegexpStatement は、指定された文字列が正規表現と一致するかどうかを確認する場合に使用されます(このセクションの例は、jetbrains.mps.regexp.examples モデルの BaseLanguageIntegration クラスにあります)。

Worddav7ca0d21a461360dc9e35c6d2732b1467

ここには興味深い機能があります。MatchRegexpStatement ブロックで名前付き一致を参照できます。これらの一致変数は、正規表現言語で定義されている他のステートメントで機能します。
FindMatchStatement は、指定された文字列に指定された正規表現との一致が含まれているかどうかを確認します。これは MatchRegexpStatement に似ています。
Worddav49147cbc3846c222907f8ab55c1ed548

ForEachMatchStatement を使用すると、指定された文字列内の指定された正規表現のすべての一致を反復処理できます。
Worddavb00b56a3a457bcfa3bd5c194d9f07a2a

文字列を操作する場合、正規表現のすべての一致を指定されたテキストに置き換えたいことがよくあります。正規表現言語では、ReplaceWithRegexpExpression を使用してこれを行うことができます。
Worddav0324d85e995d77bce18cb1796cbb79a9

文字列を正規表現で分割することも実用的です。例: 1 つ以上の空白記号で区切られた文字列の部分を抽出するには、次の SplitExpression を記述できます。
Worddav8693e20963fbd910cdc6f17e65500d23

ブロック内の一致を参照する場合、MatchVariableReference の概念が使用されます。また、式の概念から派生しています。

ライブラリサポート

正規表現を扱うときは、その一部をさまざまな場所で使用したいと考えています。これらの再利用可能な正規表現を定義するために、特別な概念である Regexps があります。これには、0 個以上の名前付き正規表現が含まれています。

Worddavb1831b4d7d4be636b5f539589a7abd8c

アクセサーリーモデル

多くの言語では、次の問題があります。非常によく似たエンティティがたくさんあり、この言語で記述されたすべてのモデルで使用できます(事前定義されたシンボルクラスの正規表現など)。そのようなすべてのエンティティの概念を作成できます。ただし、MPS にはより優れたソリューションがあります。アクセサーリモデルと呼ばれる特別なモデルを作成し、その中のすべてのエンティティを言語で宣言できます。
シンボルクラスを宣言するために使用される PredefinedSymbolClass の概念があります。また、これらのシンボルクラスを含む PredefinedSymbolClasses コンテナーの概念があります。正規表現言語のアクセサーリモデルを調べると、次のことがわかります。

Worddav26add762091c9a627c43395111298b8a

エディター

概念構造を定義した後、通常はそのためのエディターを作成します。このタスクを実行するために、エディター言語を使用します。使い方はとても簡単なので、最も一般的な構成を考えてみましょう。
すべてのエディター関連のコードは、エディターモデルに配置されます。プロジェクトツリーの言語ノードにあります。

Worddavc1136b6cec7f424fb0462579c99c0799

これが StringLiteralRegexp のエディターです:
Worddav9af071cf04fc04a21f5fda7ba65302a4

これには、水平コレクション、その中に他の構成をグループ化するために使用できるコンテナー、およびインスタンスプロパティのエディターを含めるために使用される {text} が含まれています。
これが MatchVariableReferenceRegexp のエディターです:
Worddavde451156ae66510919af6563b7264e39

これも水平方向のコレクションで構成されていますが、今回はより豊富な構成のセットが含まれています。「(ref」と「)」は定数であり、常に同じテキストが含まれています。「%match%-> {name}」は、一致リンクのターゲットのプロパティ「name」を参照するために使用されます。
これが正規表現のエディターです:
Worddav79e6df4b7ef387a833fa176607b56cd2

ネストされた水平コレクションを持つ垂直コレクションが含まれます。また、「(>%regexp%<)」構成要素が含まれています。ロール「regexp」のすべてのノードのエディターを含めるために使用されます。

スコープ

構造体で参照を宣言した後、それらのデフォルトの代替メニューがあります。これらのデフォルトメニューには、現在のモデルとそのインポートされたすべてのモデルの参照型のすべてのノードが含まれます。動作する場合もありますが、これらのメニューの範囲を絞り込む必要がある場合もあります(例: モデルのさまざまな部分に「name」という名前の一致変数が多数ある場合は、これらの Java スコープルールに従うことをお勧めします。変数。)このタスクを処理するために、制約言語のスコープがあります。
スコープは、言語ノードの制約モデルに配置されます。

Worddavaeb22e88a30788c4c39df5434c19093d

MatchVariableReference のスコープを考えてみましょう:
Worddavc378d6ee860a2581a4e9dbdc23fae9d8

スコープは、指示対象セットハンドラー、スコープ条件(「作成可能」というラベルが付いています)、およびスコープコンストラクターで構成されます。通常、スコープコンストラクターのみが指定されます。スコープコンストラクターは、ISearchScope インターフェースを実装するオブジェクトを返す必要があります。通常、SimpleSearchScope クラスのインスタンスが返されます。ノードのリストを受け取るコンストラクターがあります。つまり、指定された場所に表示されているノードのリストを返します。

アクション

MPS のデフォルトのエディターは、あまり使いやすいものではありません。このデフォルトの動作を改善するために、アクション言語およびエディター言語とは異なる構成を使用できます。
テキストベースの言語でコードを入力する場合、通常は左から右に入力します。「2」から始めて「2+」と入力し、最後に「2+2」と入力します。「右変換」と呼ばれるメカニズムを使用して、この方法で MPS にコードを入力することもできます。
適切な変換アクションを定義するには、アクションモデルに適切な変換アクションルートを作成し、それにいくつかの適切な変換アクションを追加する必要があります。1 つの正規表現を単項正規表現に変換する正規表現言語からの正しい変換アクションを考えてみましょう。つまり、「a」を「a+」、「a*」などに変換します(制約、エディター、構造など)。プロジェクトツリーの言語ノードにアクションモデルがあります):

Worddavced409801e815e076620cb81669d28df

それぞれの正しい変換には、適用可能な概念があります。このアクションを適用できる概念のタイプです。また、条件と最も重要な部分があります。それは、正しい変換メニューです。右変換メニューにはさまざまな種類があります。上の図のメニューは、非抽象 UnaryRegexp サブコンセプトごとに 1 つのメニュー項目を追加します。このメニュー部分のハンドラーは、式を単項式に変換します。

型システム

多くの言語には型システムがあります。モデルをモデルと照合することができ、編集エクスペリエンスを向上させ、ジェネレーターを簡素化するために使用できます。例: 特定の式のタイプがわかっている場合、どのメソッドをその式に適用できるかを計算できます。MPS には、HELGINS と呼ばれる型システム用の特別な言語があります。非常に単純な構造の言語では、それなしで生活することは可能ですが、複雑な言語がある場合、または BaseLanguage と統合したい場合は、少なくとも BaseLanguage 統合の概念のために型システムを作成する必要があります。
HELGINS では、タイプは MPS ノードとして表されます。BaseLanguage のように型のサブ言語がある場合は、それを型チェックに使用できます。
正規表現言語のいくつかのルールを考えてみましょう。

Worddavfbd103e572784a29f920f075b0b64b55

このコードでは、文字列と呼ばれる型を定義します(文字列は、ここでメソッドのパラメーターの型、ローカル変数や他の場所で使用される BaseLanguage、から ClassifierType のインスタンスです)。そのために、GIVETYPE ステートメントを使用します。
より複雑なルールを見てみましょう。
Worddav1a274d8e26272ef9d52b6c7f7458bfff

このルールでは、FindMatchStatement の 正規表現と照合する式が文字列型のサブタイプである必要があります。これを行うには、型方程式を指定します。記号「: <=:」はサブタイプを示します。式 TYPEOF は、括弧内の式のタイプを示します。
型を計算するために、HELGINS は多くの時間を節約する洗練されたアルゴリズムを使用します。タイプが計算される順序について心配する必要はありません。しなければならないのはタイピングルールでタイプ方程式を指定することだけです。HELGINS はあなたのために解決します。
もちろん、私たちの言語のルールは非常に単純です。HELGINS について詳しく知りたい場合は、BaseLanguage やモデル言語などの言語のルールを確認する必要があります。

生成プログラム

MPS で作成されたほとんどすべての言語にはジェネレーターがあります。MPS のジェネレーターは、高水準言語コードを低水準言語のコードに変換します。ジェネレーターの重要なコンポーネントは、そのマッピング構成です。それは私たちに言語をどうするかを教えてくれます。
正規表現言語のマッピング構成について考えてみましょう。

Worddavf77db0d024ca925203b56a14931eb487

これには、1 つのマッピングルールと複数の削減ルールが含まれています。各ルールには適用可能な概念があります。この概念のインスタンスごとに、ルールが適用されます。マッピングルールは、各アプリケーションに新しいルートノードを作成します。削減ルールは、それが適用されるノードを新しいノードに置き換えます。各ルールには、出力ノードの作成に使用されるテンプレートが関連付けられています。
そのようなテンプレートのインスタンスを見てみましょう:
Worddavc1fd76f76c7cffb01f1f0e716bd309a7

テンプレートには、マクロとテンプレートフラグメントを含む MPS コードが含まれています。
テンプレートフラグメントの外側のコードは生成中に使用されず、テンプレートフラグメントの内側のコードのコンテキストを作成するためにのみ使用されます。例: コードが node という名前のパラメーターを持つメソッド内に配置されることがわかっている場合、テンプレートフラグメントの周囲にそのようなパラメーターを持つメソッドを作成する可能性があります。生成中に、MPS はインテンションを認識し、この変数は自動的に解決されます。
マクロは、コードの可変部分を指定するために使用されます。たとえば、上のイメージの変数マッチャーにはプロパティマクロがあります。このプロパティマクロはこの変数の一意の名前を生成するため、ネストされた一致ブロックを使用できます。MPS には、さまざまな種類のマクロがあります。さまざまな種類のノードマクロ、プロパティマクロ、参照マクロです。これらの概念はすべて、jetbrains.mps.TLBase 言語で宣言されています。

参考文献

正規表現言語を見てきました。多くの MPS 言語開発機能を使用していますが、もちろんすべてではありません。MPS の使用方法を学ぶ最良の方法は、基本言語やブートストラップ言語などの別の言語を調べることです。MPS がどのように機能するかを理解するために使用できる、MPS にはいくつかのツールがあります。
それらの 1 つは使用箇所を見つけることです。これを呼び出すには、エディターのポップアップメニューから使用箇所の検索を選択するか、エディターのノードで Alt + F7 を押します。

Worddav49f451933a7d937ef0e3fc0fdcb6f70f

2 つ目は、概念インスタンスの検索です。概念に出くわし、それを使用する方法がわからない場合、それを学ぶための最良の方法は、そのインスタンスを見つけて、それらのインスタンスが何をするのかを理解しようとすることです。
Worddav8b182bc2907aa0f4261f0c1176d60061

MPS ディストリビューションには、%MPS_HOME%/help フォルダーにドキュメントシステムも含まれています。そのうちのいくつかは古く、一部はかなり不完全ですが、MPS の学習に使用できます。