diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f01972..936e54f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # TON Plugin for the IntelliJ IDEs Changelog +## [2.5.1] + +### Added +- Tolk 0.8 support + ## [2.5.0] ### Added diff --git a/gradle.properties b/gradle.properties index 5fcfcc7..8851cd3 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ org.gradle.jvmargs=-Xmx4096m pluginGroup=org.ton -pluginVersion=2.5.0 +pluginVersion=2.5.1 publishChannel=release publishToken=token enableBuildSearchableOptions=false @@ -9,7 +9,7 @@ enableBuildSearchableOptions=false # https://www.jetbrains.com/intellij-repository/snapshots/ ideaVersion=2024.3 kotlin.stdlib.default.dependency=false -buildNumber=17 +buildNumber=1 platformType=IC platformVersion=2024.3 diff --git a/modules/tolk/src/org/ton/intellij/tolk/parser/TolkParser.bnf b/modules/tolk/src/org/ton/intellij/tolk/parser/TolkParser.bnf index 7fc3f8a..ef93c4a 100644 --- a/modules/tolk/src/org/ton/intellij/tolk/parser/TolkParser.bnf +++ b/modules/tolk/src/org/ton/intellij/tolk/parser/TolkParser.bnf @@ -506,8 +506,7 @@ Var ::= IDENTIFIER (':' TypeExpression)? { extends = VarDefinition } -DotExpression ::= Expression '.' CallExpression { - pin=2 +DotExpression ::= Expression '.' (IntegerExpression|CallExpression) { methods=[ left="/Expression[0]" right="/Expression[1]" @@ -570,7 +569,7 @@ private TupleElement ::= Expression { } private TupleElement_recovery ::= !(','|']'|';') -private IntegerExpression ::= INTEGER_LITERAL +IntegerExpression ::= INTEGER_LITERAL private BoolExpression ::= 'true' | 'false' private NullExpression ::= 'null' !'(' @@ -626,8 +625,11 @@ TypeExpression ::= FunType | UnionType | AtomicType { ] } -private AtomicType ::= AutoType | VoidType | TensorOrParenType | TupleType | NullType | TypeIdentifier +private AtomicType ::= NullableType | AutoType | VoidType | TensorOrParenType | TupleType | NullType | TypeIdentifier +NullableType ::= (TensorOrParenType|TupleType|TypeIdentifier) '?' { + mixin="org.ton.intellij.tolk.psi.impl.TolkNullableTypeMixin" +} UnionType ::= unionType1 | unionType2 NullType ::= 'null' { mixin="org.ton.intellij.tolk.psi.impl.TolkNullTypeMixin" diff --git a/modules/tolk/src/org/ton/intellij/tolk/psi/impl/TolkDotExpressionMixin.kt b/modules/tolk/src/org/ton/intellij/tolk/psi/impl/TolkDotExpressionMixin.kt index 49af30b..1fed13b 100644 --- a/modules/tolk/src/org/ton/intellij/tolk/psi/impl/TolkDotExpressionMixin.kt +++ b/modules/tolk/src/org/ton/intellij/tolk/psi/impl/TolkDotExpressionMixin.kt @@ -4,7 +4,8 @@ import com.intellij.extapi.psi.ASTWrapperPsiElement import com.intellij.lang.ASTNode import org.ton.intellij.tolk.psi.TolkDotExpression import org.ton.intellij.tolk.type.TolkType +import org.ton.intellij.tolk.type.infer.inference abstract class TolkDotExpressionMixin(node: ASTNode) : ASTWrapperPsiElement(node), TolkDotExpression { - override val type: TolkType? get() = right?.type + override val type: TolkType? get() = inference?.getType(this) } diff --git a/modules/tolk/src/org/ton/intellij/tolk/psi/impl/TolkFieldExpressionMixin.kt b/modules/tolk/src/org/ton/intellij/tolk/psi/impl/TolkFieldExpressionMixin.kt new file mode 100644 index 0000000..8f984d6 --- /dev/null +++ b/modules/tolk/src/org/ton/intellij/tolk/psi/impl/TolkFieldExpressionMixin.kt @@ -0,0 +1,11 @@ +package org.ton.intellij.tolk.psi.impl + +import com.intellij.extapi.psi.ASTWrapperPsiElement +import com.intellij.lang.ASTNode +import org.ton.intellij.tolk.psi.TolkFieldExpression +import org.ton.intellij.tolk.type.TolkType +import org.ton.intellij.tolk.type.infer.inference + +abstract class TolkFieldExpressionMixin(node: ASTNode) : ASTWrapperPsiElement(node), TolkFieldExpression { + override val type: TolkType? get() = inference?.getType(this) +} \ No newline at end of file diff --git a/modules/tolk/src/org/ton/intellij/tolk/psi/impl/TolkNullTypeMixin.kt b/modules/tolk/src/org/ton/intellij/tolk/psi/impl/TolkNullTypeMixin.kt index 2156680..ca7831e 100644 --- a/modules/tolk/src/org/ton/intellij/tolk/psi/impl/TolkNullTypeMixin.kt +++ b/modules/tolk/src/org/ton/intellij/tolk/psi/impl/TolkNullTypeMixin.kt @@ -6,5 +6,5 @@ import org.ton.intellij.tolk.psi.TolkNullType import org.ton.intellij.tolk.type.TolkType abstract class TolkNullTypeMixin(node: ASTNode) : ASTWrapperPsiElement(node), TolkNullType { - override val type: TolkType get() = TolkType.Unknown + override val type: TolkType get() = TolkType.Null } \ No newline at end of file diff --git a/modules/tolk/src/org/ton/intellij/tolk/psi/impl/TolkNullableTypeMixin.kt b/modules/tolk/src/org/ton/intellij/tolk/psi/impl/TolkNullableTypeMixin.kt new file mode 100644 index 0000000..07748e3 --- /dev/null +++ b/modules/tolk/src/org/ton/intellij/tolk/psi/impl/TolkNullableTypeMixin.kt @@ -0,0 +1,12 @@ +package org.ton.intellij.tolk.psi.impl + +import com.intellij.extapi.psi.ASTWrapperPsiElement +import com.intellij.lang.ASTNode +import org.ton.intellij.tolk.psi.TolkNullableType +import org.ton.intellij.tolk.type.TolkType + +abstract class TolkNullableTypeMixin(node: ASTNode) : ASTWrapperPsiElement(node), TolkNullableType { + override val type: TolkType? get() { + return TolkType.nullable(typeExpression.type ?: return null) + } +} \ No newline at end of file diff --git a/modules/tolk/src/org/ton/intellij/tolk/type/TolkType.kt b/modules/tolk/src/org/ton/intellij/tolk/type/TolkType.kt index 5c0c810..5d9d328 100644 --- a/modules/tolk/src/org/ton/intellij/tolk/type/TolkType.kt +++ b/modules/tolk/src/org/ton/intellij/tolk/type/TolkType.kt @@ -52,7 +52,19 @@ sealed interface TolkType { class UnionType( val elements: List ) : TolkType { - override fun toString(): String = elements.joinToString(" | ") + init { + check(elements.size > 1) { "UnionType must have at least two elements, got ${elements.size}: $elements" } + } + val hasNull = elements.any { it == Null } + val isNullable = elements.size == 2 && hasNull && !elements.all { it == Null } + + override fun toString(): String { + return if (isNullable) { + "${elements.first { it != Null }}?" + } else { + elements.joinToString(" | ") + } + } } companion object { @@ -64,6 +76,18 @@ sealed interface TolkType { val Tuple = TolkPrimitiveType.Tuple val Unit = TolkPrimitiveType.Unit val Bool = TolkPrimitiveType.Bool + val Null = TolkPrimitiveType.Null + + fun nullable(element: TolkType): TolkType { + return if (element is UnionType) { + if (element.hasNull) element + else UnionType(element.elements + Null) + } else if (element != Null) { + UnionType(listOf(element, Null)) + } else { + element + } + } fun create(vararg elements: TolkType): TolkType { return create(elements.toList()) @@ -100,6 +124,7 @@ sealed class TolkPrimitiveType( object Tuple : TolkPrimitiveType("tuple") object Unit : TolkPrimitiveType("()") object Bool : TolkPrimitiveType("bool") + object Null : TolkPrimitiveType("null") override fun toString(): String = name } diff --git a/modules/tolk/src/org/ton/intellij/tolk/type/infer/TypeInference.kt b/modules/tolk/src/org/ton/intellij/tolk/type/infer/TypeInference.kt index f8d6dde..d6d009e 100644 --- a/modules/tolk/src/org/ton/intellij/tolk/type/infer/TypeInference.kt +++ b/modules/tolk/src/org/ton/intellij/tolk/type/infer/TypeInference.kt @@ -453,6 +453,7 @@ class TolkInferenceWalker( is TolkTupleExpression -> infer(element, expectedType) is TolkParenExpression -> infer(element, expectedType) is TolkTensorExpression -> infer(element, expectedType) + is TolkFieldExpression -> infer(element, expectedType) is TolkReferenceExpression -> infer(element) is TolkLiteralExpression -> element.type is TolkUnitExpression -> TolkType.Unit @@ -551,12 +552,20 @@ class TolkInferenceWalker( element: TolkDotExpression, expectedType: TolkType? = null, ): TolkType? { - infer(element.left) + val leftType = infer(element.left) val type = element.right?.let { expression -> - if (expression is TolkCallExpression) { - infer(expression, expectedType, withFirstArg = element.left) - } else { - infer(expression, expectedType) + when (expression) { + is TolkCallExpression -> infer(expression, expectedType, withFirstArg = element.left) + is TolkIntegerExpression -> { + val typeElementIndex = expression.integerLiteral.text.toIntOrNull() ?: return@let null + val typeElements = when (leftType) { + is TolkType.TypedTuple -> leftType.elements + is TolkType.Tensor -> leftType.elements + else -> null + } + typeElements?.getOrNull(typeElementIndex) + } + else -> null } } return ctx.setType(element, type ?: expectedType) @@ -604,16 +613,19 @@ class TolkInferenceWalker( unify(paramType.inputType, argType.inputType) unify(paramType.returnType, argType.returnType) } + paramType is TolkType.TypedTuple && argType is TolkType.TypedTuple -> { paramType.elements.zip(argType.elements).forEach { (param, arg) -> unify(param, arg) } } + paramType is TolkType.Tensor && argType is TolkType.Tensor -> { paramType.elements.zip(argType.elements).forEach { (param, arg) -> unify(param, arg) } } + paramType is TolkType.ParameterType && argType != null && argType !is TolkType.ParameterType -> { typeMapping[paramType.psiElement] = argType } @@ -659,6 +671,19 @@ class TolkInferenceWalker( ) } +// private fun infer(element: TolkFieldExpression, expectedType: TolkType? = null): TolkType? { +// val expressions = element.expressionList +// val left = expressions.getOrNull(0) ?: return null +// val leftType = infer(left) ?: return null +// val typeElements = when (leftType) { +// is TolkType.Tensor -> leftType.elements +// is TolkType.TypedTuple -> leftType.elements +// else -> return null +// } +// val rightIndex = element.integerLiteral?.text?.toIntOrNull() ?: return null +// return ctx.setType(element, typeElements.getOrNull(rightIndex) ?: expectedType) +// } + private fun infer(element: TolkTupleExpression, expectedType: TolkType? = null): TolkType? { val expectedTypes = (expectedType as? TolkType.TypedTuple)?.elements val expressions = element.expressionList diff --git a/modules/tolk/src/org/ton/intellij/tolk/type/printTolkType.kt b/modules/tolk/src/org/ton/intellij/tolk/type/printTolkType.kt index b54c372..237d5e6 100644 --- a/modules/tolk/src/org/ton/intellij/tolk/type/printTolkType.kt +++ b/modules/tolk/src/org/ton/intellij/tolk/type/printTolkType.kt @@ -47,6 +47,11 @@ fun PresentationTreeBuilder.printTolkType(type: TolkType) { } is TolkType.UnionType -> { + if (type.isNullable) { + printTolkType(type.elements.first { it != TolkType.Null }) + text("?") + return + } val iterator = type.elements.iterator() while (iterator.hasNext()) { val element = iterator.next()