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.0.0
Show newest version
/*
 * 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.parsing;

import com.google.common.collect.ImmutableMap;
import com.intellij.lang.ASTNode;
import com.intellij.lang.Language;
import com.intellij.lang.PsiBuilder;
import com.intellij.openapi.project.Project;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.tree.TokenSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
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;
import static org.jetbrains.kotlin.parsing.KotlinWhitespaceAndCommentsBindersKt.PRECEDING_ALL_COMMENTS_BINDER;
import static org.jetbrains.kotlin.parsing.KotlinWhitespaceAndCommentsBindersKt.TRAILING_ALL_COMMENTS_BINDER;

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 final IElementType[] LOCAL_DECLARATION_FIRST =
            new IElementType[] {CLASS_KEYWORD, INTERFACE_KEYWORD, FUN_KEYWORD, VAL_KEYWORD, VAR_KEYWORD, TYPE_ALIAS_KEYWORD};
    private static final TokenSet TOKEN_SET_TO_FOLLOW_AFTER_DESTRUCTURING_DECLARATION_IN_LAMBDA = TokenSet.create(ARROW, COMMA, COLON);

    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, RANGE_UNTIL, 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

            AT, // Just for better recovery and maybe for annotations

            LBRACKET // Collection literal expression
    );

    public 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(
            EOL_OR_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 IElementType parseRightHandSide(IElementType operation, KotlinExpressionParsing parser) {
                parser.myKotlinParsing.parseTypeRefWithoutIntersections();
                return BINARY_WITH_TYPE;
            }

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

        MULTIPLICATIVE(MUL, DIV, PERC),
        ADDITIVE(PLUS, MINUS),
        RANGE(KtTokens.RANGE, RANGE_UNTIL),
        SIMPLE_NAME(IDENTIFIER),
        ELVIS(KtTokens.ELVIS),
        IN_OR_IS(IN_KEYWORD, NOT_IN, IS_KEYWORD, NOT_IS) {
            @Override
            public IElementType parseRightHandSide(IElementType operation, KotlinExpressionParsing parser) {
                if (operation == IS_KEYWORD || operation == NOT_IS) {
                    parser.myKotlinParsing.parseTypeRefWithoutIntersections();
                    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 IElementType 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 myKotlinParsing;

    public KotlinExpressionParsing(SemanticWhitespaceAwarePsiBuilder builder, KotlinParsing kotlinParsing) {
        super(builder);
        myKotlinParsing = kotlinParsing;
    }

    /*
     * 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) {
        PsiBuilder.Marker expression = mark();

        precedence.parseHigherPrecedence(this);

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

            parseOperationReference();

            IElementType 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() {
        if (at(AT)) {
            if (!parseLocalDeclaration(/* rollbackIfDefinitelyNotExpression = */ false, false)) {
                PsiBuilder.Marker expression = mark();
                myKotlinParsing.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();
            }
        }
    }

    /*
     * doubleColonSuffix
     *   : "::" SimpleName typeArguments?
     *   ;
     */
    private boolean parseDoubleColonSuffix(@NotNull PsiBuilder.Marker expression) {
        if (!at(COLONCOLON)) return false;

        advance(); // COLONCOLON

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

            expression.done(CLASS_LITERAL_EXPRESSION);
            return true;
        }

        parseSimpleNameExpression();

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

        if (at(LPAR) && !myBuilder.newlineBeforeCurrentToken()) {
            PsiBuilder.Marker lpar = mark();
            parseCallSuffix();
            lpar.error("This syntax is reserved for future use; to call a reference, enclose it in parentheses: (foo::bar)(args)");
        }

        expression.done(CALLABLE_REFERENCE_EXPRESSION);
        return true;
    }

    private void skipQuestionMarksBeforeDoubleColon() {
        if (at(QUEST)) {
            int k = 1;
            while (lookahead(k) == QUEST) k++;
            if (lookahead(k) == COLONCOLON) {
                while (k > 0) {
                    advance(); // QUEST
                    k--;
                }
            }
        }
    }

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

        boolean firstExpressionParsed = at(COLONCOLON) ? parseDoubleColonSuffix(mark()) : parseAtomicExpression();

        while (true) {
            if (interruptedWithNewLine()) {
                break;
            }
            else if (at(LBRACKET)) {
                parseArrayAccess();
                expression.done(ARRAY_ACCESS_EXPRESSION);
            }
            else if (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();
                    firstExpressionParsed = parseAtomicExpression();
                    continue;
                }

                parseSelectorCallExpression();

                expression.done(expressionType);
            }
            else if (atSet(Precedence.POSTFIX.getOperations())) {
                parseOperationReference();
                expression.done(POSTFIX_EXPRESSION);
            }
            else {
                skipQuestionMarksBeforeDoubleColon();
                if (!parseDoubleColonSuffix(expression)) {
                    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 (myKotlinParsing.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 parseSelectorCallExpression() {
        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 = myKotlinParsing.parseAnnotations(DEFAULT);
        PsiBuilder.Marker labeled = mark();

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

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

        parseFunctionLiteral(preferBlock, /* collapse = */true);

        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();
        }
    }

    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
     *   : collectionLiteral
     *   ;
     */
    private boolean parseAtomicExpression() {
        boolean ok = true;

        if (at(LPAR)) {
            parseParenthesizedExpression();
        }
        else if (at(LBRACKET)) {
            parseCollectionLiteralExpression();
        }
        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(LOCAL_DECLARATION_FIRST) &&
                    parseLocalDeclaration(/* rollbackIfDefinitelyNotExpression = */ myBuilder.newlineBeforeCurrentToken(), false)) {
            // declaration was parsed, do nothing
        }
        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", TokenSet.orSet(EXPRESSION_FOLLOW, TokenSet.create(LONG_TEMPLATE_ENTRY_END)));
        }

        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

            while (!eof()) {
                int offset = myBuilder.getCurrentOffset();

                parseExpression();

                if (_at(LONG_TEMPLATE_ENTRY_END)) {
                    advance();
                    break;
                }
                else {
                    error("Expecting '}'");
                    if (offset == myBuilder.getCurrentOffset()) {
                        // Prevent hang if can't advance with parseExpression()
                        advance();
                    }
                }
            }

            longTemplateEntry.done(LONG_STRING_TEMPLATE_ENTRY);
        }
        else {
            errorAndAdvance("Unexpected token in a string template");
        }
    }

    /*
     * literalConstant
     *   : "true" | "false"
     *   : stringTemplate
     *   : NoEscapeString
     *   : IntegerLiteral
     *   : 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 atWhenStart = mark();
            myKotlinParsing.parseAnnotationsList(DEFAULT, TokenSet.create(EQ, RPAR));
            if (at(VAL_KEYWORD) || at(VAR_KEYWORD)) {
                IElementType declType = myKotlinParsing.parseProperty(KotlinParsing.DeclarationParsingMode.LOCAL);

                atWhenStart.done(declType);
                atWhenStart.setCustomEdgeTokenBinders(PrecedingDocCommentsBinder.INSTANCE, TrailingCommentsBinder.INSTANCE);
            }
            else {
                atWhenStart.drop();
                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 {
                    parseControlStructureBody();
                }
            }
            else if (at(LBRACE)) { // no arrow, probably it's simply missing
                parseControlStructureBody();
            }
            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
            if (at(ARROW)) {
                break;
            }
        }

        expect(ARROW, "Expecting '->'", WHEN_CONDITION_RECOVERY_SET);
        if (atSet(WHEN_CONDITION_RECOVERY_SET)) {
            error("Expecting an element");
        }
        else {
            parseControlStructureBody();
        }
        // 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 {
                myKotlinParsing.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() {
        parseAsCollectionLiteralExpression(INDICES, false, "Expecting an index element");
    }

    /*
     * collectionLiteral
     *   : "[" element{","}? "]"
     *   ;
     */
    private void parseCollectionLiteralExpression() {
        parseAsCollectionLiteralExpression(COLLECTION_LITERAL_EXPRESSION, true, "Expecting an element");
    }

    private void parseAsCollectionLiteralExpression(IElementType nodeType, boolean canBeEmpty, String missingElementErrorMessage) {
        assert _at(LBRACKET);

        PsiBuilder.Marker innerExpressions = mark();

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

        if (!canBeEmpty && at(RBRACKET)) {
            error(missingElementErrorMessage);
        }
        else {
            parseInnerExpressions(missingElementErrorMessage);
        }

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

        innerExpressions.done(nodeType);
    }

    private void parseInnerExpressions(String missingElementErrorMessage) {
        while (true) {
            if (at(COMMA)) errorAndAdvance(missingElementErrorMessage);
            if (at(RBRACKET)) {
                break;
            }
            parseExpression();

            if (!at(COMMA)) break;
            advance(); // COMMA
        }
    }

    public void parseContractDescriptionBlock() {
        assert _at(CONTRACT_KEYWORD);

        advance(); // CONTRACT_KEYWORD

        parseContractEffectList();
    }

    private void parseContractEffectList() {
        PsiBuilder.Marker block = mark();

        expect(LBRACKET, "Expecting '['");
        myBuilder.enableNewlines();

        parseContractEffects();

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

        block.done(CONTRACT_EFFECT_LIST);
    }

    private void parseContractEffects() {
        while (true) {
            if (at(COMMA)) errorAndAdvance("Expecting a contract effect");
            if (at(RBRACKET)) {
                break;
            }
            PsiBuilder.Marker effect = mark();
            parseExpression();
            effect.done(CONTRACT_EFFECT);

            if (!at(COMMA)) break;
            advance(); // COMMA
        }
    }

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

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

        IElementType declType = parseLocalDeclarationRest(detector, rollbackIfDefinitelyNotExpression, isScriptTopLevel);

        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 (":" type)?){","} "->" statements "}"
     *   ;
     */
    private void parseFunctionLiteral() {
        parseFunctionLiteral(/* preferBlock = */false, /* collapse = */true);
    }

    /**
     * If it has no ->, it's a block, otherwise a function literal
     *
     * Please update {@link org.jetbrains.kotlin.BlockExpressionElementType#isParsable(ASTNode, CharSequence, Language, Project)} if any changes occurs!
     */
    public void parseFunctionLiteral(boolean preferBlock, boolean collapse) {
        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) || at(LPAR)) {
            // Try to parse a simple name list followed by an ARROW
            //   {a -> ...}
            //   {a, b -> ...}
            //   {(a, b) -> ... }
            PsiBuilder.Marker rollbackMarker = mark();
            IElementType nextToken = lookahead(1);
            boolean preferParamsToExpressions = (nextToken == COMMA || nextToken == COLON);
            parseFunctionLiteralParameterList();

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

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

            return;
        }

        if (collapse) {
            myKotlinParsing.advanceBalancedBlock();
            literal.done(FUNCTION_LITERAL);
            literalExpression.collapse(LAMBDA_EXPRESSION);
        }
        else {
            PsiBuilder.Marker body = mark();
            parseStatements();

            body.done(BLOCK);
            body.setCustomEdgeTokenBinders(PRECEDING_ALL_COMMENTS_BINDER, TRAILING_ALL_COMMENTS_BINDER);

            expect(RBRACE, "Expecting '}'");
            literal.done(FUNCTION_LITERAL);
            literalExpression.done(LAMBDA_EXPRESSION);
        }

        myBuilder.restoreNewlinesState();
    }

    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;
    }


    /*
     * lambdaParameter{","}
     *
     * lambdaParameter
     *   : variableDeclarationEntry
     *   : multipleVariableDeclarations (":" type)?
     */
    private void parseFunctionLiteralParameterList() {
        PsiBuilder.Marker parameterList = mark();

        while (!eof()) {
            if (at(ARROW)) {
                break;
            }
            PsiBuilder.Marker parameter = mark();

            if (at(COLON)) {
                error("Expecting parameter name");
            }
            else if (at(LPAR)) {
                PsiBuilder.Marker destructuringDeclaration = mark();
                myKotlinParsing.parseMultiDeclarationName(TOKEN_SET_TO_FOLLOW_AFTER_DESTRUCTURING_DECLARATION_IN_LAMBDA);
                destructuringDeclaration.done(DESTRUCTURING_DECLARATION);
            }
            else {
                expect(IDENTIFIER, "Expecting parameter name", TokenSet.create(ARROW));
            }

            if (at(COLON)) {
                advance(); // COLON
                myKotlinParsing.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 (!isScriptTopLevel && !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
     *  : declaration
     *  : blockLevelExpression
     *  ;
     */
    private void parseStatement(boolean isScriptTopLevel) {
        if (!parseLocalDeclaration(/* rollbackIfDefinitelyNotExpression = */false, /* isScriptTopLevel = */ isScriptTopLevel)) {
            if (!atSet(EXPRESSION_FIRST)) {
                errorAndAdvance("Expecting a statement");
            }
            else if (isScriptTopLevel){
                PsiBuilder.Marker scriptInitializer = mark();
                parseBlockLevelExpression();
                scriptInitializer.done(SCRIPT_INITIALIZER);
            }
            else {
                parseBlockLevelExpression();
            }
        }
    }

    /*
     * blockLevelExpression
     *  : annotations + ("\n")+ expression
     *  ;
     */
    private void parseBlockLevelExpression() {
        if (at(AT)) {
            PsiBuilder.Marker expression = mark();
            myKotlinParsing.parseAnnotations(DEFAULT);

            if (!myBuilder.newlineBeforeCurrentToken()) {
                expression.rollbackTo();
                parseExpression();
                return;
            }

            parseBlockLevelExpression();
            expression.done(ANNOTATED_EXPRESSION);
            return;
        }

        parseExpression();
    }

    /*
     * declaration
     *   : function
     *   : property
     *   : extension
     *   : class
     *   : typeAlias
     *   : object
     *   ;
     */
    @Nullable
    private IElementType parseLocalDeclarationRest(
            @NotNull KotlinParsing.ModifierDetector modifierDetector,
            boolean failIfDefinitelyNotExpression,
            boolean isScriptTopLevel
    ) {
        IElementType keywordToken = tt();
        if (failIfDefinitelyNotExpression) {
            if (keywordToken != FUN_KEYWORD) return null;

            return myKotlinParsing.parseFunction(/* failIfIdentifierExists = */ true);
        }

        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;
            }
        }

        return myKotlinParsing.parseCommonDeclaration(
                modifierDetector, NameParsingMode.REQUIRED,
                isScriptTopLevel ? KotlinParsing.DeclarationParsingMode.SCRIPT_TOPLEVEL : KotlinParsing.DeclarationParsingMode.LOCAL
        );
    }

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

        PsiBuilder.Marker loop = mark();

        advance(); // DO_KEYWORD

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

        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();

        parseLoopBody();

        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)) {
                    myKotlinParsing.parseModifierList(DEFAULT, TokenSet.create(IN_KEYWORD, RPAR, COLON));
                }

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

                if (at(LPAR)) {
                    PsiBuilder.Marker destructuringDeclaration = mark();
                    myKotlinParsing.parseMultiDeclarationName(TokenSet.create(IN_KEYWORD, LBRACE));
                    destructuringDeclaration.done(DESTRUCTURING_DECLARATION);
                }
                else {
                    expect(IDENTIFIER, "Expecting a variable name", TokenSet.create(COLON, IN_KEYWORD));

                    if (at(COLON)) {
                        advance(); // COLON
                        myKotlinParsing.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();
        }

        parseLoopBody();

        loop.done(FOR);
    }

    private void parseControlStructureBody() {
        if (!parseAnnotatedLambda(/* preferBlock = */true)) {
            parseBlockLevelExpression();
        }
    }

    /*
     * element
     */
    private void parseLoopBody() {
        PsiBuilder.Marker body = mark();
        if (!at(SEMICOLON)) {
            parseControlStructureBody();
        }
        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

        myKotlinParsing.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)) {
                    myKotlinParsing.parseValueParameter(/*typeRequired = */ true);
                    if (at(COMMA)) {
                        advance(); // trailing comma
                    }
                    expect(RPAR, "Expecting ')'", recoverySet);
                }
                else {
                    error("Expecting exception variable declaration");
                }
                parameters.done(VALUE_PARAMETER_LIST);
            }

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

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

            advance(); // FINALLY_KEYWORD

            myKotlinParsing.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)) {
            parseControlStructureBody();
        }
        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)) {
                parseControlStructureBody();
            }
            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(IElementType 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 "@"
     */
    void parseLabelDefinition() {
        assert isAtLabelDefinitionOrMissingIdentifier() : "Callers must check that current token is IDENTIFIER followed with '@'";

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

        if (at(AT)) {
            errorAndAdvance("Expecting identifier before '@' in label definition");
            labelWrap.drop();
            mark.drop();
            return;
        }

        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

            myKotlinParsing.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)) {
                        if (atSet(EXPRESSION_FIRST)) {
                            error("Expecting ','");
                            continue;
                        }
                        else {
                            break;
                        }
                    }
                    advance(); // COMMA
                    if (at(RPAR)) {
                        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();
        myKotlinParsing.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(IElementType type) {
        PsiBuilder.Marker mark = mark();
        advance();
        mark.done(type);
    }

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

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




© 2015 - 2024 Weber Informatics LLC | Privacy Policy