Skip to content

Commit

Permalink
Merge pull request #177 from pontem-network/match-expr
Browse files Browse the repository at this point in the history
match expr parser and name resolution
  • Loading branch information
mkurnikov authored Aug 5, 2024
2 parents 891f3b9 + be5b918 commit 47972c6
Show file tree
Hide file tree
Showing 12 changed files with 2,017 additions and 19 deletions.
1 change: 1 addition & 0 deletions src/main/grammars/MoveLexer.flex
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ IDENTIFIER=[_a-zA-Z][_a-zA-Z0-9]*
"|" { return OR; }
"@" { return AT; }
"#" { return HASH; }
"=>" { return FAT_ARROW; }

// keywords
"script" { return SCRIPT_KW; }
Expand Down
44 changes: 40 additions & 4 deletions src/main/grammars/MoveParser.bnf
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
tokenTypeClass="org.move.lang.core.MvTokenType"

extends(".*Expr")=Expr
extends(".*Pat")=Pat
extends("(Struct|Binding|Tuple|Wild|EnumVariant)Pat")=Pat
// extends(".*Pat")=Pat
extends("(Lambda|Ref|Path|Tuple|Unit|Parens)Type")=Type

elementType(".+BinExpr")=BinaryExpr
Expand Down Expand Up @@ -96,6 +97,7 @@
EQ_EQ_GT = '==>'
LT_EQ_EQ_GT = '<==>'
DOT_DOT = '..'
FAT_ARROW = '=>'

ADDRESS = 'address_kw'
HAS = 'has_kw'
Expand All @@ -115,6 +117,7 @@
CONST_KW = 'const_kw'
SCHEMA_KW = 'schema_kw'
ENUM_KW = 'enum_kw'
MATCH_KW = 'match_kw'

LOCAL = 'local_kw'
GLOBAL = 'global_kw'
Expand Down Expand Up @@ -163,6 +166,8 @@ File ::= (NamedAddressDef | Script | AddressDef | Module | ModuleSpec)*
QualPathCodeFragmentElement ::= PathImpl

Attr ::= '#' '[' <<non_empty_comma_sep_items AttrItem>> ']' { pin = 1 }
private Attr_first ::= '#'

AttrItem ::= IDENTIFIER (AttrItemList | AttrItemInitializer)?
{
implements = [
Expand Down Expand Up @@ -361,6 +366,7 @@ fake Struct ::= Attr* native? STRUCT_KW IDENTIFIER? TypeParameterList? Abilities
"org.move.lang.core.psi.MvQualNamedElement"
"org.move.lang.core.psi.MvTypeParametersOwner"
"org.move.lang.core.psi.ext.MvItemElement"
"org.move.lang.core.psi.ext.MvFieldsOwner"
]
mixin = "org.move.lang.core.psi.ext.MvStructMixin"
stubClass = "org.move.lang.core.stubs.MvStructStub"
Expand Down Expand Up @@ -406,6 +412,7 @@ EnumVariant ::= Attr* IDENTIFIER BlockFields?
implements = [
"org.move.lang.core.psi.MvNameIdentifierOwner"
"org.move.lang.core.psi.ext.MvDocAndAttributeOwner"
"org.move.lang.core.psi.ext.MvFieldsOwner"
]
mixin = "org.move.lang.core.psi.ext.MvEnumVariantMixin"
stubClass = "org.move.lang.core.stubs.MvEnumVariantStub"
Expand Down Expand Up @@ -461,6 +468,7 @@ private FunctionSignatureInner_recover ::= !('{' | '}' | ';' | <<eof>> | Item_fi

private friend ::= <<friendKeyword>>
private enum ::= <<enumKeyword>>
private match ::= <<matchKeyword>>
private package ::= <<packageKeyword>>
private for ::= <<forKeyword>>

Expand Down Expand Up @@ -663,9 +671,12 @@ Pat ::= TuplePat
| WildPat
| BindingPat

MatchPat ::= Pat | EnumVariantPat
EnumVariantPat ::= PathImpl !('{')

WildPat ::= '_'

BindingPat ::= IDENTIFIER {
BindingPat ::= IDENTIFIER !'::' {
implements = [
"org.move.lang.core.psi.MvMandatoryNameIdentifierOwner"
]
Expand All @@ -675,7 +686,7 @@ BindingPat ::= IDENTIFIER {
TuplePat ::= '(' <<non_empty_comma_sep_items Pat>>? ')'

StructPat ::= PathImpl StructPatFieldsBlock
StructPatFieldsBlock ::= '{' StructPatField_with_recover* '}' { pin = 1 }
StructPatFieldsBlock ::= '{' StructPatField_with_recover* '}'

private StructPatField_with_recover ::= !'}' StructPatField (',' | &'}')
{
Expand All @@ -695,6 +706,8 @@ StructPatField ::= (BindingPat !':') | (IDENTIFIER StructPatFieldBinding)
}
StructPatFieldBinding ::= ':' Pat

private Pat_first ::= '_' | '(' | Path_first | AnyLitToken_first

///////////////////////////////////////////////////////////////////////////////////////////////////
// Stmts
///////////////////////////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -820,7 +833,7 @@ private MulExpr_items ::= DivBinExpr | MulBinExpr | ModBinExpr
private AddExpr_items ::= PlusBinExpr | MinusBinExpr
private LogicalEqExpr_items ::= EqualsBinExpr | NotEqualsBinExpr | LessEqualsBinExpr | LessBinExpr | GreaterEqualsBinExpr | GreaterBinExpr

private ControlFlowExpr_items ::= IfExpr | LoopExpr | WhileExpr | ForExpr
private ControlFlowExpr_items ::= IfExpr | LoopExpr | MatchExpr | WhileExpr | ForExpr
private UnaryExpr ::= CopyExpr | MoveExpr | DerefExpr | BangExpr
| ReturnExpr | ContinueExpr | BreakExpr | AbortExpr
private AtomExpr ::=
Expand Down Expand Up @@ -1001,6 +1014,28 @@ ValueArgument ::= !')' Expr &(',' | ')') {
}
private ValueArgument_recover ::= !(<<mslOnly SpecExpr_first>> | Expr_first | ')' | '}' | ';' | ',')

MatchExpr ::= Attr* <<remapContextualKwOnRollback (match MatchArgument MatchBody)>>
{
implements = [ "org.move.lang.core.psi.ext.MvDocAndAttributeOwner" ]
}
// argument is fake, provided just to satisfy usage checker
//private MatchExprImpl ::= <<remapMatchOnRollback (match '(' Expr ')' MatchBody)>>
//private MatchExprImplInner ::= match '(' Expr ')' MatchBody
MatchArgument ::= '(' Expr ')'

MatchBody ::= '{' MatchArm_with_recover* '}' { pin = 1 }
MatchArm ::= Attr* MatchPat '=>' Expr ','?
{
pin = 2
implements = [ "org.move.lang.core.psi.ext.MvDocAndAttributeOwner" ]
}
private MatchArm_with_recover ::= !'}' MatchArm
{
pin = 1
recoverWhile = MatchArm_recover
}
private MatchArm_recover ::= !(Pat_first | Attr_first | '}')

IfExpr ::= if Condition AnyBlock ElseBlock? { pin = 1 }

Condition ::= '(' ConditionBody ')' { pin = 1 }
Expand Down Expand Up @@ -1086,6 +1121,7 @@ fake Path ::= (Path '::')? (PathIdent | PathAddress) TypeArgumentList?
}

private PathImpl ::= PathStart PathSegment*
private Path_first ::= PATH_MODE_IDENTIFIER | DIEM_ADDRESS | INTEGER_LITERAL

PathStart ::= ((PathAddress &'::') | PathIdent) TypeArgumentList?
{
Expand Down
29 changes: 22 additions & 7 deletions src/main/kotlin/org/move/lang/core/MoveParserUtil.kt
Original file line number Diff line number Diff line change
Expand Up @@ -97,17 +97,12 @@ object MoveParserUtil: GeneratedParserUtilBase() {
return true
}

enum class PathParsingMode {
VALUE, WILDCARD;
}
enum class PathParsingMode { VALUE, WILDCARD }

@JvmStatic
fun isPathMode(b: PsiBuilder, level: Int, mode: PathParsingMode): Boolean =
mode == getPathMod(b.flags)

private val PATH_MODE_VALUE: Int = makeBitMask(2)
private val PATH_MODE_WILDCARD: Int = makeBitMask(3)

private fun setPathMod(flags: Int, mode: PathParsingMode): Int {
val flag = when (mode) {
PathParsingMode.VALUE -> PATH_MODE_VALUE
Expand Down Expand Up @@ -317,6 +312,15 @@ object MoveParserUtil: GeneratedParserUtilBase() {
return true
}

@JvmStatic
fun remapContextualKwOnRollback(b: PsiBuilder, level: Int, p: Parser): Boolean {
val result = p.parse(b, level)
if (!result && b.tokenType in CONTEXTUAL_KEYWORDS) {
b.remapCurrentToken(IDENTIFIER)
}
return result
}

@Suppress("FunctionName")
@JvmStatic
fun VECTOR_IDENTIFIER(b: PsiBuilder, level: Int): Boolean {
Expand Down Expand Up @@ -355,6 +359,9 @@ object MoveParserUtil: GeneratedParserUtilBase() {
@JvmStatic
fun enumKeyword(b: PsiBuilder, level: Int): Boolean = contextualKeyword(b, "enum", ENUM_KW)

@JvmStatic
fun matchKeyword(b: PsiBuilder, level: Int): Boolean = contextualKeyword(b, "match", MATCH_KW)

@JvmStatic
fun packageKeyword(b: PsiBuilder, level: Int): Boolean = contextualKeyword(b, "package", PACKAGE)

Expand Down Expand Up @@ -455,7 +462,7 @@ object MoveParserUtil: GeneratedParserUtilBase() {

private val FLAGS: Key<Int> = Key("MoveParserUtil.FLAGS")
private var PsiBuilder.flags: Int
get() = getUserData(FLAGS) ?: TOP_LEVEL
get() = getUserData(FLAGS) ?: DEFAULT_FLAGS
set(value) = putUserData(FLAGS, value)

private fun Int.setFlag(flag: Int, mode: Boolean): Int =
Expand All @@ -465,6 +472,14 @@ object MoveParserUtil: GeneratedParserUtilBase() {
private val TOP_LEVEL: Int = makeBitMask(0)
private val INCLUDE_STMT_MODE: Int = makeBitMask(1)

private val PATH_MODE_VALUE: Int = makeBitMask(2)
private val PATH_MODE_WILDCARD: Int = makeBitMask(3)

// private val STRUCT_ALLOWED: Int = makeBitMask(4)

private val DEFAULT_FLAGS: Int = TOP_LEVEL
// private val DEFAULT_FLAGS: Int = TOP_LEVEL or STRUCT_ALLOWED

// msl
private val MSL_LEVEL: Key<Int> = Key("MoveParserUtil.MSL_LEVEL")
private var PsiBuilder.mslLevel: Int
Expand Down
3 changes: 2 additions & 1 deletion src/main/kotlin/org/move/lang/core/MvTokenType.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@ val MOVE_KEYWORDS = tokenSetOf(
ASSUME, ASSERT, REQUIRES, ENSURES, INVARIANT, MODIFIES, PRAGMA, INCLUDE, ABORTS_IF, WITH, UPDATE, DECREASES,
SPEC, SCHEMA_KW, GLOBAL, LOCAL,
EMITS, APPLY, TO, EXCEPT, INTERNAL,
FORALL, EXISTS, IN, WHERE,
FORALL, EXISTS, IN, WHERE, MATCH_KW,
)
val CONTEXTUAL_KEYWORDS = tokenSetOf(MATCH_KW)

val TYPES = tokenSetOf(PATH_TYPE, REF_TYPE, TUPLE_TYPE)

Expand Down
31 changes: 31 additions & 0 deletions src/main/kotlin/org/move/lang/core/psi/ext/MvFieldsOwner.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package org.move.lang.core.psi.ext

import org.move.lang.core.psi.MvBlockFields
import org.move.lang.core.psi.MvNameIdentifierOwner
import org.move.lang.core.psi.MvNamedFieldDecl

interface MvFieldsOwner: MvNameIdentifierOwner {
val blockFields: MvBlockFields?
}

val MvFieldsOwner.fields: List<MvNamedFieldDecl>
get() = namedFields //+ positionalFields

val MvFieldsOwner.namedFields: List<MvNamedFieldDecl>
get() = blockFields?.namedFieldDeclList.orEmpty()


/**
* True for:
* ```
* struct S;
* enum E { A }
* ```
* but false for:
* ```
* struct S {}
* struct S();
* ```
*/
val MvFieldsOwner.isFieldless: Boolean
get() = blockFields == null //&& tupleFields == null
4 changes: 3 additions & 1 deletion src/main/kotlin/org/move/lang/core/psi/ext/MvPath.kt
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,9 @@ fun MvPath.allowedNamespaces(): Set<Namespace> {
parentElement is MvCallExpr -> EnumSet.of(FUNCTION)
parentElement is MvRefExpr && this.hasAncestor<MvAttrItem>() -> EnumSet.of(NAME, MODULE)
parentElement is MvRefExpr -> EnumSet.of(NAME)
parentElement is MvStructLitExpr || parentElement is MvStructPat -> EnumSet.of(TYPE)
parentElement is MvStructLitExpr
|| parentElement is MvStructPat
|| parentElement is MvEnumVariantPat -> EnumSet.of(TYPE)
parentElement is MvAccessSpecifier -> EnumSet.of(TYPE)
parentElement is MvAddressSpecifierArg -> EnumSet.of(FUNCTION)
parentElement is MvAddressSpecifierCallParam -> EnumSet.of(NAME)
Expand Down
14 changes: 9 additions & 5 deletions src/main/kotlin/org/move/lang/core/psi/ext/MvStructLitField.kt
Original file line number Diff line number Diff line change
Expand Up @@ -53,18 +53,22 @@ fun processStructRefFieldResolveVariants(
fieldRef: MvStructRefField,
processor: RsResolveProcessor
): Boolean {
val structItem = fieldRef.maybeStruct ?: return false
return structItem.fields
val fieldsOwnerItem = fieldRef.fieldOwner ?: return false
return fieldsOwnerItem.fields
.any { field ->
processor.process(SimpleScopeEntry(field.name, field, setOf(Namespace.NAME)))
}
}

private val MvStructRefField.maybeStruct: MvStruct?
private val MvStructRefField.fieldOwner: MvFieldsOwner?
get() {
return when (this) {
is MvStructPatField -> this.structPat.structItem
is MvStructLitField -> this.structLitExpr.path.maybeStruct
is MvStructPatField -> {
this.structPat.path.reference?.resolveFollowingAliases() as? MvFieldsOwner
}
is MvStructLitField -> {
this.structLitExpr.path.reference?.resolveFollowingAliases() as? MvFieldsOwner
}
else -> null
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,16 @@ fun processItemsInScope(
false
}
}
is MvMatchArm -> {
if (cameFrom is MvMatchPat) continue
// only use those bindings for the match arm rhs
processor.processAll(scope.matchPat.pat.bindings.toList())
}
is MvItemSpec -> {
val item = scope.item
when (item) {
is MvFunction -> {
processor.processAll(
// contextScopeInfo,
item.valueParamsAsBindings,
item.specResultParameters.map { it.bindingPat },
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ class CompleteParsingTest: MvParsingTestCase("complete") {
fun `test index expr`() = doTest()
fun `test public package`() = doTest()
fun `test enums`() = doTest()
fun `test match`() = doTest()

// feature declaration is required here, as it's a parser-level feature
@CompilerV2Features(RESOURCE_CONTROL)
Expand Down
Loading

0 comments on commit 47972c6

Please sign in to comment.