/* * 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.indentation; import com.intellij.lang.PsiBuilder; import com.intellij.psi.tree.IElementType; import com.intellij.psi.tree.TokenSet; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; public class OperationParserHelper { private static boolean parsePostfixOperation(@NotNull final BinaryOperationParser parser) { final PsiBuilder.Marker tempMarker = parser.mark(); PsiBuilder.Marker lastMarker = tempMarker; boolean result = parsePrefixOperation(parser); boolean tempMarkerDeleted = false; while (parser.getPostfixOperators().contains(parser.getTokenType()) && !parser.getWhitespaceTokenSet().contains(parser.rawLookup(-1)) && !parser.isNewLine()) { final PsiBuilder.Marker operationMarker = lastMarker.precede(); if (!tempMarkerDeleted) { tempMarker.drop(); tempMarkerDeleted = true; } lastMarker = operationMarker; parser.advance(); parser.done(operationMarker, parser.getPostfixExpressionElementType()); result = true; } if (!tempMarkerDeleted) { tempMarker.drop(); } return result; } private static boolean parsePrefixOperation(@NotNull final BinaryOperationParser parser) { int prefixCount = 0; while (parser.getPrefixOperators().contains(parser.lookAhead(prefixCount))) { prefixCount++; } final PsiBuilder.Marker[] prefixMarkers = new PsiBuilder.Marker[prefixCount]; final IElementType[] elementTypes = new IElementType[prefixCount]; for (int i = 0; i < prefixCount; i++) { prefixMarkers[i] = parser.mark(); elementTypes[i] = parser.getPrefixExpressionElementType(); parser.advance(); } final boolean result = parser.parseSimpleExpression() || prefixCount > 0; for (int i = prefixCount - 1; i >= 0; i--) { parser.done(prefixMarkers[i], elementTypes[i]); } return result; } public static boolean callParsingBinaryOperation(@NotNull final BinaryOperationParser parser, int level) { if (level < 0) { return parsePostfixOperation(parser); } return parseBinaryOperation(parser, level); } private static boolean isBinaryOperator(@NotNull final BinaryOperationParser parser, int level) { if (parser instanceof CustomBinaryOperationParser) { return ((CustomBinaryOperationParser)parser).isBinaryOperator(level); } final IElementType tokenType = parser.getTokenType(); return parser.getOperatorsByPriority()[level].contains(tokenType); } private static void parseBinaryOperator(@NotNull final BinaryOperationParser parser) { if (parser instanceof CustomBinaryOperationParser) { ((CustomBinaryOperationParser)parser).parseBinaryOperator(); } else { parser.advance(); } } /** * Parses arithmetic mult, arithmetic sum, bit operation, relation operation * @param level 0 for mult, 1, for sum, 2 for bit, 3 for relations */ private static boolean parseBinaryOperation(@NotNull final BinaryOperationParser parser, int level) { final PsiBuilder.Marker tempMarker = parser.mark(); PsiBuilder.Marker lastMarker = tempMarker; boolean result = callParsingBinaryOperation(parser, level - 1); boolean tempMarkerDeleted = false; while (isBinaryOperator(parser, level) && !parser.isNewLine()) { final PsiBuilder.Marker operationMarker = lastMarker.precede(); if (!tempMarkerDeleted) { tempMarker.drop(); tempMarkerDeleted = true; } lastMarker = operationMarker; parseBinaryOperator(parser); callParsingBinaryOperation(parser, level - 1); parser.done(operationMarker, parser.getOperationElementTypes()[level]); result = true; } if (!tempMarkerDeleted) { tempMarker.drop(); } return result; } public interface BinaryOperationParser { /** * Gets the TokenType from PsiBuilder * @return IElementType of current element */ IElementType getTokenType(); /** * Checks current token starts the line * @return true if new line */ boolean isNewLine(); /** * Advance current position of PsiBuilder */ void advance(); /** * See what token type is in {@code step} ahead / benind (including whitespaces) * @param step 0 is current token, -1 is previous, 1 is next and so on * @return IElementType of the required element */ IElementType rawLookup(int step); /** * See what token type is in {@code step} ahead (not including whitespaces) * @param step 0 is current token, 1 is next and so on * @return IElementType of the required element */ IElementType lookAhead(int step); /** * Create new marker * @return PsiBuilder.Marker of created marker */ PsiBuilder.Marker mark(); /** * Close marker with element type * @param marker to close * @param elementType to close marker as */ void done(PsiBuilder.Marker marker, IElementType elementType); /** * Parses operand * @return boolean if success */ boolean parseSimpleExpression(); /** * Provides all whitespace tokens * @return TokenSet of whitespaces */ TokenSet getWhitespaceTokenSet(); /** * Provides prefix operators * @return TokenSet of prefix operators */ TokenSet getPrefixOperators(); /** * Provides postfix operators * @return TokenSet of prefix operators */ TokenSet getPostfixOperators(); /** * Provides operation priority and operands * @return array of TokenSets */ @NotNull TokenSet[] getOperatorsByPriority(); /** * Provides element types to finish postfix element marker * @return IElementType */ @Nullable IElementType getPostfixExpressionElementType(); /** * Provides element types to finish prefix element marker * @return IElementType */ @Nullable IElementType getPrefixExpressionElementType(); /** * Provides element types to finish binary operation element * @return array of Element Types */ @NotNull IElementType[] getOperationElementTypes(); } public interface CustomBinaryOperationParser { boolean isBinaryOperator(int level); void parseBinaryOperator(); } }