diff --git a/compiler/frontend/src/org/jetbrains/kotlin/KtNodeTypes.java b/compiler/frontend/src/org/jetbrains/kotlin/KtNodeTypes.java index b8af50d254b..78e8f0a9633 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/KtNodeTypes.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/KtNodeTypes.java @@ -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"); * 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_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 SCRIPT = KtStubElementTypes.SCRIPT; diff --git a/compiler/frontend/src/org/jetbrains/kotlin/parsing/KotlinExpressionParsing.java b/compiler/frontend/src/org/jetbrains/kotlin/parsing/KotlinExpressionParsing.java index caffeedc21c..cd4fdf7cc2c 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/parsing/KotlinExpressionParsing.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/parsing/KotlinExpressionParsing.java @@ -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"); * you may not use this file except in compliance with the License. @@ -111,7 +111,9 @@ public class KotlinExpressionParsing extends AbstractKotlinParsing { 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( @@ -613,6 +615,7 @@ public class KotlinExpressionParsing extends AbstractKotlinParsing { * : functionLiteral * : declaration * : SimpleName + * : collectionLiteral * ; */ private boolean parseAtomicExpression() { @@ -621,6 +624,9 @@ public class KotlinExpressionParsing extends AbstractKotlinParsing { if (at(LPAR)) { parseParenthesizedExpression(); } + else if (at(LBRACKET)) { + parseCollectionLiteralExpression(); + } else if (at(THIS_KEYWORD)) { parseThisExpression(); } @@ -987,28 +993,59 @@ public class KotlinExpressionParsing extends AbstractKotlinParsing { * ; */ 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); - PsiBuilder.Marker indices = mark(); + PsiBuilder.Marker innerExpressions = mark(); myBuilder.disableNewlines(); advance(); // LBRACKET - while (true) { - if (at(COMMA)) errorAndAdvance("Expecting an index element"); - if (at(RBRACKET)) { - error("Expecting an index element"); - break; - } - parseExpression(); - if (!at(COMMA)) break; - advance(); // COMMA + if (!canBeEmpty && at(RBRACKET)) { + error(missingElementErrorMessage); + } + else { + parseInnerExpressions(missingElementErrorMessage); } expect(RBRACKET, "Expecting ']'"); 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 + } } /* diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/KtCollectionLiteralExpression.kt b/compiler/frontend/src/org/jetbrains/kotlin/psi/KtCollectionLiteralExpression.kt new file mode 100644 index 00000000000..0f53b8f135e --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/KtCollectionLiteralExpression.kt @@ -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) \ No newline at end of file diff --git a/compiler/testData/psi/CollectionLiterals.kt b/compiler/testData/psi/CollectionLiterals.kt new file mode 100644 index 00000000000..6f9888359e9 --- /dev/null +++ b/compiler/testData/psi/CollectionLiterals.kt @@ -0,0 +1,14 @@ +fun test() { + [] + [1] + [1, 2] + [[]] + [[1]] + [1, []] + [[], 1] + [[], [1, []]] + [1, + 2] + [1, + [2]] +} \ No newline at end of file diff --git a/compiler/testData/psi/CollectionLiterals.txt b/compiler/testData/psi/CollectionLiterals.txt new file mode 100644 index 00000000000..52960592fd5 --- /dev/null +++ b/compiler/testData/psi/CollectionLiterals.txt @@ -0,0 +1,117 @@ +JetFile: CollectionLiterals.kt + PACKAGE_DIRECTIVE + + IMPORT_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)('}') \ No newline at end of file diff --git a/compiler/testData/psi/CollectionLiterals_ERR.kt b/compiler/testData/psi/CollectionLiterals_ERR.kt new file mode 100644 index 00000000000..e8a2e67e471 --- /dev/null +++ b/compiler/testData/psi/CollectionLiterals_ERR.kt @@ -0,0 +1,8 @@ +fun test() { + [,] + [1, ] + [, 1] + [1, 2 3] + [1, 2 3, ] + [ +} \ No newline at end of file diff --git a/compiler/testData/psi/CollectionLiterals_ERR.txt b/compiler/testData/psi/CollectionLiterals_ERR.txt new file mode 100644 index 00000000000..4a29f75f529 --- /dev/null +++ b/compiler/testData/psi/CollectionLiterals_ERR.txt @@ -0,0 +1,79 @@ +JetFile: CollectionLiterals_ERR.kt + PACKAGE_DIRECTIVE + + IMPORT_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 + + 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 ']' + + 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 ']' + + 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 + + PsiWhiteSpace('\n') + PsiElement(RBRACE)('}') \ No newline at end of file diff --git a/compiler/testData/psi/annotation/oldAnnotationsRecovery.txt b/compiler/testData/psi/annotation/oldAnnotationsRecovery.txt index 27a4a9f73fe..d3da1f082a7 100644 --- a/compiler/testData/psi/annotation/oldAnnotationsRecovery.txt +++ b/compiler/testData/psi/annotation/oldAnnotationsRecovery.txt @@ -35,49 +35,50 @@ JetFile: oldAnnotationsRecovery.kt BLOCK PsiElement(LBRACE)('{') PsiWhiteSpace('\n ') - PsiErrorElement:Expecting an element + COLLECTION_LITERAL_EXPRESSION PsiElement(LBRACKET)('[') - REFERENCE_EXPRESSION - PsiElement(IDENTIFIER)('inline') - PsiErrorElement:Unexpected tokens (use ';' to separate expressions on the same line) + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('inline') PsiElement(RBRACKET)(']') - PsiWhiteSpace(' ') + PsiErrorElement:Unexpected tokens (use ';' to separate expressions on the same line) + + PsiWhiteSpace(' ') + FUN PsiElement(fun)('fun') PsiWhiteSpace(' ') PsiElement(IDENTIFIER)('bar') - PsiElement(LPAR)('(') - PsiElement(RPAR)(')') - PsiWhiteSpace(' ') - LAMBDA_EXPRESSION - FUNCTION_LITERAL + VALUE_PARAMETER_LIST + PsiElement(LPAR)('(') + PsiElement(RPAR)(')') + PsiWhiteSpace(' ') + BLOCK PsiElement(LBRACE)('{') PsiWhiteSpace('\n ') - BLOCK - RETURN - PsiElement(return)('return') - PsiWhiteSpace(' ') - INTEGER_CONSTANT - PsiElement(INTEGER_LITERAL)('1') + RETURN + PsiElement(return)('return') + PsiWhiteSpace(' ') + INTEGER_CONSTANT + PsiElement(INTEGER_LITERAL)('1') PsiWhiteSpace('\n ') PsiElement(RBRACE)('}') PsiWhiteSpace('\n\n ') - PsiErrorElement:Expecting an element + COLLECTION_LITERAL_EXPRESSION PsiElement(LBRACKET)('[') - CALL_EXPRESSION - REFERENCE_EXPRESSION - PsiElement(IDENTIFIER)('suppress') - VALUE_ARGUMENT_LIST - PsiElement(LPAR)('(') - VALUE_ARGUMENT - STRING_TEMPLATE - PsiElement(OPEN_QUOTE)('"') - LITERAL_STRING_TEMPLATE_ENTRY - PsiElement(REGULAR_STRING_PART)('1') - PsiElement(CLOSING_QUOTE)('"') - PsiElement(RPAR)(')') - PsiErrorElement:Unexpected tokens (use ';' to separate expressions on the same line) + CALL_EXPRESSION + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('suppress') + VALUE_ARGUMENT_LIST + PsiElement(LPAR)('(') + VALUE_ARGUMENT + STRING_TEMPLATE + PsiElement(OPEN_QUOTE)('"') + LITERAL_STRING_TEMPLATE_ENTRY + PsiElement(REGULAR_STRING_PART)('1') + PsiElement(CLOSING_QUOTE)('"') + PsiElement(RPAR)(')') PsiElement(RBRACKET)(']') - PsiWhiteSpace(' ') + PsiWhiteSpace(' ') + PsiErrorElement:Unexpected tokens (use ';' to separate expressions on the same line) PsiElement(INTEGER_LITERAL)('1') PsiElement(PLUS)('+') PsiElement(INTEGER_LITERAL)('1') diff --git a/compiler/tests/org/jetbrains/kotlin/parsing/ParsingTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/parsing/ParsingTestGenerated.java index e02553f97d4..8d90df896d0 100644 --- a/compiler/tests/org/jetbrains/kotlin/parsing/ParsingTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/parsing/ParsingTestGenerated.java @@ -122,6 +122,18 @@ public class ParsingTestGenerated extends AbstractParsingTest { 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") public void testCommentsBinding() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/psi/CommentsBinding.kt"); diff --git a/grammar/src/expressions.grm b/grammar/src/expressions.grm index c38df0b12a2..7eb964e7c42 100644 --- a/grammar/src/expressions.grm +++ b/grammar/src/expressions.grm @@ -120,6 +120,7 @@ atomicExpression : objectLiteral : jump : loop + : collectionLiteral : SimpleName ; @@ -275,3 +276,7 @@ arrayAccess objectLiteral : "object" (":" delegationSpecifier{","})? classBody ; + +collectionLiteral + : "[" element{","}? "]" + ;