MPS 2019.1ヘルプ

MPS電卓の言語チュートリアル

このチュートリアル

導入

このチュートリアルでは、MPSでの言語デザインのさまざまな分野について説明します。単純なスタンドアロン言語の抽象的な構造を定義し、そのためのエディターを設計し、タイプを制限し、スコープを作成し、最後にJavaコードを生成するジェネレータを準備します。以前のMPSへの露出に応じて、このチュートリアルでは完了までに約1日かかります。このチュートリアルでは、主にMPSを評価する必要のある言語デザイナーを対象とし、ベアボーンの例以外のものを見たいと思っています。Javaの基礎知識はジェネレータをより簡単に完成させるのに役立ちます。


ゴール

このチュートリアルでは電卓言語を作成します。この言語は、ある種の計算を記述する単純な実体をいくつか定義します。これらのエンティティを計算機と呼びます。計算機には入力値と出力値のセットがあり、さらに使用可能な入力値を使用して出力値を計算するための1つ以上の数式が定義されています。

人工的なユースケースを取ります。Java/ PHPプロジェクトに費やされた時間数を単に入力することによって彼女の収益を素早く計算したいJava / PHP 開発者です。結果のアプリケーションは次のようになります(出力値は時間値から自動的に計算されます)。

image001

ゴールは、開発者が以下の4行のコード(「10」と「5」はJavaとPHPの対応する支払いレートを表す単なる定数です)を書くことによって、そのようなアプリケーションを作成することができるようにすることです。

calculator MySalary input PHP Hours input Java Hours output Java Hours*10 + PHP Hours*5

Swingベースのアプリケーション全体は、これらの4行のDSLコードから生成されます。

前提条件

このチュートリアルを通して、不要なグリッチを避けたい場合は、MPSプロジェクションエディターに慣れる必要があります。Fast Track to MPSチュートリアルに従えば、準備は万端です。そうでない場合は、少なくともエディターのコマンドページで基本的なエディターのキーボードショートカットを学ぶか、このページの付録MPSエディターの使い方にリストされている簡単なヒントを調べることを検討してください。

主要なステップ

このチュートリアル

  • 前述の構文を使用して電卓を実装するための言語を作成します。言語は、電卓、その構造仕様、関係、および個々の行動を構成する基本的な論理概念を定義します。

  • 電卓からスイングアプリケーションを構築するためのルールを定義するジェネレータを作成します。

  • 言語で電卓を実装します。

プロジェクトの作成

MPSのプロジェクトは、言語、ソリューション、またはその両方で構成されます。もう少し詳しく、どのように構造化されているかを見ることができます。

まず、MPSを開始します。

さまざまなオプションのウェルカム画面が表示されます。ここからサンプルをすばやく開き、ブラウジングのドキュメントなどを開始できます。

新しい言語を作成する予定なので、新しいプロジェクトを最初に作成する必要があります。

ファイル | 新規プロジェクトをクリックしてください。ウィザードが表示されます。

image002

プロジェクトを「電卓」と名づけましょう。

image003

次に、作成したい言語(DSL)の名前を付ける必要があります。

image004

ご使用の言語には次の命名規則をお勧めします。この規約は、Javaパッケージ(companyName.meaningful.name)に使用されているものに似ています。言語jetbrains.mps.tutorial.calculatorの名前をつけましょう。

さらに、サンドボックスソリューションを作成するチェックボックスをチェックします。解決策は、特定の言語で書かれた一連のモデルです。このソリューションを使用して、新しく作成した言語をすぐにテストします。

理想的には、これらの言語を使用する言語とソリューション用に別々のプロジェクトを用意することになります。ただし、開発している言語用の「サンドボックス」ソリューションがプロジェクトに含まれていることを推奨します。これにより、サンプルアプリケーションを作成して新しい言語を即座にテストできます。

今度はOKを打つ時です。

開いたプロジェクト・ビューで、言語記号(image007)でマークされた jetbrains.mps.tutorial.calculatorツリーノードを展開します。

image007 1

参考までに、その上にある「S」マークの付いたツリーノード(image009)に、使用可能なすべての言語とソリューションのコレクションが モジュールプール ノード(image010)に含まれています。今後の章でこれらの項目について詳しく見ていきます。

まず言語ノードの構造を調べてみましょう。これはまた私達がMPSの後ろに基本的な考えを実現するのを手助けします。

その言語の子ノードには、「M」のひし形のアイコンが付いています。それらはアスペクトモデルと呼ばれます。プログラムが通常テキストファイルに格納されている他の言語とは異なり、MPSではモデル内に格納されています。各モデルはノードのツリーを表し、各ノードは親、子ノード、プロパティー、および他のノードへの参照を持つことができます。各ノードはその概念によって定義されます。

言語を構成するアスペクトモデルは、言語の異なる特徴を表現しています(アイコンは色によって異なります): 今のところ、言語に必要な4つの基本的なアスペクトモデルを考えます:

  • 構造体 - 言語の構文を記述する

  • エディター - 言語で書かれたモデルがエディター内でどのように見えるかを記述する

  • 制約 - どの名前がノードに適しているか、変数参照が指すことができる変数などを記述します。

  • 型システム - ノードの型を計算する方法を説明します

電卓のコンセプト

言語に取り組んでみましょう。

まず、ケースでは電卓で、トップレベルの概念を作成する必要があります。この概念の各インスタンスは、入力フィールドと出力フィールドを含みます。

概念を作成するには、構造アスペクトモデルを右クリックして新規 | 概念を選択します。

image008

概念宣言がエディターで開きます。

image009

概念宣言はノードのクラスを定義し、そのクラスのノードの構造を指定します。


ノードは、次の要素を持つことができます。これらの要素は、概念宣言の対応するセクションで定義されています。

  • プロパティーはノード内にプリミティブ値を格納します。例:ノード名をプロパティーとして定義できます。

  • 参照は他のノードへのリンクを格納します。例:ローカル変数への参照を格納するためにそれらを使うことができます。

  • は集約されたノード、すなわち現在のノード内に物理的に含まれるノードを格納します。例:メソッド宣言はその戻り型と引数を集約します。場合、これらは入力フィールドと出力フィールドになります。

  • 他のセクションは非常に高度なので、このチュートリアルでは説明しません。MPSのユーザーガイドでそれらについて読むことができます。

概念宣言は継承階層を形成します。デフォルトでは、すべてのコンセプトがBaseConceptコンセプトを拡張しています(extendsキーワードの直後にそれを見ることができます)。ただし、必要に応じて別の直接親を指定することもできます。概念が別の概念を拡張する場合、概念は親からすべてのプロパティー、子、および参照を継承します。


コンセプト電卓を名づけましょう。

インスタンスがrootになることができるようになるまで Tab を押し、その値をtrueに設定するために Ctrl+Space を押します。直接trueと入力することもできます。どこでも Ctrl+Space を押す必要はありません。これにより、プロジェクト・ビュールートノードを作成するメニュー項目を使用して電卓インスタンスを作成できます(電卓の概念自体の場合と同じ)。

image010

電卓を名前で参照する必要があります。その中にプロパティー名を作成できます。しかし、そうするためのより良い方法(そして推奨される方法)があります。概念にINamedConceptインターフェースを実装させる必要があります。この概念インターフェースには、プロパティー名が 1つだけ含まれています。MPS IDEはこのインターフェースについて知っていて、それを実装する概念に対するより高度なサポートを可能にします。例: INamedConceptへの参照を作成したい場合、それらの名前は補完メニューに表示されます。プロジェクト・ビューでノードを探索すると、ツリーにINamedConceptの名前が表示されます。

implementsキーワードの後ろにある <なし> プレースホルダにキャレットを置き、Ctrl+Space を押します(エディターの効果的な使用方法の詳細については、付録Aを参照してください)。

image011

INamedConceptを選択し、Enterを押します:

image012

ノードの構文を定義しました。今その側面を定義する必要があります。エディターを作ってみましょう。

電卓のエディターの作成

MPSエディターはテキストエディターのように見えますが、正確にはそうではありません。これは、構文木を直接操作する構造エディターです。

ほとんどすべての既存の言語がテキストベースである場合、構造エディターを使用したいと思う理由は不思議に思うかもしれません。テキストベースの言語は、それらを拡張したいときまで有効です。テキストベースの言語には通常、パーサーがあります。構文解析を決定論的にするためには、文法を慎重に設計する必要があります。これは、言語拡張の場合にはほとんど不可能であることを証明します。言語を拡張する場合、文法を拡張する必要がありますが、他の拡張が何をするかわからないため、この文法が十分であるかどうかはわかりません。Javaに金銭的価値のサポートを追加する2つの言語拡張を考えてみましょう。どちらもmoneyキーワードを追加します。これらの2つの言語拡張を使用するコードを解析すると、moneyキーワードの解釈方法はわかりません。第1言語のキーワードまたは第2言語のキーワードとして解釈する必要がありますか?構造エディターの場合、プログラムが中間テキストプレゼンテーションなしで構文木として直接格納される場合、そのような問題はありません。

MPSの構造エディターは、セルを使用してノードを表します。ノードと同様に、セルはツリーを形成します。セルにはいくつかの種類があります。

  • プロパティーセルを使用してノードのプロパティーを編集する

  • 一定のセルは常に同じ値を示します

  • 収集セルは、その内部の他のセルをレイアウトするために使用されます

電卓のエディターを次のように見せたい:

電卓

このような設計を実装するには、次の操作を行います(Ctrl+Spaceをそのまま使用します)。

  • インデント収集セルを作成します。インデントの収集レイアウトは、セルのテキストを好きなようにします。

  • コレクション内に一定のセルとプロパティーセルを作成します。

Calculatorの概念に新しいエディターを定義するには、エディタータブの一番下の行でエディタータブを選択する必要があります。エディターなしラベルが付いた空のタブが表示されます。エディターのアスペクトはまだ定義されていません。この空のエディターペインをクリックして、支援メニューからコンセプトエディターを選択できます。

image012 1

新しいエディターを作成する

image013

ルートインデントコレクションセルを作成しましょう: Ctrl+Space を押して[ - there:

image014

定数セルを作成しましょう:

image015

その中の電卓とタイプ:

image016

次のように入力すると、この定数を1つのステップで入力できます。"calculator。"記号は、そのセルを定数セルにします。

今度は、(INamedConceptコンセプトインターフェースからの) nameプロパティーのプロパティーセルが必要です。水平方向のリストの最後に別のセルを挿入するには、計算語の末尾で Enter を押して、最後に {name}を選択します。

image017

これで、基本が定義されたら、言語を構築してその概念のインスタンスを作成しようとします。言語を構築するには、プロジェクト・ビューのポップアップメニューから言語を作るアクションを選択してください。

image018

コードが生成されたので、MPSモデルでこの概念のインスタンスを作成および編集できます。MPSはおそらく最初のプロジェクト作成時にすでに空のソリューションモデルを作成していました。何らかの理由でモデルがない場合、jetbrains.mps.tutorial.calculator.sandboxで新しいMPSモデルを作成できます:プロジェクトツリーのサンドボックスノードを右クリックし、ポップアップメニューから新規 | モデルを選択します。

image018 1

New Modelダイアログのモデル名フィールドにjetbrains.mps.tutorial.calculator.sandboxと入力します

image018 2

そしてOKボタンを押します。このモデル内でjetbrains.mps.tutorial.calculator言語を使用するには、表示されたモデル特性ダイアログの使用言語セクションにある+ボタンを押してjetbrains.mps.tutorial.calculator言語を選択してインポートする必要があります。

image018 3

これで、モデル特性ダイアログでOKを押して、最終的にサンドボックスモデルを作成し、そこでそこでCalculatorインスタンスの操作を開始できます。

最初の計算機を定義する

サンドボックスモデルをクリックし、[新規作成]メニューから[電卓]を選択します。

image019

名前を入力できる簡単な電卓があります:

image020

プロパティーセルに名前を入力します。この例では、MyCalcという名前を使用しました。

ご覧のとおり、計算機のエディターは、エディターの観点で書いたものと非常によく似ています。

入力フィールド

次に、入力フィールドの概念を作成しましょう。このフィールドは、出力フィールドでINamedConceptを参照したいので、INamedConceptを実装します。ノードを参照するときは、完了メニューでノードを参照する必要があるため、ノードが必要です。INamedConceptインスタンスの場合、MPSはこれらのインスタンスの名前を認識するため、名前が正しく表示されます。INamedConcept以外の概念を参照することは確かに可能ですが、INamedConceptはこれを行う最も簡単な方法です。

image021

エディターを作成しましょう:

image022

電卓に入力フィールドを含めるには、その構造とエディターを少し変更する必要があります。0..nカーディナリティを持つCalculatorへのInputField型の子を作成しましょう。これを行うには、子セクションにキャレットを置き、Enter またはInsertを押します。その後、InputFieldをターゲットコンセプトとして指定し、子名をinputFieldに、カーディナリティを0..nに設定します。

image023

今度はcellという名前に新しい行を追加します。名前のセルにカーソルを合わせると電球が表示されます。電球で呼び出せるアクションはインテンションと呼ばれます。さまざまな言語でそれらの多くがあります。入力方法がわからない場合は、Ctrl+Space を押すだけでなく、Alt+Enter を押して対応するインテンションを使用することもできます。今 Alt+Enter を押して、新しい行を追加 インテンションを適用してください。

image024

次に、エディターに入力フィールドの垂直リストを表示します。{name}ラベルの最後にある Enter を押して、Ctrl+Spaceを押します。そこに%inputField%を選択してください:

image025

入力フィールドを垂直に配置します。すべてのセルの後に新しい行を追加する必要があります。これは、インテンションで行うことができます: Alt+Enter を押し、子供のための新しい行を追加するを選択します。

image026

次に、左側のプロジェクト・ビューパネルの言語ノードのポップアップメニューを使用して言語を作ってみましょう( Ctrl/Cmd+F9 を押して実行することもできます): そして、サンドボックスを見てみましょう:

image027

エディターが更新されました。これで、新しい入力フィールドを追加できる名前の行にセルがあります。いくつかの入力フィールドを追加しましょう:

image028

出力フィールド

出力フィールドのコンセプトを作成しましょう: 入力フィールドでのみ値を参照したいので、プロパティー名を含める必要はありません(INamedConceptを実装するInputFieldと比較してください):

image029

エディターを作成しましょう:

image030

OutputField型の子を計算コンセプトに追加しましょう:

image031

今度はエディターを変更します。コピー/ペーストはここで使用できます。 Ctrl+Up/Down を使用してセルを選択し、次に Ctrl+C/V をコピー/ペーストします。%inputField%コレクションの後にノードを貼り付けるには、閉じた " - )"括弧で Ctrl+V を押します。inputFieldをoutputFieldに置き換えます。

image032

空のセルで入力フィールドと出力フィールドを分離しました。それを追加するには、inputFieldのセルの後ろにキャレットを置いてEnterを押し、定数を選択してから新しい行を追加 インテンションを使用してこの定数の後に行区切り記号を追加します。

image033

言語を作ってみましょう(言語を右クリックするか、コントロール/ Cmd + F9を押す)し、サンドボックスのコンセプトを見てみましょう:

image034

出力フィールドを追加することはできますが、式の格納を宣言していないため、出力フィールドを入力することはできません。

image035

式サポートの追加

MPSには、JavaのMPSのBaseLanguageがあります。新しい言語を作成するときには、それを拡張したり、基本言語の概念を再利用したりすることがよくあります。出力フィールドに式言語を再利用しましょう。これを行うには、言語をBaseLanguageに拡張する必要があります。MPSの言語拡張は、拡張言語の概念を使用して拡張できることを意味します。BaseLanguageのExpressionコンセプトを使用したいので、拡張言語セクションに追加する必要があります。言語プロパティーダイアログを開きます。

image036

拡張言語のリストにBaseLanguageを追加しましょう。これを行うには、依存関係タブを選択し、追加ボタン(image011)をクリックしてjetbrains.mps.baseLanguageを選択します。次に、スコープのドロップダウンボックスから拡張を選択します。

image037

BaseLanguageにはの概念が含まれています。"2"、"2 + 3"、"abc + abc"などの形式の式を表します。これは出力フィールド式に必要なものです。それを見てみましょう。 Ctrl+N を押し、そこのBaseLanguageからExpressionを選択してください:

image038

表現自体は抽象的な概念です: それを見てみましょう:

image039

式には独自のプロパティーはありません。MPSでどのような表現があるのかを理解するために、その下位概念を見てみましょう。ポップアップメニューの[階層でコンセプトを表示]アクションを選択します。

image040

ご覧のとおり、さまざまな言語でさまざまな表現があります: 表現は非常に広く拡張されています:

image041

OutputFieldに型式の子を追加しましょう:

image042

それに応じてエディターを変更してください(ここでは Ctrl+Space を使用できます):

image043

言語を作ってみましょう(言語を右クリックするか、コントロール/ Cmd + F9を押します)、持っているものを見てみましょう:

image044

モデルの依存関係にBaseLanguageを追加する必要があるかもしれません:

image044 1
image044 2

これで、出力フィールドに式を入力できますが、残念ながらそこの入力フィールドを参照することはできません。

image045

エクスプレッションの概念

入力フィールドへの参照をサポートするには、独自の種類のExpressionを作成する必要があります。それを "InputFieldReference"と名付けて、それをに拡張しましょう。それを作りましょう:

image046

ここに参考文献を追加しました。これには、InputFieldと1-cardinalityの型があります。1カーディナリティの参照が1つだけある概念は、スマートな参照と呼ばれ、エディターで特別なサポートを受けています。少し後でそれについてさらに学びます。これでエディターを作成しましょう。%フィールド% - >を選択してください:

image047

一番右のセルの内側で、{name}を選択します。

image048

フィールド参照によって参照されるノードの名前を表示するには、%field% - > {name}を使用します。

今言語を作って、持っているものを見てみましょう: ノードの入力フィールドを入力できるようになりました:

image049

これはスマート参照のために可能です。これが彼らのしくみです。スマートリファレンスである概念が現在の文脈で利用可能であるなら、MPSはそれが参照することができる可能なノードのリストを見て、そのような各ノードのために1つの補完項目を追加します。それが、完成メニューに幅、高さ、深さがある理由です。

ジェネレータの作成

言語のジェネレータを作成しましょう - Javaで実装を生成したいのです。MPSはすでに作成しているかもしれませんが、作成していない場合は、言語ポップアップメニューの新規→ジェネレータメニュー項目を選択します。

image050

ジェネレーター名を空のままにします。

image051

言語には新しいジェネレータが含まれています:

image052

ジェネレータのエントリポイントはマッピング設定です。どのノードが変換され、どのようにするかを指定します。

image053

生成するものを決める

ジェネレータの開発を始める前に、生成するコードの種類について考えなければなりません: 次のようなものを生成したいかもしれません:

public class Sandbox extends JFrame { private JTextField myInput1 = new JTextField(); private JTextField myOutput = new JTextField(); private MyListener myListener = new MyListener(); public Sandbox() { setTitle("Sandbox"); setLayout(new GridLayout(0, 2)); myInput1.getDocument().addDocumentListener(myListener); add(new JLabel("Input 1")); add(myInput1); add(new JLabel("Output")); add(myOutput); update(); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); pack(); setVisible(true); } private void update() { int i1 = 0; try { i1 = Integer.parseInt(myInput1.getText()); } catch (NumberFormatException e) { } myOutput.setText("" + (i1)); } private class MyListener implements DocumentListener { public void insertUpdate(DocumentEvent e) { update(); } public void removeUpdate(DocumentEvent e) { update(); } public void changedUpdate(DocumentEvent e) { update(); } } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { new Sandbox(); } }); } }

実行時にアプリケーションがどのように見えるかは次のとおりです。

image054

さあ、それを実装しましょう。

ジェネレータの実装

最初にメインクラスのスケルトンを作成しましょう。更新メソッド、メインメソッド、および更新メソッドを呼び出すDocumentListenerでクラスを作成する必要があります。新しいメニューから新しいクラスを選択しましょう:

image055

そして、これを得るでしょう:

image056

ご覧のとおり、MPSはクラスの先頭にルートテンプレート注釈を追加しました。ジェネレータモデル内の非ジェネレータ言語ノードはすべてテンプレートとして扱われます。すべてのテンプレートには入力ノード、つまり生成に使用されるノードが必要です。この入力ノードのタイプを指定するには、ルートテンプレート注釈が必要です。Calculatorに入力を設定しましょう:

image057

クラスに名前をつけましょう:

image058

次に、入力モデルの各電卓からこのテンプレートを使ってクラスを生成する、マッピングのルールを作成しましょう: これをマッピングに追加しましょう:

image059

このルールは、各電卓を入力モデルから取り出し、それを電卓テンプレートに適用するよう指示します: 言語を再構築してみましょう(言語を右クリックし、言語の再構築を選択してください)、モデルから生成されたテキストをプレビューしてみましょう:

image060

生成されたJavaファイルの名前が付けられた新しいエディタータブに出力が表示されます。

image061

ご覧のように、出力にはCalculatorImplという名前のクラスが1つあります。

テンプレートの実装

テンプレートに、ソースモデルで与えたCalculatorノードの名前を使用させましょう。クラス名にキャレットを置きます。 Alt+Enter を押し、プロパティーマクロを追加するを選択します:

image062

プロパティーマクロを使用すると、入力ノードに依存するプロパティーの値を指定できます: これをインスペクタに入力してみましょう:

image063

次に、言語を再構築してみましょう(言語を右クリックして言語の再構築を選択してください)。次に、サンドボックスモデルから生成されたコードをプレビューします。Calculatorインスタンスの名前に対応する正しい名前のクラスが表示されます。

image064

残りのクラススケルトンをテンプレートに入力しましょう。そのためには、Javaクラスに対応するモデルをインポートする必要があります。あるモデルが別のモデルをインポートすると、インポートモデルは別のモデルからノードを参照できます。Javaとの相互運用を簡素化するために、MPSは、クラスを持つjarファイルまたはフォルダーに対応するモデルを作成できます。これらのモデルはpackage.name@java_stubフォームの名前を持ち、モデルプロパティーダイアログから追加できます。

image065

javax.swing@java_stub、javax.swing.event@java_stub、java.awt@java_stub、javax.swing.text@java_stubモデルをテンプレートモデルにインポートしましょう:

image066

JFrameからクラスを継承し、Calculatorテンプレートにmainメソッド、documentListenerフィールド、updateメソッドを作成しましょう。updateメソッドを呼び出す前に作成する必要があることに注意してください。

image067

次に、ためにフレームを設定するコードを作成しましょう:

image068

"Calculator"文字列にプロパティーマクロを置き、node.nameをインスペクタに追加して、タイトルが電卓の名前を反映するようにします。

image069

次に、入力フィールドごとに視覚的なテキストフィールドコンポーネントを作成しましょう。まず、JTextField型の1つのテキストフィールドを追加する必要があります。

image070

今度は、電卓の入力フィールドごとにそのようなテキストフィールドが作成されるようにします: フィールド宣言全体を選択して( Ctrl+Up/Down ショートカットを使用)、ノードマクロの追加を選択してみましょう:

image071

以下を取得します:

image072

補完メニューからループを選択します。

image073

Loopマクロを使用すると、マクロ内のコンストラクトを何度でも繰り返すことができます。つまり、インスペクタのコードによって返されるコレクション内の各ノードに対して1回です。

ループのインスペクタで次のように入力します:

image074

これは、ソース電卓にあるinputFieldごとにクラス内にフィールドを持つことを意味します。

それぞれの視覚的なテキストフィールドに一意の名前を付けましょう: その名前にプロパティーマクロを追加し、このコードをマクロの中に入力する必要があります:

image075

このコードは、変数にinputField_a、inputField_bなどの一意の名前を与えます。

outputFieldsでも同じことをする必要があります:

image076

唯一の違いは、$ LOOP $にnode.outputFieldを使用し、ベース名として "outputField"を使用することです。変更をMPSで利用できるようにするには、言語を作成します。

サンドボックスを作り、持っているものを見てみましょう:

image077

ご覧のとおり、フィールドが生成されています。これらのフィールドをビジュアルフレームに追加します。次のように入力します。

image078

基本的に、ブロックステートメント(コードは{}で囲まれたコード)を作成するので、BlockStatementの内容を単一のInputFieldに属する単一の単位として扱うことができます。そして、各電卓のInputFieldのブロックを簡単に繰り返すことができます。$LOOP$を使用してブロックとその内部のコードを囲み、インスペクタの値としてnode.inputFieldを使用します。

image079

JLabelのコンストラクターのパラメータにプロパティーマクロを追加して、ラベルが対応する入力フィールドの名前と同じテキストを持つようにします。

image080

現在のフィールドが格納されているフィールド宣言への参照を作成すると状況がより複雑になります。inputFieldへの参照から、現在のinputFieldから生成された対応するJTextFieldへの参照を生成する必要があります。これを行うには、フィールド宣言にラベルを付け、このラベルを使用してフィールドを見つける必要があります。

マッピングにジャンプし、これを追加してください:

image081

これで、オリジナルの入力フィールドによって索引付けされた、生成されたFieldDeclarations用のストレージができました。つまり、inputFieldラベルは、InputFieldから生成されたタイプFieldDeclarationのノードを参照します。それでは、生成されたフィールドのLOOPマクロにラベルを追加しましょう。

image082

次に、inputFieldへの参照を、ラベルを使った参照を使って見つかった参照に変更する必要があります。これを行うには、フィールド参照に参照マクロを追加する必要があります。

image083

インスペクタに移動し、次のように入力します。

image084

image085

2番目の参照と同じ操作を行います:

image086

言語(言語を右クリックして言語の再構築を選択)を作って、サンドボックスモデルから生成するものを見てみましょう:

image087

ご覧のとおり、参照は正しく設定されています。

さて出力フィールドで同じことをしましょう。ここでも、初期化コードを作成し、それを$LOOP$マクロで囲む必要があります。フィールド宣言のラベルを作成し、outputFieldの$ LOOP $に配置します。次に、このラベルを使用して参照を作成する必要があります。これらの変更を行った後、テンプレートのコードは次のようになります。

image088

ここで残された唯一のもの、つまり結果を計算された値で更新するコードを実装しましょう: このようなことをしたいと思います:

public void update() { int i1 = 0; try { i1 = Integer.parseInt(myInput1.getText()); } catch (NumberFormatException e) { } myOutput.setText("" + (i1)); }

入力ノードごとにintローカル変数を作成し、そのフィールドに値を代入します。そのような変数のループマクロを作成しましょう。";" $ LOOP $マクロの中にあります:

image089

これらの変数のそれぞれに固有の名前を生成しましょう:

image090

これらの変数を初期化しましょう。変数を参照するには、さらに別のラベルを作成する必要があります。

image091

それをローカル変数宣言に割り当てます。これを行うには、ローカル変数を$MAP_SRC$マクロで囲む必要があります。ほとんどの場合、このマクロは参照するノードにラベルを追加するためだけに使用され、他のマクロは関連付けられていません。

LocalVariableDeclarationStatementではなく、LocalVariableDeclarationを必ず選択してください。これを確認するには、セミコロンを見てください。それがマクロ角括弧の外側にある場合は、適切なノードを選択します。

image092

これらのローカル変数を電卓の入力フィールドの値で適切に初期化するには、別のLOOPが必要です。まず、try ... catchブロックを追加します。これを持っています:

image093

LOOPマクロで囲みます:

image094

ステートメント全体 (最後のセミコロンを含む行)がLOOPマクロでラップされていることを確認してください。次に、参照マクロを作成し、ラベルを使用してこれらの参照のための妥当なターゲット(それぞれLocalVariableDeclarationsとInputFields)を見つけます。ローカル変数の場合:

image095

フィールド参照あり:

image096

次に、出力値を、計算された出力を表すテキストフィールドに設定する必要があります。次のコードを入力します。

image097

LOOPマクロで囲みます:

image098

再度、終了セミコロンを含むステートメント全体がLOOPマクロにラップされていることを確認してください。次に、参照マクロをoutputFieldに追加します。

image099

今度は、計算結果が表示されるようにテキストを設定する必要があります。そのため、null値がある場所に出力フィールドの式を生成する必要があります。setText()メソッドの引数nullを削除し、"" +(null)と入力します。各ステップでCtrl + Spaceを押します。出力コードが正しいことを確認するためにここに括弧を追加します。括弧がなく、式が2〜3の場合、出力コードは "" + 2 - 3になりますが、これは正しくありません。一方、"" +(2 - 3)は正しいです。

image100

null値にノードマクロを追加し、$ COPY_SRC $マクロに変更します。$ COPY_SRC $ macroはマクロのノードをインスペクタのコードで返されたノードのコピーで置き換えます。node.expressionを返します。

image101

残っているのは、InputFieldReferenceを処理することだけです。まだそれのためのジェネレータを持っていません。参照を、参照される入力フィールドに対応するJTextFieldから取り出された値で置き換える必要があります。つまり、対応するi変数がLocalVarラベルに格納されています。InputFieldReferenceのジェネレータを作成するには、リダクションルールを定義する必要があります。削減ルールは、生成中にコピーされるすべてのノード(たとえば、$ COPY_SRC $ macro)に適用されます。マッピング設定で対応するリダクションルールを作成しましょう:

image102

テンプレートはあまり長くないので、インラインテンプレートにはコンテキストを使用します。補完リストの<コンテキスト付きインラインテンプレート>を選択してください。

image103 1

InputNodeReferenceをローカル変数参照に変換するので、そのようなローカル変数参照を含む有効なコンテキストノードを作成する必要があります: BlockStatementをコンテキストノードとして使用しましょう:

image103 2

このBlockStatementの中で、ローカル変数iを定義し、それを参照する単純な式を作成しましょう:

image103 3

このローカル変数iへの参照は、インラインテンプレートの結果として使用するので、テンプレートフラグメントとしてマークする必要があります: Alt+Enter を押して "Create Template Fragment"を選択してくださいインテンション:

image103 4

次に、このローカル変数参照の参照マクロを作成しましょう:

image103 5

正しい参照クエリを指定して、LocalVarから正しいiを取得します。

image103 6

言語を再構築してみましょう(言語を右クリックして言語の再構築を選択してください)、サンドボックスモデルがどうなるかを見てみましょう。ご覧のとおり、MPSは必要なコードを正確に生成します。

image106

入社給与プログラム

言語とそのジェネレータを終了したら、給与プログラムに入ることができます: ここにあります:

image107

生成されたコードの実行

コードを実行するには、ソリューションモデルを作成する必要があります。

image108

ソースは、ソリューションのsource_genディレクトリーにあります: (ファイルシステムビューに切り替えることを忘れないでください):

image109

好きなJava IDEでそれらを実行できます。

言語の主要部分を終えました。今度は研磨をしましょう。

InputFieldReferenceのスコープの作成

サンドボックスモデルで別の電卓のルートを作成すると、最初の電卓から入力フィールドを参照することができますが、これは正しくありません。

image110

それを修正しましょう。そのためには、InputFieldReferenceの概念の範囲を定義する必要があります。これを行うには、制約を定義します。InputFieldReferenceを開き、制約タブに移動して、それに対する制約ルートを作成します。スコープ解決の新しい階層( 継承 )メカニズムを使用します。このメカニズムは、ScopeProviderを実装する先祖にスコープ解決を委譲します。InputFieldReferenceはこのようにInputFieldノードを検索し、それらのリストを構築するためにその先祖に頼ります。

image111

参照制約を追加し、そのリンクとしてfieldを選択します。

image112

それでは、このリンクのスコープを作成しましょう。scopeセクションにジャンプし、Ctrl+Spaceを押してください。InputFieldReferenceがリンクできる要素を解決する方法を指定する必要があります。

image113 1

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

このケースの電卓は、InputFieldの範囲を照会するたびに、その入力フィールドのすべてのリストを返す必要があります。電卓振る舞いの面では、getScope()のメソッド(Ctrl+O)をオーバーライドします。

image113 3

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

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

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

image114

クイックヒント:SimpleRoleScopeクラスの使用に注目してください。独自のカスタムスコープを構築するのに役立ついくつかのヘルパークラスの1つです。SimpleRoleScope(Ctrl+N)に移動し、含まれているパッケージ構造(Alt+F1)を開いてチェックアウトします。

言語を作成した後(言語を右クリックして言語を作るを選択)、2番目の電卓のフィールドにはアクセスできなくなります。

image115

しかし、最初の電卓のフィールドにはまだアクセスできます。

image116

InputFieldReferenceの型システムルールの作成

これで次のように入力できます:

image117

このコードは、3進演算子の条件の一部として使用されているため、widthはブール値でなければならないため、正しくありません。このエラーを表示するには、概念の型システムルールを作成する必要があります。やってみましょう。InputFieldReferenceを開き、型システムタブに移動します。そこにInferenceRuleを作成してください:

image118

型システムエンジンは型方程式と不等式を操作します。方程式はどのノードの型が等しいかを指定します。方程式では、どのノードのタイプを互いのサブタイプにするかを指定します。参照の型が常にintであることを示す式を追加する必要があります。方程式を定義しましょう。補完メニューで等号を選択します。

image119

左側のtypeOfを選択してください:

image120

typeofの中に 'inputFieldReference'と入力してください:

image121

方程式の右辺の<quotation>を選択します。

image122

引用は、引用符の中にあるノードを値として取得するための特別な構文です。通常、作成したいノードは非常に複雑です。順次プロパティー/子/参照割り当てを使用してノードを作成すると、エラーが発生しやすく、退屈な作業になります。引用はこのようなタスクを大幅に簡素化します。

その内部にIntegerType型を入れます:

image123

今度はルールが完成しました:

image124

言語を作ってみましょう(言語を右クリックして言語を作るを選んでください)、エラーのあるコードを見てみましょう:

image125

今すぐ強調表示されています。Ctrl + F1を押して、"type int is not type of Boolean"というエラーメッセージを表示することができます。

MPSエディターの使い方

テキストエディターのように見えますが、MPSエディターは正確には1つではありません。このため、テキストエディターを使用して作業するのとは異なります。このセクションでは、MPSエディターの操作の基本について説明します。

ナビゲーションはテキストエディターと同様に動作します。矢印キー、page up/downhome/endを使用することができ、テキストエディターと同じように動作します。選択は異なっています:エディターで任意の間隔を選択することはできません。MPSにはそのようなことがないからです。セルまたは隣接するセルのセットのみを選択できます。現在のセルの親を選択するには、Ctrl+Upを押します。現在のセルの子を選択するには、Ctrl+Downを押します。隣接するセルを選択する場合は、Shift+Left/Rightを使用します。

コード補完がオプションのテキストエディターとは異なり、MPSはこの機能をそのまま使用できます。 Ctrl+Space をほぼどこにでも押して、現在の位置にある可能なアイテムのリストを見ることができます。言語の機能を調べたい場合は、目的の場所でこのショートカットを使用し、補完メニューの内容を確認するのが最適な方法です。

MPSの一般的に使用されるもう1つの機能はインテンションです。インテンションは、エディター内のノードに適用できるアクションです。エディターにライトバルブが表示されている場合は、インテンションを適用することができます。 Alt+Enterを押すと、電球がメニューに展開され、目的のアクションを選択できます。

MPSでは、このような要素が複数存在する場所に新しい要素を追加したいことがよくあります。例:メソッド本体に新しいステートメントを追加したり、クラス宣言の新しいメソッドを追加したりすることができます。これを行うには、InsertEnterの2つのショートカットを使用できます。前者は、現在のアイテムの前に新しいアイテムを追加します。後者は、現在のアイテムの後に新しいアイテムを追加します。現在の項目を削除するには、Ctrl+Deleteを押します。

JetBrains TV(英語)MPSエディターの使用専用のスクリーンキャスト(英語)をチェックアウトすることができます。さらにMPSのヒントやテクニックについては、MPSチャネル(英語)のチューニングも検討してください。

最終更新日: 2019年4月12日