MPS 2020.3 ヘルプ


TextGen 言語アスペクト


The TextGen language aspect defines a model to text transformation. It comes in handy each time you need to convert your models into the text form directly. The language contains constructs to print out text, transform nodes into text values and give the output some reasonable layout.


The append command performs the transformation and adds resulting text to the output. You can use found error command to report problems in the model. The with indent command demarcates blocks with increased indentation. Alternatively, the increase depth and decrease depth commands manipulate the current indentation depth without being limited to a block structure. The indent buffer command applies the current indentation (as specified by with ident or increase/decrease depth) for the current line.





  • {string value} , to insert use the " char, or pick constant from the completion menu

  • \n

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

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

  • $ ref {node.reference} , e.g. $ref{node.reference<target>} - deprecated and will be removed

  • ${node.child}

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

found error


decrease depth


increase depth


indent buffer


with indent { <code> }



Proper indentation is easy to get right once you understand the underlying principle. TextGen flushes the AST into text. The TextGen commands simply manipulate sequentially the output buffer and output some text to it, one node at a time. A variable holding the current depth of indentation (indentation buffer) is preserved for each root concept. インデントバッファ starts at zero and is changed by 深さの増減 and with indent commands.

The "indentation", however, must be inserted into the output stream explicitly by the append commands. Simply marking a block with with indent will not automatically indent the text generated by the wrapped TextGen code. The with indent block only increases the value of the indentation buffer, but the individual appends may or may not wish to be prepended with the indentation buffer of the current size.

There are two ways to explicitly insert indentation buffer into the output stream:

  • indent buffer command

  • with indent flag in the inspector for the parameters of the append command

For example, to properly indent 定数 in a list of constants, we call indent buffer at the beginning of each emitted line. This ensures that the indentation is inserted only at the beginning of each line.

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

Alternatively, we could specify the with indent flag in the inspector for the first parameter to the append command. This will also insert the indentation only at the beginning of each line.

Text gen1


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

  • text gen component, represented by the ConceptTextGenDeclaration concept, which encodes a transformation of a concept into text. For rootable concepts the target file can also be specified.

  • base text gen component, represented by the LanguageTextGenDeclaration concept, which allows definition of reusable textgen operations and utility methods. These can be called from other text gen components of the same language as well as extending languages

拡張概念での TextGen

MPS does not create files for root concept automatically. Even sub-concepts of a concept that has TextGen defined will have no file created automatically. Only exact concept matches are considered. If an extending concept desires to re-use textgen component of an ancestor as is, it shall declare its own empty TextGen component, stating the essentials as the file name, encoding and extension, and leaving the body of the component empty.


There's provisional mechanism to control layout of output files. The text layout section of ConceptTextGenDeclaration (available only in rootable concepts) allows the authors to define multiple logical sections (with a default one) and then optionally specify for each append, to which section to append the text.

Text generation is not always possible in a sequence that corresponds to lines in a physical file. E.g. for a Java source, one could distinguish 2 distinct areas, e.g. imports and class body, where imports is populated along with the body. A passionate language designer might want to break up the file further, e.g. up to file comment , package statement , imports, and class body that consists of fields and methods, and populate each one independently while traversing a ClassConcept. That's what we call a layout of an output file, and that's what we give control over now. MPS veterans might be aware of two buffers (TOP and BOTTOM) that used to be available in TextGen for years. These were predefined, hard-coded values. Now it's up to language designer to designate areas of an output file and their order.

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

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

The support for this mechanism is preliminary and is quite rudimentary now. We utilize it in our BaseLanguage implementation, so this notice is to explain you what's going on rather than encourage you to put this into production.



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

Context object1

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

TextGen で属性を処理する

When nodes are annotated with 属性 , the TexGen for these attributes is processed first. The ${attributed node} construct within the attribute's TextGen will then insert the TextGen of the attributes node itself.
If there are multiple attributes on a single node, they are processed in turn, starting with the last-assigned (top-most) attribute. Attributes without TextGen associated are ignored and skipped.

Here is an example of the text gen component for the ForeachStatement (jetbrains.mps.baseLanguage).

text gen component for concept ForeachStatement { (node, context, buffer)->void { if (node.loopLabel != null) { append \n ${} {:} ; } 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 {}} ; } }

This is an artificial example of the text gen:

text gen component for concept CodeBlockConcept { (node, context, buffer)->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 の例:

Method doc comment attribute