MPS 2019.2ヘルプ

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

推論ルール

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

平等

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

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

注引用は、タイプを参照するために使用されます。<文字列>新しいノード<StringType>()と入力するのと同等です。要素のタイプは、他の要素のタイプと同じです。例:括弧がラップされた要素のタイプを保持することを表すため、括弧式コンセプト

宣言します:

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

不平等

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

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変数をフロートにキャストできることを宣言しています。

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

ここで、MPSは、LinkedListリストデキューまたはスタックのいずれかのサブタイプであることを宣言しています。

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からのMapTypeとJavaからのマップインターフェース(ここでは、パターン内の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

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

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

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; } } } } }
最終更新日: 2019年8月30日