/* * Copyright 2000-2013 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 com.intellij.lang.java.parser; import com.intellij.codeInsight.daemon.JavaErrorMessages; import com.intellij.lang.PsiBuilder; import com.intellij.lang.PsiBuilderUtil; import com.intellij.psi.JavaTokenType; import com.intellij.psi.TokenType; import com.intellij.psi.impl.source.tree.ElementType; import com.intellij.psi.impl.source.tree.JavaElementType; import com.intellij.psi.tree.IElementType; import com.intellij.psi.tree.TokenSet; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import static com.intellij.lang.PsiBuilderUtil.expect; import static com.intellij.lang.java.parser.JavaParserUtil.*; public class ExpressionParser { private enum ExprType { CONDITIONAL_OR, CONDITIONAL_AND, OR, XOR, AND, EQUALITY, RELATIONAL, SHIFT, ADDITIVE, MULTIPLICATIVE, UNARY, TYPE } private static final TokenSet ASSIGNMENT_OPS = TokenSet.create( JavaTokenType.EQ, JavaTokenType.ASTERISKEQ, JavaTokenType.DIVEQ, JavaTokenType.PERCEQ, JavaTokenType.PLUSEQ, JavaTokenType.MINUSEQ, JavaTokenType.LTLTEQ, JavaTokenType.GTGTEQ, JavaTokenType.GTGTGTEQ, JavaTokenType.ANDEQ, JavaTokenType.OREQ, JavaTokenType.XOREQ); private static final TokenSet RELATIONAL_OPS = TokenSet.create(JavaTokenType.LT, JavaTokenType.GT, JavaTokenType.LE, JavaTokenType.GE); private static final TokenSet POSTFIX_OPS = TokenSet.create(JavaTokenType.PLUSPLUS, JavaTokenType.MINUSMINUS); private static final TokenSet PREF_ARITHMETIC_OPS = TokenSet.orSet(POSTFIX_OPS, TokenSet.create(JavaTokenType.PLUS, JavaTokenType.MINUS)); private static final TokenSet PREFIX_OPS = TokenSet.orSet(PREF_ARITHMETIC_OPS, TokenSet.create(JavaTokenType.TILDE, JavaTokenType.EXCL)); private static final TokenSet LITERALS = TokenSet.create( JavaTokenType.TRUE_KEYWORD, JavaTokenType.FALSE_KEYWORD, JavaTokenType.NULL_KEYWORD, JavaTokenType.INTEGER_LITERAL, JavaTokenType.LONG_LITERAL, JavaTokenType.FLOAT_LITERAL, JavaTokenType.DOUBLE_LITERAL, JavaTokenType.CHARACTER_LITERAL, JavaTokenType.STRING_LITERAL); private static final TokenSet CONDITIONAL_OR_OPS = TokenSet.create(JavaTokenType.OROR); private static final TokenSet CONDITIONAL_AND_OPS = TokenSet.create(JavaTokenType.ANDAND); private static final TokenSet OR_OPS = TokenSet.create(JavaTokenType.OR); private static final TokenSet XOR_OPS = TokenSet.create(JavaTokenType.XOR); private static final TokenSet AND_OPS = TokenSet.create(JavaTokenType.AND); private static final TokenSet EQUALITY_OPS = TokenSet.create(JavaTokenType.EQEQ, JavaTokenType.NE); private static final TokenSet SHIFT_OPS = TokenSet.create(JavaTokenType.LTLT, JavaTokenType.GTGT, JavaTokenType.GTGTGT); private static final TokenSet ADDITIVE_OPS = TokenSet.create(JavaTokenType.PLUS, JavaTokenType.MINUS); private static final TokenSet MULTIPLICATIVE_OPS = TokenSet.create(JavaTokenType.ASTERISK, JavaTokenType.DIV, JavaTokenType.PERC); private static final TokenSet ARGS_LIST_END = TokenSet.create(JavaTokenType.RPARENTH, JavaTokenType.RBRACE, JavaTokenType.RBRACKET); private static final TokenSet ARGS_LIST_CONTINUE = TokenSet.create( JavaTokenType.IDENTIFIER, TokenType.BAD_CHARACTER, JavaTokenType.COMMA, JavaTokenType.INTEGER_LITERAL, JavaTokenType.STRING_LITERAL); private static final TokenSet THIS_OR_SUPER = TokenSet.create(JavaTokenType.THIS_KEYWORD, JavaTokenType.SUPER_KEYWORD); private static final TokenSet ID_OR_SUPER = TokenSet.create(JavaTokenType.IDENTIFIER, JavaTokenType.SUPER_KEYWORD); private static final TokenSet TYPE_START = TokenSet.orSet( ElementType.PRIMITIVE_TYPE_BIT_SET, TokenSet.create(JavaTokenType.IDENTIFIER, JavaTokenType.AT)); private final JavaParser myParser; public ExpressionParser(@NotNull final JavaParser javaParser) { myParser = javaParser; } @Nullable public PsiBuilder.Marker parse(final PsiBuilder builder) { return parseAssignment(builder); } @Nullable private PsiBuilder.Marker parseAssignment(final PsiBuilder builder) { final PsiBuilder.Marker left = parseConditional(builder); if (left == null) return null; final IElementType tokenType = getGtTokenType(builder); if (ASSIGNMENT_OPS.contains(tokenType) && tokenType != null) { final PsiBuilder.Marker assignment = left.precede(); advanceGtToken(builder, tokenType); final PsiBuilder.Marker right = parse(builder); if (right == null) { error(builder, JavaErrorMessages.message("expected.expression")); } assignment.done(JavaElementType.ASSIGNMENT_EXPRESSION); return assignment; } return left; } @Nullable public PsiBuilder.Marker parseConditional(final PsiBuilder builder) { final PsiBuilder.Marker condition = parseExpression(builder, ExprType.CONDITIONAL_OR); if (condition == null) return null; if (builder.getTokenType() != JavaTokenType.QUEST) return condition; final PsiBuilder.Marker ternary = condition.precede(); builder.advanceLexer(); final PsiBuilder.Marker truePart = parse(builder); if (truePart == null) { error(builder, JavaErrorMessages.message("expected.expression")); ternary.done(JavaElementType.CONDITIONAL_EXPRESSION); return ternary; } if (builder.getTokenType() != JavaTokenType.COLON) { error(builder, JavaErrorMessages.message("expected.colon")); ternary.done(JavaElementType.CONDITIONAL_EXPRESSION); return ternary; } builder.advanceLexer(); final PsiBuilder.Marker falsePart = parseConditional(builder); if (falsePart == null) { error(builder, JavaErrorMessages.message("expected.expression")); ternary.done(JavaElementType.CONDITIONAL_EXPRESSION); return ternary; } ternary.done(JavaElementType.CONDITIONAL_EXPRESSION); return ternary; } @Nullable private 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 myParser.getReferenceParser().parseType(builder, ReferenceParser.EAT_LAST_DOT | ReferenceParser.WILDCARD); default: assert false : "Unexpected type: " + type; return null; } } @Nullable private PsiBuilder.Marker parseBinary(final PsiBuilder builder, final ExprType type, final TokenSet ops) { PsiBuilder.Marker result = parseExpression(builder, type); if (result == null) return null; int operandCount = 1; IElementType tokenType = getGtTokenType(builder); IElementType currentExprTokenType = tokenType; while (true) { if (tokenType == null || !ops.contains(tokenType)) break; advanceGtToken(builder, tokenType); final PsiBuilder.Marker right = parseExpression(builder, type); operandCount++; tokenType = getGtTokenType(builder); if (tokenType == null || !ops.contains(tokenType) || tokenType != currentExprTokenType || right == null) { // save result = result.precede(); if (right == null) { error(builder, JavaErrorMessages.message("expected.expression")); } result.done(operandCount > 2 ? JavaElementType.POLYADIC_EXPRESSION : JavaElementType.BINARY_EXPRESSION); if (right == null) break; currentExprTokenType = tokenType; operandCount = 1; } } return result; } @Nullable private PsiBuilder.Marker parseRelational(final PsiBuilder builder) { PsiBuilder.Marker left = parseExpression(builder, ExprType.SHIFT); if (left == null) return null; IElementType tokenType; while ((tokenType = getGtTokenType(builder)) != null) { final IElementType toCreate; final ExprType toParse; if (RELATIONAL_OPS.contains(tokenType)) { toCreate = JavaElementType.BINARY_EXPRESSION; toParse = ExprType.SHIFT; } else if (tokenType == JavaTokenType.INSTANCEOF_KEYWORD) { toCreate = JavaElementType.INSTANCE_OF_EXPRESSION; toParse = ExprType.TYPE; } else { break; } final PsiBuilder.Marker expression = left.precede(); advanceGtToken(builder, tokenType); final PsiBuilder.Marker right = parseExpression(builder, toParse); if (right == null) { error(builder, toParse == ExprType.TYPE ? JavaErrorMessages.message("expected.type") : JavaErrorMessages.message("expected.expression")); expression.done(toCreate); return expression; } expression.done(toCreate); left = expression; } return left; } @Nullable private 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(); ReferenceParser.TypeInfo typeInfo = myParser.getReferenceParser().parseTypeInfo( builder, ReferenceParser.EAT_LAST_DOT | ReferenceParser.WILDCARD | ReferenceParser.CONJUNCTIONS | ReferenceParser.INCOMPLETE_ANNO); if (typeInfo == null || !expect(builder, JavaTokenType.RPARENTH)) { typeCast.rollbackTo(); return parsePostfix(builder); } if (PREF_ARITHMETIC_OPS.contains(builder.getTokenType()) && !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 PsiBuilder.Marker parsePostfix(final PsiBuilder builder) { PsiBuilder.Marker operand = parsePrimary(builder, null, -1); if (operand == null) return null; while (POSTFIX_OPS.contains(builder.getTokenType())) { final PsiBuilder.Marker postfix = operand.precede(); builder.advanceLexer(); postfix.done(JavaElementType.POSTFIX_EXPRESSION); operand = postfix; } return operand; } private enum BreakPoint {P1, P2, P3, P4} // todo[r.sh] make 'this', 'super' and 'class' reference expressions @Nullable private PsiBuilder.Marker parsePrimary(final PsiBuilder builder, @Nullable 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(); IElementType dotTokenType = builder.getTokenType(); if (dotTokenType == JavaTokenType.AT) { myParser.getDeclarationParser().parseAnnotations(builder); 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 = parseClassAccessOrMethodReference(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 (THIS_OR_SUPER.contains(dotTokenType) && 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 = myParser.getReferenceParser().parseJavaCodeReference(builder, false, true, 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.mark().done(JavaElementType.REFERENCE_PARAMETER_LIST); builder.advanceLexer(); refExpr.done(JavaElementType.REFERENCE_EXPRESSION); expr = refExpr; } else { dotPos.drop(); final PsiBuilder.Marker refExpr = expr.precede(); myParser.getReferenceParser().parseReferenceParameterList(builder, false, false); if (!expectOrError(builder, ID_OR_SUPER, "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 = parseClassAccessOrMethodReference(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 if (tokenType == JavaTokenType.DOUBLE_COLON) { return parseMethodReference(builder, startMarker); } else { startMarker.drop(); return expr; } } } @Nullable private 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.LBRACE) { return parseArrayInitializer(builder); } if (tokenType == JavaTokenType.NEW_KEYWORD) { return parseNew(builder, null); } if (tokenType == JavaTokenType.LPARENTH) { final PsiBuilder.Marker lambda = parseLambdaAfterParenth(builder, null); if (lambda != null) { return lambda; } 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 (TYPE_START.contains(tokenType)) { final PsiBuilder.Marker mark = builder.mark(); final ReferenceParser.TypeInfo typeInfo = myParser.getReferenceParser().parseTypeInfo(builder, 0); if (typeInfo != null) { boolean optionalClassKeyword = typeInfo.isPrimitive || typeInfo.isArray; if (optionalClassKeyword || !typeInfo.hasErrors && typeInfo.isParameterized) { final PsiBuilder.Marker result = continueClassAccessOrMethodReference(builder, mark, optionalClassKeyword); if (result != null) { return result; } } } mark.rollbackTo(); } PsiBuilder.Marker annotation = null; if (tokenType == JavaTokenType.AT) { annotation = myParser.getDeclarationParser().parseAnnotations(builder); tokenType = builder.getTokenType(); } if (tokenType == JavaTokenType.IDENTIFIER) { if (builder.lookAhead(1) == JavaTokenType.ARROW) { return parseLambdaExpression(builder, false, null); } 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); return refExpr; } if (annotation != null) { annotation.rollbackTo(); tokenType = builder.getTokenType(); } PsiBuilder.Marker expr = null; if (tokenType == JavaTokenType.LT) { expr = builder.mark(); if (!myParser.getReferenceParser().parseReferenceParameterList(builder, false, false)) { expr.rollbackTo(); return null; } tokenType = builder.getTokenType(); if (!THIS_OR_SUPER.contains(tokenType)) { expr.rollbackTo(); return null; } } if (THIS_OR_SUPER.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; } return null; } @Nullable private PsiBuilder.Marker parseArrayInitializer(final PsiBuilder builder) { if (builder.getTokenType() != JavaTokenType.LBRACE) return null; final PsiBuilder.Marker arrayInit = builder.mark(); builder.advanceLexer(); boolean expressionMissed = false; PsiBuilder.Marker lastComma = null; while (true) { if (builder.getTokenType() == JavaTokenType.RBRACE) { builder.advanceLexer(); break; } if (builder.getTokenType() == null) { error(builder, JavaErrorMessages.message("expected.rbrace")); break; } if (expressionMissed && lastComma != null) { // before comma must be an expression lastComma.precede().errorBefore(JavaErrorMessages.message("expected.expression"), lastComma); lastComma.drop(); lastComma = null; } final PsiBuilder.Marker arg = parse(builder); if (arg == null) { if (builder.getTokenType() == JavaTokenType.COMMA) { expressionMissed = true; lastComma = builder.mark(); } else { error(builder, JavaErrorMessages.message("expected.rbrace")); break; } } final IElementType tokenType = builder.getTokenType(); if (tokenType == JavaTokenType.COMMA) { builder.advanceLexer(); } else if (tokenType != JavaTokenType.RBRACE) { error(builder, JavaErrorMessages.message("expected.comma")); } } if (lastComma != null) { lastComma.drop(); } arrayInit.done(JavaElementType.ARRAY_INITIALIZER_EXPRESSION); return arrayInit; } @NotNull private PsiBuilder.Marker parseNew(PsiBuilder builder, @Nullable PsiBuilder.Marker start) { PsiBuilder.Marker newExpr = (start != null ? start.precede() : builder.mark()); builder.advanceLexer(); myParser.getReferenceParser().parseReferenceParameterList(builder, false, true); PsiBuilder.Marker refOrType; PsiBuilder.Marker anno = myParser.getDeclarationParser().parseAnnotations(builder); IElementType tokenType = builder.getTokenType(); if (tokenType == JavaTokenType.IDENTIFIER) { if (anno != null) { anno.rollbackTo(); } refOrType = myParser.getReferenceParser().parseJavaCodeReference(builder, true, true, true, true); 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(); myParser.getDeclarationParser().parseClassBodyWithBraces(builder, false, false); classElement.done(JavaElementType.ANONYMOUS_CLASS); } newExpr.done(JavaElementType.NEW_EXPRESSION); return newExpr; } myParser.getDeclarationParser().parseAnnotations(builder); 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) { myParser.getDeclarationParser().parseAnnotations(builder); if (builder.getTokenType() != JavaTokenType.LBRACKET) break; builder.advanceLexer(); if (bracketCount == dimCount) { final PsiBuilder.Marker dimExpr = parse(builder); if (dimExpr != null) { dimCount++; } } bracketCount++; if (!expectOrError(builder, JavaTokenType.RBRACKET, "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 PsiBuilder.Marker parseClassAccessOrMethodReference(final PsiBuilder builder) { final PsiBuilder.Marker expr = builder.mark(); final boolean primitive = ElementType.PRIMITIVE_TYPE_BIT_SET.contains(builder.getTokenType()); if (myParser.getReferenceParser().parseType(builder, 0) == null) { expr.drop(); return null; } final PsiBuilder.Marker result = continueClassAccessOrMethodReference(builder, expr, primitive); if (result == null) expr.rollbackTo(); return result; } @Nullable private PsiBuilder.Marker continueClassAccessOrMethodReference(final PsiBuilder builder, final PsiBuilder.Marker expr, final boolean optionalClassKeyword) { final IElementType tokenType = builder.getTokenType(); if (tokenType == JavaTokenType.DOT) { return parseClassObjectAccess(builder, expr, optionalClassKeyword); } else if (tokenType == JavaTokenType.DOUBLE_COLON) { return parseMethodReference(builder, expr); } return null; } @Nullable private static PsiBuilder.Marker parseClassObjectAccess(PsiBuilder builder, PsiBuilder.Marker expr, boolean optionalClassKeyword) { final PsiBuilder.Marker mark = builder.mark(); builder.advanceLexer(); if (builder.getTokenType() == JavaTokenType.CLASS_KEYWORD) { mark.drop(); builder.advanceLexer(); } else { if (!optionalClassKeyword) return null; mark.rollbackTo(); builder.error(".class expected"); } expr.done(JavaElementType.CLASS_OBJECT_ACCESS_EXPRESSION); return expr; } @NotNull private PsiBuilder.Marker parseMethodReference(final PsiBuilder builder, final PsiBuilder.Marker start) { builder.advanceLexer(); myParser.getReferenceParser().parseReferenceParameterList(builder, false, false); if (!expect(builder, JavaTokenType.IDENTIFIER) && !expect(builder, JavaTokenType.NEW_KEYWORD)) { error(builder, JavaErrorMessages.message("expected.identifier")); } start.done(JavaElementType.METHOD_REF_EXPRESSION); return start; } @Nullable private PsiBuilder.Marker parseLambdaAfterParenth(final PsiBuilder builder, @Nullable final PsiBuilder.Marker typeList) { final boolean isLambda; final boolean isTyped; final IElementType nextToken1 = builder.lookAhead(1); final IElementType nextToken2 = builder.lookAhead(2); if (nextToken1 == JavaTokenType.RPARENTH && nextToken2 == JavaTokenType.ARROW) { isLambda = true; isTyped = false; } else if (nextToken1 == JavaTokenType.AT || ElementType.MODIFIER_BIT_SET.contains(nextToken1) || ElementType.PRIMITIVE_TYPE_BIT_SET.contains(nextToken1)) { isLambda = true; isTyped = true; } else if (nextToken1 == JavaTokenType.IDENTIFIER) { if (nextToken2 == JavaTokenType.COMMA || nextToken2 == JavaTokenType.RPARENTH && builder.lookAhead(3) == JavaTokenType.ARROW) { isLambda = true; isTyped = false; } else if (nextToken2 == JavaTokenType.ARROW) { isLambda = false; isTyped = false; } else { boolean arrow = false; final PsiBuilder.Marker marker = builder.mark(); while (!builder.eof()) { builder.advanceLexer(); final IElementType tokenType = builder.getTokenType(); if (tokenType == JavaTokenType.ARROW) { arrow = true; break; } if (tokenType == JavaTokenType.RPARENTH) { arrow = builder.lookAhead(1) == JavaTokenType.ARROW; break; } else if (tokenType == JavaTokenType.LPARENTH || tokenType == JavaTokenType.SEMICOLON || tokenType == JavaTokenType.LBRACE || tokenType == JavaTokenType.RBRACE) { break; } } marker.rollbackTo(); isLambda = arrow; isTyped = true; } } else { isLambda = false; isTyped = false; } return isLambda ? parseLambdaExpression(builder, isTyped, typeList) : null; } @Nullable private PsiBuilder.Marker parseLambdaExpression(final PsiBuilder builder, final boolean typed, @Nullable final PsiBuilder.Marker typeList) { final PsiBuilder.Marker start = typeList != null ? typeList.precede() : builder.mark(); myParser.getDeclarationParser().parseLambdaParameterList(builder, typed); if (!expect(builder, JavaTokenType.ARROW)) { start.rollbackTo(); return null; } final PsiBuilder.Marker body; if (builder.getTokenType() == JavaTokenType.LBRACE) { body = myParser.getStatementParser().parseCodeBlock(builder); } else { body = parse(builder); } if (body == null) { builder.error(JavaErrorMessages.message("expected.lbrace")); } start.done(JavaElementType.LAMBDA_EXPRESSION); return start; } @NotNull public PsiBuilder.Marker parseArgumentList(final PsiBuilder builder) { final PsiBuilder.Marker list = builder.mark(); builder.advanceLexer(); boolean first = true; while (true) { final IElementType tokenType = builder.getTokenType(); if (first && (ARGS_LIST_END.contains(tokenType) || builder.eof())) break; if (!first && !ARGS_LIST_CONTINUE.contains(tokenType)) break; boolean hasError = false; if (!first) { if (builder.getTokenType() == JavaTokenType.COMMA) { builder.advanceLexer(); } else { hasError = true; error(builder, JavaErrorMessages.message("expected.comma.or.rparen")); emptyExpression(builder); } } first = false; final PsiBuilder.Marker arg = parse(builder); if (arg == null) { if (!hasError) { error(builder, JavaErrorMessages.message("expected.expression")); emptyExpression(builder); } if (!ARGS_LIST_CONTINUE.contains(builder.getTokenType())) break; if (builder.getTokenType() != JavaTokenType.COMMA && !builder.eof()) { builder.advanceLexer(); } } } final boolean closed = expectOrError(builder, JavaTokenType.RPARENTH, "expected.rparen"); list.done(JavaElementType.EXPRESSION_LIST); if (!closed) { list.setCustomEdgeTokenBinders(null, GREEDY_RIGHT_EDGE_PROCESSOR); } return list; } private static void emptyExpression(final PsiBuilder builder) { emptyElement(builder, JavaElementType.EMPTY_EXPRESSION); } @Nullable private static IElementType getGtTokenType(final PsiBuilder builder) { IElementType tokenType = builder.getTokenType(); if (tokenType != JavaTokenType.GT) return tokenType; if (builder.rawLookup(1) == JavaTokenType.GT) { if (builder.rawLookup(2) == JavaTokenType.GT) { if (builder.rawLookup(3) == JavaTokenType.EQ) { tokenType = JavaTokenType.GTGTGTEQ; } else { tokenType = JavaTokenType.GTGTGT; } } else if (builder.rawLookup(2) == JavaTokenType.EQ) { tokenType = JavaTokenType.GTGTEQ; } else { tokenType = JavaTokenType.GTGT; } } else if (builder.rawLookup(1) == JavaTokenType.EQ) { tokenType = JavaTokenType.GE; } return tokenType; } private static void advanceGtToken(final PsiBuilder builder, final IElementType type) { final PsiBuilder.Marker gtToken = builder.mark(); if (type == JavaTokenType.GTGTGTEQ) { PsiBuilderUtil.advance(builder, 4); } else if (type == JavaTokenType.GTGTGT || type == JavaTokenType.GTGTEQ) { PsiBuilderUtil.advance(builder, 3); } else if (type == JavaTokenType.GTGT || type == JavaTokenType.GE) { PsiBuilderUtil.advance(builder, 2); } else { gtToken.drop(); builder.advanceLexer(); return; } gtToken.collapse(type); } }