All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jetbrains.kotlin.parsing.KotlinExpressionParsing Maven / Gradle / Ivy

There is a newer version: 2.1.0-Beta1
Show newest version
/*
 * Copyright 2010-2015 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.parsing;

import com.google.common.collect.ImmutableMap;
import com.intellij.lang.PsiBuilder;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.tree.TokenSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.KtNodeType;
import org.jetbrains.kotlin.KtNodeTypes;
import org.jetbrains.kotlin.lexer.KtToken;
import org.jetbrains.kotlin.lexer.KtTokens;
import org.jetbrains.kotlin.parsing.KotlinParsing.NameParsingMode;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

import static org.jetbrains.kotlin.KtNodeTypes.*;
import static org.jetbrains.kotlin.lexer.KtTokens.*;
import static org.jetbrains.kotlin.parsing.KotlinParsing.AnnotationParsingMode.DEFAULT;

public class KotlinExpressionParsing extends AbstractKotlinParsing {
    private static final TokenSet WHEN_CONDITION_RECOVERY_SET = TokenSet.create(RBRACE, IN_KEYWORD, NOT_IN, IS_KEYWORD, NOT_IS, ELSE_KEYWORD);
    private static final TokenSet WHEN_CONDITION_RECOVERY_SET_WITH_ARROW = TokenSet.create(RBRACE, IN_KEYWORD, NOT_IN, IS_KEYWORD, NOT_IS, ELSE_KEYWORD, ARROW, DOT);


    private static final ImmutableMap KEYWORD_TEXTS = tokenSetToMap(KEYWORDS);

    private static ImmutableMap tokenSetToMap(TokenSet tokens) {
        ImmutableMap.Builder builder = ImmutableMap.builder();
        for (IElementType token : tokens.getTypes()) {
            builder.put(token.toString(), (KtToken) token);
        }
        return builder.build();
    }

    private static final TokenSet TYPE_ARGUMENT_LIST_STOPPERS = TokenSet.create(
            INTEGER_LITERAL, FLOAT_LITERAL, CHARACTER_LITERAL, OPEN_QUOTE,
            PACKAGE_KEYWORD, AS_KEYWORD, TYPE_ALIAS_KEYWORD, INTERFACE_KEYWORD, CLASS_KEYWORD, THIS_KEYWORD, VAL_KEYWORD, VAR_KEYWORD,
            FUN_KEYWORD, FOR_KEYWORD, NULL_KEYWORD,
            TRUE_KEYWORD, FALSE_KEYWORD, IS_KEYWORD, THROW_KEYWORD, RETURN_KEYWORD, BREAK_KEYWORD,
            CONTINUE_KEYWORD, OBJECT_KEYWORD, IF_KEYWORD, TRY_KEYWORD, ELSE_KEYWORD, WHILE_KEYWORD, DO_KEYWORD,
            WHEN_KEYWORD, RBRACKET, RBRACE, RPAR, PLUSPLUS, MINUSMINUS, EXCLEXCL,
            //            MUL,
            PLUS, MINUS, EXCL, DIV, PERC, LTEQ,
            // TODO GTEQ,   foo=x
            EQEQEQ, EXCLEQEQEQ, EQEQ, EXCLEQ, ANDAND, OROR, SAFE_ACCESS, ELVIS,
            SEMICOLON, RANGE, EQ, MULTEQ, DIVEQ, PERCEQ, PLUSEQ, MINUSEQ, NOT_IN, NOT_IS,
            COLONCOLON,
            COLON
    );

    /*package*/ static final TokenSet EXPRESSION_FIRST = TokenSet.create(
            // Prefix
            MINUS, PLUS, MINUSMINUS, PLUSPLUS,
            EXCL, EXCLEXCL, // Joining complex tokens makes it necessary to put EXCLEXCL here
            // Atomic

            COLONCOLON, // callable reference

            LPAR, // parenthesized

            // literal constant
            TRUE_KEYWORD, FALSE_KEYWORD,
            OPEN_QUOTE,
            INTEGER_LITERAL, CHARACTER_LITERAL, FLOAT_LITERAL,
            NULL_KEYWORD,

            LBRACE, // functionLiteral
            FUN_KEYWORD, // expression function

            THIS_KEYWORD, // this
            SUPER_KEYWORD, // super

            IF_KEYWORD, // if
            WHEN_KEYWORD, // when
            TRY_KEYWORD, // try
            OBJECT_KEYWORD, // object

            // jump
            THROW_KEYWORD,
            RETURN_KEYWORD,
            CONTINUE_KEYWORD,
            BREAK_KEYWORD,

            // loop
            FOR_KEYWORD,
            WHILE_KEYWORD,
            DO_KEYWORD,

            IDENTIFIER, // SimpleName

            PACKAGE_KEYWORD, // for absolute qualified names
            AT // Just for better recovery and maybe for annotations
    );

    private static final TokenSet STATEMENT_FIRST = TokenSet.orSet(
            EXPRESSION_FIRST,
            TokenSet.create(
                    // declaration
                    FUN_KEYWORD,
                    VAL_KEYWORD, VAR_KEYWORD,
                    INTERFACE_KEYWORD,
                    CLASS_KEYWORD,
                    TYPE_ALIAS_KEYWORD
            ),
            MODIFIER_KEYWORDS
    );

    private static final TokenSet STATEMENT_NEW_LINE_QUICK_RECOVERY_SET =
            TokenSet.orSet(
                    TokenSet.andSet(STATEMENT_FIRST, TokenSet.andNot(KEYWORDS, TokenSet.create(IN_KEYWORD))),
                    TokenSet.create(EOL_OR_SEMICOLON));

    /*package*/ static final TokenSet EXPRESSION_FOLLOW = TokenSet.create(
            SEMICOLON, ARROW, COMMA, RBRACE, RPAR, RBRACKET
    );

    @SuppressWarnings({"UnusedDeclaration"})
    public enum Precedence {
        POSTFIX(PLUSPLUS, MINUSMINUS, EXCLEXCL,
                DOT, SAFE_ACCESS), // typeArguments? valueArguments : typeArguments : arrayAccess

        PREFIX(MINUS, PLUS, MINUSMINUS, PLUSPLUS, EXCL) { // annotations

            @Override
            public void parseHigherPrecedence(KotlinExpressionParsing parser) {
                throw new IllegalStateException("Don't call this method");
            }
        },

        AS(AS_KEYWORD, AS_SAFE) {
            @Override
            public KtNodeType parseRightHandSide(IElementType operation, KotlinExpressionParsing parser) {
                parser.myJetParsing.parseTypeRef();
                return BINARY_WITH_TYPE;
            }

            @Override
            public void parseHigherPrecedence(KotlinExpressionParsing parser) {
                parser.parsePrefixExpression();
            }
        },

        MULTIPLICATIVE(MUL, DIV, PERC),
        ADDITIVE(PLUS, MINUS),
        RANGE(KtTokens.RANGE),
        SIMPLE_NAME(IDENTIFIER),
        ELVIS(KtTokens.ELVIS),
        IN_OR_IS(IN_KEYWORD, NOT_IN, IS_KEYWORD, NOT_IS) {
            @Override
            public KtNodeType parseRightHandSide(IElementType operation, KotlinExpressionParsing parser) {
                if (operation == IS_KEYWORD || operation == NOT_IS) {
                    parser.myJetParsing.parseTypeRef();
                    return IS_EXPRESSION;
                }

                return super.parseRightHandSide(operation, parser);
            }
        },
        COMPARISON(LT, GT, LTEQ, GTEQ),
        EQUALITY(EQEQ, EXCLEQ, EQEQEQ, EXCLEQEQEQ),
        CONJUNCTION(ANDAND),
        DISJUNCTION(OROR),
        //        ARROW(KtTokens.ARROW),
        ASSIGNMENT(EQ, PLUSEQ, MINUSEQ, MULTEQ, DIVEQ, PERCEQ),
        ;

        static {
            Precedence[] values = Precedence.values();
            for (Precedence precedence : values) {
                int ordinal = precedence.ordinal();
                precedence.higher = ordinal > 0 ? values[ordinal - 1] : null;
            }
        }

        private Precedence higher;
        private final TokenSet operations;

        Precedence(IElementType... operations) {
            this.operations = TokenSet.create(operations);
        }

        public void parseHigherPrecedence(KotlinExpressionParsing parser) {
            assert higher != null;
            parser.parseBinaryExpression(higher);
        }

        /**
         *
         * @param operation the operation sign (e.g. PLUS or IS)
         * @param parser the parser object
         * @return node type of the result
         */
        public KtNodeType parseRightHandSide(IElementType operation, KotlinExpressionParsing parser) {
            parseHigherPrecedence(parser);
            return BINARY_EXPRESSION;
        }

        @NotNull
        public final TokenSet getOperations() {
            return operations;
        }
    }

    public static final TokenSet ALLOW_NEWLINE_OPERATIONS = TokenSet.create(
            DOT, SAFE_ACCESS,
            COLON, AS_KEYWORD, AS_SAFE,
            ELVIS,
            // Can't allow `is` and `!is` because of when entry conditions: IS_KEYWORD, NOT_IS,
            ANDAND,
            OROR
    );

    public static final TokenSet ALL_OPERATIONS;

    static {
        Set operations = new HashSet();
        Precedence[] values = Precedence.values();
        for (Precedence precedence : values) {
            operations.addAll(Arrays.asList(precedence.getOperations().getTypes()));
        }
        ALL_OPERATIONS = TokenSet.create(operations.toArray(new IElementType[operations.size()]));
    }

    static {
        IElementType[] operations = OPERATIONS.getTypes();
        Set opSet = new HashSet(Arrays.asList(operations));
        IElementType[] usedOperations = ALL_OPERATIONS.getTypes();
        Set usedSet = new HashSet(Arrays.asList(usedOperations));

        if (opSet.size() > usedSet.size()) {
            opSet.removeAll(usedSet);
            assert false : opSet;
        }
        assert usedSet.size() == opSet.size() : "Either some ops are unused, or something a non-op is used";

        usedSet.removeAll(opSet);

        assert usedSet.isEmpty() : usedSet.toString();
    }


    private final KotlinParsing myJetParsing;

    public KotlinExpressionParsing(SemanticWhitespaceAwarePsiBuilder builder, KotlinParsing jetParsing) {
        super(builder);
        myJetParsing = jetParsing;
    }

    /*
     * element
     *   : annotations element
     *   : "(" element ")" // see tupleLiteral
     *   : literalConstant
     *   : functionLiteral
     *   : tupleLiteral
     *   : "null"
     *   : "this" ("<" type ">")?
     *   : expressionWithPrecedences
     *   : if
     *   : try
     *   : "typeof" "(" element ")"
     *   : "new" constructorInvocation
     *   : objectLiteral
     *   : declaration
     *   : jump
     *   : loop
     *   // block is syntactically equivalent to a functionLiteral with no parameters
     *   ;
     */
    public void parseExpression() {
        if (!atSet(EXPRESSION_FIRST)) {
            error("Expecting an expression");
            return;
        }
        parseBinaryExpression(Precedence.ASSIGNMENT);
    }

    /*
     * element (operation element)*
     *
     * see the precedence table
     */
    private void parseBinaryExpression(Precedence precedence) {
        //        System.out.println(precedence.name() + " at " + myBuilder.getTokenText());

        PsiBuilder.Marker expression = mark();

        precedence.parseHigherPrecedence(this);

        while (!interruptedWithNewLine() && atSet(precedence.getOperations())) {
            IElementType operation = tt();

            parseOperationReference();

            KtNodeType resultType = precedence.parseRightHandSide(operation, this);
            expression.done(resultType);
            expression = expression.precede();
        }

        expression.drop();
    }

    /*
     * label prefixExpression
     */
    private void parseLabeledExpression() {
        PsiBuilder.Marker expression = mark();
        parseLabelDefinition();
        parsePrefixExpression();
        expression.done(LABELED_EXPRESSION);
    }

    /*
     * operation? prefixExpression
     */
    private void parsePrefixExpression() {
        //        System.out.println("pre at "  + myBuilder.getTokenText());

        if (at(AT)) {
            if (!parseLocalDeclaration()) {
                PsiBuilder.Marker expression = mark();
                myJetParsing.parseAnnotations(DEFAULT);
                parsePrefixExpression();
                expression.done(ANNOTATED_EXPRESSION);
            }
        }
        else {
            myBuilder.disableJoiningComplexTokens();
            if (isAtLabelDefinitionOrMissingIdentifier()) {
                myBuilder.restoreJoiningComplexTokensState();
                parseLabeledExpression();
            }
            else if (atSet(Precedence.PREFIX.getOperations())) {
                PsiBuilder.Marker expression = mark();

                parseOperationReference();

                myBuilder.restoreJoiningComplexTokensState();

                parsePrefixExpression();
                expression.done(PREFIX_EXPRESSION);
            }
            else {
                myBuilder.restoreJoiningComplexTokensState();
                parsePostfixExpression();
            }
        }
    }

    /*
     * callableReference
     *   : (userType "?"*)? "::" SimpleName typeArguments?
     *   ;
     */
    private boolean parseDoubleColonExpression() {
        PsiBuilder.Marker expression = mark();

        if (!at(COLONCOLON)) {
            PsiBuilder.Marker typeReference = mark();
            myJetParsing.parseUserType();
            typeReference = myJetParsing.parseNullableTypeSuffix(typeReference);
            typeReference.done(TYPE_REFERENCE);

            if (!at(COLONCOLON)) {
                expression.rollbackTo();
                return false;
            }
        }

        advance(); // COLONCOLON

        if (at(CLASS_KEYWORD)) {
            advance(); // CLASS_KEYWORD
            expression.done(CLASS_LITERAL_EXPRESSION);
        }
        else {
            parseSimpleNameExpression();

            if (at(LT)) {
                PsiBuilder.Marker typeArgumentList = mark();
                if (myJetParsing.tryParseTypeArgumentList(TYPE_ARGUMENT_LIST_STOPPERS)) {
                    typeArgumentList.error("Type arguments are not allowed");
                }
                else {
                    typeArgumentList.rollbackTo();
                }
            }

            expression.done(CALLABLE_REFERENCE_EXPRESSION);
        }

        return true;
    }

    /*
     * postfixUnaryExpression
     *   : atomicExpression postfixUnaryOperation*
     *   : callableReference postfixUnaryOperation*
     *   ;
     *
     * postfixUnaryOperation
     *   : "++" : "--" : "!!"
     *   : typeArguments? valueArguments (getEntryPoint? functionLiteral)
     *   : typeArguments (getEntryPoint? functionLiteral)
     *   : arrayAccess
     *   : memberAccessOperation postfixUnaryExpression // TODO: Review
     *   ;
     */
    private void parsePostfixExpression() {
        PsiBuilder.Marker expression = mark();

        boolean firstExpressionParsed;
        boolean doubleColonExpression = parseDoubleColonExpression();
        if (!doubleColonExpression) {
            firstExpressionParsed = parseAtomicExpression();
        }
        else {
            firstExpressionParsed = true;
        }

        while (true) {
            if (interruptedWithNewLine()) {
                break;
            }
            else if (at(LBRACKET)) {
                parseArrayAccess();
                expression.done(ARRAY_ACCESS_EXPRESSION);
            }
            else if (!doubleColonExpression && parseCallSuffix()) {
                expression.done(CALL_EXPRESSION);
            }
            else if (at(DOT) || at(SAFE_ACCESS)) {
                IElementType expressionType = at(DOT) ? DOT_QUALIFIED_EXPRESSION : SAFE_ACCESS_EXPRESSION;
                advance(); // DOT or SAFE_ACCESS

                if (!firstExpressionParsed) {
                    expression.drop();
                    expression = mark();
                }

                parseCallExpression();

                if (firstExpressionParsed) {
                    expression.done(expressionType);
                }
                else {
                    firstExpressionParsed = true;
                    continue;
                }
            }
            else if (atSet(Precedence.POSTFIX.getOperations())) {
                parseOperationReference();
                expression.done(POSTFIX_EXPRESSION);
            }
            else {
                break;
            }
            expression = expression.precede();
        }
        expression.drop();
    }

    /*
     * callSuffix
     *   : typeArguments? valueArguments annotatedLambda
     *   : typeArguments annotatedLambda
     *   ;
     */
    private boolean parseCallSuffix() {
        if (parseCallWithClosure()) {
            // do nothing
        }
        else if (at(LPAR)) {
            parseValueArgumentList();
            parseCallWithClosure();
        }
        else if (at(LT)) {
            PsiBuilder.Marker typeArgumentList = mark();
            if (myJetParsing.tryParseTypeArgumentList(TYPE_ARGUMENT_LIST_STOPPERS)) {
                typeArgumentList.done(TYPE_ARGUMENT_LIST);
                if (!myBuilder.newlineBeforeCurrentToken() && at(LPAR)) parseValueArgumentList();
                parseCallWithClosure();
            }
            else {
                typeArgumentList.rollbackTo();
                return false;
            }
        }
        else {
            return false;
        }

        return true;
    }

    /*
     * atomicExpression typeParameters? valueParameters? functionLiteral*
     */
    private void parseCallExpression() {
        PsiBuilder.Marker mark = mark();
        parseAtomicExpression();
        if (!myBuilder.newlineBeforeCurrentToken() && parseCallSuffix()) {
            mark.done(CALL_EXPRESSION);
        }
        else {
            mark.drop();
        }
    }

    private void parseOperationReference() {
        PsiBuilder.Marker operationReference = mark();
        advance(); // operation
        operationReference.done(OPERATION_REFERENCE);
    }

    /*
     * annotatedLambda*
     */
    protected boolean parseCallWithClosure() {
        boolean success = false;

        while (true) {
            PsiBuilder.Marker argument = mark();

            if (!parseAnnotatedLambda(/* preferBlock = */false)) {
                argument.drop();
                break;
            }

            argument.done(LAMBDA_ARGUMENT);
            success = true;
        }

        return success;
    }

    /*
     * annotatedLambda
     *  : ("@" annotationEntry)* labelDefinition? functionLiteral
     */
    private boolean parseAnnotatedLambda(boolean preferBlock) {
        PsiBuilder.Marker annotated = mark();

        boolean wereAnnotations = myJetParsing.parseAnnotations(DEFAULT);
        PsiBuilder.Marker labeled = mark();

        boolean wasLabel = isAtLabelDefinitionOrMissingIdentifier();
        if (wasLabel) {
            parseLabelDefinition();
        }

        if (!at(LBRACE)) {
            annotated.rollbackTo();
            return false;
        }

        parseFunctionLiteral(preferBlock);

        doneOrDrop(labeled, LABELED_EXPRESSION, wasLabel);
        doneOrDrop(annotated, ANNOTATED_EXPRESSION, wereAnnotations);

        return true;
    }

    private static void doneOrDrop(
            @NotNull PsiBuilder.Marker marker,
            @NotNull IElementType type,
            boolean condition
    ) {
        if (condition) {
            marker.done(type);
        }
        else {
            marker.drop();
        }
    }

    private boolean isAtLabelDefinitionBeforeLBrace() {
        if (at(IDENTIFIER)) {
            if (myBuilder.rawLookup(1) != AT) return false;
            return lookahead(2) == LBRACE;
        }

        return at(AT) && lookahead(1) == LBRACE;
    }

    private boolean isAtLabelDefinitionOrMissingIdentifier() {
        return (at(IDENTIFIER) && myBuilder.rawLookup(1) == AT) || at(AT);
    }

    /*
     * atomicExpression
     *   : "this" label?
     *   : "super" ("<" type ">")? label?
     *   : objectLiteral
     *   : jump
     *   : if
     *   : when
     *   : try
     *   : loop
     *   : literalConstant
     *   : functionLiteral
     *   : declaration
     *   : SimpleName
     *   ;
     */
    private boolean parseAtomicExpression() {
        //        System.out.println("atom at "  + myBuilder.getTokenText());

        boolean ok = true;

        if (at(LPAR)) {
            parseParenthesizedExpression();
        }
        else if (at(THIS_KEYWORD)) {
            parseThisExpression();
        }
        else if (at(SUPER_KEYWORD)) {
            parseSuperExpression();
        }
        else if (at(OBJECT_KEYWORD)) {
            parseObjectLiteral();
        }
        else if (at(THROW_KEYWORD)) {
            parseThrow();
        }
        else if (at(RETURN_KEYWORD)) {
            parseReturn();
        }
        else if (at(CONTINUE_KEYWORD)) {
            parseJump(CONTINUE);
        }
        else if (at(BREAK_KEYWORD)) {
            parseJump(BREAK);
        }
        else if (at(IF_KEYWORD)) {
            parseIf();
        }
        else if (at(WHEN_KEYWORD)) {
            parseWhen();
        }
        else if (at(TRY_KEYWORD)) {
            parseTry();
        }
        else if (at(FOR_KEYWORD)) {
            parseFor();
        }
        else if (at(WHILE_KEYWORD)) {
            parseWhile();
        }
        else if (at(DO_KEYWORD)) {
            parseDoWhile();
        }
        else if (atSet(CLASS_KEYWORD, INTERFACE_KEYWORD, FUN_KEYWORD, VAL_KEYWORD,
                       VAR_KEYWORD, TYPE_ALIAS_KEYWORD)) {
            parseLocalDeclaration();
        }
        else if (at(IDENTIFIER)) {
            parseSimpleNameExpression();
        }
        else if (at(LBRACE)) {
            parseFunctionLiteral();
        }
        else if (at(OPEN_QUOTE)) {
            parseStringTemplate();
        }
        else if (!parseLiteralConstant()) {
            ok = false;
            // TODO: better recovery if FIRST(element) did not match
            errorWithRecovery("Expecting an element", EXPRESSION_FOLLOW);
        }

        return ok;
    }

    /*
     * stringTemplate
     *   : OPEN_QUOTE stringTemplateElement* CLOSING_QUOTE
     *   ;
     */
    private void parseStringTemplate() {
        assert _at(OPEN_QUOTE);

        PsiBuilder.Marker template = mark();

        advance(); // OPEN_QUOTE

        while (!eof()) {
            if (at(CLOSING_QUOTE) || at(DANGLING_NEWLINE)) {
                break;
            }
            parseStringTemplateElement();
        }

        if (at(DANGLING_NEWLINE)) {
            errorAndAdvance("Expecting '\"'");
        }
        else {
            expect(CLOSING_QUOTE, "Expecting '\"'");
        }
        template.done(STRING_TEMPLATE);
    }

    /*
     * stringTemplateElement
     *   : RegularStringPart
     *   : ShortTemplateEntrySTART (SimpleName | "this")
     *   : EscapeSequence
     *   : longTemplate
     *   ;
     *
     * longTemplate
     *   : "${" expression "}"
     *   ;
     */
    private void parseStringTemplateElement() {
        if (at(REGULAR_STRING_PART)) {
            PsiBuilder.Marker mark = mark();
            advance(); // REGULAR_STRING_PART
            mark.done(LITERAL_STRING_TEMPLATE_ENTRY);
        }
        else if (at(ESCAPE_SEQUENCE)) {
            PsiBuilder.Marker mark = mark();
            advance(); // ESCAPE_SEQUENCE
            mark.done(ESCAPE_STRING_TEMPLATE_ENTRY);
        }
        else if (at(SHORT_TEMPLATE_ENTRY_START)) {
            PsiBuilder.Marker entry = mark();
            advance(); // SHORT_TEMPLATE_ENTRY_START

            if (at(THIS_KEYWORD)) {
                PsiBuilder.Marker thisExpression = mark();
                PsiBuilder.Marker reference = mark();
                advance(); // THIS_KEYWORD
                reference.done(REFERENCE_EXPRESSION);
                thisExpression.done(THIS_EXPRESSION);
            }
            else {
                KtToken keyword = KEYWORD_TEXTS.get(myBuilder.getTokenText());
                if (keyword != null) {
                    myBuilder.remapCurrentToken(keyword);
                    errorAndAdvance("Keyword cannot be used as a reference");
                }
                else {
                    PsiBuilder.Marker reference = mark();
                    expect(IDENTIFIER, "Expecting a name");
                    reference.done(REFERENCE_EXPRESSION);
                }
            }

            entry.done(SHORT_STRING_TEMPLATE_ENTRY);
        }
        else if (at(LONG_TEMPLATE_ENTRY_START)) {
            PsiBuilder.Marker longTemplateEntry = mark();

            advance(); // LONG_TEMPLATE_ENTRY_START

            parseExpression();

            expect(LONG_TEMPLATE_ENTRY_END, "Expecting '}'", TokenSet.create(CLOSING_QUOTE, DANGLING_NEWLINE, REGULAR_STRING_PART, ESCAPE_SEQUENCE, SHORT_TEMPLATE_ENTRY_START));
            longTemplateEntry.done(LONG_STRING_TEMPLATE_ENTRY);
        }
        else {
            errorAndAdvance("Unexpected token in a string template");
        }
    }

    /*
     * literalConstant
     *   : "true" | "false"
     *   : stringTemplate
     *   : NoEscapeString
     *   : IntegerLiteral
     *   : LongLiteral
     *   : CharacterLiteral
     *   : FloatLiteral
     *   : "null"
     *   ;
     */
    private boolean parseLiteralConstant() {
        if (at(TRUE_KEYWORD) || at(FALSE_KEYWORD)) {
            parseOneTokenExpression(BOOLEAN_CONSTANT);
        }
        else if (at(INTEGER_LITERAL)) {
            parseOneTokenExpression(INTEGER_CONSTANT);
        }
        else if (at(CHARACTER_LITERAL)) {
            parseOneTokenExpression(CHARACTER_CONSTANT);
        }
        else if (at(FLOAT_LITERAL)) {
            parseOneTokenExpression(FLOAT_CONSTANT);
        }
        else if (at(NULL_KEYWORD)) {
            parseOneTokenExpression(NULL);
        }
        else {
            return false;
        }
        return true;
    }

    /*
     * when
     *   : "when" ("(" (modifiers "val" SimpleName "=")? element ")")? "{"
     *         whenEntry*
     *     "}"
     *   ;
     */
    private void parseWhen() {
        assert _at(WHEN_KEYWORD);

        PsiBuilder.Marker when = mark();

        advance(); // WHEN_KEYWORD

        // Parse condition
        myBuilder.disableNewlines();
        if (at(LPAR)) {
            advanceAt(LPAR);

            PsiBuilder.Marker property = mark();
            myJetParsing.parseModifierList(DEFAULT, TokenSet.create(EQ, RPAR));
            if (at(VAL_KEYWORD) || at(VAR_KEYWORD)) {
                myJetParsing.parseProperty(true);
                property.done(PROPERTY);
            }
            else {
                property.rollbackTo();
                parseExpression();
            }

            expect(RPAR, "Expecting ')'");
        }
        myBuilder.restoreNewlinesState();

        // Parse when block
        myBuilder.enableNewlines();
        if (expect(LBRACE, "Expecting '{'")) {
            while (!eof() && !at(RBRACE)) {
                parseWhenEntry();
            }

            expect(RBRACE, "Expecting '}'");
        }
        myBuilder.restoreNewlinesState();

        when.done(WHEN);
    }

    /*
     * whenEntry
     *   // TODO : consider empty after ->
     *   : whenCondition{","} "->" element SEMI
     *   : "else" "->" element SEMI
     *   ;
     */
    private void parseWhenEntry() {
        PsiBuilder.Marker entry = mark();

        if (at(ELSE_KEYWORD)) {
            advance(); // ELSE_KEYWORD

            if (!at(ARROW)) {
                errorUntil("Expecting '->'", TokenSet.create(ARROW, LBRACE, RBRACE, EOL_OR_SEMICOLON));
            }

            if (at(ARROW)) {
                advance(); // ARROW

                if (atSet(WHEN_CONDITION_RECOVERY_SET)) {
                    error("Expecting an element");
                }
                else {
                    parseExpressionPreferringBlocks();
                }
            }
            else if (at(LBRACE)) { // no arrow, probably it's simply missing
                parseExpressionPreferringBlocks();
            }
            else if (!atSet(WHEN_CONDITION_RECOVERY_SET)) {
                errorAndAdvance("Expecting '->'");
            }
        }
        else {
            parseWhenEntryNotElse();
        }

        entry.done(WHEN_ENTRY);
        consumeIf(SEMICOLON);
    }

    /*
     * : whenCondition{","} "->" element SEMI
     */
    private void parseWhenEntryNotElse() {
        while (true) {
            while (at(COMMA)) errorAndAdvance("Expecting a when-condition");
            parseWhenCondition();
            if (!at(COMMA)) break;
            advance(); // COMMA
        }

        expect(ARROW, "Expecting '->'", WHEN_CONDITION_RECOVERY_SET);
        if (atSet(WHEN_CONDITION_RECOVERY_SET)) {
            error("Expecting an element");
        }
        else {
            parseExpressionPreferringBlocks();
        }
        // SEMI is consumed in parseWhenEntry
    }

    /*
     * whenCondition
     *   : expression
     *   : ("in" | "!in") expression
     *   : ("is" | "!is") isRHS
     *   ;
     */
    private void parseWhenCondition() {
        PsiBuilder.Marker condition = mark();
        myBuilder.disableNewlines();
        if (at(IN_KEYWORD) || at(NOT_IN)) {
            PsiBuilder.Marker mark = mark();
            advance(); // IN_KEYWORD or NOT_IN
            mark.done(OPERATION_REFERENCE);


            if (atSet(WHEN_CONDITION_RECOVERY_SET_WITH_ARROW)) {
                error("Expecting an element");
            }
            else {
                parseExpression();
            }
            condition.done(WHEN_CONDITION_IN_RANGE);
        }
        else if (at(IS_KEYWORD) || at(NOT_IS)) {
            advance(); // IS_KEYWORD or NOT_IS

            if (atSet(WHEN_CONDITION_RECOVERY_SET_WITH_ARROW)) {
                error("Expecting a type");
            }
            else {
                myJetParsing.parseTypeRef();
            }
            condition.done(WHEN_CONDITION_IS_PATTERN);
        }
        else {
            if (atSet(WHEN_CONDITION_RECOVERY_SET_WITH_ARROW)) {
                error("Expecting an expression, is-condition or in-condition");
            }
            else {
                parseExpression();
            }
            condition.done(WHEN_CONDITION_EXPRESSION);
        }
        myBuilder.restoreNewlinesState();
    }

    /*
     * arrayAccess
     *   : "[" element{","} "]"
     *   ;
     */
    private void parseArrayAccess() {
        assert _at(LBRACKET);

        PsiBuilder.Marker indices = 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
        }

        expect(RBRACKET, "Expecting ']'");
        myBuilder.restoreNewlinesState();

        indices.done(INDICES);
    }

    /*
     * SimpleName
     */
    public void parseSimpleNameExpression() {
        PsiBuilder.Marker simpleName = mark();
        expect(IDENTIFIER, "Expecting an identifier");
        simpleName.done(REFERENCE_EXPRESSION);
    }

    /*
     * modifiers declarationRest
     */
    private boolean parseLocalDeclaration() {
        PsiBuilder.Marker decl = mark();
        KotlinParsing.ModifierDetector detector = new KotlinParsing.ModifierDetector();
        myJetParsing.parseModifierList(detector, DEFAULT, TokenSet.EMPTY);

        IElementType declType = parseLocalDeclarationRest(detector.isEnumDetected());

        if (declType != null) {
            // we do not attach preceding comments (non-doc) to local variables because they are likely commenting a few statements below
            closeDeclarationWithCommentBinders(decl, declType,
                                               declType != KtNodeTypes.PROPERTY && declType != KtNodeTypes.DESTRUCTURING_DECLARATION);
            return true;
        }
        else {
            decl.rollbackTo();
            return false;
        }
    }

    /*
     * functionLiteral  // one can use "it" as a parameter name
     *   : "{" expressions "}"
     *   : "{" (modifiers SimpleName){","} "->" statements "}"
     *   ;
     */
    private void parseFunctionLiteral() {
        parseFunctionLiteral(/* preferBlock = */false);
    }

    private void parseFunctionLiteral(boolean preferBlock) {
        assert _at(LBRACE);

        PsiBuilder.Marker literalExpression = mark();

        PsiBuilder.Marker literal = mark();

        myBuilder.enableNewlines();
        advance(); // LBRACE

        boolean paramsFound = false;

        if (at(ARROW)) {
            //   { -> ...}
            mark().done(VALUE_PARAMETER_LIST);
            advance(); // ARROW
            paramsFound = true;
        }
        else {
            if (at(IDENTIFIER) || at(COLON)) {
                // Try to parse a simple name list followed by an ARROW
                //   {a -> ...}
                //   {a, b -> ...}
                PsiBuilder.Marker rollbackMarker = mark();
                IElementType nextToken = lookahead(1);
                boolean preferParamsToExpressions = (nextToken == COMMA || nextToken == COLON);
                parseFunctionLiteralShorthandParameterList();

                paramsFound = preferParamsToExpressions ?
                              rollbackOrDrop(rollbackMarker, ARROW, "An -> is expected", RBRACE) :
                              rollbackOrDropAt(rollbackMarker, ARROW);
            }
        }

        if (!paramsFound) {
            if (preferBlock) {
                literal.drop();
                parseStatements();
                expect(RBRACE, "Expecting '}'");
                literalExpression.done(BLOCK);
                myBuilder.restoreNewlinesState();

                return;
            }
        }

        PsiBuilder.Marker body = mark();
        parseStatements();
        body.done(BLOCK);

        expect(RBRACE, "Expecting '}'");
        myBuilder.restoreNewlinesState();

        literal.done(FUNCTION_LITERAL);
        literalExpression.done(LAMBDA_EXPRESSION);
    }

    private boolean rollbackOrDropAt(PsiBuilder.Marker rollbackMarker, IElementType dropAt) {
        if (at(dropAt)) {
            advance(); // dropAt
            rollbackMarker.drop();
            return true;
        }
        rollbackMarker.rollbackTo();
        return false;
    }

    private boolean rollbackOrDrop(PsiBuilder.Marker rollbackMarker,
            KtToken expected, String expectMessage,
            IElementType validForDrop) {
        if (at(expected)) {
            advance(); // dropAt
            rollbackMarker.drop();
            return true;
        }
        else if (at(validForDrop)) {
            rollbackMarker.drop();
            expect(expected, expectMessage);
            return true;
        }

        rollbackMarker.rollbackTo();
        return false;
    }


    /*
     * SimpleName{,}
     */
    private void parseFunctionLiteralShorthandParameterList() {
        PsiBuilder.Marker parameterList = mark();

        while (!eof()) {
            PsiBuilder.Marker parameter = mark();

            //            int parameterNamePos = matchTokenStreamPredicate(new LastBefore(new At(IDENTIFIER), new AtOffset(doubleArrowPos)));
            //            createTruncatedBuilder(parameterNamePos).parseModifierList(MODIFIER_LIST, false);

            if (at(COLON)) {
                error("Expecting parameter name");
            }
            else {
                expect(IDENTIFIER, "Expecting parameter name", TokenSet.create(ARROW));
            }

            if (at(COLON)) {
                advance(); // COLON
                myJetParsing.parseTypeRef(TokenSet.create(ARROW, COMMA));
            }
            parameter.done(VALUE_PARAMETER);

            if (at(ARROW)) {
                break;
            }
            else if (at(COMMA)) {
                advance(); // COMMA
            }
            else {
                error("Expecting '->' or ','");
                break;
            }
        }

        parameterList.done(VALUE_PARAMETER_LIST);
    }

    /*
     * expressions
     *   : SEMI* statement{SEMI+} SEMI*
     */
    public void parseStatements() {
        parseStatements(false);
    }

    /*
         * expressions
         *   : SEMI* statement{SEMI+} SEMI*
         */
    public void parseStatements(boolean isScriptTopLevel) {
        while (at(SEMICOLON)) advance(); // SEMICOLON
        while (!eof() && !at(RBRACE)) {
            if (!atSet(STATEMENT_FIRST)) {
                errorAndAdvance("Expecting an element");
            }
            if (atSet(STATEMENT_FIRST)) {
                parseStatement(isScriptTopLevel);
            }
            if (at(SEMICOLON)) {
                while (at(SEMICOLON)) advance(); // SEMICOLON
            }
            else if (at(RBRACE)) {
                break;
            }
            else if (!myBuilder.newlineBeforeCurrentToken()) {
                String severalStatementsError = "Unexpected tokens (use ';' to separate expressions on the same line)";

                if (atSet(STATEMENT_NEW_LINE_QUICK_RECOVERY_SET)) {
                    error(severalStatementsError);
                }
                else {
                    errorUntil(severalStatementsError, TokenSet.create(EOL_OR_SEMICOLON, LBRACE, RBRACE));
                }
            }
        }
    }

    /*
     * statement
     *  : expression
     *  : declaration
     *  ;
     */
    private void parseStatement(boolean isScriptTopLevel) {
        if (!parseLocalDeclaration()) {
            if (!atSet(EXPRESSION_FIRST)) {
                errorAndAdvance("Expecting a statement");
            }
            else if (isScriptTopLevel){
                PsiBuilder.Marker scriptInitializer = mark();
                parseExpression();
                scriptInitializer.done(SCRIPT_INITIALIZER);
            }
            else {
                parseExpression();
            }
        }
    }

    /*
     * declaration
     *   : function
     *   : property
     *   : extension
     *   : class
     *   : typeAlias
     *   : object
     *   ;
     */
    private IElementType parseLocalDeclarationRest(boolean isEnum) {
        IElementType keywordToken = tt();
        IElementType declType = null;
        if (keywordToken == CLASS_KEYWORD ||  keywordToken == INTERFACE_KEYWORD) {
            declType = myJetParsing.parseClass(isEnum);
        }
        else if (keywordToken == FUN_KEYWORD) {
            declType = myJetParsing.parseFunction();
        }
        else if (keywordToken == VAL_KEYWORD || keywordToken == VAR_KEYWORD) {
            declType = myJetParsing.parseProperty(true);
        }
        /*
        else if (keywordToken == TYPE_ALIAS_KEYWORD) {
            declType = myJetParsing.parseTypeAlias();
        }
        */
        else if (keywordToken == OBJECT_KEYWORD) {
            // Object expression may appear at the statement position: should parse it
            // as expression instead of object declaration
            // sample:
            // {
            //   object : Thread() {
            //   }
            // }
            IElementType lookahead = lookahead(1);
            if (lookahead == COLON || lookahead == LBRACE) {
                return null;
            }

            myJetParsing.parseObject(NameParsingMode.REQUIRED, true);
            declType = OBJECT_DECLARATION;
        }
        return declType;
    }

    /*
     * doWhile
     *   : "do" element "while" "(" element ")"
     *   ;
     */
    private void parseDoWhile() {
        assert _at(DO_KEYWORD);

        PsiBuilder.Marker loop = mark();

        advance(); // DO_KEYWORD

        if (!at(WHILE_KEYWORD)) {
            parseControlStructureBody();
        }

        if (expect(WHILE_KEYWORD, "Expecting 'while' followed by a post-condition")) {
            parseCondition();
        }

        loop.done(DO_WHILE);
    }

    /*
     * while
     *   : "while" "(" element ")" element
     *   ;
     */
    private void parseWhile() {
        assert _at(WHILE_KEYWORD);

        PsiBuilder.Marker loop = mark();

        advance(); // WHILE_KEYWORD

        parseCondition();

        parseControlStructureBody();

        loop.done(WHILE);
    }

    /*
     * for
     *   : "for" "(" annotations ("val" | "var")? (multipleVariableDeclarations | variableDeclarationEntry) "in" expression ")" expression
     *   ;
     *
     *   TODO: empty loop body (at the end of the block)?
     */
    private void parseFor() {
        assert _at(FOR_KEYWORD);

        PsiBuilder.Marker loop = mark();

        advance(); // FOR_KEYWORD

        if (expect(LPAR, "Expecting '(' to open a loop range", EXPRESSION_FIRST)) {
            myBuilder.disableNewlines();

            if (!at(RPAR)) {
                PsiBuilder.Marker parameter = mark();

                if (!at(IN_KEYWORD)) {
                    myJetParsing.parseModifierList(DEFAULT, TokenSet.create(IN_KEYWORD, RPAR, COLON));
                }

                if (at(VAL_KEYWORD) || at(VAR_KEYWORD)) advance(); // VAL_KEYWORD or VAR_KEYWORD

                if (at(LPAR)) {
                    myJetParsing.parseMultiDeclarationName(TokenSet.create(IN_KEYWORD, LBRACE));
                    parameter.done(DESTRUCTURING_DECLARATION);
                }
                else {
                    expect(IDENTIFIER, "Expecting a variable name", TokenSet.create(COLON, IN_KEYWORD));

                    if (at(COLON)) {
                        advance(); // COLON
                        myJetParsing.parseTypeRef(TokenSet.create(IN_KEYWORD));
                    }
                    parameter.done(VALUE_PARAMETER);
                }

                if (expect(IN_KEYWORD, "Expecting 'in'", TokenSet.create(LPAR, LBRACE, RPAR))) {
                    PsiBuilder.Marker range = mark();
                    parseExpression();
                    range.done(LOOP_RANGE);
                }
            }
            else {
                error("Expecting a variable name");
            }

            expectNoAdvance(RPAR, "Expecting ')'");
            myBuilder.restoreNewlinesState();
        }

        parseControlStructureBody();

        loop.done(FOR);
    }

    /**
     * If it has no ->, it's a block, otherwise a function literal
     */
    private void parseExpressionPreferringBlocks() {
        if (!parseAnnotatedLambda(/* preferBlock = */true)) {
            parseExpression();
        }
    }

    /*
     * element
     */
    private void parseControlStructureBody() {
        PsiBuilder.Marker body = mark();
        if (!at(SEMICOLON)) {
            parseExpressionPreferringBlocks();
        }
        body.done(BODY);
    }

    /*
     * try
     *   : "try" block catchBlock* finallyBlock?
     *   ;
     * catchBlock
     *   : "catch" "(" annotations SimpleName ":" userType ")" block
     *   ;
     *
     * finallyBlock
     *   : "finally" block
     *   ;
     */
    private void parseTry() {
        assert _at(TRY_KEYWORD);

        PsiBuilder.Marker tryExpression = mark();

        advance(); // TRY_KEYWORD

        myJetParsing.parseBlock();

        boolean catchOrFinally = false;
        while (at(CATCH_KEYWORD)) {
            catchOrFinally = true;
            PsiBuilder.Marker catchBlock = mark();
            advance(); // CATCH_KEYWORD

            TokenSet recoverySet = TokenSet.create(LBRACE, RBRACE, FINALLY_KEYWORD, CATCH_KEYWORD);
            if (atSet(recoverySet)) {
                error("Expecting exception variable declaration");
            }
            else {
                PsiBuilder.Marker parameters = mark();
                expect(LPAR, "Expecting '('", recoverySet);
                if (!atSet(recoverySet)) {
                    myJetParsing.parseValueParameter(/*typeRequired = */ true);
                    expect(RPAR, "Expecting ')'", recoverySet);
                }
                else {
                    error("Expecting exception variable declaration");
                }
                parameters.done(VALUE_PARAMETER_LIST);
            }

            if (at(LBRACE)) {
                myJetParsing.parseBlock();
            }
            else {
                error("Expecting a block: { ... }");
            }
            catchBlock.done(CATCH);
        }

        if (at(FINALLY_KEYWORD)) {
            catchOrFinally = true;
            PsiBuilder.Marker finallyBlock = mark();

            advance(); // FINALLY_KEYWORD

            myJetParsing.parseBlock();

            finallyBlock.done(FINALLY);
        }

        if (!catchOrFinally) {
            error("Expecting 'catch' or 'finally'");
        }

        tryExpression.done(TRY);
    }

    /*
     * if
     *   : "if" "(" element ")" element SEMI? ("else" element)?
     *   ;
     */
    private void parseIf() {
        assert _at(IF_KEYWORD);

        PsiBuilder.Marker marker = mark();

        advance(); //IF_KEYWORD

        parseCondition();

        PsiBuilder.Marker thenBranch = mark();
        if (!at(ELSE_KEYWORD) && !at(SEMICOLON)) {
            parseExpressionPreferringBlocks();
        }
        if (at(SEMICOLON) && lookahead(1) == ELSE_KEYWORD) {
            advance(); // SEMICOLON
        }
        thenBranch.done(THEN);

        // lookahead for arrow is needed to prevent capturing of whenEntry like "else -> "
        if (at(ELSE_KEYWORD) && lookahead(1) != ARROW) {
            advance(); // ELSE_KEYWORD

            PsiBuilder.Marker elseBranch = mark();
            if (!at(SEMICOLON)) {
                parseExpressionPreferringBlocks();
            }
            elseBranch.done(ELSE);
        }

        marker.done(IF);
    }

    /*
     * "(" element ")"
     */
    private void parseCondition() {
        myBuilder.disableNewlines();

        if (expect(LPAR, "Expecting a condition in parentheses '(...)'", EXPRESSION_FIRST)) {
            PsiBuilder.Marker condition = mark();
            parseExpression();
            condition.done(CONDITION);
            expect(RPAR, "Expecting ')");
        }

        myBuilder.restoreNewlinesState();
    }

    /*
     * : "continue" getEntryPoint?
     * : "break" getEntryPoint?
     */
    private void parseJump(KtNodeType type) {
        assert _at(BREAK_KEYWORD) || _at(CONTINUE_KEYWORD);

        PsiBuilder.Marker marker = mark();

        advance(); // BREAK_KEYWORD or CONTINUE_KEYWORD

        parseLabelReferenceWithNoWhitespace();

        marker.done(type);
    }

    /*
     * "return" getEntryPoint? element?
     */
    private void parseReturn() {
        assert _at(RETURN_KEYWORD);

        PsiBuilder.Marker returnExpression = mark();

        advance(); // RETURN_KEYWORD

        parseLabelReferenceWithNoWhitespace();

        if (atSet(EXPRESSION_FIRST) && !at(EOL_OR_SEMICOLON)) parseExpression();

        returnExpression.done(RETURN);
    }

    /*
     * labelReference?
     */
    private void parseLabelReferenceWithNoWhitespace() {
        if (at(AT) && !myBuilder.newlineBeforeCurrentToken()) {
            if (WHITE_SPACE_OR_COMMENT_BIT_SET.contains(myBuilder.rawLookup(-1))) {
                error("There should be no space or comments before '@' in label reference");
            }
            parseLabelReference();
        }
    }

    /*
     * IDENTIFIER "@"
     */
    private void parseLabelDefinition() {
        if (at(AT)) {
            // recovery for empty label identifier
            errorAndAdvance("Label must be named"); // AT
            return;
        }

        PsiBuilder.Marker labelWrap = mark();
        PsiBuilder.Marker mark = mark();

        assert _at(IDENTIFIER) && myBuilder.rawLookup(1) == AT : "Callers must check that current token is IDENTIFIER followed with '@'";

        advance(); // IDENTIFIER
        advance(); // AT

        mark.done(LABEL);

        labelWrap.done(LABEL_QUALIFIER);
    }

    /*
     * "@" IDENTIFIER
     */
    private void parseLabelReference() {
        assert _at(AT);

        PsiBuilder.Marker labelWrap = mark();

        PsiBuilder.Marker mark = mark();

        if (myBuilder.rawLookup(1) != IDENTIFIER) {
            errorAndAdvance("Label must be named"); // AT
            labelWrap.drop();
            mark.drop();
            return;
        }

        advance(); // AT
        advance(); // IDENTIFIER

        mark.done(LABEL);

        labelWrap.done(LABEL_QUALIFIER);
    }

    /*
     * : "throw" element
     */
    private void parseThrow() {
        assert _at(THROW_KEYWORD);

        PsiBuilder.Marker marker = mark();

        advance(); // THROW_KEYWORD

        parseExpression();

        marker.done(THROW);
    }

    /*
     * "(" expression ")"
     */
    private void parseParenthesizedExpression() {
        assert _at(LPAR);

        PsiBuilder.Marker mark = mark();

        myBuilder.disableNewlines();
        advance(); // LPAR
        if (at(RPAR)) {
            error("Expecting an expression");
        }
        else {
            parseExpression();
        }

        expect(RPAR, "Expecting ')'");
        myBuilder.restoreNewlinesState();

        mark.done(PARENTHESIZED);
    }

    /*
     * "this" label?
     */
    private void parseThisExpression() {
        assert _at(THIS_KEYWORD);
        PsiBuilder.Marker mark = mark();

        PsiBuilder.Marker thisReference = mark();
        advance(); // THIS_KEYWORD
        thisReference.done(REFERENCE_EXPRESSION);

        parseLabelReferenceWithNoWhitespace();

        mark.done(THIS_EXPRESSION);
    }

    /*
     * "this" ("<" type ">")? label?
     */
    private void parseSuperExpression() {
        assert _at(SUPER_KEYWORD);
        PsiBuilder.Marker mark = mark();

        PsiBuilder.Marker superReference = mark();
        advance(); // SUPER_KEYWORD
        superReference.done(REFERENCE_EXPRESSION);

        if (at(LT)) {
            // This may be "super < foo" or "super", thus the backtracking
            PsiBuilder.Marker supertype = mark();

            myBuilder.disableNewlines();
            advance(); // LT

            myJetParsing.parseTypeRef();

            if (at(GT)) {
                advance(); // GT
                supertype.drop();
            }
            else {
                supertype.rollbackTo();
            }
            myBuilder.restoreNewlinesState();
        }
        parseLabelReferenceWithNoWhitespace();

        mark.done(SUPER_EXPRESSION);
    }

    /*
     * valueArguments
     *   : "(" (SimpleName "=")? "*"? element{","} ")"
     *   ;
     */
    public void parseValueArgumentList() {
        PsiBuilder.Marker list = mark();

        myBuilder.disableNewlines();

        if (expect(LPAR, "Expecting an argument list", EXPRESSION_FOLLOW)) {
            if (!at(RPAR)) {
                while (true) {
                    while (at(COMMA)) errorAndAdvance("Expecting an argument");
                    parseValueArgument();
                    if (at(COLON) && lookahead(1) == IDENTIFIER) {
                        errorAndAdvance("Unexpected type specification", 2);
                    }
                    if (!at(COMMA)) break;
                    advance(); // COMMA
                    if (at(RPAR)) {
                        error("Expecting an argument");
                        break;
                    }
                }
            }

            expect(RPAR, "Expecting ')'", EXPRESSION_FOLLOW);
        }

        myBuilder.restoreNewlinesState();

        list.done(VALUE_ARGUMENT_LIST);
    }

    /*
     * (SimpleName "=")? "*"? element
     */
    private void parseValueArgument() {
        PsiBuilder.Marker argument = mark();
        if (at(IDENTIFIER) && lookahead(1) == EQ) {
            PsiBuilder.Marker argName = mark();
            PsiBuilder.Marker reference = mark();
            advance(); // IDENTIFIER
            reference.done(REFERENCE_EXPRESSION);
            argName.done(VALUE_ARGUMENT_NAME);
            advance(); // EQ
        }
        if (at(MUL)) {
            advance(); // MUL
        }
        parseExpression();
        argument.done(VALUE_ARGUMENT);
    }

    /*
     * "object" (":" delegationSpecifier{","})? classBody // Cannot make class body optional: foo(object : F, A)
     */
    public void parseObjectLiteral() {
        PsiBuilder.Marker literal = mark();
        PsiBuilder.Marker declaration = mark();
        myJetParsing.parseObject(NameParsingMode.PROHIBITED, false); // Body is not optional because of foo(object : A, B)
        declaration.done(OBJECT_DECLARATION);
        literal.done(OBJECT_LITERAL);
    }

    private void parseOneTokenExpression(KtNodeType type) {
        PsiBuilder.Marker mark = mark();
        advance();
        mark.done(type);
    }

    @Override
    protected KotlinParsing create(SemanticWhitespaceAwarePsiBuilder builder) {
        return myJetParsing.create(builder);
    }

    private boolean interruptedWithNewLine() {
        return !ALLOW_NEWLINE_OPERATIONS.contains(tt()) && myBuilder.newlineBeforeCurrentToken();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy