MPS 2018.3ヘルプ

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

このチュートリアルでは

導入

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


ゴール

このチュートリアルでは、電卓の言語を作成します。この言語は、ある種の計算を記述する単純なエンティティの2つを定義します。これらのエンティティを電卓と呼びます。計算機には入力値と出力値のセットがあり、使用可能な入力値を使用して出力値を計算するための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コードから生成されます。

前提条件

The experience shows that if you want to avoid unnecessary glitches on your way throughout this tutorial, you need to familiarize yourself with the MPS projectional editor. If you followed Fast Track to MPSチュートリアル(英語) , you are well prepared. If you didn't, please consider at least learning the basic editor keyboard shortcuts at 編集者を指揮する(英語) page or checking up the quick tips listed in the How to use MPS editor appendix of this page.

主要なステップ

このチュートリアルでは、

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

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

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

プロジェクトの作成

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

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


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

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

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

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

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

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


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

Tab を押して、「instance can be root」プレースホルダになり、 Ctrl+Space を押してその値をtrueに設定します。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ステップで入力することができます。「電卓は、セルを一定のセルにします。

今度は、名前プロパティ(INamedConceptコンセプトインターフェースから)のプロパティセルが必要です。水平リストの最後に別のセルを挿入するには、電卓ワードの最後に Enter を押し、完了時に{名}を選択します。

image017

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

image018

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

image018 1

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

image018 2

OKボタンを押します。このモデル内でjetbrains.mps.tutorial.calculator言語を使用するには、表示されているModel PropertiesダイアログのUsed Languagesセクションで+ボタンを押し、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

今度は名前のセルに新しい行を追加します。名前のセルにカーソルを合わせると、電球が表示されます。電球で呼び出すことができるアクションはインテンションです。異なる言語でそれらの多くがあります。何かを入力する方法がわからない場合、 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がすでに作成しているかもしれませんが、そうでなければ、言語ポップアップメニューのNew-> Generatorメニュー項目を選択してください:

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 +スペースを使用します。出力コードが正しいことを確認するためにここに括弧を追加します。括弧がなく、式が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

Let's fix it. To do so, we need to define the scope for the InputFieldReference concept. We do that by defining a constraint: open InputFieldReference , go to the 制約 tab, and create a constraints root for it: We will use the new hierarchical (inherited) mechanism of scope resolution. This mechanism delegates scope resolution to the ancestors, who implement ScopeProvider . Our InputFieldReference thus searches for InputField nodes and relies on its ancestors to build a list of those.

image111

参照対象制約を追加し、リンクとしてフィールドを選択します:

image112

次に、このリンクのスコープを作成しましょう。範囲セクションに行き、 Ctrl+Spaceを押す。InputFieldReferenceがリンクできる要素を解決する方法を指定する必要があります。

image113 1

Once we have specified that the scope for InputFieldReference when searching for an InputField is inherited, we must indicate that 電卓 is a ScopeProvider . This ensures that 電卓 will have say in building the scope for all InputFieldReferences that are placed as its descendants.

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

image113 3

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

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

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

image114

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

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

image115

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

image116

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

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

image117

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

image118

typesytemエンジンは型方程式と不等式で動作します。方程式は、どのノードの型が等しくなければならないかを指定します。方程式では、どのノードのタイプを相互のサブタイプにするかを指定します。参照の型が常に整数であるという方程式を追加する必要があります。方程式を定義しましょう。補完メニューで等号を選択してください:

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チャネル(英語)のチューニングも検討してください。

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