private boolean parseFunctionTypeDotParametersAndType() {
    PsiBuilder.Marker rollbackMarker = mark();

    // True when it's confirmed that body of literal can't be simple expressions and we prefer to
    // parse
    // it to function params if possible.
    boolean preferParamsToExpressions = false;

    // Last dot before ARROW and RPAR, but also stop at top-level keywords and '{', '}'
    int lastDot =
        matchTokenStreamPredicate(
            new LastBefore(
                new At(DOT),
                new AtSet(
                    TokenSet.orSet(
                        TokenSet.create(ARROW, RPAR),
                        TokenSet.orSet(
                            TokenSet.create(LBRACE, RBRACE),
                            TokenSet.andNot(
                                KEYWORDS, TokenSet.create(CAPITALIZED_THIS_KEYWORD)))))));

    if (lastDot >= 0) {
      createTruncatedBuilder(lastDot).parseTypeRef();
      if (at(DOT)) {
        advance(); // DOT
        preferParamsToExpressions = parseFunctionLiteralParametersAndType();
      }
    }

    return preferParamsToExpressions
        ? rollbackOrDrop(rollbackMarker, ARROW, "An -> is expected", RBRACE)
        : rollbackOrDropAt(rollbackMarker, ARROW);
  }
Beispiel #2
0
  static {
    keys1 = new HashMap<IElementType, TextAttributesKey>();
    keys2 = new HashMap<IElementType, TextAttributesKey>();

    fillMap(keys1, JetTokens.KEYWORDS, JetHighlightingColors.KEYWORD);

    keys1.put(JetTokens.AS_SAFE, JetHighlightingColors.KEYWORD);
    keys1.put(JetTokens.LABEL_IDENTIFIER, JetHighlightingColors.LABEL);
    keys1.put(JetTokens.INTEGER_LITERAL, JetHighlightingColors.NUMBER);
    keys1.put(JetTokens.FLOAT_LITERAL, JetHighlightingColors.NUMBER);

    fillMap(
        keys1,
        TokenSet.andNot(
            JetTokens.OPERATIONS,
            TokenSet.orSet(
                TokenSet.create(JetTokens.IDENTIFIER, JetTokens.LABEL_IDENTIFIER),
                JetTokens.KEYWORDS)),
        JetHighlightingColors.OPERATOR_SIGN);

    keys1.put(JetTokens.LPAR, JetHighlightingColors.PARENTHESIS);
    keys1.put(JetTokens.RPAR, JetHighlightingColors.PARENTHESIS);
    keys1.put(JetTokens.LBRACE, JetHighlightingColors.BRACES);
    keys1.put(JetTokens.RBRACE, JetHighlightingColors.BRACES);
    keys1.put(JetTokens.LBRACKET, JetHighlightingColors.BRACKETS);
    keys1.put(JetTokens.RBRACKET, JetHighlightingColors.BRACKETS);
    keys1.put(JetTokens.COMMA, JetHighlightingColors.COMMA);
    keys1.put(JetTokens.SEMICOLON, JetHighlightingColors.SEMICOLON);
    keys1.put(JetTokens.DOT, JetHighlightingColors.DOT);
    keys1.put(JetTokens.ARROW, JetHighlightingColors.ARROW);

    keys1.put(JetTokens.OPEN_QUOTE, JetHighlightingColors.STRING);
    keys1.put(JetTokens.CLOSING_QUOTE, JetHighlightingColors.STRING);
    keys1.put(JetTokens.REGULAR_STRING_PART, JetHighlightingColors.STRING);
    keys1.put(JetTokens.LONG_TEMPLATE_ENTRY_END, JetHighlightingColors.STRING_ESCAPE);
    keys1.put(JetTokens.LONG_TEMPLATE_ENTRY_START, JetHighlightingColors.STRING_ESCAPE);
    keys1.put(JetTokens.SHORT_TEMPLATE_ENTRY_START, JetHighlightingColors.STRING_ESCAPE);

    keys1.put(JetTokens.ESCAPE_SEQUENCE, JetHighlightingColors.STRING_ESCAPE);

    keys1.put(JetTokens.CHARACTER_LITERAL, JetHighlightingColors.STRING);

    keys1.put(JetTokens.EOL_COMMENT, JetHighlightingColors.LINE_COMMENT);
    keys1.put(JetTokens.SHEBANG_COMMENT, JetHighlightingColors.LINE_COMMENT);
    keys1.put(JetTokens.BLOCK_COMMENT, JetHighlightingColors.BLOCK_COMMENT);
    keys1.put(JetTokens.DOC_COMMENT, JetHighlightingColors.DOC_COMMENT);

    fillMap(keys1, KDocTokens.KDOC_HIGHLIGHT_TOKENS, JetHighlightingColors.DOC_COMMENT);
    keys1.put(KDocTokens.TAG_NAME, JetHighlightingColors.KDOC_TAG);
    keys2.put(KDocTokens.TAG_NAME, JetHighlightingColors.DOC_COMMENT);

    keys1.put(TokenType.BAD_CHARACTER, JetHighlightingColors.BAD_CHARACTER);
  }
public class JetExpressionParsing extends AbstractJetParsing {
  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<String, JetToken> KEYWORD_TEXTS = tokenSetToMap(KEYWORDS);

  private static ImmutableMap<String, JetToken> tokenSetToMap(TokenSet tokens) {
    ImmutableMap.Builder<String, JetToken> builder = ImmutableMap.builder();
    for (IElementType token : tokens.getTypes()) {
      builder.put(token.toString(), (JetToken) 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,
          TRAIT_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<bar, baz>=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
          LBRACKET,
          LABEL_IDENTIFIER,
          // Atomic

          COLONCOLON, // callable reference
          LPAR, // parenthesized
          HASH, // Tuple

          // literal constant
          TRUE_KEYWORD,
          FALSE_KEYWORD,
          OPEN_QUOTE,
          INTEGER_LITERAL,
          CHARACTER_LITERAL,
          FLOAT_LITERAL,
          NULL_KEYWORD,
          LBRACE, // functionLiteral
          FUN_KEYWORD, // expression function
          LPAR, // tuple
          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
          FIELD_IDENTIFIER, // Field reference
          PACKAGE_KEYWORD // for absolute qualified names
          );

  private static final TokenSet STATEMENT_FIRST =
      TokenSet.orSet(
          EXPRESSION_FIRST,
          TokenSet.create(
              // declaration
              LBRACKET, // annotation
              FUN_KEYWORD,
              VAL_KEYWORD,
              VAR_KEYWORD,
              TRAIT_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, LABEL_IDENTIFIER) { // annotations

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

    COLON_AS(COLON, AS_KEYWORD, AS_SAFE) {
      @Override
      public JetNodeType parseRightHandSide(IElementType operation, JetExpressionParsing parser) {
        parser.myJetParsing.parseTypeRef();
        return BINARY_WITH_TYPE;
      }

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

    MULTIPLICATIVE(MUL, DIV, PERC),
    ADDITIVE(PLUS, MINUS),
    RANGE(JetTokens.RANGE),
    SIMPLE_NAME(IDENTIFIER),
    ELVIS(JetTokens.ELVIS),
    IN_OR_IS(IN_KEYWORD, NOT_IN, IS_KEYWORD, NOT_IS) {
      @Override
      public JetNodeType parseRightHandSide(IElementType operation, JetExpressionParsing 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(JetTokens.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(JetExpressionParsing 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 JetNodeType parseRightHandSide(IElementType operation, JetExpressionParsing 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<IElementType> operations = new HashSet<IElementType>();
    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<IElementType> opSet = new HashSet<IElementType>(Arrays.asList(operations));
    IElementType[] usedOperations = ALL_OPERATIONS.getTypes();
    Set<IElementType> usedSet = new HashSet<IElementType>(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 JetParsing myJetParsing;

  public JetExpressionParsing(SemanticWhitespaceAwarePsiBuilder builder, JetParsing 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();

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

    expression.drop();
  }

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

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

    if (at(LBRACKET)) {
      if (!parseLocalDeclaration()) {
        PsiBuilder.Marker expression = mark();
        myJetParsing.parseAnnotations(REGULAR_ANNOTATIONS_ONLY_WITH_BRACKETS);
        parsePrefixExpression();
        expression.done(ANNOTATED_EXPRESSION);
      }
    } else {
      myBuilder.disableJoiningComplexTokens();
      if (at(LABEL_IDENTIFIER)) {
        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
   *   ;
   */
  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();
      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 doubleColonExpression = parseDoubleColonExpression();
    if (!doubleColonExpression) {
      parseAtomicExpression();
    }

    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)) {
        advance(); // DOT

        parseCallExpression();

        expression.done(DOT_QUALIFIED_EXPRESSION);
      } else if (at(SAFE_ACCESS)) {
        advance(); // SAFE_ACCESS

        parseCallExpression();

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

  /*
   * callSuffix
   *   : typeArguments? valueArguments (getEntryPoint? functionLiteral*)
   *   : typeArguments (getEntryPoint? functionLiteral*)
   *   ;
   */
  private boolean parseCallSuffix() {
    if (parseCallWithClosure()) {
      parseCallWithClosure();
    } 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);
  }

  /*
   * element (getEntryPoint? functionLiteral)?
   */
  protected boolean parseCallWithClosure() {
    boolean success = false;
    while ((at(LBRACE) || at(LABEL_IDENTIFIER) && lookahead(1) == LBRACE)) {
      PsiBuilder.Marker argument = mark();
      if (!at(LBRACE)) {
        assert _at(LABEL_IDENTIFIER);
        parseLabeledExpression();
      } else {
        parseFunctionLiteral();
      }
      argument.done(FUNCTION_LITERAL_ARGUMENT);
      success = true;
    }

    return success;
  }

  /*
   * atomicExpression
   *   : tupleLiteral // or parenthesized element
   *   : "this" label?
   *   : "super" ("<" type ">")? label?
   *   : objectLiteral
   *   : jump
   *   : if
   *   : when
   *   : try
   *   : loop
   *   : literalConstant
   *   : functionLiteral
   *   : declaration
   *   : SimpleName
   *   : "package" // for the root package
   *   ;
   */
  private void parseAtomicExpression() {
    //        System.out.println("atom at "  + myBuilder.getTokenText());

    if (at(LPAR)) {
      parseParenthesizedExpression();
    } else if (at(HASH)) {
      parseTupleExpression();
    } else if (at(PACKAGE_KEYWORD)) {
      parseOneTokenExpression(ROOT_PACKAGE);
    } 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, FUN_KEYWORD, VAL_KEYWORD, VAR_KEYWORD, TYPE_ALIAS_KEYWORD)) {
      parseLocalDeclaration();
    } else if (at(FIELD_IDENTIFIER)) {
      parseSimpleNameExpression();
    } else if (at(IDENTIFIER)) {
      parseSimpleNameExpression();
    } else if (at(LBRACE)) {
      parseFunctionLiteral();
    } else if (at(OPEN_QUOTE)) {
      parseStringTemplate();
    } else if (!parseLiteralConstant()) {
      // TODO: better recovery if FIRST(element) did not match
      errorWithRecovery("Expecting an element", EXPRESSION_FOLLOW);
    }
  }

  /*
   * 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 {
        JetToken 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"
   *   : StringWithTemplates
   *   : 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);

      int valPos =
          matchTokenStreamPredicate(
              new FirstBefore(new At(VAL_KEYWORD), new AtSet(RPAR, LBRACE, RBRACE, SEMICOLON, EQ)));
      if (valPos >= 0) {
        PsiBuilder.Marker property = mark();
        myJetParsing.parseModifierList(MODIFIER_LIST, REGULAR_ANNOTATIONS_ALLOW_SHORTS);
        myJetParsing.parseProperty(true);
        property.done(PROPERTY);
      } else {
        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, RBRACE, EOL_OR_SEMICOLON));
      }

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

        if (atSet(WHEN_CONDITION_RECOVERY_SET)) {
          error("Expecting an element");
        } else {
          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();
    if (at(FIELD_IDENTIFIER)) {
      advance(); //
    } else {
      expect(IDENTIFIER, "Expecting an identifier");
    }
    simpleName.done(REFERENCE_EXPRESSION);
  }

  /*
   * modifiers declarationRest
   */
  private boolean parseLocalDeclaration() {
    PsiBuilder.Marker decl = mark();
    JetParsing.ModifierDetector detector = new JetParsing.ModifierDetector();
    myJetParsing.parseModifierList(MODIFIER_LIST, detector, REGULAR_ANNOTATIONS_ONLY_WITH_BRACKETS);

    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 != JetNodeTypes.PROPERTY && declType != JetNodeTypes.MULTI_VARIABLE_DECLARATION);
      return true;
    } else {
      decl.rollbackTo();
      return false;
    }
  }

  /*
   * functionLiteral  // one can use "it" as a parameter name
   *   : "{" expressions "}"
   *   : "{" (modifiers SimpleName){","} "->" statements "}"
   *   : "{" (type ".")? "(" (modifiers SimpleName (":" type)?){","} ")" (":" type)? "->" expressions "}"
   *   ;
   */
  private void parseFunctionLiteral() {
    parseFunctionLiteral(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(LPAR)) {
      // Look for ARROW after matching RPAR
      //   {(a, b) -> ...}

      {
        PsiBuilder.Marker rollbackMarker = mark();
        boolean preferParamsToExpressions = parseFunctionLiteralParametersAndType();

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

      if (!paramsFound) {
        // If not found, try a typeRef DOT and then LPAR .. RPAR ARROW
        //   {((A) -> B).(x) -> ... }
        paramsFound = parseFunctionTypeDotParametersAndType();
      }
    } else {
      if (at(IDENTIFIER)) {
        // Try to parse a simple name list followed by an ARROW
        //   {a -> ...}
        //   {a, b -> ...}
        PsiBuilder.Marker rollbackMarker = mark();
        boolean preferParamsToExpressions = (lookahead(1) == COMMA);
        parseFunctionLiteralShorthandParameterList();
        parseOptionalFunctionLiteralType();

        paramsFound =
            preferParamsToExpressions
                ? rollbackOrDrop(rollbackMarker, ARROW, "An -> is expected", RBRACE)
                : rollbackOrDropAt(rollbackMarker, ARROW);
      }
      if (!paramsFound && atSet(JetParsing.TYPE_REF_FIRST)) {
        // Try to parse a type DOT valueParameterList ARROW
        //   {A.(b) -> ...}
        paramsFound = parseFunctionTypeDotParametersAndType();
      }
    }

    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(FUNCTION_LITERAL_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,
      JetToken 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);

      expect(IDENTIFIER, "Expecting parameter name", TokenSet.create(ARROW));

      parameter.done(VALUE_PARAMETER);

      if (at(COLON)) {
        PsiBuilder.Marker errorMarker = mark();
        advance(); // COLON
        myJetParsing.parseTypeRef();
        errorMarker.error(
            "To specify a type of a parameter or a return type, use the full notation: {(parameter : Type) : ReturnType -> ...}");
      } else if (at(ARROW)) {
        break;
      } else if (at(COMMA)) {
        advance(); // COMMA
      } else {
        error("Expecting '->' or ','");
        break;
      }
    }

    parameterList.done(VALUE_PARAMETER_LIST);
  }

  private boolean parseFunctionTypeDotParametersAndType() {
    PsiBuilder.Marker rollbackMarker = mark();

    // True when it's confirmed that body of literal can't be simple expressions and we prefer to
    // parse
    // it to function params if possible.
    boolean preferParamsToExpressions = false;

    // Last dot before ARROW and RPAR, but also stop at top-level keywords and '{', '}'
    int lastDot =
        matchTokenStreamPredicate(
            new LastBefore(
                new At(DOT),
                new AtSet(
                    TokenSet.orSet(
                        TokenSet.create(ARROW, RPAR),
                        TokenSet.orSet(
                            TokenSet.create(LBRACE, RBRACE),
                            TokenSet.andNot(
                                KEYWORDS, TokenSet.create(CAPITALIZED_THIS_KEYWORD)))))));

    if (lastDot >= 0) {
      createTruncatedBuilder(lastDot).parseTypeRef();
      if (at(DOT)) {
        advance(); // DOT
        preferParamsToExpressions = parseFunctionLiteralParametersAndType();
      }
    }

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

  private boolean parseFunctionLiteralParametersAndType() {
    boolean hasCommaInParametersList = parseFunctionLiteralParameterList();
    parseOptionalFunctionLiteralType();
    return hasCommaInParametersList;
  }

  /*
   * (":" type)?
   */
  private void parseOptionalFunctionLiteralType() {
    if (at(COLON)) {
      advance(); // COLON
      if (at(ARROW)) {
        error("Expecting a type");
      } else {
        myJetParsing.parseTypeRef();
      }
    }
  }

  /**
   * "(" (modifiers SimpleName (":" type)?){","} ")"
   *
   * @return true if at least one comma was found
   */
  private boolean parseFunctionLiteralParameterList() {
    PsiBuilder.Marker list = mark();
    expect(LPAR, "Expecting a parameter list in parentheses (...)", TokenSet.create(ARROW, COLON));

    boolean hasComma = false;

    myBuilder.disableNewlines();

    if (!at(RPAR)) {
      while (true) {
        if (at(COMMA)) errorAndAdvance("Expecting a parameter declaration");

        PsiBuilder.Marker parameter = mark();
        int parameterNamePos =
            matchTokenStreamPredicate(
                new LastBefore(
                    new At(IDENTIFIER), new AtSet(COMMA, RPAR, COLON, ARROW, RBRACE, LBRACE)));
        createTruncatedBuilder(parameterNamePos)
            .parseModifierList(MODIFIER_LIST, REGULAR_ANNOTATIONS_ONLY_WITH_BRACKETS);

        expect(IDENTIFIER, "Expecting parameter declaration");

        if (at(COLON)) {
          advance(); // COLON
          myJetParsing.parseTypeRef();
        }
        parameter.done(VALUE_PARAMETER);
        if (!at(COMMA)) break;
        advance(); // COMMA

        hasComma = true;

        if (at(RPAR)) {
          error("Expecting a parameter declaration");
          break;
        }
      }
    }

    myBuilder.restoreNewlinesState();

    expect(RPAR, "Expecting ')'", TokenSet.create(ARROW, COLON));
    list.done(VALUE_PARAMETER_LIST);

    return hasComma;
  }

  /*
   * expressions
   *   : SEMI* statement{SEMI+} SEMI*
   */
  public void parseStatements() {
    while (at(SEMICOLON)) advance(); // SEMICOLON
    while (!eof() && !at(RBRACE)) {
      if (!atSet(STATEMENT_FIRST)) {
        errorAndAdvance("Expecting an element");
      }
      if (atSet(STATEMENT_FIRST)) {
        parseStatement();
      }
      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() {
    if (!parseLocalDeclaration()) {
      if (!atSet(EXPRESSION_FIRST)) {
        errorAndAdvance("Expecting a statement");
      } 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 == TRAIT_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(VAL_KEYWORD) || at(VAR_KEYWORD)) advance(); // VAL_KEYWORD or VAR_KEYWORD
        if (at(LPAR)) {
          myJetParsing.parseMultiDeclarationName(TokenSet.create(IN_KEYWORD, LBRACE));
          parameter.done(MULTI_VARIABLE_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 (at(LBRACE)) {
      parseFunctionLiteral(true);
    } else if (at(LABEL_IDENTIFIER) && lookahead(1) == LBRACE) {
      PsiBuilder.Marker mark = mark();

      parseLabel();

      parseFunctionLiteral(true);

      mark.done(LABELED_EXPRESSION);
    } else {
      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, 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);

    if (at(ELSE_KEYWORD)) {
      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(JetNodeType type) {
    assert _at(BREAK_KEYWORD) || _at(CONTINUE_KEYWORD);

    PsiBuilder.Marker marker = mark();

    advance(); // BREAK_KEYWORD or CONTINUE_KEYWORD

    parseLabelOnTheSameLine();

    marker.done(type);
  }

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

    PsiBuilder.Marker returnExpression = mark();

    advance(); // RETURN_KEYWORD

    parseLabelOnTheSameLine();

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

    returnExpression.done(RETURN);
  }

  /*
   * label?
   */
  private void parseLabelOnTheSameLine() {
    if (!eol() && at(LABEL_IDENTIFIER)) {
      parseLabel();
    }
  }

  /*
   * label
   */
  private void parseLabel() {
    assert _at(LABEL_IDENTIFIER);

    String labelText = myBuilder.getTokenText();
    if ("@".equals(labelText)) {
      errorAndAdvance("Label must be named");
      return;
    }

    PsiBuilder.Marker labelWrap = mark();

    PsiBuilder.Marker mark = mark();
    advance(); // LABEL_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);
  }

  /*
   * tupleLiteral
   *   : "#" "(" (((SimpleName "=")? expression){","})? ")"
   *   ;
   */
  @Deprecated // Tuples are dropped, but parsing is left to minimize surprising. This code should be
              // removed some time (in Kotlin 1.0?)
  private void parseTupleExpression() {
    assert _at(HASH);
    PsiBuilder.Marker mark = mark();

    advance(); // HASH
    advance(); // LPAR
    myBuilder.disableNewlines();
    if (!at(RPAR)) {
      while (true) {
        while (at(COMMA)) {
          advance();
        }

        if (at(IDENTIFIER) && lookahead(1) == EQ) {
          advance(); // IDENTIFIER
          advance(); // EQ
          parseExpression();
        } else {
          parseExpression();
        }

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

        if (at(RPAR)) {
          break;
        }
      }
    }
    consumeIf(RPAR);
    myBuilder.restoreNewlinesState();

    mark.error("Tuples are not supported. Use data classes instead.");
  }

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

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

    parseLabelOnTheSameLine();

    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<foo>", 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();
    }
    parseLabelOnTheSameLine();

    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(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(JetNodeType type) {
    PsiBuilder.Marker mark = mark();
    advance();
    mark.done(type);
  }

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

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