@Nullable
  private static PsiBuilder.Marker parseUnary(final PsiBuilder builder) {
    final IElementType tokenType = builder.getTokenType();

    if (PREFIX_OPS.contains(tokenType)) {
      final PsiBuilder.Marker unary = builder.mark();
      builder.advanceLexer();

      final PsiBuilder.Marker operand = parseUnary(builder);
      if (operand == null) {
        error(builder, JavaErrorMessages.message("expected.expression"));
      }

      unary.done(JavaElementType.PREFIX_EXPRESSION);
      return unary;
    } else if (tokenType == JavaTokenType.LPARENTH) {
      final PsiBuilder.Marker typeCast = builder.mark();
      builder.advanceLexer();

      final ReferenceParser.TypeInfo typeInfo =
          ReferenceParser.parseTypeInfo(
              builder, ReferenceParser.EAT_LAST_DOT | ReferenceParser.WILDCARD);

      if (typeInfo == null || builder.getTokenType() != JavaTokenType.RPARENTH) {
        typeCast.rollbackTo();
        return parsePostfix(builder);
      }
      builder.advanceLexer();

      if (PREF_ARITHMETIC_OPS.contains(builder.getTokenType())) {
        if (!typeInfo.isPrimitive) {
          typeCast.rollbackTo();
          return parsePostfix(builder);
        }
      }

      final PsiBuilder.Marker expr = parseUnary(builder);
      if (expr == null) {
        if (!typeInfo
            .isParameterized) { // cannot parse correct parenthesized expression after correct
          // parameterized type
          typeCast.rollbackTo();
          return parsePostfix(builder);
        } else {
          error(builder, JavaErrorMessages.message("expected.expression"));
        }
      }

      typeCast.done(JavaElementType.TYPE_CAST_EXPRESSION);
      return typeCast;
    } else {
      return parsePostfix(builder);
    }
  }
  @Nullable
  private static PsiBuilder.Marker parseExpression(final PsiBuilder builder, final ExprType type) {
    switch (type) {
      case CONDITIONAL_OR:
        return parseBinary(builder, ExprType.CONDITIONAL_AND, CONDITIONAL_OR_OPS);

      case CONDITIONAL_AND:
        return parseBinary(builder, ExprType.OR, CONDITIONAL_AND_OPS);

      case OR:
        return parseBinary(builder, ExprType.XOR, OR_OPS);

      case XOR:
        return parseBinary(builder, ExprType.AND, XOR_OPS);

      case AND:
        return parseBinary(builder, ExprType.EQUALITY, AND_OPS);

      case EQUALITY:
        return parseBinary(builder, ExprType.RELATIONAL, EQUALITY_OPS);

      case RELATIONAL:
        return parseRelational(builder);

      case SHIFT:
        return parseBinary(builder, ExprType.ADDITIVE, SHIFT_OPS);

      case ADDITIVE:
        return parseBinary(builder, ExprType.MULTIPLICATIVE, ADDITIVE_OPS);

      case MULTIPLICATIVE:
        return parseBinary(builder, ExprType.UNARY, MULTIPLICATIVE_OPS);

      case UNARY:
        return parseUnary(builder);

      case TYPE:
        return ReferenceParser.parseType(
            builder, ReferenceParser.EAT_LAST_DOT | ReferenceParser.WILDCARD);

      default:
        assert false : "Unexpected type: " + type;
        return null;
    }
  }
  @Nullable
  private static PsiBuilder.Marker parseClassObjectAccess(final PsiBuilder builder) {
    final PsiBuilder.Marker expr = builder.mark();

    if (ReferenceParser.parseType(builder, 0) == null) {
      expr.drop();
      return null;
    }

    if (builder.getTokenType() != JavaTokenType.DOT) {
      expr.rollbackTo();
      return null;
    }
    builder.advanceLexer();

    if (builder.getTokenType() != JavaTokenType.CLASS_KEYWORD) {
      expr.rollbackTo();
      return null;
    }
    builder.advanceLexer();

    expr.done(JavaElementType.CLASS_OBJECT_ACCESS_EXPRESSION);
    return expr;
  }
  @NotNull
  private static PsiBuilder.Marker parseNew(
      final PsiBuilder builder, final PsiBuilder.Marker start) {
    final PsiBuilder.Marker newExpr = (start != null ? start.precede() : builder.mark());
    builder.advanceLexer();

    final boolean parseDiamonds = areDiamondsSupported(builder);
    ReferenceParser.parseReferenceParameterList(builder, false, parseDiamonds);

    final PsiBuilder.Marker refOrType;
    final boolean parseAnnotations =
        areTypeAnnotationsSupported(builder) && builder.getTokenType() == JavaTokenType.AT;

    final IElementType tokenType = builder.getTokenType();
    if (tokenType == JavaTokenType.IDENTIFIER || parseAnnotations) {
      refOrType =
          ReferenceParser.parseJavaCodeReference(
              builder, true, true, parseAnnotations, true, parseDiamonds);
      if (refOrType == null) {
        error(builder, JavaErrorMessages.message("expected.identifier"));
        newExpr.done(JavaElementType.NEW_EXPRESSION);
        return newExpr;
      }
    } else if (ElementType.PRIMITIVE_TYPE_BIT_SET.contains(tokenType)) {
      refOrType = null;
      builder.advanceLexer();
    } else {
      error(builder, JavaErrorMessages.message("expected.identifier"));
      newExpr.done(JavaElementType.NEW_EXPRESSION);
      return newExpr;
    }

    if (refOrType != null && builder.getTokenType() == JavaTokenType.LPARENTH) {
      parseArgumentList(builder);
      if (builder.getTokenType() == JavaTokenType.LBRACE) {
        final PsiBuilder.Marker classElement = refOrType.precede();
        DeclarationParser.parseClassBodyWithBraces(builder, false, false);
        classElement.done(JavaElementType.ANONYMOUS_CLASS);
      }
    } else {
      if (builder.getTokenType() != JavaTokenType.LBRACKET) {
        error(
            builder,
            refOrType == null
                ? JavaErrorMessages.message("expected.lbracket")
                : JavaErrorMessages.message("expected.lparen.or.lbracket"));
        newExpr.done(JavaElementType.NEW_EXPRESSION);
        return newExpr;
      }

      int bracketCount = 0;
      int dimCount = 0;
      while (true) {
        if (builder.getTokenType() != JavaTokenType.LBRACKET) break;
        builder.advanceLexer();

        if (bracketCount == dimCount) {
          final PsiBuilder.Marker dimExpr = parse(builder);
          if (dimExpr != null) {
            dimCount++;
          }
        }
        bracketCount++;

        if (!JavaParserUtil.expectOrError(
            builder, JavaTokenType.RBRACKET, JavaErrorMessages.message("expected.rbracket"))) {
          newExpr.done(JavaElementType.NEW_EXPRESSION);
          return newExpr;
        }
      }

      if (dimCount == 0) {
        if (builder.getTokenType() == JavaTokenType.LBRACE) {
          parseArrayInitializer(builder);
        } else {
          error(builder, JavaErrorMessages.message("expected.array.initializer"));
        }
      }
    }

    newExpr.done(JavaElementType.NEW_EXPRESSION);
    return newExpr;
  }
  @Nullable
  private static PsiBuilder.Marker parsePrimaryExpressionStart(final PsiBuilder builder) {
    IElementType tokenType = builder.getTokenType();

    if (LITERALS.contains(tokenType)) {
      final PsiBuilder.Marker literal = builder.mark();
      builder.advanceLexer();
      literal.done(JavaElementType.LITERAL_EXPRESSION);
      return literal;
    }
    if (tokenType == JavaTokenType.LPARENTH) {
      final PsiBuilder.Marker parenth = builder.mark();
      builder.advanceLexer();

      final PsiBuilder.Marker inner = parse(builder);
      if (inner == null) {
        error(builder, JavaErrorMessages.message("expected.expression"));
      }

      if (!expect(builder, JavaTokenType.RPARENTH)) {
        if (inner != null) {
          error(builder, JavaErrorMessages.message("expected.rparen"));
        }
      }

      parenth.done(JavaElementType.PARENTH_EXPRESSION);
      return parenth;
    }
    if (tokenType == JavaTokenType.LBRACE) {
      return parseArrayInitializer(builder);
    }

    PsiBuilder.Marker annotation = null;
    final PsiBuilder.Marker beforeAnnotation = builder.mark();
    if (tokenType == JavaTokenType.AT) {
      annotation = DeclarationParser.parseAnnotations(builder);
      tokenType = builder.getTokenType();
    }

    if (tokenType == JavaTokenType.IDENTIFIER) {
      final PsiBuilder.Marker refExpr;
      if (annotation != null) {
        final PsiBuilder.Marker refParam = annotation.precede();
        refParam.doneBefore(JavaElementType.REFERENCE_PARAMETER_LIST, annotation);
        refExpr = refParam.precede();
      } else {
        refExpr = builder.mark();
        builder.mark().done(JavaElementType.REFERENCE_PARAMETER_LIST);
      }

      builder.advanceLexer();
      refExpr.done(JavaElementType.REFERENCE_EXPRESSION);
      beforeAnnotation.drop();
      return refExpr;
    }

    if (annotation != null) {
      beforeAnnotation.rollbackTo();
      tokenType = builder.getTokenType();
    } else {
      beforeAnnotation.drop();
    }

    PsiBuilder.Marker expr = null;
    if (tokenType == JavaTokenType.LT) {
      expr = builder.mark();

      if (!ReferenceParser.parseReferenceParameterList(builder, false, false)) {
        expr.rollbackTo();
        return null;
      }

      tokenType = builder.getTokenType();
      if (!CONSTRUCTOR_CALL.contains(tokenType)) {
        expr.rollbackTo();
        return null;
      }
    }

    if (CONSTRUCTOR_CALL.contains(tokenType)) {
      if (expr == null) {
        expr = builder.mark();
        builder.mark().done(JavaElementType.REFERENCE_PARAMETER_LIST);
      }
      builder.advanceLexer();
      expr.done(
          builder.getTokenType() == JavaTokenType.LPARENTH
              ? JavaElementType.REFERENCE_EXPRESSION
              : tokenType == JavaTokenType.THIS_KEYWORD
                  ? JavaElementType.THIS_EXPRESSION
                  : JavaElementType.SUPER_EXPRESSION);
      return expr;
    }
    if (tokenType == JavaTokenType.NEW_KEYWORD) {
      return parseNew(builder, null);
    }
    if (ElementType.PRIMITIVE_TYPE_BIT_SET.contains(tokenType)) {
      return parseClassObjectAccess(builder);
    }

    return null;
  }
  @Nullable
  private static PsiBuilder.Marker parsePrimary(
      final PsiBuilder builder, final BreakPoint breakPoint, final int breakOffset) {
    PsiBuilder.Marker startMarker = builder.mark();

    PsiBuilder.Marker expr = parsePrimaryExpressionStart(builder);
    if (expr == null) {
      startMarker.drop();
      return null;
    }

    while (true) {
      final IElementType tokenType = builder.getTokenType();
      if (tokenType == JavaTokenType.DOT) {
        final PsiBuilder.Marker dotPos = builder.mark();
        final int dotOffset = builder.getCurrentOffset();
        builder.advanceLexer();

        final IElementType dotTokenType = builder.getTokenType();
        if (dotTokenType == JavaTokenType.CLASS_KEYWORD
            && exprType(expr) == JavaElementType.REFERENCE_EXPRESSION) {
          if (breakPoint == BreakPoint.P1 && builder.getCurrentOffset() == breakOffset) {
            error(builder, JavaErrorMessages.message("expected.identifier"));
            PsiBuilderUtil.drop(startMarker, dotPos);
            return expr;
          }

          final PsiBuilder.Marker copy = startMarker.precede();
          final int offset = builder.getCurrentOffset();
          startMarker.rollbackTo();

          final PsiBuilder.Marker classObjAccess = parseClassObjectAccess(builder);
          if (classObjAccess == null || builder.getCurrentOffset() < offset) {
            copy.rollbackTo();
            return parsePrimary(builder, BreakPoint.P1, offset);
          }

          startMarker = copy;
          expr = classObjAccess;
        } else if (dotTokenType == JavaTokenType.NEW_KEYWORD) {
          dotPos.drop();
          expr = parseNew(builder, expr);
        } else if ((dotTokenType == JavaTokenType.THIS_KEYWORD
                || dotTokenType == JavaTokenType.SUPER_KEYWORD)
            && exprType(expr) == JavaElementType.REFERENCE_EXPRESSION) {
          if (breakPoint == BreakPoint.P2 && builder.getCurrentOffset() == breakOffset) {
            dotPos.rollbackTo();
            startMarker.drop();
            return expr;
          }

          final PsiBuilder.Marker copy = startMarker.precede();
          final int offset = builder.getCurrentOffset();
          startMarker.rollbackTo();

          final PsiBuilder.Marker ref =
              ReferenceParser.parseJavaCodeReference(builder, false, true, false, false, false);
          if (ref == null
              || builder.getTokenType() != JavaTokenType.DOT
              || builder.getCurrentOffset() != dotOffset) {
            copy.rollbackTo();
            return parsePrimary(builder, BreakPoint.P2, offset);
          }
          builder.advanceLexer();

          if (builder.getTokenType() != dotTokenType) {
            copy.rollbackTo();
            return parsePrimary(builder, BreakPoint.P2, offset);
          }
          builder.advanceLexer();

          startMarker = copy;
          expr = ref.precede();
          expr.done(
              dotTokenType == JavaTokenType.THIS_KEYWORD
                  ? JavaElementType.THIS_EXPRESSION
                  : JavaElementType.SUPER_EXPRESSION);
        } else if (dotTokenType == JavaTokenType.SUPER_KEYWORD) {
          dotPos.drop();
          final PsiBuilder.Marker refExpr = expr.precede();
          builder.advanceLexer();
          refExpr.done(JavaElementType.REFERENCE_EXPRESSION);
          expr = refExpr;
        } else {
          dotPos.drop();
          final PsiBuilder.Marker refExpr = expr.precede();
          ReferenceParser.parseReferenceParameterList(builder, false, false);

          if (!JavaParserUtil.expectOrError(
              builder,
              JavaTokenType.IDENTIFIER,
              JavaErrorMessages.message("expected.identifier"))) {
            refExpr.done(JavaElementType.REFERENCE_EXPRESSION);
            startMarker.drop();
            return refExpr;
          }

          refExpr.done(JavaElementType.REFERENCE_EXPRESSION);
          expr = refExpr;
        }
      } else if (tokenType == JavaTokenType.LPARENTH) {
        if (exprType(expr) != JavaElementType.REFERENCE_EXPRESSION) {
          if (exprType(expr) == JavaElementType.SUPER_EXPRESSION) {
            if (breakPoint == BreakPoint.P3) {
              startMarker.drop();
              return expr;
            }

            final PsiBuilder.Marker copy = startMarker.precede();
            startMarker.rollbackTo();

            final PsiBuilder.Marker qualifier = parsePrimaryExpressionStart(builder);
            if (qualifier != null) {
              final PsiBuilder.Marker refExpr = qualifier.precede();
              if (builder.getTokenType() == JavaTokenType.DOT) {
                builder.advanceLexer();
                if (builder.getTokenType() == JavaTokenType.SUPER_KEYWORD) {
                  builder.advanceLexer();
                  refExpr.done(JavaElementType.REFERENCE_EXPRESSION);
                  expr = refExpr;
                  startMarker = copy;
                  continue;
                }
              }
            }

            copy.rollbackTo();
            return parsePrimary(builder, BreakPoint.P3, -1);
          } else {
            startMarker.drop();
            return expr;
          }
        }

        final PsiBuilder.Marker callExpr = expr.precede();
        parseArgumentList(builder);
        callExpr.done(JavaElementType.METHOD_CALL_EXPRESSION);
        expr = callExpr;
      } else if (tokenType == JavaTokenType.LBRACKET) {
        if (breakPoint == BreakPoint.P4) {
          startMarker.drop();
          return expr;
        }

        builder.advanceLexer();

        if (builder.getTokenType() == JavaTokenType.RBRACKET
            && exprType(expr) == JavaElementType.REFERENCE_EXPRESSION) {
          final int pos = builder.getCurrentOffset();
          final PsiBuilder.Marker copy = startMarker.precede();
          startMarker.rollbackTo();

          final PsiBuilder.Marker classObjAccess = parseClassObjectAccess(builder);
          if (classObjAccess == null || builder.getCurrentOffset() <= pos) {
            copy.rollbackTo();
            return parsePrimary(builder, BreakPoint.P4, -1);
          }

          startMarker = copy;
          expr = classObjAccess;
        } else {
          final PsiBuilder.Marker arrayAccess = expr.precede();

          final PsiBuilder.Marker index = parse(builder);
          if (index == null) {
            error(builder, JavaErrorMessages.message("expected.expression"));
            arrayAccess.done(JavaElementType.ARRAY_ACCESS_EXPRESSION);
            startMarker.drop();
            return arrayAccess;
          }

          if (builder.getTokenType() != JavaTokenType.RBRACKET) {
            error(builder, JavaErrorMessages.message("expected.rbracket"));
            arrayAccess.done(JavaElementType.ARRAY_ACCESS_EXPRESSION);
            startMarker.drop();
            return arrayAccess;
          }
          builder.advanceLexer();

          arrayAccess.done(JavaElementType.ARRAY_ACCESS_EXPRESSION);
          expr = arrayAccess;
        }
      } else {
        startMarker.drop();
        return expr;
      }
    }
  }