Skip to content

Commit

Permalink
Support Tact 1.4.0, closes #180
Browse files Browse the repository at this point in the history
  • Loading branch information
andreypfau committed Jul 10, 2024
1 parent bd33263 commit daf1900
Show file tree
Hide file tree
Showing 11 changed files with 227 additions and 61 deletions.
5 changes: 4 additions & 1 deletion src/main/grammar/TactLexer.flex
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,9 @@ ESCAPE_SEQUENCE=\\\\ // backslash
"*=" { return TIMESLET; }
"/=" { return DIVLET; }
"%=" { return MODLET; }
"&=" { return ANDLET; }
"|=" { return ORLET; }
"^=" { return XORLET; }
"==" { return EQEQ; }
"!=" { return EXCLEQ; }
">=" { return GTEQ; }
Expand Down Expand Up @@ -185,7 +188,7 @@ ESCAPE_SEQUENCE=\\\\ // backslash
"catch" { return CATCH_KEYWORD; }
"foreach" { return FOREACH_KEYWORD; }
"in" { return IN_KEYWORD; }
"bounced" { return zzBlockDepth == 1 && zzContractScope ? BOUNCED_KEYWORD : IDENTIFIER; }
"bounced" { return BOUNCED_KEYWORD; }
"init" { return zzBlockDepth == 1 && zzParenDepth == 0 ? INIT_KEYWORD : IDENTIFIER; }
"get" { return zzBlockDepth <= 1 ? GET_KEYWORD : IDENTIFIER; }
"@interface" { return INTERFACE_MACRO; }
Expand Down
60 changes: 43 additions & 17 deletions src/main/grammar/TactParser.bnf
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@
TIMESLET = '*='
DIVLET = '/='
MODLET = '%='
ANDLET = '&='
ORLET = '|='
XORLET = '^='

IF_KEYWORD = 'if'
ELSE_KEYWORD = 'else'
Expand Down Expand Up @@ -174,7 +177,7 @@ As ::= 'as' IDENTIFIER {
}

// Field
Field ::= IDENTIFIER TypeAscription As? Assigment? ';' {
Field ::= IDENTIFIER TypeAscription As? Assigment? {
pin=1
implements = [
"org.ton.intellij.tact.psi.TactNameIdentifierOwner"
Expand Down Expand Up @@ -225,7 +228,7 @@ Message ::= 'message' MessageId? IDENTIFIER BlockFields {
MessageId ::= '(' INTEGER_LITERAL ')' {
pin=1
}
BlockFields ::= '{' Field* '}' {pin=1}
BlockFields ::= '{' [<<trailing_semicolon_separated_list Field>>] '}' {pin=1}

// Contract
WithClause ::= 'with' <<trailing_comma_separated_list Type>> {pin=1}
Expand All @@ -245,14 +248,16 @@ private ContractItem_with_recover ::= !('}' | <<eof>>) ContractItem {
// recoverWhile=ContractItem_recover
}
private ContractItem_recover ::= !('}' | Item_first | IDENTIFIER)
private ContractItem ::= Field
private ContractItem ::= StorageVariable
| Constant
| ContractInit
| ReceiveFunction
| BouncedFunction
| ExternalFunction
| Function

private StorageVariable ::= Field ';'

ContractAttribute ::= '@interface' StringId {pin=1}
ContractInit ::= 'init' FunctionParameters Block {
pin=1
Expand All @@ -276,7 +281,7 @@ Trait ::= ContractAttribute* 'trait' IDENTIFIER WithClause? TraitBody {
TraitBody ::= '{' TraitItem* '}' {
pin = 1
}
private TraitItem ::= Field
private TraitItem ::= StorageVariable
| Constant
| ReceiveFunction
| BouncedFunction
Expand Down Expand Up @@ -365,28 +370,28 @@ private BlockItem ::= !'}' Statement {
}
private BlockItem_recover ::= !('}' | Statement_first | Item_first | ';')

LetStatement ::= 'let' IDENTIFIER TypeAscription '=' Expression ';' {
LetStatement ::= 'let' (IDENTIFIER|'_') TypeAscription? '=' Expression ';' {
pin = 1
rightAssociative=true
implements=[
"org.ton.intellij.tact.psi.TactNamedElement"
]
mixin="org.ton.intellij.tact.psi.impl.TactLetStatementImplMixin"
mixin="org.ton.intellij.tact.psi.impl.TactLetStatementImplMixin"
}
private TypeAscription ::= ':' Type {
pin = 1
}

BlockStatement ::= Block
ReturnStatement ::= 'return' Expression? ';' { pin = 1 }
ExpressionStatement ::= Expression ';' {
ReturnStatement ::= 'return' Expression? ';'? { pin = 1 }
ExpressionStatement ::= Expression (';' | &'}') {
pin=1
}
AssignStatement ::= Expression AssignBinOp Expression ';' {
rightAssociative = true
}

AssignBinOp ::= '=' | '+=' | '-=' | '*=' | '/=' | '%=' {
AssignBinOp ::= '=' | '+=' | '-=' | '*=' | '/=' | '%=' | '&=' | '|=' | '^=' {
name = "operator"
}

Expand All @@ -396,12 +401,28 @@ WhileStatement ::= 'while' Condition Block {pin=1}
RepeatStatement ::= 'repeat' Condition Block {pin=1}
UntilStatement ::= 'do' Block 'until' Condition ';' {pin=1}
TryStatement ::= 'try' Block CatchClause? {pin=1}
CatchClause ::= 'catch' '(' IDENTIFIER ')' Block {pin=1}
ForEachStatement ::= 'foreach' '(' IDENTIFIER ',' IDENTIFIER 'in' Expression ')' Block {pin=1}

Condition ::= '(' Expression ')' {
pin=1
CatchClause ::= 'catch' '(' CatchParameter ')' Block {pin=1}
CatchParameter ::= IDENTIFIER {
mixin = "org.ton.intellij.tact.psi.impl.TactCatchParameterMixin"
implements = [
"org.ton.intellij.tact.psi.TactNamedElement"
]
}
ForEachStatement ::= 'foreach' '(' ForEachKey ',' ForEachValue 'in' Expression ')' Block {pin=1}
ForEachKey ::= IDENTIFIER {
mixin = "org.ton.intellij.tact.psi.impl.TactForEachKeyMixin"
implements = [
"org.ton.intellij.tact.psi.TactNamedElement"
]
}
ForEachValue ::= IDENTIFIER {
mixin = "org.ton.intellij.tact.psi.impl.TactForEachValueMixin"
implements = [
"org.ton.intellij.tact.psi.TactNamedElement"
]
}

Condition ::= Expression

// Expressions
Expression ::= TernaryExpression
Expand Down Expand Up @@ -497,10 +518,14 @@ FieldExpression ::= IDENTIFIER !'(' {
ParenExpression ::= '(' ParenExpressionItem ')' {pin=1}
private ParenExpressionItem ::= Expression

StructExpression ::= IDENTIFIER '{' [<<trailing_comma_separated_list StructExpressionField_with_recover>>] '}' {
pin=2
StructExpression ::= IDENTIFIER '{' StructExpressionFields '}' {
mixin = "org.ton.intellij.tact.psi.impl.TactStructExpressionImplMixin"
}

private StructExpressionFields ::= <<trailing_comma_separated_list StructExpressionField_with_recover>> {

}

StructExpressionField ::= IDENTIFIER (':' Expression)? {pin=1}
private StructExpressionField_with_recover ::= !('}') StructExpressionField {
pin=1
Expand All @@ -523,4 +548,5 @@ InitOfExpression ::= 'initOf' IDENTIFIER '(' [<<trailing_comma_separated_list Ex
StringExpression ::= StringLiteral

//private meta comma_separated_list ::= <<param>> ( ',' <<param>> )*
private meta trailing_comma_separated_list ::= <<param>> (',' (<<param>> | &')'))*
private meta trailing_comma_separated_list ::= <<param>> (',' (<<param>> | &')' | &'}'))*
private meta trailing_semicolon_separated_list ::= <<param>> (';' (<<param>> | &'}'))*
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import org.ton.intellij.tact.inspections.TactLocalInspectionTool
import org.ton.intellij.tact.inspections.TactTypeCheckInspection
import org.ton.intellij.tact.psi.TactNamedElement
import org.ton.intellij.tact.type.TactTy
import org.ton.intellij.tact.type.TactTyRef
import org.ton.intellij.util.PreparedAnnotation

sealed class TactDiagnostic(
Expand Down Expand Up @@ -48,7 +49,23 @@ sealed class TactDiagnostic(
override fun prepare(): PreparedAnnotation = PreparedAnnotation(
ProblemHighlightType.GENERIC_ERROR,
"Type mismatch",
"expected `$expectedTy`, found `$actualTy`",
buildString {
val expectedName = expectedTy.toString()
val actualName = actualTy.toString()
if (expectedTy is TactTyRef && actualTy is TactTyRef && expectedName == actualName) {
append("expected: `")
append(expectedName)
append("` [")
append(expectedTy.item.containingFile.virtualFile.path)
append("], found: `")
append(actualName)
append("` [")
append(actualTy.item.containingFile.virtualFile.path)
append("]")
} else {
append("expected `$expectedName`, found `$actualName`")
}
},
fixes = buildList {

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ abstract class TactCallExpressionImplMixin(node: ASTNode) : ASTWrapperPsiElement

"get",
"set",
"del",
"asCell" -> {
val parent = parent
if (parent is TactDotExpression) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package org.ton.intellij.tact.psi.impl

import com.intellij.extapi.psi.ASTWrapperPsiElement
import com.intellij.lang.ASTNode
import com.intellij.psi.PsiElement
import org.ton.intellij.tact.TactIcons
import org.ton.intellij.tact.psi.TactCatchParameter
import org.ton.intellij.tact.psi.TactPsiFactory
import javax.swing.Icon

abstract class TactCatchParameterMixin(node: ASTNode) : ASTWrapperPsiElement(node), TactCatchParameter {
override fun getName(): String = identifier.text

override fun setName(name: String): PsiElement {
(identifier).replace(TactPsiFactory(project).createIdentifier(name))
return this
}

override fun getTextOffset(): Int = identifier.textOffset

override fun getIcon(flags: Int): Icon = TactIcons.VARIABLE
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package org.ton.intellij.tact.psi.impl

import com.intellij.extapi.psi.ASTWrapperPsiElement
import com.intellij.lang.ASTNode
import com.intellij.psi.PsiElement
import org.ton.intellij.tact.TactIcons
import org.ton.intellij.tact.psi.TactForEachKey
import org.ton.intellij.tact.psi.TactPsiFactory
import javax.swing.Icon

abstract class TactForEachKeyMixin(node: ASTNode) : ASTWrapperPsiElement(node), TactForEachKey {
override fun getName(): String? = identifier.text

override fun setName(name: String): PsiElement {
identifier.replace(TactPsiFactory(project).createIdentifier(name))
return this
}

override fun getTextOffset(): Int = identifier.textOffset

override fun getIcon(flags: Int): Icon = TactIcons.VARIABLE
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package org.ton.intellij.tact.psi.impl

import com.intellij.extapi.psi.ASTWrapperPsiElement
import com.intellij.lang.ASTNode
import com.intellij.psi.PsiElement
import org.ton.intellij.tact.TactIcons
import org.ton.intellij.tact.psi.TactForEachValue
import org.ton.intellij.tact.psi.TactPsiFactory
import javax.swing.Icon

abstract class TactForEachValueMixin(node: ASTNode) : ASTWrapperPsiElement(node), TactForEachValue {
override fun getName(): String? = identifier.text

override fun setName(name: String): PsiElement {
identifier.replace(TactPsiFactory(project).createIdentifier(name))
return this
}

override fun getTextOffset(): Int = identifier.textOffset

override fun getIcon(flags: Int): Icon = TactIcons.VARIABLE
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,12 @@ package org.ton.intellij.tact.resolve
import com.intellij.openapi.util.TextRange
import org.ton.intellij.tact.psi.TactElement
import org.ton.intellij.tact.psi.TactTypeDeclarationElement
import org.ton.intellij.tact.stub.index.TactTypesIndex
import org.ton.intellij.tact.type.TactTy

class TactTypeReference<T : TactElement>(element: T, range: TextRange) : TactReferenceBase<T>(
element, range
) {
override fun multiResolve(): Collection<TactTypeDeclarationElement> {
val currentFile = element.containingFile
val result = TactTypesIndex.findElementsByName(element.project, value)
val localType = result.find { it.containingFile == currentFile }
if (localType != null) {
return listOf(localType)
}
return listOf(result.firstOrNull() ?: return emptyList())
return listOf(TactTy.searchDeclarations(element, value).firstOrNull() ?: return emptyList())
}
}
16 changes: 12 additions & 4 deletions src/main/kotlin/org/ton/intellij/tact/type/TactTy.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package org.ton.intellij.tact.type

import com.intellij.openapi.project.Project
import org.ton.intellij.tact.psi.TactElement
import org.ton.intellij.tact.psi.TactReferencedType
import org.ton.intellij.tact.psi.TactType
import org.ton.intellij.tact.psi.TactTypeDeclarationElement
Expand All @@ -12,10 +12,18 @@ interface TactTy {
fun isAssignable(other: TactTy): Boolean

companion object {
fun search(project: Project, name: String): List<TactTy> {
return TactTypesIndex.findElementsByName(project, name).map {
it.declaredTy
fun search(element: TactElement, name: String): List<TactTy> {
return searchDeclarations(element, name).map { it.declaredTy }
}

fun searchDeclarations(element: TactElement, name: String): List<TactTypeDeclarationElement> {
val types = TactTypesIndex.findElementsByName(element.project, name)
val currentFile = element.containingFile
val localType = types.find { it.containingFile == currentFile }
if (localType != null) {
return listOf(localType)
}
return types.toList()
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,13 +116,20 @@ class TactInferenceContext(
}
}
}

is TactForEachStatement -> {
scope.forEachKey?.let { variableCandidates.add(it) }
scope.forEachValue?.let { variableCandidates.add(it) }
}
is TactFunctionLike -> {
scope.functionParameters?.functionParameterList?.forEach { param ->
variableCandidates.add(param)
}
return@treeWalkUp false
}

is TactCatchClause -> {
scope.catchParameter?.let { variableCandidates.add(it) }
}
}
true
}
Expand Down
Loading

0 comments on commit daf1900

Please sign in to comment.