/** * Named list operators * * @param b PerlBuilder * @param l Parsing level * @return parsing result */ public static boolean isListOperator(PsiBuilder b, int l) { PerlTokenData prevTokenData = ((PerlBuilder) b).lookupToken(-1); if (prevTokenData != null && prevTokenData.getTokenType() == OPERATOR_DEREFERENCE) return false; IElementType tokenType = b.getTokenType(); IElementType nextTokenType = b.lookAhead(1); if (CONVERTABLE_TOKENS.contains(tokenType) && nextTokenType != LEFT_PAREN // not function call && !PACKAGE_TOKENS.contains(nextTokenType) // not method Package:: && !(nextTokenType == IDENTIFIER && ((PerlBuilder) b) .isKnownPackage( ((PerlBuilder) b).lookupToken(1).getTokenText())) // not Method Package ) // todo we should check current namespace here return !PerlSubUtil.BUILT_IN_UNARY.contains(b.getTokenText()); else if (PACKAGE_TOKENS.contains(tokenType) && CONVERTABLE_TOKENS.contains(nextTokenType) && b.lookAhead(2) != LEFT_PAREN) return !PerlSubUtil.isUnary( b.getTokenText(), ((PerlBuilder) b).lookupToken(1).getTokenText()); return false; }
private static boolean reportError(ErrorState state, PsiBuilder builder_, boolean force) { String expectedText = state.getExpectedText(builder_); boolean notEmpty = StringUtil.isNotEmpty(expectedText); if (force || notEmpty) { final String gotText = builder_.eof() ? "unexpected end of file" : notEmpty ? "got '" + builder_.getTokenText() + "'" : "'" + builder_.getTokenText() + "' unexpected"; builder_.error(expectedText + gotText); return true; } return false; }
private void getNextTokenValue(PsiBuilder builder) { PsiBuilder.Marker rb = builder.mark(); builder.advanceLexer(); nextToken = builder.getTokenType(); nextTokenText = builder.getTokenText(); rb.rollbackTo(); }
private static boolean reportError( PsiBuilder builder, ErrorState state, Frame frame, IElementType elementType, boolean force, boolean advance) { String expectedText = state.getExpectedText(builder); boolean notEmpty = StringUtil.isNotEmpty(expectedText); if (force || notEmpty || advance) { String gotText = builder.eof() ? "unexpected end of file" : notEmpty ? "got '" + builder.getTokenText() + "'" : "'" + builder.getTokenText() + "' unexpected"; String message = expectedText + gotText; if (advance) { PsiBuilder.Marker mark = builder.mark(); builder.advanceLexer(); mark.error(message); } else if (!force) { PsiBuilder.Marker extensionMarker = null; IElementType extensionTokenType = null; PsiBuilderImpl.ProductionMarker latestDoneMarker = elementType == null ? null : (PsiBuilderImpl.ProductionMarker) builder.getLatestDoneMarker(); if (latestDoneMarker != null && frame.position >= latestDoneMarker.getStartIndex() && frame.position <= latestDoneMarker.getEndIndex()) { extensionMarker = ((PsiBuilder.Marker) latestDoneMarker).precede(); extensionTokenType = latestDoneMarker.getTokenType(); ((PsiBuilder.Marker) latestDoneMarker).drop(); } builder.error(message); if (extensionMarker != null) extensionMarker.done(extensionTokenType); } else { builder.error(message); } builder.eof(); // skip whitespaces frame.errorReportedAt = builder.rawTokenIndex(); return true; } return false; }
@NotNull protected String getTokenText() { String result = myBuilder.getTokenText(); if (result == null) { result = ""; } return result; }
/** * Named unary operators * * @param b PerlBuilder * @param l parsing level * @return parsing result */ public static boolean isUnaryOperator(PsiBuilder b, int l) { assert b instanceof PerlBuilder; PerlTokenData prevTokenData = ((PerlBuilder) b).lookupToken(-1); if (prevTokenData != null && prevTokenData.getTokenType() == OPERATOR_DEREFERENCE) return false; IElementType tokenType = b.getTokenType(); IElementType nextTokenType = b.lookAhead(1); if (CONVERTABLE_TOKENS.contains(tokenType) && nextTokenType != LEFT_PAREN && !PACKAGE_TOKENS.contains(nextTokenType)) // todo we should check current namespace here return PerlSubUtil.BUILT_IN_UNARY.contains(b.getTokenText()); else if (PACKAGE_TOKENS.contains(tokenType) && CONVERTABLE_TOKENS.contains(SUB) && b.lookAhead(2) != LEFT_PAREN) { PerlTokenData nextToken = ((PerlBuilder) b).lookupToken(1); if (nextToken != null) return PerlSubUtil.isUnary(b.getTokenText(), nextToken.getTokenText()); } return false; }
public static IElementType parseDefinitions( @NotNull PsiBuilder builder, boolean isInClass, boolean isInAnnotation, @Nullable String typeDefinitionName, boolean hasModifiers, boolean canBeTuple, @NotNull GroovyParser parser) { boolean isLParenth = builder.getTokenType() == GroovyTokenTypes.mLPAREN; boolean isStringName = builder.getTokenType() == GroovyTokenTypes.mSTRING_LITERAL || builder.getTokenType() == GroovyTokenTypes.mGSTRING_LITERAL; if (builder.getTokenType() != GroovyTokenTypes.mIDENT && !isStringName && !isLParenth) { builder.error(GroovyBundle.message("indentifier.or.string.or.left.parenth.literal.expected")); return GroovyElementTypes.WRONGWAY; } if (isLParenth && !canBeTuple) { builder.error(GroovyBundle.message("indentifier.or.string.or.left.parenth.literal.expected")); return GroovyElementTypes.WRONGWAY; } if (isInAnnotation && isStringName) { builder.error(GroovyBundle.message("string.name.unexpected")); } if (!isLParenth) { // id or string => method name PsiBuilder.Marker varMarker = builder.mark(); final boolean isConstructor = isInClass && !isInAnnotation && typeDefinitionName != null && builder.getTokenType() == GroovyTokenTypes.mIDENT && typeDefinitionName.equals(builder.getTokenText()); builder.advanceLexer(); if (GroovyTokenTypes.mLPAREN != builder.getTokenType()) { varMarker.rollbackTo(); } else { varMarker.drop(); return parseMethod(builder, isInAnnotation, hasModifiers, parser, isConstructor); } } return parseVar(builder, isInClass, hasModifiers, parser, isLParenth); }
public void advance() { final String tokenText = myBuilder.getTokenText(); final int tokenLength = tokenText == null ? 0 : tokenText.length(); final int whiteSpaceStart = getCurrentOffset() + tokenLength; myBuilder.advanceLexer(); final int whiteSpaceEnd = getCurrentOffset(); final String whiteSpaceText = myBuilder.getOriginalText().subSequence(whiteSpaceStart, whiteSpaceEnd).toString(); int i = whiteSpaceText.lastIndexOf('\n'); if (i >= 0) { myCurrentIndent = whiteSpaceText.length() - i - 1; myNewLine = true; } else { myNewLine = false; } }
/** * parser for print/say/printf filehandle * * @param b PerlBuilder * @param l parsing level * @return parsing result */ public static boolean parsePrintHandle(PsiBuilder b, int l) { IElementType currentTokenType = b.getTokenType(); IElementType nextTokenType = b.lookAhead(1); assert b instanceof PerlBuilder; if (CONVERTABLE_TOKENS.contains(currentTokenType) // it's identifier && !PRINT_HANDLE_NEGATE_SUFFIX.contains(nextTokenType) // no negation tokens && !PerlSubUtil.BUILT_IN.contains( b.getTokenText()) // it's not built in. crude, probably we should check any known sub ) { PsiBuilder.Marker m = b.mark(); b.advanceLexer(); m.collapse(HANDLE); return true; } return false; }
@NotNull public ASTNode parse(final IElementType root, final PsiBuilder builder) { final PsiBuilder.Marker fileMarker = builder.mark(); final Stack<Pair<Integer, PsiBuilder.Marker>> stack = new Stack<Pair<Integer, PsiBuilder.Marker>>(); stack.push(Pair.create(0, builder.mark())); PsiBuilder.Marker startLineMarker = null; int currentIndent = 0; boolean eolSeen = false; while (!builder.eof()) { final IElementType type = builder.getTokenType(); // EOL if (type == myEolTokenType) { // Handle variant with several EOLs if (startLineMarker == null) { startLineMarker = builder.mark(); } eolSeen = true; } else // Indent { if (type == myIndentTokenType) { //noinspection ConstantConditions currentIndent = builder.getTokenText().length(); } else if (eolSeen) { if (startLineMarker != null) { startLineMarker.rollbackTo(); startLineMarker = null; } // Close indentation blocks while (!stack.isEmpty() && currentIndent < stack.peek().first) { stack.pop().second.done(myBlockElementType); } if (!stack.isEmpty()) { final Pair<Integer, PsiBuilder.Marker> pair = stack.peek(); if (currentIndent == pair.first) { stack.pop().second.done(myBlockElementType); passEOLsAndIndents(builder); stack.push(Pair.create(currentIndent, builder.mark())); } if (currentIndent > pair.first) { passEOLsAndIndents(builder); stack.push(Pair.create(currentIndent, builder.mark())); } } eolSeen = false; currentIndent = 0; } } advanceLexer(builder); } // Close all left opened markers if (startLineMarker != null) { startLineMarker.drop(); } while (!stack.isEmpty()) { stack.pop().second.done(myBlockElementType); } return buildTree(fileMarker, builder, root); }
@Nullable public static IElementType parseAfterModifiers( PsiBuilder builder, boolean isInClass, boolean isInAnnotation, GroovyParser parser, PsiBuilder.Marker declMarker, boolean modifiersParsed) { if (modifiersParsed && mLT == builder.getTokenType()) { TypeParameters.parse(builder); PsiBuilder.Marker checkMarker = builder.mark(); // point to begin of type or variable if (TypeSpec.parse(builder, true) == fail) { // if type wasn't recognized trying parse VariableDeclaration checkMarker.rollbackTo(); } else { checkMarker.drop(); } IElementType decl = VariableDefinitions.parseDefinitions( builder, isInClass, false, false, true, modifiersParsed, false, parser); if (WRONGWAY.equals(decl)) { return WRONGWAY; } return METHOD_DEFINITION; } if (modifiersParsed) { PsiBuilder.Marker checkMarker = builder.mark(); // point to begin of type or variable if (TypeSpec.parse(builder, false) == fail) { // if type wasn't recognized trying parse VariableDeclaration checkMarker.rollbackTo(); if (isInAnnotation) { builder.error(GroovyBundle.message("type.expected")); } // current token isn't identifier IElementType varDecl = VariableDefinitions.parse(builder, isInClass, modifiersParsed, parser); if (WRONGWAY.equals(varDecl)) { return WRONGWAY; } return varDecl; } else { // type was recognized, identifier here // starts after type IElementType varDeclarationTop = VariableDefinitions.parse(builder, isInClass, modifiersParsed, false, parser); if (WRONGWAY.equals(varDeclarationTop)) { checkMarker.rollbackTo(); if (isInAnnotation) { builder.error(GroovyBundle.message("type.expected")); } // starts before "type" identifier, here can't be tuple, because next token is identifier // (we are in "type recognized" branch) IElementType varDecl = VariableDefinitions.parse(builder, isInClass, modifiersParsed, false, parser); if (WRONGWAY.equals(varDecl)) { return WRONGWAY; } else { return varDecl; } } else { checkMarker.drop(); return varDeclarationTop; } } } else { // if definition starts with lower case letter than it can be just call expression String text = builder.getTokenText(); if (!builder.eof() && !TokenSets.BUILT_IN_TYPE.contains(builder.getTokenType()) && text != null && (Character.isLowerCase((text.charAt(0))) || !Character.isLetter(text.charAt(0))) && (ParserUtils.lookAhead(builder, mIDENT, mIDENT) || ParserUtils.lookAhead(builder, mIDENT, mLPAREN))) { // call expression return WRONGWAY; } boolean typeParsed = false; if (!ParserUtils.lookAhead(builder, mIDENT, mLPAREN)) { typeParsed = TypeSpec.parse(builder, true) != fail; // type specification starts with upper case letter if (!typeParsed) { builder.error(GroovyBundle.message("type.specification.expected")); return WRONGWAY; } } IElementType varDef = VariableDefinitions.parseDefinitions( builder, isInClass, false, false, false, typeParsed, false, parser); if (varDef != WRONGWAY) { return varDef; } else if (isInClass && typeParsed) { return typeParsed ? null : WRONGWAY; } return WRONGWAY; } }
public static boolean exitErrorRecordingSection( PsiBuilder builder_, int level, boolean result, boolean pinned, @NotNull String sectionType, @Nullable Parser eatMore) { ErrorState state = ErrorState.get(builder_); Frame frame = state.levelCheck.pollLast(); int initialOffset = builder_.getCurrentOffset(); if (frame == null || level != frame.level || !sectionType.equals(frame.section)) { LOG.error( "Unbalanced error section: got " + new Frame().init(initialOffset, level, sectionType, "", 0) + ", expected " + frame); if (frame != null) state.FRAMES.recycle(frame); return result; } if (sectionType == _SECTION_AND_ || sectionType == _SECTION_NOT_) { state.predicateCount--; if (sectionType == _SECTION_NOT_) state.predicateSign = !state.predicateSign; state.FRAMES.recycle(frame); return result; } if (!result && !pinned && initialOffset == frame.offset && state.lastExpectedVariantOffset == frame.offset && frame.name != null && state.variants.size() - frame.variantCount > 1) { state.clearVariants(true, frame.variantCount); addVariantInner(state, initialOffset, frame.name); } if (sectionType == _SECTION_RECOVER_ && !state.suppressErrors && eatMore != null) { state.suppressErrors = true; final boolean eatMoreFlagOnce = !builder_.eof() && eatMore.parse(builder_, frame.level + 1); final int lastErrorPos = getLastVariantOffset(state, initialOffset); boolean eatMoreFlag = eatMoreFlagOnce || frame.offset == initialOffset && lastErrorPos > frame.offset; final LighterASTNode latestDoneMarker = (pinned || result) && (state.altMode || lastErrorPos > initialOffset) && eatMoreFlagOnce ? builder_.getLatestDoneMarker() : null; PsiBuilder.Marker extensionMarker = null; IElementType extensionTokenType = null; if (latestDoneMarker instanceof PsiBuilder.Marker) { extensionMarker = ((PsiBuilder.Marker) latestDoneMarker).precede(); extensionTokenType = latestDoneMarker.getTokenType(); ((PsiBuilder.Marker) latestDoneMarker).drop(); } // advance to the last error pos // skip tokens until lastErrorPos. parseAsTree might look better here... int parenCount = 0; while (eatMoreFlag && builder_.getCurrentOffset() < lastErrorPos) { if (state.braces != null) { if (builder_.getTokenType() == state.braces[0].getLeftBraceType()) parenCount++; else if (builder_.getTokenType() == state.braces[0].getRightBraceType()) parenCount--; } builder_.advanceLexer(); eatMoreFlag = parenCount != 0 || eatMore.parse(builder_, frame.level + 1); } boolean errorReported = frame.errorReportedAt == initialOffset; if (errorReported) { if (eatMoreFlag) { builder_.advanceLexer(); parseAsTree(state, builder_, frame.level + 1, DUMMY_BLOCK, true, TOKEN_ADVANCER, eatMore); } } else if (eatMoreFlag) { String tokenText = builder_.getTokenText(); String expectedText = state.getExpectedText(builder_); PsiBuilder.Marker mark = builder_.mark(); builder_.advanceLexer(); final String gotText = !expectedText.isEmpty() ? "got '" + tokenText + "'" : "'" + tokenText + "' unexpected"; mark.error(expectedText + gotText); parseAsTree(state, builder_, frame.level + 1, DUMMY_BLOCK, true, TOKEN_ADVANCER, eatMore); errorReported = true; } else if (eatMoreFlagOnce || (!result && frame.offset != builder_.getCurrentOffset())) { reportError(state, builder_, true); errorReported = true; } if (extensionMarker != null) { extensionMarker.done(extensionTokenType); } state.suppressErrors = false; if (errorReported || result) { state.clearVariants(true, 0); state.clearVariants(false, 0); state.lastExpectedVariantOffset = -1; } if (!result && eatMoreFlagOnce && frame.offset != builder_.getCurrentOffset()) result = true; } else if (!result && pinned && frame.errorReportedAt < 0) { // do not report if there're errors after current offset if (getLastVariantOffset(state, initialOffset) == initialOffset) { // do not force, inner recoverRoot might have skipped some tokens if (reportError(state, builder_, false)) { frame.errorReportedAt = initialOffset; } } } // propagate errorReportedAt up the stack to avoid duplicate reporting Frame prevFrame = state.levelCheck.isEmpty() ? null : state.levelCheck.getLast(); if (prevFrame != null && prevFrame.errorReportedAt < frame.errorReportedAt) prevFrame.errorReportedAt = frame.errorReportedAt; state.FRAMES.recycle(frame); return result; }
/** * Parses invocable method As input we may have: PACKAGE_IDENTIFIER IDENTIFIER Foo::sub IDENTIFIER * PACKAGE_IDENTIFIER sub Foo:: IDENTIFIER IDENTIFIER sub Foo * * @param b PerlBuilder * @param l parsing level * @return parsing result */ public static boolean parseMethod(PsiBuilder b, int l) { IElementType currentTokenType = b.getTokenType(); IElementType nextTokenType = b.lookAhead(1); assert b instanceof PerlBuilder; // can be // Foo::method // Foo::Bar if (PACKAGE_TOKENS.contains(currentTokenType) && CONVERTABLE_TOKENS.contains(nextTokenType)) { PerlTokenData nextTokenData = ((PerlBuilder) b).lookupToken(1); PerlTokenData nextNextTokenData = ((PerlBuilder) b).lookupToken(2); IElementType nextNextTokenType = nextNextTokenData == null ? null : nextNextTokenData.getTokenType(); String canonicalPackageName = PerlPackageUtil.getCanonicalPackageName(b.getTokenText()); String potentialSubName = canonicalPackageName + "::" + nextTokenData.getTokenText(); if (nextNextTokenType == LEFT_PAREN // Package::Identifier( - what can it be? || ((PerlBuilder) b).isKnownSub(potentialSubName) // we know this sub || !((PerlBuilder) b).isKnownPackage(potentialSubName)) // we don't know such package return convertPackageIdentifier(b, l) && convertIdentifier(b, l, SUB); else return false; } // method else if (CONVERTABLE_TOKENS.contains(currentTokenType)) { PerlTokenData prevTokenData = ((PerlBuilder) b).lookupToken(-1); // ->sub if (prevTokenData != null && prevTokenData.getTokenType() == OPERATOR_DEREFERENCE) return convertIdentifier(b, l, SUB); // may be // method Foo:: // method Foo::Bar // method Foo::othermethod else if (PACKAGE_TOKENS.contains(nextTokenType)) { IElementType nextNextTokenType = b.lookAhead(2); // sub Foo::->method if (nextNextTokenType == OPERATOR_DEREFERENCE) return convertIdentifier(b, l, SUB); // identifier Package::identifier else if (CONVERTABLE_TOKENS.contains(nextNextTokenType)) { // identifier Package::identifier-> if (b.lookAhead(3) == OPERATOR_DEREFERENCE) return convertIdentifier(b, l, SUB); PerlTokenData nextTokenData = ((PerlBuilder) b).lookupToken(1); PerlTokenData nextNextTokenData = ((PerlBuilder) b).lookupToken(2); String packageOrSub = PerlPackageUtil.getCanonicalPackageName(nextTokenData.getTokenText()) + "::" + nextNextTokenData.getTokenText(); if (((PerlBuilder) b).isKnownSub(packageOrSub)) return convertIdentifier(b, l, SUB); else if (((PerlBuilder) b).isKnownPackage(packageOrSub)) return convertIdentifier(b, l, SUB) && mergePackageName(b, l); return convertIdentifier(b, l, SUB); } else // it's method Package:: return convertIdentifier(b, l, SUB) && convertPackageIdentifier(b, l); } // may be // method Foo else if (CONVERTABLE_TOKENS.contains(nextTokenType) && b.lookAhead(2) != OPERATOR_DEREFERENCE) { PerlTokenData nextTokenData = ((PerlBuilder) b).lookupToken(1); String potentialSubName = nextTokenData.getTokenText() + "::" + b.getTokenText(); if (((PerlBuilder) b).isKnownSub(potentialSubName)) return convertIdentifier(b, l, SUB) && convertIdentifier(b, l, PACKAGE); else return convertIdentifier(b, l, SUB); } // KnownPackage-> else if (nextTokenType == OPERATOR_DEREFERENCE && ((PerlBuilder) b).isKnownPackage(b.getTokenText())) return false; // it's just sub else return convertIdentifier(b, l, SUB); } return false; }