Skip to content

Commit

Permalink
adding support for structure matching
Browse files Browse the repository at this point in the history
  • Loading branch information
m0rkeulv committed Jul 24, 2024
1 parent 9a0d124 commit 7e68043
Show file tree
Hide file tree
Showing 5 changed files with 112 additions and 33 deletions.
9 changes: 8 additions & 1 deletion src/main/java/com/intellij/plugins/haxe/lang/parser/haxe.bnf
Original file line number Diff line number Diff line change
Expand Up @@ -683,7 +683,14 @@ enumExtractedValue::= enumExtractedValueReference | primitiveExtractedValue

private primitiveExtractedValue ::= enumExtractObjectLiteral | enumExtractArrayLiteral
private expressionOrExtractorMatchExpression ::= switchExtractorExpression | enumExtractArrayLiteral | enumExtractObjectLiteral | expression {extends=expression}
private enumObjectLiteralElement ::= (identifier | stringLiteralExpression) ':' expressionOrExtractorMatchExpression {pin=2 recoverWhile="object_literal_part_recover"}
enumObjectLiteralElement ::= objectLiteralComponentName ':' ( expressionOrExtractorMatchExpression)
{
pin=2
recoverWhile="object_literal_part_recover"
methods=[componentName="objectLiteralComponentName" other="stringLiteralExpression"]
mixin="com.intellij.plugins.haxe.lang.psi.impl.HaxePsiFieldImpl"
implements="com.intellij.plugins.haxe.lang.psi.HaxePsiField"
}
private enumObjectLiteralElementList ::= enumObjectLiteralElement (',' enumObjectLiteralElement)* ','? {pin=1 recoverWhile="object_literal_list_recover"}
enumExtractObjectLiteral ::= '{' enumObjectLiteralElementList '}' {pin=2}
enumExtractArrayLiteral ::= '[' expressionOrExtractorMatchExpression (',' expressionOrExtractorMatchExpression)* ']' {pin=2}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -811,6 +811,9 @@ private List<? extends PsiElement> checkIsSwitchVar(HaxeReference reference) {
// try to match when expression is array literal
if (result == null) result = tryResolveCaseArrayElement(reference, switchCaseExpr);

// try to match when expression is object literal
if (result == null) result = tryResolveCaseObjectElement(reference);

// checks if it matches default name inside array (ex. `case [2, _]:` when switch expression is array reference)
if (result == null) result = checkIfDefaultNameInCaseArray(reference, switchCaseExpr);

Expand All @@ -824,6 +827,45 @@ private List<? extends PsiElement> checkIsSwitchVar(HaxeReference reference) {
return result;
}

private List<? extends PsiElement> tryResolveCaseObjectElement(HaxeReference reference) {
if (reference.getParent() != null && reference.getParent() instanceof HaxeEnumObjectLiteralElement objectLiteralElement) {
Stack<String> objectNames = new Stack<>();
objectNames.add(extractObjectLiteralName(objectLiteralElement));

while (objectLiteralElement.getParent() instanceof HaxeEnumExtractObjectLiteral objectLiteral
&& objectLiteral.getParent() instanceof HaxeEnumObjectLiteralElement subElement) {
objectNames.push(extractObjectLiteralName(subElement));
objectLiteralElement = subElement;
}
HaxeSwitchStatement switchStatement = PsiTreeUtil.getParentOfType(reference, HaxeSwitchStatement.class);
if(switchStatement != null && switchStatement.getExpression() != null) {
ResultHolder result = HaxeExpressionEvaluator.evaluate(switchStatement.getExpression(), null).result;
if (result.isClassType()) {
while (!objectNames.isEmpty()
&& result.getClassType() != null
&& result.getClassType().getHaxeClass() instanceof HaxeObjectLiteral objectLiteral) {
String memberName = objectNames.pop();
HaxeBaseMemberModel member = objectLiteral.getModel().getMember(memberName, null);

if (member != null) {
if (objectNames.isEmpty()) {
return List.of(member.getBasePsi());
}
result = member.getResultType(null);
}else {
break;
}
}
}
}
}
return null;
}

private static String extractObjectLiteralName(HaxeEnumObjectLiteralElement objectLiteralElement) {
return objectLiteralElement.getComponentName().getIdentifier().getText();
}

@Nullable
private static List<? extends PsiElement> tryResolveVariableForGuardsAndBlock(HaxeReference reference) {
HaxeGuard guard = PsiTreeUtil.getParentOfType(reference, HaxeGuard.class);
Expand Down Expand Up @@ -1676,6 +1718,14 @@ private ResolveScopeProcessor(List<PsiElement> result, String name, PsiElement t

@Override
public boolean execute(@NotNull PsiElement element, ResolveState state) {
//TODO: should probably make a better solution for this using a HaxeComponentName
if (element.getParent() instanceof HaxeEnumObjectLiteralElement) {
if (element.textMatches(name)) {
result.add(element);
return false;
}
}

HaxeComponentName componentName = null;
if (element instanceof HaxeComponentName) {
componentName = (HaxeComponentName)element;
Expand All @@ -1697,7 +1747,7 @@ else if (element instanceof HaxeExtractorMatchAssignExpression assignExpression)
}
}

if (componentName != null && name.equals(componentName.getText())) {
if (componentName != null && componentName.textMatches(name)) {
result.add(componentName);
return false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ private List<PsiElement> getDeclarationElementToProcess(PsiElement lastParent) {
for (HaxeSwitchCaseExpr expr : list) {
addDeclarations(result, PsiTreeUtil.findChildrenOfType(expr, HaxeSwitchCaseCapture.class));
addDeclarations(result, PsiTreeUtil.findChildrenOfType(expr, HaxeExtractorMatchAssignExpression.class));
addDeclarations(result, getObjectLiteralReferences(expr));
}
}

Expand Down Expand Up @@ -217,6 +218,17 @@ else if (valueIterator != null && valueIterator != lastParent) {
return result;
}

private static @NotNull Collection<PsiElement> getObjectLiteralReferences(HaxeSwitchCaseExpr expr) {
Collection<HaxeEnumObjectLiteralElement> objectLiterals = PsiTreeUtil.findChildrenOfType(expr, HaxeEnumObjectLiteralElement.class);
return objectLiterals.stream()
.map(HaxeEnumObjectLiteralElement::getExpression)
.filter(expression -> expression instanceof HaxeReferenceExpression)
.map(HaxeReferenceExpression.class::cast)
.filter(expression -> expression.getChildren().length == 1)
.map(PsiElement.class::cast)
.toList();
}

private static void addLocalVarDeclarations(@NotNull List<PsiElement> result,
@Nullable HaxeLocalVarDeclarationList[] items) {
if (items == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ class PatternMachingTest {
}

public function testSwitchOnStructure() {
var person = { name: "Mark", age: 33 };
var person = { name: "Mark", age: 33, subElement: {id:"abc"} };

switch person {
// match person with age older than 50
Expand All @@ -75,9 +75,11 @@ class PatternMachingTest {
trace('Found Jose, who is 42');

// match on name
//TODO mlo: needs resolver work
case { name: <warning descr="Unresolved symbol">name</warning> }:
trace('Found someone called $<warning descr="Unresolved symbol">name</warning>');
case { name: name }:
trace('Found someone called $name');

case { name: name, subElement: {id: id}}:
trace('Found someone called $name with id' + id);

// matches anything
case _:
Expand Down
62 changes: 35 additions & 27 deletions src/test/resources/testData/parsing/haxe/expressions/Haxe3.txt
Original file line number Diff line number Diff line change
Expand Up @@ -589,21 +589,25 @@ Haxe File
SWITCH_CASE_EXPR
ENUM_EXTRACT_OBJECT_LITERAL
HaxePsiToken:{('{')
IDENTIFIER
HaxePsiToken:ID('name')
HaxePsiToken::(':')
STRING_LITERAL_EXPRESSION
HaxePsiToken:OPEN_QUOTE('"')
HaxePsiToken:REGULAR_STRING_PART('haxe')
HaxePsiToken:CLOSING_QUOTE('"')
ENUM_OBJECT_LITERAL_ELEMENT
OBJECT_LITERAL_COMPONENT_NAME
IDENTIFIER
HaxePsiToken:ID('name')
HaxePsiToken::(':')
STRING_LITERAL_EXPRESSION
HaxePsiToken:OPEN_QUOTE('"')
HaxePsiToken:REGULAR_STRING_PART('haxe')
HaxePsiToken:CLOSING_QUOTE('"')
HaxePsiToken:,(',')
IDENTIFIER
HaxePsiToken:ID('rating')
HaxePsiToken::(':')
STRING_LITERAL_EXPRESSION
HaxePsiToken:OPEN_QUOTE('"')
HaxePsiToken:REGULAR_STRING_PART('poor')
HaxePsiToken:CLOSING_QUOTE('"')
ENUM_OBJECT_LITERAL_ELEMENT
OBJECT_LITERAL_COMPONENT_NAME
IDENTIFIER
HaxePsiToken:ID('rating')
HaxePsiToken::(':')
STRING_LITERAL_EXPRESSION
HaxePsiToken:OPEN_QUOTE('"')
HaxePsiToken:REGULAR_STRING_PART('poor')
HaxePsiToken:CLOSING_QUOTE('"')
HaxePsiToken:}('}')
HaxePsiToken::(':')
SWITCH_CASE_BLOCK
Expand All @@ -617,20 +621,24 @@ Haxe File
SWITCH_CASE_EXPR
ENUM_EXTRACT_OBJECT_LITERAL
HaxePsiToken:{('{')
IDENTIFIER
HaxePsiToken:ID('rating')
HaxePsiToken::(':')
STRING_LITERAL_EXPRESSION
HaxePsiToken:OPEN_QUOTE('"')
HaxePsiToken:REGULAR_STRING_PART('awesome')
HaxePsiToken:CLOSING_QUOTE('"')
ENUM_OBJECT_LITERAL_ELEMENT
OBJECT_LITERAL_COMPONENT_NAME
IDENTIFIER
HaxePsiToken:ID('rating')
HaxePsiToken::(':')
STRING_LITERAL_EXPRESSION
HaxePsiToken:OPEN_QUOTE('"')
HaxePsiToken:REGULAR_STRING_PART('awesome')
HaxePsiToken:CLOSING_QUOTE('"')
HaxePsiToken:,(',')
IDENTIFIER
HaxePsiToken:ID('name')
HaxePsiToken::(':')
REFERENCE_EXPRESSION
IDENTIFIER
HaxePsiToken:ID('n')
ENUM_OBJECT_LITERAL_ELEMENT
OBJECT_LITERAL_COMPONENT_NAME
IDENTIFIER
HaxePsiToken:ID('name')
HaxePsiToken::(':')
REFERENCE_EXPRESSION
IDENTIFIER
HaxePsiToken:ID('n')
HaxePsiToken:}('}')
HaxePsiToken::(':')
SWITCH_CASE_BLOCK
Expand Down

0 comments on commit 7e68043

Please sign in to comment.