MPS 2026.1 ヘルプ

TextGen

TextGen 言語アスペクト

導入

TextGen 言語の側面は、モデルからテキストへの変換を定義します。モデルを直接テキスト形式に変換する必要があるたびに便利です。この言語には、テキストを印刷し、ノードをテキスト値に変換し、出力に適切なレイアウトを与えるための構造が含まれています。

TextGen アスペクトでは、バイナリ形式での出力も可能です。これは、たとえば、jetbrains.mps.lang.resources 言語のアイコンのイメージを生成するために活用されます。このメカニズムにより、直接 byte[] API と新しい書き込み textgen 操作が容易になります。

操作

append コマンドは変換を実行し、結果のテキストを出力に追加します。found error コマンドを使用して、モデルの問題を報告することができます。with indent コマンドは、インデントを増やしてブロックを区切ります。あるいは、increase depth コマンドdecrease depth コマンドは、ブロック構造に限定されずに現在のインデントの深さを操作します。indent buffer コマンドは、現在の行に現在のインデント (with indent または increase/decrease depth で指定) を適用します。

操作

引数

append

任意の数

  • {string value} を挿入するには、" 文字を使用するか、補完メニューから定数を選択します。

  • \n

  • $ list {node.list} - 区切りなしのリスト

  • $ list {node.list with、} - セパレータ付き (セパレータを追加 / 削除するインテンションが利用可能です)

  • $ ref {node.reference}、例: $ ref {node.reference <target>}- 非推奨であり、削除されます

  • ${node.child}

  • ${ 属性付きノード } $ - 属性ノードで使用可能、属性付きノードへの委譲

書く

byte[] に評価される式

単一の textgen 操作 (別名、GenerateTextDeclaration のインスタンス) でテキスト出力とバイナリ出力を組み合わせることはできません。デフォルトでは、テキスト出力が有効になっており、単一の textgen 操作のバイナリ出力形式とテキスト出力形式を切り替えるためにインテンションを使用できます。

found error

エラーテキスト

decrease depth

これからインデントレベルを下げます

increase depth

これからインデントレベルを上げる

indent buffer

現在の行に字下げを適用する

with indent { <code> }

<code> のインデントレベルを上げる

インデント

基本的な原則を理解すれば、適切なインデントを簡単に正しく行うことができます。TextGen は AST をテキストにフラッシュします。TextGen コマンドは、出力バッファーを順番に操作し、一度に 1 ノードずつテキストを出力するだけです。現在のインデントの深さを保持する変数(インデントバッファー)は、ルートコンセプトごとに保持されます。インデントバッファはゼロから始まり、深さの増減インデントコマンドによって変更されます。

ただし、「インデント」は append コマンドで明示的に出力ストリームに挿入する必要があります。ブロックを単に indent でマークしても、ラップされた TextGen コードによって生成されたテキストは自動的にはインデントされません。with indent ブロックはインデントバッファの値を増やすだけですが、個々の追加は現在のサイズのインデントバッファを前に付けることを望むかもしれません。

インデントバッファを出力ストリームに明示的に挿入する方法は 2 つあります。

  • インデントバッファコマンド

  • append コマンドのパラメーターに対するインスペクタの indent フラグ付き

例: 定数のリストの中で定数を適切にインデントするために、発行された各行の先頭で indent buffer を呼び出します。これにより、インデントは各行の先頭にのみ挿入されます。

text gen component for concept Constants {  file name : Constants     extension : <no extension>                                                                                                                                                                                                                                                            encoding : utf-8                                                                                                                                                                                                                                                                        (node)->void {      append ${node.name} \n ;      with indent {        node.constants.forEach({~constant =>          indent buffer ;          append ${constant.name} {=} ${constant.initializer} \n ;        });      }    }  }                                                                                                                                                                                                                                                                                                                                 

あるいは、append コマンドの最初のパラメーターにインスペクタで with indent フラグを指定することもできます。これはまた各行の始めにだけ字下げを挿入します。

TextGen1.png

ルートの概念

TextGen には、2 種類のルート概念があります。

  • ConceptTextGenDeclaration コンセプトで表される textgen コンポーネント。これは、コンセプトのテキストへの変換をエンコードします。ルート化可能な概念の場合、ターゲットファイルも指定できます。

  • 基本テキスト生成コンポーネントは、LanguageTextGenDeclaration という概念で表され、再利用可能なテキスト生成操作とユーティリティメソッドを定義できます。これらは、同じ言語の他のテキスト生成コンポーネントからだけでなく、拡張言語からも呼び出すことができます。

拡張概念での TextGen

MPS はルート概念のファイルを自動的に作成しません。TextGen が定義されている概念のサブ概念であっても、ファイルは自動的に作成されません。完全に一致する概念のみが考慮されます。拡張概念が祖先の TextGen コンポーネントをそのまま再利用したい場合は、ファイル名、エンコーディング、拡張子などの必須事項のみを指定して、コンポーネント本体を空のままにした、独自のの TextGen コンポーネントを宣言する必要があります。

レイアウト

出力ファイルのレイアウトを制御するための暫定的な仕組みがあります。ConceptTextGenDeclaration のテキスト レイアウトセクション(ルート可能なコンセプトでのみ使用可能)を使用すると、作成者は複数の論理セクション(デフォルトのセクションを含む)を定義し、必要に応じて各追加箇所に対して、テキストを追加するセクションを指定できます。

テキスト生成は、物理ファイルの行に対応する順序で常に可能とは限りません。たとえば、Java ソースでは、インポートクラス本体という 2 つの異なる領域を区別できます。インポート本体とともに生成されます。熱心な言語設計者は、ファイルをさらに分割し、たとえばファイルコメントパッケージステートメントインポートフィールドメソッドで構成されるクラス本体まで分割し、ClassConcept を走査しながらそれぞれを個別に生成したいと考えるかもしれません。これが出力ファイルのレイアウトと呼ばれ、現在ではこれを制御できます。MPS のベテランは、TextGen で長年利用可能だった 2 つのバッファ (TOPBOTTOM) をご存知かもしれません。これらは定義済みのハードコードされた値でした。現在では、出力ファイルの領域とその順序を指定するのは言語設計者のロールです。

実行順序が変わるため、属性からテキストを生成する場合は特に、個別の領域が便利になります。使って、テキスト gen の流れが物理的なテキスト行に対応することを確認するのはさらにトリッキーであり、指定された領域は生成をずっと快適にします。

ファイルのレイアウトは、ファイルを生成するトップテキストの gen に指定できます。

このメカニズムのサポートは準備ものであり、現在は非常に初歩的なものです。BaseLanguage の実装で使用しているため、この通知は、これを本番環境に移行することを推奨するのではなく、何が起こっているのかを説明するためのものです。

tgp1.png

コンテキストオブジェクト

特定のモデルからテキストへの変換シナリオでは、TextGen の間にいくつかのコンテキスト情報を保存することが重要です。たとえば BaseLanguage では、TextGen はモデルのインポートと修飾されたクラス名を追跡する必要があります。直接テキストバッファ操作に基づく以前のバージョンの面倒で低レベルのアプローチは、概念の textgen 仕様の一部としてカスタマイズされたオブジェクトを定義して使用する可能性に置き換えられました。

ContextObject1.png

現時点では、通常の Java クラス(引数なし、または概念インスタンスを受け取る引数 1 つのコンストラクターを持つクラス)がコンテキストオブジェクトとしてサポートされています。コンテキストオブジェクトは、コードから通常の変数として参照できます。

TextGen で属性を処理する

ノードに属性のアノテーションが付けられると、まずこれらの属性の TextGen が処理されます。次に、属性の TextGen 内の ${attributed node} 構造によって、属性ノード自体の TextGen が挿入されます。

1 つのノードに複数の属性がある場合、それらは最後に割り当てられた(最上位の)属性から順番に処理されます。TextGen が関連付けられていない属性は無視され、スキップされます。

サンプル

ForeachStatement (jetbrains.mps.baseLanguage)のテキスト生成コンポーネントの例を次に示します。

text gen component for concept ForeachStatement { (node)->void { if (node.loopLabel != null) { append \n ${node.loopLabel.name} {:} ; } else if (node.label != null) { append \n ${node.label} {:} ; } append \n ; indent buffer ; append {for (} ${node.variable} { : } ${node.iterable} {) {} ; with indent { append ${node.body} ; } append \n {}} ; } }

これは、テキスト gen の人工的な例です。

text gen component for concept CodeBlockConcept { (node)->void { indent buffer ; append {codeBlock {} \n ; with indent { indent buffer ; append {// Begin of codeBlock} \n ; indent buffer ; append {int i = 0} \n ; indent buffer ; append {// End of codeBlock} \n ; } append {}} ; } }

インデント付きの行数を含む次のコードブロックを生成します。

text gen component for concept CodeBlockConcept { codeBlock { // Begin of codeBlock int i = 0 // End of codeBlock }

属性付きノードの出力に追加テキストを追加する attribute の TextGen の例:

MethodDocCommentAttribute.png
2026 年 5 月 05 日