MPS 2024.1 ヘルプ

クックブック - 型システム

推論ルール

このクックブックは、言語の型を設計する際の簡単な答えとガイドラインを提供するものです。型システムの詳細な説明については、ユーザーガイドの型システムセクションを参照してください。

等価

ノードのタイプが常に特定の具体的なタイプである必要がある場合は、タイプ方程式を使用します。typeof コマンドを使用して、目的のノードのタイプが特定のタイプと等しくなければならないことを宣言します。

rule typeof_StringLiteral { applicable for concept = StringLiteral as nodeToCheck applicable always overrides false do { typeof(nodeToCheck) :==: <string>; } }

タイプを参照するために引用符が使用されていることに注意してください。<string> は、new node <StringType>() と入力するのと同じです。要素のタイプは、他の要素のタイプと同じです。例: 括弧がラップされた要素のタイプを保持することを表現するために、ParenthesizedExpression の概念は次のように宣言します。

rule typeOf_ParenthesizedExpression { applicable for concept = ParenthesizedExpression as parExpr applicable always overrides false do { typeof(parExpr) :==: typeof(parExpr.expression); } }

不等価

タイプを他のタイプのサブタイプまたはスーパータイプにする必要がある場合は、infertypeof コマンドを使用します。例として三項演算子を参照してください。

rule typeOf_TernaryOperator { applicable for concept = TernaryOperatorExpression as toe applicable always overrides false do { infer typeof(toe.condition) :<=: <boolean>; infer typeof(toe) :>=: typeof(toe.ifTrue); infer typeof(toe) :>=: typeof(toe.ifFalse); } }

ForEachStatement の概念は、非常に複雑なシナリオを解決する方法を示しています。ループ変数の型は、反復コレクション内の要素の型と同じである必要がありますが、コレクションの型は、elementType 型の要素のシーケンスまたは配列のサブ型である必要があります。

rule typeof_ForEachStatement { applicable for concept = ForEachStatement as forEachStatement applicable always overrides false do { node<ForEachVariable> variable = forEachStatement.variable; node<Expression> inputSequence = forEachStatement.inputSequence; if (inputSequence.isNotNull && variable.isNotNull) { var elementType; infer <join(sequence<%( elementType)%>| %( elementType)%[])> :>=: typeof(inputSequence); typeof(variable) :==: elementType; } } }

var elementType を使用して変数を宣言し、それを使用してコレクション要素の型とループ変数の型を結び付けていることに注意してください。また、%(...)% は、いわゆるアンチクォーテーションの境界を定めます。これにより、ローカルコンテキストから操作している AST に値を提供したり、値を取得したりできます。

交換ルール

置換ルールは、ある型を別の型に置き換える可能性を型システムに示します。例: NullType はすべての型(プリミティブ型を除く)のサブタイプであるため、型システムは NullTypeBaseConcept の間の不等式を簡単に削除できます。

replacement rule any_type_supertypeof_nulltype applicable for concept = NullType as nullType <: concept = BaseConcept as baseConcept custom condition: ()->boolean { !(baseConcept.isInstanceOf(RuntimeTypeVariable)); } rule { if (baseConcept.isInstanceOf(PrimitiveType) || baseConcept.isInstanceOf(PrimitiveTypeDescriptor)) { error "null type is not a subtype of primitive type" -> equationInfo.getNodeWithError(); } }

置換ルールは、共分散と反分散を宣言するのにも便利です。例: シーケンスの共分散は、MPS で次のように宣言されます。

replacement rule sequence_subtypeOf_sequence applicable for concept = SequenceType as left <: concept = SequenceType as right custom condition: true rule { if (right.elementType.isNotNull) { infer left.elementType :<=: right.elementType; } }

左のコレクションが右のコレクションのサブタイプであると主張する元のルールは、左のコレクションの要素のタイプが右のコレクションの要素のタイプのサブタイプであることを保証するルールに置き換えられます。

サブタイプ規則

サブ型ルールを使用すると、特定の型が型階層のどこに属するかを指定できます。ルールは型のコレクションを返し、それを直接のスーパー型として識別します。たとえば、次のルールは、Long 変数を Float にキャストできることを宣言しています。

subtyping rule long_extends_float { weak = false applicable for concept = LongType as longType rule { return <float>; } }

ここで MPS は、LinkedList が ListDeque、または Stack のいずれかのサブタイプであることを宣言しています。

subtyping rule supertypesOf_linkedlist { weak = false applicable for concept = LinkedListType as llt rule { nlist<> res = new nlist<>; res.add(<list<%( llt.elementType)%>>); res.add(<deque<%( llt.elementType)%>>); res.add(<stack<%( llt.elementType)%>>); return res; } }

比較ルール

2 つのタイプを交換可能にする必要がある場合は、比較ルールを使用してそれを定義します。例: 次のルールにより、NullType は、プリミティブタイプを除くすべてのタイプと比較可能になります。

comparison rule any_type_comparable_with_nulltype applicable for concept = BaseConcept as baseConcept , concept = NullType as nullType rule { if (baseConcept.isInstanceOf(PrimitiveType) || baseConcept.isInstanceOf(PrimitiveTypeDescriptor)) { return false; } return true; } weak = false

同様に、BaseLanguage の MapTypeJava の Map インターフェース(ここでは、パターン内の ClassifierType の概念を通じて参照されます)は同等である必要があります。

comparison rule map_type_comparableWith_Map applicable for concept = MapType as mapType , > Map<# KEY, # VALUE> < as classifierMapType rule { return true; } weak = true

代替タイプルール

これらは、型を表すノードを定義済みの置換で置き換えるように型システムに指示します。

例: タスクが 1 つのタイプを使用するのか別のタイプを使用するのかに応じて、int または long を使用するなど、プログラム構成ごとに異なるタイプを使用することを決定する場合があります。これは、タイプチェックの実行時に置換が行われるため、単純にジェネレーターを使用して正しい「実装」タイプを生成するのとは異なるため、発生する可能性のあるエラーを早期に発見できます。

最も単純な形式では、型システムモデルで Substitute Type Rule のインスタンスを作成することによって型置換を使用できます。

substitute type rule substituteType_MyType { applicable for concept = MyType as mt substitute {   if (mt.isConditionSatisfied()) { return new node<IntegerType>; } null; } } 

Substitute Type Rule は型を表すノードに適用可能です。タイプチェッカによって新しい型が導入されるときはいつでも、適切な代入規則を検索して実行します。この規則は、置換として `node <>` のインスタンス、または null 値を返さなければなりません。その場合、元のノードが型を表すのに使われます(デフォルトの振る舞い)。

タイプチェッカーによって使用される型をオーバーライドするもう 1 つの可能性は、ノード属性の使用にあります。元のタイプノードに含まれるノード属性がある場合、タイプチェッカーは最初にその属性に適用可能な代替タイプルールを見つけようとします。このようにして、言語が実装されていても型ノードを上書きすることができます。

substitute type rule substituteType_SubstituteAnnotation { applicable for concept = SubstituteAnnotation as substituteAnnotation substitute { if (substituteAnnotation.condition.isSatisfied(attributedNode)) { return substituteAnnotation.substitute; } null;  } }

上記のルールは属性ノードに対して定義されており、明示的なパラメーターとしてルールに渡されるのは属性ノードです。この規則は、型ノードを置換するための条件が満たされているかどうかをチェックでき、attributedNode 式を介して元の型を表す属性付きノードにもアクセスできます。

チェックとクイックフィックス

チェックルールは MPS コード分析プロセスの一部となり、エディターで対話形式でユーザーに見つかった問題を報告します。例: これは、余分な型キャストのチェックです。

checking rule CheckExcessTypeCasts { applicable for concept = CastExpression as expr overrides none do { if (isStrongSubtype(expr.expression.type :<< expr.type)) { info "Typecast expression is superflous" -> expr ; } } }

これで、上記の問題が報告されたときにユーザーにポップアップするクイックフィックスを定義できます。その後、ユーザーはクイックフィックスをすばやく呼び出して、報告された問題を修正できます。

quick fix RemoveExcessTypeCast arguments: node<CastExpression> castExpr fields: << ... >> description(node)->string { "Remove Excess Typecast"; } execute(node)->void { castExpr.replace with(castExpr.expression); }

報告されたエラーにクイックフィックスをフックするには、クイックフィックスを情報メッセージにリンクされたインテンション(オプション)として指定する必要があります。

checkingRules153.png

さらに、クイックフィックスにパラメーターを渡し、それを「すぐに 適用」でマークすることもできます。その場合、エディターでエラーが検出されるとすぐにクイックフィックスが自動的に適用されます。

コンクリートのオーバーロード操作

When-concrete ブロックを使用すると、ノードのタイプが計算された後、タイプチェックを実行できます。以下の例では、計算された操作のタイプが、演算子のオーバーライドルールに基づいて操作タイプコマンドによって提案されたタイプと一致することを確認しています。

rule typeof_BinaryOperation { applicable for concept = BinaryOperation as operation overrides false do { when concrete (typeof(operation.leftExpression) as leftType) { when concrete (typeof(operation.rightExpression) as rightType) { node<> opType = operation type( operation , leftType , rightType ); if (opType.isNotNull) { typeof(operation) :==: opType; } else { error "operation is not applicable to these operands" -> operation; } } } } }

関連ページ:

型システム

言語のための型システムを定義する:このページでは、MPS 型システムについて詳しく説明します。最初の型システムの規則を定義する際に、さらに簡単に導入したい場合は、型システムクックブックをチェックしてください。コードから型システムを使用する方法をよく知りたい場合は、型システムを使うの章も参照してください。型システムとは:型システムは、言語を使用して書かれたモデル内のノードに型を割り当てる言語定義の一部です。型システム言語は、ノードとそのタイプに対する特定の制約をチェックするためにも使用されます

HowTo- 追加のツールを追加する (別名再生)

追加のツールを追加する (別名再生):このドキュメントでは、MPS 用の新しいツール(Eclipse ユーザーの場合、MPS のツールは Eclipse のビューのようなもの)を作成する方法について説明します。これは、MPS に任意の追加ツールを組み込むための例として役立ちます。このテキストは、ツール開発の側面、つまり言語とメニューシステムに新しいツールを追加する方法、および現在編集されているものとビューを同期する方法を強調しています。他のすべての点で、ツールは単なる Swing UI プログラ...

インタープリタークックブックの作成

バージョン 3.1 以降 MPS にバンドルされている Shapes サンプルプロジェクトを確認してください。エディターでいくつかの凝ったトリックを行うことができます。デフォルトでは、エディターは、キャンバス上に指定されたサイズと色の視覚的形状を描画するためのコマンドで構成されるプレーンコードを表示します。コードを Java に生成して実行することができます。これにより、上記のコードから生成されたばかりのアプリケーションを実行する新しい Java プロセスが起動します。ただし、MPS は Java...