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

      myJetParsing.parseObject(NameParsingMode.REQUIRED, true);
      declType = OBJECT_DECLARATION;
    }
    return declType;
  }
  /*
   * try
   *   : "try" block catchBlock* finallyBlock?
   *   ;
   * catchBlock
   *   : "catch" "(" annotations SimpleName ":" userType ")" block
   *   ;
   *
   * finallyBlock
   *   : "finally" block
   *   ;
   */
  private void parseTry() {
    assert _at(TRY_KEYWORD);

    PsiBuilder.Marker tryExpression = mark();

    advance(); // TRY_KEYWORD

    myJetParsing.parseBlock();

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

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

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

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

      advance(); // FINALLY_KEYWORD

      myJetParsing.parseBlock();

      finallyBlock.done(FINALLY);
    }

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

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

    PsiBuilder.Marker loop = mark();

    advance(); // FOR_KEYWORD

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

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

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

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

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

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

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

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

    parseControlStructureBody();

    loop.done(FOR);
  }
  /*
   * 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();
  }
  /*
   * operation? prefixExpression
   */
  private void parsePrefixExpression() {
    //        System.out.println("pre at "  + myBuilder.getTokenText());

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

        parseOperationReference();

        myBuilder.restoreJoiningComplexTokensState();

        parsePrefixExpression();
        expression.done(PREFIX_EXPRESSION);
      } else {
        myBuilder.restoreJoiningComplexTokensState();
        parsePostfixExpression();
      }
    }
  }
  /*
   * "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();
    }
    parseLabelReferenceWithNoWhitespace();

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

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

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

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

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

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

    parameterList.done(VALUE_PARAMETER_LIST);
  }
 /*
  * "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);
 }
  /*
   * when
   *   : "when" ("(" (modifiers "val" SimpleName "=")? element ")")? "{"
   *         whenEntry*
   *     "}"
   *   ;
   */
  private void parseWhen() {
    assert _at(WHEN_KEYWORD);

    PsiBuilder.Marker when = mark();

    advance(); // WHEN_KEYWORD

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

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

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

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

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

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

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

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

    advance(); // COLONCOLON

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

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

      expression.done(CALLABLE_REFERENCE_EXPRESSION);
    }

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

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

    if (declType != null) {
      // we do not attach preceding comments (non-doc) to local variables because they are likely
      // commenting a few statements below
      closeDeclarationWithCommentBinders(
          decl,
          declType,
          declType != KtNodeTypes.PROPERTY && declType != KtNodeTypes.DESTRUCTURING_DECLARATION);
      return true;
    } else {
      decl.rollbackTo();
      return false;
    }
  }
  /*
   * callSuffix
   *   : typeArguments? valueArguments annotatedLambda
   *   : typeArguments annotatedLambda
   *   ;
   */
  private boolean parseCallSuffix() {
    if (parseCallWithClosure()) {
      // do nothing
    } else if (at(LPAR)) {
      parseValueArgumentList();
      parseCallWithClosure();
    } else if (at(LT)) {
      PsiBuilder.Marker typeArgumentList = mark();
      if (myJetParsing.tryParseTypeArgumentList(TYPE_ARGUMENT_LIST_STOPPERS)) {
        typeArgumentList.done(TYPE_ARGUMENT_LIST);
        if (!myBuilder.newlineBeforeCurrentToken() && at(LPAR)) parseValueArgumentList();
        parseCallWithClosure();
      } else {
        typeArgumentList.rollbackTo();
        return false;
      }
    } else {
      return false;
    }

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

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

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

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

    parseFunctionLiteral(preferBlock);

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

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