Parse collection literals as atomic expressions

This commit is contained in:
Mikhail Zarechenskiy
2017-03-17 12:40:47 +03:00
parent 3c7678092e
commit d3fd96ceed
10 changed files with 341 additions and 45 deletions
@@ -1,5 +1,5 @@
/* /*
* Copyright 2010-2015 JetBrains s.r.o. * Copyright 2010-2017 JetBrains s.r.o.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -201,6 +201,8 @@ public interface KtNodeTypes {
KtNodeType WHEN_CONDITION_IS_PATTERN = new KtNodeType("WHEN_CONDITION_IS_PATTERN", KtWhenConditionIsPattern.class); KtNodeType WHEN_CONDITION_IS_PATTERN = new KtNodeType("WHEN_CONDITION_IS_PATTERN", KtWhenConditionIsPattern.class);
KtNodeType WHEN_CONDITION_EXPRESSION = new KtNodeType("WHEN_CONDITION_WITH_EXPRESSION", KtWhenConditionWithExpression.class); KtNodeType WHEN_CONDITION_EXPRESSION = new KtNodeType("WHEN_CONDITION_WITH_EXPRESSION", KtWhenConditionWithExpression.class);
KtNodeType COLLECTION_LITERAL_EXPRESSION = new KtNodeType("COLLECTION_LITERAL_EXPRESSION", KtCollectionLiteralExpression.class);
IElementType PACKAGE_DIRECTIVE = KtStubElementTypes.PACKAGE_DIRECTIVE; IElementType PACKAGE_DIRECTIVE = KtStubElementTypes.PACKAGE_DIRECTIVE;
IElementType SCRIPT = KtStubElementTypes.SCRIPT; IElementType SCRIPT = KtStubElementTypes.SCRIPT;
@@ -1,5 +1,5 @@
/* /*
* Copyright 2010-2015 JetBrains s.r.o. * Copyright 2010-2017 JetBrains s.r.o.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -111,7 +111,9 @@ public class KotlinExpressionParsing extends AbstractKotlinParsing {
IDENTIFIER, // SimpleName IDENTIFIER, // SimpleName
AT // Just for better recovery and maybe for annotations AT, // Just for better recovery and maybe for annotations
LBRACKET // Collection literal expression
); );
private static final TokenSet STATEMENT_FIRST = TokenSet.orSet( private static final TokenSet STATEMENT_FIRST = TokenSet.orSet(
@@ -613,6 +615,7 @@ public class KotlinExpressionParsing extends AbstractKotlinParsing {
* : functionLiteral * : functionLiteral
* : declaration * : declaration
* : SimpleName * : SimpleName
* : collectionLiteral
* ; * ;
*/ */
private boolean parseAtomicExpression() { private boolean parseAtomicExpression() {
@@ -621,6 +624,9 @@ public class KotlinExpressionParsing extends AbstractKotlinParsing {
if (at(LPAR)) { if (at(LPAR)) {
parseParenthesizedExpression(); parseParenthesizedExpression();
} }
else if (at(LBRACKET)) {
parseCollectionLiteralExpression();
}
else if (at(THIS_KEYWORD)) { else if (at(THIS_KEYWORD)) {
parseThisExpression(); parseThisExpression();
} }
@@ -987,28 +993,59 @@ public class KotlinExpressionParsing extends AbstractKotlinParsing {
* ; * ;
*/ */
private void parseArrayAccess() { private void parseArrayAccess() {
parseAsCollectionLiteralExpression(INDICES, false, "Expecting an index element");
}
/*
* collectionLiteral
* : "[" element{","}? "]"
* ;
*/
private void parseCollectionLiteralExpression() {
parseAsCollectionLiteralExpression(COLLECTION_LITERAL_EXPRESSION, true, "Expecting an element");
}
private void parseAsCollectionLiteralExpression(KtNodeType nodeType, boolean canBeEmpty, String missingElementErrorMessage) {
assert _at(LBRACKET); assert _at(LBRACKET);
PsiBuilder.Marker indices = mark(); PsiBuilder.Marker innerExpressions = mark();
myBuilder.disableNewlines(); myBuilder.disableNewlines();
advance(); // LBRACKET advance(); // LBRACKET
while (true) { if (!canBeEmpty && at(RBRACKET)) {
if (at(COMMA)) errorAndAdvance("Expecting an index element"); error(missingElementErrorMessage);
if (at(RBRACKET)) { }
error("Expecting an index element"); else {
break; parseInnerExpressions(missingElementErrorMessage);
}
parseExpression();
if (!at(COMMA)) break;
advance(); // COMMA
} }
expect(RBRACKET, "Expecting ']'"); expect(RBRACKET, "Expecting ']'");
myBuilder.restoreNewlinesState(); myBuilder.restoreNewlinesState();
indices.done(INDICES); innerExpressions.done(nodeType);
}
private void parseInnerExpressions(String missingElementErrorMessage) {
boolean firstElement = true;
while (true) {
if (at(COMMA)) errorAndAdvance(missingElementErrorMessage);
if (at(RBRACKET)) {
if (firstElement) {
break;
}
else {
error(missingElementErrorMessage);
}
break;
}
parseExpression();
firstElement = false;
if (!at(COMMA)) break;
advance(); // COMMA
}
} }
/* /*
@@ -0,0 +1,21 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.psi
import com.intellij.lang.ASTNode
class KtCollectionLiteralExpression(node: ASTNode) : KtExpressionImpl(node)
+14
View File
@@ -0,0 +1,14 @@
fun test() {
[]
[1]
[1, 2]
[[]]
[[1]]
[1, []]
[[], 1]
[[], [1, []]]
[1,
2]
[1,
[2]]
}
+117
View File
@@ -0,0 +1,117 @@
JetFile: CollectionLiterals.kt
PACKAGE_DIRECTIVE
<empty list>
IMPORT_LIST
<empty list>
FUN
PsiElement(fun)('fun')
PsiWhiteSpace(' ')
PsiElement(IDENTIFIER)('test')
VALUE_PARAMETER_LIST
PsiElement(LPAR)('(')
PsiElement(RPAR)(')')
PsiWhiteSpace(' ')
BLOCK
PsiElement(LBRACE)('{')
PsiWhiteSpace('\n ')
COLLECTION_LITERAL_EXPRESSION
PsiElement(LBRACKET)('[')
PsiElement(RBRACKET)(']')
PsiWhiteSpace('\n ')
COLLECTION_LITERAL_EXPRESSION
PsiElement(LBRACKET)('[')
INTEGER_CONSTANT
PsiElement(INTEGER_LITERAL)('1')
PsiElement(RBRACKET)(']')
PsiWhiteSpace('\n ')
COLLECTION_LITERAL_EXPRESSION
PsiElement(LBRACKET)('[')
INTEGER_CONSTANT
PsiElement(INTEGER_LITERAL)('1')
PsiElement(COMMA)(',')
PsiWhiteSpace(' ')
INTEGER_CONSTANT
PsiElement(INTEGER_LITERAL)('2')
PsiElement(RBRACKET)(']')
PsiWhiteSpace('\n ')
COLLECTION_LITERAL_EXPRESSION
PsiElement(LBRACKET)('[')
COLLECTION_LITERAL_EXPRESSION
PsiElement(LBRACKET)('[')
PsiElement(RBRACKET)(']')
PsiElement(RBRACKET)(']')
PsiWhiteSpace('\n ')
COLLECTION_LITERAL_EXPRESSION
PsiElement(LBRACKET)('[')
COLLECTION_LITERAL_EXPRESSION
PsiElement(LBRACKET)('[')
INTEGER_CONSTANT
PsiElement(INTEGER_LITERAL)('1')
PsiElement(RBRACKET)(']')
PsiElement(RBRACKET)(']')
PsiWhiteSpace('\n ')
COLLECTION_LITERAL_EXPRESSION
PsiElement(LBRACKET)('[')
INTEGER_CONSTANT
PsiElement(INTEGER_LITERAL)('1')
PsiElement(COMMA)(',')
PsiWhiteSpace(' ')
COLLECTION_LITERAL_EXPRESSION
PsiElement(LBRACKET)('[')
PsiElement(RBRACKET)(']')
PsiElement(RBRACKET)(']')
PsiWhiteSpace('\n ')
COLLECTION_LITERAL_EXPRESSION
PsiElement(LBRACKET)('[')
COLLECTION_LITERAL_EXPRESSION
PsiElement(LBRACKET)('[')
PsiElement(RBRACKET)(']')
PsiElement(COMMA)(',')
PsiWhiteSpace(' ')
INTEGER_CONSTANT
PsiElement(INTEGER_LITERAL)('1')
PsiElement(RBRACKET)(']')
PsiWhiteSpace('\n ')
COLLECTION_LITERAL_EXPRESSION
PsiElement(LBRACKET)('[')
COLLECTION_LITERAL_EXPRESSION
PsiElement(LBRACKET)('[')
PsiElement(RBRACKET)(']')
PsiElement(COMMA)(',')
PsiWhiteSpace(' ')
COLLECTION_LITERAL_EXPRESSION
PsiElement(LBRACKET)('[')
INTEGER_CONSTANT
PsiElement(INTEGER_LITERAL)('1')
PsiElement(COMMA)(',')
PsiWhiteSpace(' ')
COLLECTION_LITERAL_EXPRESSION
PsiElement(LBRACKET)('[')
PsiElement(RBRACKET)(']')
PsiElement(RBRACKET)(']')
PsiElement(RBRACKET)(']')
PsiWhiteSpace('\n ')
COLLECTION_LITERAL_EXPRESSION
PsiElement(LBRACKET)('[')
INTEGER_CONSTANT
PsiElement(INTEGER_LITERAL)('1')
PsiElement(COMMA)(',')
PsiWhiteSpace('\n ')
INTEGER_CONSTANT
PsiElement(INTEGER_LITERAL)('2')
PsiElement(RBRACKET)(']')
PsiWhiteSpace('\n ')
COLLECTION_LITERAL_EXPRESSION
PsiElement(LBRACKET)('[')
INTEGER_CONSTANT
PsiElement(INTEGER_LITERAL)('1')
PsiElement(COMMA)(',')
PsiWhiteSpace('\n ')
COLLECTION_LITERAL_EXPRESSION
PsiElement(LBRACKET)('[')
INTEGER_CONSTANT
PsiElement(INTEGER_LITERAL)('2')
PsiElement(RBRACKET)(']')
PsiElement(RBRACKET)(']')
PsiWhiteSpace('\n')
PsiElement(RBRACE)('}')
+8
View File
@@ -0,0 +1,8 @@
fun test() {
[,]
[1, ]
[, 1]
[1, 2 3]
[1, 2 3, ]
[
}
+79
View File
@@ -0,0 +1,79 @@
JetFile: CollectionLiterals_ERR.kt
PACKAGE_DIRECTIVE
<empty list>
IMPORT_LIST
<empty list>
FUN
PsiElement(fun)('fun')
PsiWhiteSpace(' ')
PsiElement(IDENTIFIER)('test')
VALUE_PARAMETER_LIST
PsiElement(LPAR)('(')
PsiElement(RPAR)(')')
PsiWhiteSpace(' ')
BLOCK
PsiElement(LBRACE)('{')
PsiWhiteSpace('\n ')
COLLECTION_LITERAL_EXPRESSION
PsiElement(LBRACKET)('[')
PsiErrorElement:Expecting an element
PsiElement(COMMA)(',')
PsiElement(RBRACKET)(']')
PsiWhiteSpace('\n ')
COLLECTION_LITERAL_EXPRESSION
PsiElement(LBRACKET)('[')
INTEGER_CONSTANT
PsiElement(INTEGER_LITERAL)('1')
PsiElement(COMMA)(',')
PsiErrorElement:Expecting an element
<empty list>
PsiWhiteSpace(' ')
PsiElement(RBRACKET)(']')
PsiWhiteSpace('\n ')
COLLECTION_LITERAL_EXPRESSION
PsiElement(LBRACKET)('[')
PsiErrorElement:Expecting an element
PsiElement(COMMA)(',')
PsiWhiteSpace(' ')
INTEGER_CONSTANT
PsiElement(INTEGER_LITERAL)('1')
PsiElement(RBRACKET)(']')
PsiWhiteSpace('\n ')
COLLECTION_LITERAL_EXPRESSION
PsiElement(LBRACKET)('[')
INTEGER_CONSTANT
PsiElement(INTEGER_LITERAL)('1')
PsiElement(COMMA)(',')
PsiWhiteSpace(' ')
INTEGER_CONSTANT
PsiElement(INTEGER_LITERAL)('2')
PsiErrorElement:Expecting ']'
<empty list>
PsiWhiteSpace(' ')
PsiErrorElement:Unexpected tokens (use ';' to separate expressions on the same line)
PsiElement(INTEGER_LITERAL)('3')
PsiElement(RBRACKET)(']')
PsiWhiteSpace('\n ')
COLLECTION_LITERAL_EXPRESSION
PsiElement(LBRACKET)('[')
INTEGER_CONSTANT
PsiElement(INTEGER_LITERAL)('1')
PsiElement(COMMA)(',')
PsiWhiteSpace(' ')
INTEGER_CONSTANT
PsiElement(INTEGER_LITERAL)('2')
PsiErrorElement:Expecting ']'
<empty list>
PsiWhiteSpace(' ')
PsiErrorElement:Unexpected tokens (use ';' to separate expressions on the same line)
PsiElement(INTEGER_LITERAL)('3')
PsiElement(COMMA)(',')
PsiWhiteSpace(' ')
PsiElement(RBRACKET)(']')
PsiWhiteSpace('\n ')
COLLECTION_LITERAL_EXPRESSION
PsiElement(LBRACKET)('[')
PsiErrorElement:Expecting an expression
<empty list>
PsiWhiteSpace('\n')
PsiElement(RBRACE)('}')
+32 -31
View File
@@ -35,49 +35,50 @@ JetFile: oldAnnotationsRecovery.kt
BLOCK BLOCK
PsiElement(LBRACE)('{') PsiElement(LBRACE)('{')
PsiWhiteSpace('\n ') PsiWhiteSpace('\n ')
PsiErrorElement:Expecting an element COLLECTION_LITERAL_EXPRESSION
PsiElement(LBRACKET)('[') PsiElement(LBRACKET)('[')
REFERENCE_EXPRESSION REFERENCE_EXPRESSION
PsiElement(IDENTIFIER)('inline') PsiElement(IDENTIFIER)('inline')
PsiErrorElement:Unexpected tokens (use ';' to separate expressions on the same line)
PsiElement(RBRACKET)(']') PsiElement(RBRACKET)(']')
PsiWhiteSpace(' ') PsiErrorElement:Unexpected tokens (use ';' to separate expressions on the same line)
<empty list>
PsiWhiteSpace(' ')
FUN
PsiElement(fun)('fun') PsiElement(fun)('fun')
PsiWhiteSpace(' ') PsiWhiteSpace(' ')
PsiElement(IDENTIFIER)('bar') PsiElement(IDENTIFIER)('bar')
PsiElement(LPAR)('(') VALUE_PARAMETER_LIST
PsiElement(RPAR)(')') PsiElement(LPAR)('(')
PsiWhiteSpace(' ') PsiElement(RPAR)(')')
LAMBDA_EXPRESSION PsiWhiteSpace(' ')
FUNCTION_LITERAL BLOCK
PsiElement(LBRACE)('{') PsiElement(LBRACE)('{')
PsiWhiteSpace('\n ') PsiWhiteSpace('\n ')
BLOCK RETURN
RETURN PsiElement(return)('return')
PsiElement(return)('return') PsiWhiteSpace(' ')
PsiWhiteSpace(' ') INTEGER_CONSTANT
INTEGER_CONSTANT PsiElement(INTEGER_LITERAL)('1')
PsiElement(INTEGER_LITERAL)('1')
PsiWhiteSpace('\n ') PsiWhiteSpace('\n ')
PsiElement(RBRACE)('}') PsiElement(RBRACE)('}')
PsiWhiteSpace('\n\n ') PsiWhiteSpace('\n\n ')
PsiErrorElement:Expecting an element COLLECTION_LITERAL_EXPRESSION
PsiElement(LBRACKET)('[') PsiElement(LBRACKET)('[')
CALL_EXPRESSION CALL_EXPRESSION
REFERENCE_EXPRESSION REFERENCE_EXPRESSION
PsiElement(IDENTIFIER)('suppress') PsiElement(IDENTIFIER)('suppress')
VALUE_ARGUMENT_LIST VALUE_ARGUMENT_LIST
PsiElement(LPAR)('(') PsiElement(LPAR)('(')
VALUE_ARGUMENT VALUE_ARGUMENT
STRING_TEMPLATE STRING_TEMPLATE
PsiElement(OPEN_QUOTE)('"') PsiElement(OPEN_QUOTE)('"')
LITERAL_STRING_TEMPLATE_ENTRY LITERAL_STRING_TEMPLATE_ENTRY
PsiElement(REGULAR_STRING_PART)('1') PsiElement(REGULAR_STRING_PART)('1')
PsiElement(CLOSING_QUOTE)('"') PsiElement(CLOSING_QUOTE)('"')
PsiElement(RPAR)(')') PsiElement(RPAR)(')')
PsiErrorElement:Unexpected tokens (use ';' to separate expressions on the same line)
PsiElement(RBRACKET)(']') PsiElement(RBRACKET)(']')
PsiWhiteSpace(' ') PsiWhiteSpace(' ')
PsiErrorElement:Unexpected tokens (use ';' to separate expressions on the same line)
PsiElement(INTEGER_LITERAL)('1') PsiElement(INTEGER_LITERAL)('1')
PsiElement(PLUS)('+') PsiElement(PLUS)('+')
PsiElement(INTEGER_LITERAL)('1') PsiElement(INTEGER_LITERAL)('1')
@@ -122,6 +122,18 @@ public class ParsingTestGenerated extends AbstractParsingTest {
doParsingTest(fileName); doParsingTest(fileName);
} }
@TestMetadata("CollectionLiterals.kt")
public void testCollectionLiterals() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/psi/CollectionLiterals.kt");
doParsingTest(fileName);
}
@TestMetadata("CollectionLiterals_ERR.kt")
public void testCollectionLiterals_ERR() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/psi/CollectionLiterals_ERR.kt");
doParsingTest(fileName);
}
@TestMetadata("CommentsBinding.kt") @TestMetadata("CommentsBinding.kt")
public void testCommentsBinding() throws Exception { public void testCommentsBinding() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/psi/CommentsBinding.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/psi/CommentsBinding.kt");
+5
View File
@@ -120,6 +120,7 @@ atomicExpression
: objectLiteral : objectLiteral
: jump : jump
: loop : loop
: collectionLiteral
: SimpleName : SimpleName
; ;
@@ -275,3 +276,7 @@ arrayAccess
objectLiteral objectLiteral
: "object" (":" delegationSpecifier{","})? classBody : "object" (":" delegationSpecifier{","})? classBody
; ;
collectionLiteral
: "[" element{","}? "]"
;