static bool qtMocParseProperty(void) { char *pszPropType; CXX_DEBUG_ENTER(); if(!cxxParserParseNextToken()) { CXX_DEBUG_LEAVE_TEXT("EOF in cxxParserParseNextToken"); return false; } if (!cxxTokenTypeIs(g_cxx.pToken, CXXTokenTypeOpeningParenthesis)) { CXX_DEBUG_LEAVE_TEXT("Found no Opening Parenthesis after Q_PROPERTY"); return false; } if (!cxxParserParseNextToken()) { CXX_DEBUG_LEAVE_TEXT("EOF in cxxParserParseNextToken"); return false; } if (!(cxxTokenTypeIs(g_cxx.pToken, CXXTokenTypeIdentifier) || (cxxTokenTypeIs(g_cxx.pToken, CXXTokenTypeKeyword) && cxxKeywordMayBePartOfTypeName (g_cxx.pToken->eKeyword)))) { CXX_DEBUG_LEAVE_TEXT("Found no identifier after Q_PROPERTY("); cxxParserSkipToClosingParenthesisOrEOF (); return false; } pszPropType = vStringStrdup (g_cxx.pToken->pszWord); if(!cxxParserParseNextToken()) { CXX_DEBUG_LEAVE_TEXT("EOF in cxxParserParseNextToken"); eFree (pszPropType); return false; } if (!cxxTokenTypeIs(g_cxx.pToken, CXXTokenTypeIdentifier)) { CXX_DEBUG_LEAVE_TEXT("Found no identifier after Q_PROPERTY(%s", pszPropType); cxxParserSkipToClosingParenthesisOrEOF (); eFree (pszPropType); return false; } qtMocMakeTagForProperty (g_cxx.pToken, pszPropType); eFree (pszPropType); cxxParserSkipToClosingParenthesisOrEOF (); CXX_DEBUG_LEAVE(); return true; }
// An macro token was encountered and it expects a parameter list. // The routine has to check if there is a following parenthesis // and eventually skip it but it MUST NOT parse the next token // if it is not a parenthesis. This is because the macro token // may have a replacement and is that one that has to be returned // back to the caller from cxxParserParseNextToken(). static bool cxxParserParseNextTokenSkipMacroParenthesis(CXXToken ** ppChain) { CXX_DEBUG_ENTER(); CXX_DEBUG_ASSERT(ppChain,"ppChain should not be null here"); cxxParserSkipToNonWhiteSpace(); if(g_cxx.iChar != '(') { *ppChain = NULL; return true; // no parenthesis } if(!cxxParserParseNextToken()) { CXX_DEBUG_LEAVE_TEXT("No next token after ignored identifier"); return false; } if(!cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeOpeningParenthesis)) { CXX_DEBUG_ASSERT(false,"Should have found an open parenthesis token here!"); CXX_DEBUG_LEAVE_TEXT("Internal error"); return false; } if(!cxxParserParseAndCondenseCurrentSubchain( CXXTokenTypeOpeningParenthesis | CXXTokenTypeOpeningSquareParenthesis | CXXTokenTypeOpeningBracket, false )) { CXX_DEBUG_LEAVE_TEXT("Failed to parse and condense subchains"); return false; } CXX_DEBUG_ASSERT( cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeParenthesisChain), "Should have a parenthesis chain as last token!" ); // Now just kill the chain. *ppChain = cxxTokenChainTakeLast(g_cxx.pTokenChain); CXX_DEBUG_LEAVE(); return true; }
// The __attribute__((...)) sequence complicates parsing quite a lot. // For this reason we attempt to "hide" it from the rest of the parser // at tokenizer level. static bool cxxParserParseNextTokenCondenseAttribute(void) { CXX_DEBUG_ENTER(); CXX_DEBUG_ASSERT( cxxTokenIsKeyword(g_cxx.pToken,CXXKeyword__ATTRIBUTE__), "This function should be called only after we have parsed __attribute__" ); // Kill it cxxTokenDestroy(cxxTokenChainTakeLast(g_cxx.pTokenChain)); // And go ahead. if(!cxxParserParseNextToken()) { CXX_DEBUG_LEAVE_TEXT("No next token after __attribute__"); return false; } if(!cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeOpeningParenthesis)) { CXX_DEBUG_LEAVE_TEXT("Something that is not an opening parenthesis"); return true; } if(!cxxParserParseAndCondenseCurrentSubchain( CXXTokenTypeOpeningParenthesis | CXXTokenTypeOpeningSquareParenthesis | CXXTokenTypeOpeningBracket, false )) { CXX_DEBUG_LEAVE_TEXT("Failed to parse and condense subchains"); return false; } CXX_DEBUG_ASSERT( cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeParenthesisChain), "Should have a parenthesis chain as last token!" ); // Try to make sense of certain kinds of __attribute__. // the proper syntax is __attribute__(()), so look at the inner chain CXXToken * pInner = cxxTokenChainFirst(g_cxx.pToken->pChain); if(pInner) { if(pInner->pNext && cxxTokenTypeIs(pInner->pNext,CXXTokenTypeParenthesisChain)) pInner = cxxTokenChainFirst(pInner->pNext->pChain); while(pInner) { if(cxxTokenTypeIs(pInner,CXXTokenTypeIdentifier)) { CXX_DEBUG_PRINT("Analyzing attribyte %s",vStringValue(pInner->pszWord)); if( (strcmp(vStringValue(pInner->pszWord),"always_inline") == 0) || (strcmp(vStringValue(pInner->pszWord),"__always_inline__") == 0) ) { CXX_DEBUG_PRINT("Found attribute 'always_inline'"); // assume "inline" has been seen. g_cxx.uKeywordState |= CXXParserKeywordStateSeenInline; } else if( (strcmp(vStringValue(pInner->pszWord),"deprecated") == 0) || (strcmp(vStringValue(pInner->pszWord),"__deprecated__") == 0) ) { CXX_DEBUG_PRINT("Found attribute 'deprecated'"); // assume "inline" has been seen. g_cxx.uKeywordState |= CXXParserKeywordStateSeenAttributeDeprecated; } } // If needed, we could attach certain attributes to previous // identifiers. But note that __attribute__ may apply to a // following identifier too. pInner = pInner->pNext; } } // Now just kill the chain. cxxTokenDestroy(cxxTokenChainTakeLast(g_cxx.pTokenChain)); // And finally extract yet another token. CXX_DEBUG_LEAVE(); return cxxParserParseNextToken(); }
bool cxxParserParseNextToken(void) { CXXToken * t = cxxTokenCreate(); // The token chain should not be allowed to grow arbitrairly large. // The token structures are quite big and it's easy to grow up to // 5-6GB or memory usage. However this limit should be large enough // to accomodate all the reasonable statements that could have some // information in them. This includes multiple function prototypes // in a single statement (ImageMagick has some examples) but probably // does NOT include large data tables. if(g_cxx.pTokenChain->iCount > 16384) cxxTokenChainDestroyLast(g_cxx.pTokenChain); cxxTokenChainAppend(g_cxx.pTokenChain,t); g_cxx.pToken = t; cxxParserSkipToNonWhiteSpace(); // FIXME: this cpp handling is kind of broken: // it works only because the moon is in the correct phase. cppBeginStatement(); // This must be done after getting char from input t->iLineNumber = getInputLineNumber(); t->oFilePosition = getInputFilePosition(); if(g_cxx.iChar == EOF) { t->eType = CXXTokenTypeEOF; t->bFollowedBySpace = false; return false; } unsigned int uInfo = UINFO(g_cxx.iChar); //fprintf(stderr,"Char %c %02x info %u\n",g_cxx.iChar,g_cxx.iChar,uInfo); if(uInfo & CXXCharTypeStartOfIdentifier) { // word t->eType = CXXTokenTypeIdentifier; t->bFollowedBySpace = false; vStringPut(t->pszWord,g_cxx.iChar); // special case for tile, which may actually be an operator if(g_cxx.iChar == '~') { // may be followed by space! g_cxx.iChar = cppGetc(); if(isspace(g_cxx.iChar)) { t->bFollowedBySpace = true; g_cxx.iChar = cppGetc(); while(isspace(g_cxx.iChar)) g_cxx.iChar = cppGetc(); } // non space uInfo = UINFO(g_cxx.iChar); if(!(uInfo & CXXCharTypeStartOfIdentifier)) { // this is not an identifier after all t->eType = CXXTokenTypeOperator; if((!t->bFollowedBySpace) && g_cxx.iChar == '=') { // make ~= single token so it's not handled as // a separate assignment vStringPut(t->pszWord,g_cxx.iChar); g_cxx.iChar = cppGetc(); t->bFollowedBySpace = isspace(g_cxx.iChar); } return true; } } else { g_cxx.iChar = cppGetc(); } for(;;) { uInfo = UINFO(g_cxx.iChar); if(!(uInfo & CXXCharTypePartOfIdentifier)) break; vStringPut(t->pszWord,g_cxx.iChar); g_cxx.iChar = cppGetc(); } int iCXXKeyword = lookupKeyword(t->pszWord->buffer,g_cxx.eLanguage); if(iCXXKeyword >= 0) { if( ( (iCXXKeyword == CXXKeywordFINAL) && (!g_cxx.bParsingClassStructOrUnionDeclaration) ) || ( ( (iCXXKeyword == CXXKeywordPUBLIC) || (iCXXKeyword == CXXKeywordPROTECTED) || (iCXXKeyword == CXXKeywordPRIVATE) ) && (!g_cxx.bEnablePublicProtectedPrivateKeywords) ) ) { t->eType = CXXTokenTypeIdentifier; } else { t->eType = CXXTokenTypeKeyword; t->eKeyword = (enum CXXKeyword)iCXXKeyword; if(iCXXKeyword == CXXKeyword__ATTRIBUTE__) { // special handling for __attribute__ return cxxParserParseNextTokenCondenseAttribute(); } } } else { const cppMacroInfo * pMacro = cppFindMacro(vStringValue(t->pszWord)); if(pMacro) { CXX_DEBUG_PRINT("Macro %s",vStringValue(t->pszWord)); cxxTokenChainDestroyLast(g_cxx.pTokenChain); CXXToken * pParameterChain = NULL; if(pMacro->hasParameterList) { CXX_DEBUG_PRINT("Macro has parameter list"); if(!cxxParserParseNextTokenSkipMacroParenthesis(&pParameterChain)) return false; } // This is used to avoid infinite recursion in substitution // (things like -D foo=foo or similar) static int iReplacementRecursionCount = 0; if(pMacro->replacements) { CXX_DEBUG_PRINT("The token has replacements: applying"); if(iReplacementRecursionCount < 1024) { // unget last char cppUngetc(g_cxx.iChar); // unget the replacement cxxParserParseNextTokenApplyReplacement( pMacro, pParameterChain ); g_cxx.iChar = cppGetc(); } } if(pParameterChain) cxxTokenDestroy(pParameterChain); iReplacementRecursionCount++; // Have no token to return: parse it CXX_DEBUG_PRINT("Parse inner token"); bool bRet = cxxParserParseNextToken(); CXX_DEBUG_PRINT("Parsed inner token: %s type %d",g_cxx.pToken->pszWord->buffer,g_cxx.pToken->eType); iReplacementRecursionCount--; return bRet; } } t->bFollowedBySpace = isspace(g_cxx.iChar); return true; } if(g_cxx.iChar == '-') { // special case for pointer vStringPut(t->pszWord,g_cxx.iChar); g_cxx.iChar = cppGetc(); if(g_cxx.iChar == '>') { t->eType = CXXTokenTypePointerOperator; vStringPut(t->pszWord,g_cxx.iChar); g_cxx.iChar = cppGetc(); } else { t->eType = CXXTokenTypeOperator; if(g_cxx.iChar == '-') { vStringPut(t->pszWord,g_cxx.iChar); g_cxx.iChar = cppGetc(); } } t->bFollowedBySpace = isspace(g_cxx.iChar); return true; } #if 0 // As long as we use cppGetc() we don't need this if(g_cxx.iChar == '"') { // special case for strings t->eType = CXXTokenTypeStringConstant; vStringPut(t->pszWord,g_cxx.iChar); // We don't even care of storing the other chars: we don't need // them for parsing // FIXME: We might need them in signature:() tag.. maybe add // them up to a certain length only? for(;;) { g_cxx.iChar = cppGetc(); if(g_cxx.iChar == EOF) { t->bFollowedBySpace = false; return true; } if(g_cxx.iChar == '\\') { // escape g_cxx.iChar = cppGetc(); if(g_cxx.iChar == EOF) { t->bFollowedBySpace = false; return true; } } else if(g_cxx.iChar == '"') { g_cxx.iChar = cppGetc(); break; } } t->bFollowedBySpace = isspace(g_cxx.iChar); return true; } #else if(g_cxx.iChar == STRING_SYMBOL) { t->eType = CXXTokenTypeStringConstant; vStringPut(t->pszWord,'"'); vStringPut(t->pszWord,'"'); g_cxx.iChar = cppGetc(); t->bFollowedBySpace = isspace(g_cxx.iChar); return true; } #endif #if 0 // As long as we use cppGetc() we don't need this if(g_cxx.iChar == '\'') { // special case for strings t->eType = CXXTokenTypeCharacterConstant; vStringPut(t->pszWord,g_cxx.iChar); // We don't even care storing the other chars: we don't // need them for parsing for(;;) { g_cxx.iChar = cppGetc(); if(g_cxx.iChar == EOF) { t->bFollowedBySpace = false; return true; } if(g_cxx.iChar == '\\') { // escape g_cxx.iChar = cppGetc(); if(g_cxx.iChar == EOF) { t->bFollowedBySpace = false; return true; } } else if(g_cxx.iChar == '\'') { g_cxx.iChar = cppGetc(); break; } } t->bFollowedBySpace = isspace(g_cxx.iChar); return true; } #else if(g_cxx.iChar == CHAR_SYMBOL) { t->eType = CXXTokenTypeCharacterConstant; vStringPut(t->pszWord,'\''); vStringPut(t->pszWord,'\''); g_cxx.iChar = cppGetc(); t->bFollowedBySpace = isspace(g_cxx.iChar); return true; } #endif if(uInfo & CXXCharTypeDecimalDigit) { // number t->eType = CXXTokenTypeNumber; vStringPut(t->pszWord,g_cxx.iChar); for(;;) { g_cxx.iChar = cppGetc(); uInfo = UINFO(g_cxx.iChar); if(!(uInfo & CXXCharTypeValidInNumber)) break; vStringPut(t->pszWord,g_cxx.iChar); } t->bFollowedBySpace = isspace(g_cxx.iChar); return true; } if(uInfo & CXXCharTypeNamedSingleOrRepeatedCharToken) { t->eType = g_aCharTable[g_cxx.iChar].uSingleTokenType; vStringPut(t->pszWord,g_cxx.iChar); int iChar = g_cxx.iChar; g_cxx.iChar = cppGetc(); if(g_cxx.iChar == iChar) { t->eType = g_aCharTable[g_cxx.iChar].uMultiTokenType; // We could signal a syntax error with more than two colons // or equal signs...but we're tolerant do { vStringPut(t->pszWord,g_cxx.iChar); g_cxx.iChar = cppGetc(); } while(g_cxx.iChar == iChar); } t->bFollowedBySpace = isspace(g_cxx.iChar); return true; } if(uInfo & CXXCharTypeNamedSingleOrOperatorToken) { t->eType = g_aCharTable[g_cxx.iChar].uSingleTokenType; vStringPut(t->pszWord,g_cxx.iChar); g_cxx.iChar = cppGetc(); uInfo = UINFO(g_cxx.iChar); if(uInfo & (CXXCharTypeOperator | CXXCharTypeNamedSingleOrOperatorToken)) { t->eType = CXXTokenTypeOperator; do { vStringPut(t->pszWord,g_cxx.iChar); g_cxx.iChar = cppGetc(); uInfo = UINFO(g_cxx.iChar); } while( uInfo & (CXXCharTypeOperator | CXXCharTypeNamedSingleOrOperatorToken) ); } t->bFollowedBySpace = isspace(g_cxx.iChar); return true; } if(uInfo & CXXCharTypeNamedSingleCharToken) { t->eType = g_aCharTable[g_cxx.iChar].uSingleTokenType; vStringPut(t->pszWord,g_cxx.iChar); g_cxx.iChar = cppGetc(); t->bFollowedBySpace = isspace(g_cxx.iChar); return true; } if(uInfo & CXXCharTypeOperator) { t->eType = CXXTokenTypeOperator; vStringPut(t->pszWord,g_cxx.iChar); g_cxx.iChar = cppGetc(); uInfo = UINFO(g_cxx.iChar); while(uInfo & CXXCharTypeOperator) { vStringPut(t->pszWord,g_cxx.iChar); g_cxx.iChar = cppGetc(); uInfo = UINFO(g_cxx.iChar); } t->bFollowedBySpace = isspace(g_cxx.iChar); return true; } t->eType = CXXTokenTypeUnknown; vStringPut(t->pszWord,g_cxx.iChar); g_cxx.iChar = cppGetc(); t->bFollowedBySpace = isspace(g_cxx.iChar); return true; }
boolean cxxParserParseNamespace(void) { CXX_DEBUG_ENTER(); CXX_DEBUG_ASSERT(cxxParserCurrentLanguageIsCPP(),"This should be called only in C++"); /* Spec is: namespace ns_name { declarations } (1) inline namespace ns_name { declarations } (2) (since C++11) namespace { declarations } (3) namespace name = qualified-namespace ; (7) namespace ns_name::name { (8) (since C++17) Note that the using clauses have their own parsing routine and do not end up here. */ // namespace <X> { // namespace <X>::<Y>::<Z> { // namespace <X>::<Y>::<Z>; // namespace <X>; // namespace; unsigned int uProperties = 0; if(cxxTagFieldEnabled(CXXTagFieldProperties)) { if(g_cxx.uKeywordState & CXXParserKeywordStateSeenInline) uProperties |= CXXTagPropertyInline; } cxxParserNewStatement(); // always a new statement int iScopeCount = 0; int i; int aCorkQueueIndices[MAX_NESTED_NAMESPACES]; for(i=0;i<MAX_NESTED_NAMESPACES;i++) aCorkQueueIndices[i] = CORK_NIL; if(!cxxParserParseNextToken()) { // syntax error, but we tolerate this CXX_DEBUG_LEAVE_TEXT("Implicit EOF in cxxParserParseNextToken"); return TRUE; // EOF } if(cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeIdentifier)) { // OK, check next token to see what's coming after CXX_DEBUG_PRINT("Got identifier %s",g_cxx.pToken->pszWord->buffer); CXXToken * pFirstIdentifier = g_cxx.pToken; CXXToken * pLastIdentifier = g_cxx.pToken; if(!cxxParserParseNextToken()) { // syntax error, but we tolerate this CXX_DEBUG_LEAVE_TEXT("EOF in cxxParserParseNextToken"); return TRUE; // EOF } switch(g_cxx.pToken->eType) { case CXXTokenTypeAssignment: { // probably namespace alias CXX_DEBUG_PRINT("Found assignment"); if(!cxxParserParseNextToken()) { // syntax error, but we tolerate this CXX_DEBUG_LEAVE_TEXT("EOF in cxxParserParseNextToken"); return TRUE; // EOF } if(!cxxTokenTypeIsOneOf( g_cxx.pToken, CXXTokenTypeIdentifier | CXXTokenTypeMultipleColons )) { CXX_DEBUG_LEAVE_TEXT("Some kind of syntax error here"); return cxxParserSkipToSemicolonOrEOF(); } CXXToken * pAlias = pFirstIdentifier; pFirstIdentifier = g_cxx.pToken; if(!cxxParserParseToEndOfQualifedName()) { CXX_DEBUG_LEAVE_TEXT("Failed to parse the aliased name"); return cxxParserSkipToSemicolonOrEOF(); } pLastIdentifier = g_cxx.pToken->pPrev; tagEntryInfo * tag = cxxTagBegin(CXXTagCPPKindALIAS,pAlias); if(tag) { // This is highly questionable but well.. it's how old ctags did, so we do. tag->isFileScope = !isInputHeaderFile(); CXXToken * pAliasedName = cxxTokenChainExtractRange( pFirstIdentifier, pLastIdentifier, CXXTokenChainExtractRangeNoTrailingSpaces ); cxxTagSetField( CXXTagCPPFieldAliasedName, vStringValue(pAliasedName->pszWord) ); cxxTagCommit(); cxxTokenDestroy(pAliasedName); } CXX_DEBUG_LEAVE_TEXT("Finished parsing namespace alias"); return cxxParserSkipToSemicolonOrEOF(); } break; case CXXTokenTypeMultipleColons: // multi-namespace CXX_DEBUG_PRINT("Found multiple colons"); if(!cxxParserParseToEndOfQualifedName()) { CXX_DEBUG_LEAVE_TEXT("Failed to parse the namespace name"); return cxxParserSkipToSemicolonOrEOF(); } pLastIdentifier = g_cxx.pToken->pPrev; CXX_DEBUG_ASSERT( pFirstIdentifier != pLastIdentifier, "We expected multiple identifiers here" ); if(!cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeOpeningBracket)) { if(!cxxParserParseUpToOneOf( CXXTokenTypeOpeningBracket | CXXTokenTypeSemicolon | CXXTokenTypeEOF )) { CXX_DEBUG_LEAVE_TEXT("Failed to parse up to an opening bracket"); return FALSE; } if(!cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeOpeningBracket)) { // tolerate syntax error CXX_DEBUG_LEAVE_TEXT("Found semicolon just after namespace declaration"); return TRUE; } CXX_DEBUG_LEAVE_TEXT("Was expecting an opening bracket here"); // FIXME: Maybe we could attempt to recover here? return TRUE; } break; case CXXTokenTypeOpeningBracket: // single name namespace CXX_DEBUG_PRINT("Found opening bracket"); break; case CXXTokenTypeSemicolon: // tolerate syntax error CXX_DEBUG_LEAVE_TEXT("Found semicolon just after namespace declaration"); return TRUE; break; default: CXX_DEBUG_LEAVE_TEXT("Some kind of syntax error here"); return cxxParserSkipToSemicolonOrEOF(); break; } CXX_DEBUG_ASSERT( cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeOpeningBracket), "Should have an opening bracket here!" ); CXX_DEBUG_PRINT("Found regular namespace start"); CXXToken * t = pFirstIdentifier; while(t) { tagEntryInfo * tag = cxxTagBegin(CXXTagCPPKindNAMESPACE,t); if(tag) { // This is highly questionable but well.. it's how old ctags did, so we do. tag->isFileScope = !isInputHeaderFile(); vString * pszProperties = uProperties ? cxxTagSetProperties(uProperties) : NULL; int iCorkQueueIndex = cxxTagCommit(); if(iScopeCount < MAX_NESTED_NAMESPACES) aCorkQueueIndices[iScopeCount] = iCorkQueueIndex; if(pszProperties) vStringDelete(pszProperties); } CXXToken * pNext = (t == pLastIdentifier) ? NULL : t->pNext->pNext; cxxTokenChainTake(g_cxx.pTokenChain,t); cxxScopePush( t, CXXScopeTypeNamespace, CXXScopeAccessUnknown ); iScopeCount++; t = pNext; } } else if(cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeOpeningBracket)) { // anonymous namespace CXX_DEBUG_PRINT("Found anonymous namespace start"); CXXToken * t = cxxTokenCreateAnonymousIdentifier(CXXTagCPPKindNAMESPACE); tagEntryInfo * tag = cxxTagBegin(CXXTagCPPKindNAMESPACE,t); if(tag) { tag->isFileScope = !isInputHeaderFile(); vString * pszProperties = uProperties ? cxxTagSetProperties(uProperties) : NULL; aCorkQueueIndices[0] = cxxTagCommit(); if(pszProperties) vStringDelete(pszProperties); } cxxScopePush(t,CXXScopeTypeNamespace,CXXScopeAccessUnknown); iScopeCount++; } else { CXX_DEBUG_LEAVE_TEXT("Some kind of syntax error after namespace declaration"); return cxxParserSkipToSemicolonOrEOF(); } CXX_DEBUG_ASSERT( cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeOpeningBracket), "Should have an opening bracket here!" ); // Here we certainly got an opening bracket: namespace block if(!cxxParserParseBlock(TRUE)) { CXX_DEBUG_LEAVE_TEXT("Failed to parse namespace block"); return FALSE; } while(iScopeCount > 0) { cxxScopePop(); iScopeCount--; if(aCorkQueueIndices[iScopeCount] > CORK_NIL) cxxParserMarkEndLineForTagInCorkQueue(aCorkQueueIndices[iScopeCount]); } CXX_DEBUG_LEAVE_TEXT("Finished parsing namespace"); return TRUE; }
// // Parses the <parameters> part of the template prefix. // Here we are pointing at the initial < (but the token chain has been emptied by cxxParserParseTemplatePrefix()) // static boolean cxxParserParseTemplatePrefixAngleBrackets(void) { CXX_DEBUG_ENTER(); // Here we have the big prolem of <> characters which may be // template argument delimiters, less than/greater than operators, shift left/right operators. // // A well written code will have parentheses around all the ambiguous cases. We handle that // and we permit any kind of syntax inside a parenthesis. // // Without parentheses we still try to handle the << and >> shift operator cases: // - << is always recognized as shift operator // - >> is recognized as shift unless it's non-nested. This is what C++11 spec says // and theoretically it should be also pseudo-compatible with C++03 which treats this // case as a syntax error. // // The 'less-than' and 'greater-than' operators are hopeless in the general case: gcc // is smart enough to figure them out by looking at the identifiers around but without // proper state (include headers, macro expansion, full type database etc) we simply can't // do the same. However, we try to recover if we figure out we (or the programmer?) screwed up. int iTemplateLevel = 0; for(;;) { // Within parentheses everything is permitted. if(!cxxParserParseAndCondenseSubchainsUpToOneOf( CXXTokenTypeGreaterThanSign | CXXTokenTypeSmallerThanSign | CXXTokenTypeOpeningBracket | CXXTokenTypeSemicolon | CXXTokenTypeEOF | CXXTokenTypeAssignment, CXXTokenTypeOpeningParenthesis | CXXTokenTypeOpeningSquareParenthesis )) { CXX_DEBUG_LEAVE_TEXT("Failed to parse up to '<>{EOF'"); return FALSE; } evaluate_current_token: switch(g_cxx.pToken->eType) { case CXXTokenTypeSmallerThanSign: if(g_cxx.pToken->pPrev && cxxTokenTypeIsOneOf(g_cxx.pToken->pPrev,CXXTokenTypeIdentifier | CXXTokenTypeKeyword)) { if(!cxxParserParseNextToken()) { CXX_DEBUG_LEAVE_TEXT("Syntax error, but tolerate it at this level"); return TRUE; } CXX_DEBUG_PRINT("Got token '%s' of type 0x%02x",vStringValue(g_cxx.pToken->pszWord),g_cxx.pToken->eType); if(cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeSmallerThanSign)) { // assume it's an operator CXX_DEBUG_PRINT("Treating < as shift-left operator"); } else { CXX_DEBUG_PRINT("Increasing template level"); iTemplateLevel++; if(cxxTokenTypeIsOneOf(g_cxx.pToken,CXXTokenTypeOpeningParenthesis | CXXTokenTypeOpeningSquareParenthesis)) { CXX_DEBUG_PRINT("Condensing subchain"); // hmmm.. must condense as subchain if(!cxxParserParseAndCondenseCurrentSubchain(CXXTokenTypeOpeningParenthesis | CXXTokenTypeOpeningSquareParenthesis,TRUE)) { CXX_DEBUG_LEAVE_TEXT("Failed to condense current subchain"); return TRUE; } if(cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeEOF)) { CXX_DEBUG_LEAVE_TEXT("Found EOF, syntax error but we tolerate it"); return TRUE; } } else { // it's ok CXX_DEBUG_PRINT("No need to condense subchain"); } } } else { // assume it's an operator CXX_DEBUG_PRINT("Treating < as less-than operator"); } break; case CXXTokenTypeGreaterThanSign: if(iTemplateLevel == 0) { // Non-nested > : always a terminator cxxTokenChainDestroyLast(g_cxx.pTokenChain); CXX_DEBUG_LEAVE_TEXT("Found end of template"); return TRUE; } // Nested > : is is a shift operator? if(!cxxParserParseNextToken()) { CXX_DEBUG_LEAVE_TEXT("Syntax error, but tolerate it at this level"); return TRUE; } CXX_DEBUG_PRINT("Got token '%s' of type 0x%02x",vStringValue(g_cxx.pToken->pszWord),g_cxx.pToken->eType); if(cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeGreaterThanSign)) { // assume it's an operator CXX_DEBUG_PRINT("Treating > as shift-right operator"); } else { CXX_DEBUG_PRINT("Decreasing template level"); iTemplateLevel--; if(cxxTokenTypeIsOneOf(g_cxx.pToken,CXXTokenTypeOpeningParenthesis | CXXTokenTypeOpeningSquareParenthesis)) { CXX_DEBUG_PRINT("Condensing subchain"); // hmmm.. must condense as subchain if(!cxxParserParseAndCondenseCurrentSubchain(CXXTokenTypeOpeningParenthesis | CXXTokenTypeOpeningSquareParenthesis,TRUE)) { CXX_DEBUG_LEAVE_TEXT("Failed to condense current subchain"); return TRUE; } if(cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeEOF)) { CXX_DEBUG_LEAVE_TEXT("Found EOF, syntax error but we tolerate it"); return TRUE; } } else { // it's ok CXX_DEBUG_PRINT("No need to condense subchain"); } } break; case CXXTokenTypeAssignment: CXX_DEBUG_PRINT("Found assignment, trying to skip up to a 'notable' point"); // try to skip to the next > or , without stopping at < characters. if(!cxxParserParseUpToOneOf( CXXTokenTypeGreaterThanSign | CXXTokenTypeComma | CXXTokenTypeOpeningBracket | CXXTokenTypeSemicolon | CXXTokenTypeEOF )) { CXX_DEBUG_LEAVE_TEXT("Failed to parse up to '}EOF'"); return FALSE; } goto evaluate_current_token; // backward jump! break; case CXXTokenTypeEOF: CXX_DEBUG_LEAVE_TEXT("Syntax error, but tolerate it at this level"); return TRUE; break; case CXXTokenTypeSemicolon: cxxParserNewStatement(); CXX_DEBUG_LEAVE_TEXT("Broken template arguments, attempting to continue"); return TRUE; break; case CXXTokenTypeOpeningBracket: CXX_DEBUG_PRINT("Found opening bracket: either syntax error or we screwed up parsing the template parameters (some kind of ugly C++11 syntax?), but we try to recover..."); // skip the whole bracketed part. if(!cxxParserParseUpToOneOf(CXXTokenTypeClosingBracket | CXXTokenTypeEOF)) { CXX_DEBUG_LEAVE_TEXT("Failed to parse up to '}EOF'"); return FALSE; } cxxParserNewStatement(); CXX_DEBUG_LEAVE_TEXT("Broken template arguments recovery complete"); return TRUE; break; default: CXX_DEBUG_ASSERT(FALSE,"Should not end up here"); CXX_DEBUG_LEAVE_TEXT("Found unexpected token type 0x%02x",g_cxx.pToken->eType); return FALSE; break; } } // never reached CXX_DEBUG_LEAVE(); return TRUE; }
boolean cxxParserParseNextToken(void) { CXXToken * t = cxxTokenCreate(); // The token chain should not be allowed to grow arbitrairly large. // The token structures are quite big and it's easy to grow up to // 5-6GB or memory usage. However this limit should be large enough // to accomodate all the reasonable statements that could have some // information in them. This includes multiple function prototypes // in a single statement (ImageMagick has some examples) but probably // does NOT include large data tables. if(g_cxx.pTokenChain->iCount > 16384) cxxTokenChainDestroyLast(g_cxx.pTokenChain); cxxTokenChainAppend(g_cxx.pTokenChain,t); g_cxx.pToken = t; cxxParserSkipToNonWhiteSpace(); // FIXME: this cpp handling is kind of broken: // it works only because the moon is in the correct phase. cppBeginStatement(); // This must be done after getting char from input t->iLineNumber = getInputLineNumber(); t->oFilePosition = getInputFilePosition(); if(g_cxx.iChar == EOF) { t->eType = CXXTokenTypeEOF; t->bFollowedBySpace = FALSE; return FALSE; } unsigned int uInfo = (g_cxx.iChar < 0x80) ? g_aCharTable[g_cxx.iChar].uType : 0; //printf("Char %c %02x info %u\n",g_cxx.iChar,g_cxx.iChar,uInfo); if(uInfo & CXXCharTypeStartOfIdentifier) { // word t->eType = CXXTokenTypeIdentifier; t->bFollowedBySpace = FALSE; vStringPut(t->pszWord,g_cxx.iChar); // special case for tile, which may actually be an operator if(g_cxx.iChar == '~') { // may be followed by space! g_cxx.iChar = cppGetc(); if(isspace(g_cxx.iChar)) { t->bFollowedBySpace = TRUE; g_cxx.iChar = cppGetc(); while(isspace(g_cxx.iChar)) g_cxx.iChar = cppGetc(); } // non space uInfo = (g_cxx.iChar < 0x80) ? g_aCharTable[g_cxx.iChar].uType : 0; if(!(uInfo & CXXCharTypeStartOfIdentifier)) { // this is not an identifier after all t->eType = CXXTokenTypeOperator; if((!t->bFollowedBySpace) && g_cxx.iChar == '=') { // make ~= single token so it's not handled as // a separate assignment vStringPut(t->pszWord,g_cxx.iChar); g_cxx.iChar = cppGetc(); t->bFollowedBySpace = isspace(g_cxx.iChar); } return TRUE; } } else { g_cxx.iChar = cppGetc(); } for(;;) { uInfo = (g_cxx.iChar < 0x80) ? g_aCharTable[g_cxx.iChar].uType : 0; if(!(uInfo & CXXCharTypePartOfIdentifier)) break; vStringPut(t->pszWord,g_cxx.iChar); g_cxx.iChar = cppGetc(); } int iCXXKeyword = lookupKeyword(t->pszWord->buffer,g_cxx.eLanguage); if(iCXXKeyword >= 0) { if( ( (iCXXKeyword == CXXKeywordFINAL) && (!g_cxx.bParsingClassStructOrUnionDeclaration) ) || ( ( (iCXXKeyword == CXXKeywordPUBLIC) || (iCXXKeyword == CXXKeywordPROTECTED) || (iCXXKeyword == CXXKeywordPRIVATE) ) && (!g_cxx.bEnablePublicProtectedPrivateKeywords) ) ) { t->eType = CXXTokenTypeIdentifier; } else { t->eType = CXXTokenTypeKeyword; t->eKeyword = (enum CXXKeyword)iCXXKeyword; if(iCXXKeyword == CXXKeyword__ATTRIBUTE__) { // special handling for __attribute__ return cxxParserParseNextTokenCondenseAttribute(); } } } else { boolean bIgnoreParens = FALSE; const char * szReplacement = NULL; if(isIgnoreToken( vStringValue(t->pszWord), &bIgnoreParens, &szReplacement )) { CXX_DEBUG_PRINT("Ignore token %s",vStringValue(t->pszWord)); // FIXME: Handle ignore parens! if(szReplacement && *szReplacement) { vStringClear(t->pszWord); vStringCatS(t->pszWord,szReplacement); } else { // skip cxxTokenChainDestroyLast(g_cxx.pTokenChain); return cxxParserParseNextToken(); } } } t->bFollowedBySpace = t->bFollowedBySpace | isspace(g_cxx.iChar); return TRUE; } if(g_cxx.iChar == '-') { // special case for pointer vStringPut(t->pszWord,g_cxx.iChar); g_cxx.iChar = cppGetc(); if(g_cxx.iChar == '>') { t->eType = CXXTokenTypePointerOperator; vStringPut(t->pszWord,g_cxx.iChar); g_cxx.iChar = cppGetc(); } else { t->eType = CXXTokenTypeOperator; if(g_cxx.iChar == '-') { vStringPut(t->pszWord,g_cxx.iChar); g_cxx.iChar = cppGetc(); } } t->bFollowedBySpace = isspace(g_cxx.iChar); return TRUE; } #if 0 // As long as we use cppGetc() we don't need this if(g_cxx.iChar == '"') { // special case for strings t->eType = CXXTokenTypeStringConstant; vStringPut(t->pszWord,g_cxx.iChar); // We don't even care of storing the other chars: we don't need // them for parsing // FIXME: We might need them in signature:() tag.. maybe add // them up to a certain length only? for(;;) { g_cxx.iChar = cppGetc(); if(g_cxx.iChar == EOF) { t->bFollowedBySpace = FALSE; return TRUE; } if(g_cxx.iChar == '\\') { // escape g_cxx.iChar = cppGetc(); if(g_cxx.iChar == EOF) { t->bFollowedBySpace = FALSE; return TRUE; } } else if(g_cxx.iChar == '"') { g_cxx.iChar = cppGetc(); break; } } t->bFollowedBySpace = isspace(g_cxx.iChar); return TRUE; } #else if(g_cxx.iChar == STRING_SYMBOL) { t->eType = CXXTokenTypeStringConstant; vStringPut(t->pszWord,'"'); vStringPut(t->pszWord,'"'); g_cxx.iChar = cppGetc(); t->bFollowedBySpace = isspace(g_cxx.iChar); return TRUE; } #endif #if 0 // As long as we use cppGetc() we don't need this if(g_cxx.iChar == '\'') { // special case for strings t->eType = CXXTokenTypeCharacterConstant; vStringPut(t->pszWord,g_cxx.iChar); // We don't even care storing the other chars: we don't // need them for parsing for(;;) { g_cxx.iChar = cppGetc(); if(g_cxx.iChar == EOF) { t->bFollowedBySpace = FALSE; return TRUE; } if(g_cxx.iChar == '\\') { // escape g_cxx.iChar = cppGetc(); if(g_cxx.iChar == EOF) { t->bFollowedBySpace = FALSE; return TRUE; } } else if(g_cxx.iChar == '\'') { g_cxx.iChar = cppGetc(); break; } } t->bFollowedBySpace = isspace(g_cxx.iChar); return TRUE; } #else if(g_cxx.iChar == CHAR_SYMBOL) { t->eType = CXXTokenTypeCharacterConstant; vStringPut(t->pszWord,'\''); vStringPut(t->pszWord,'\''); g_cxx.iChar = cppGetc(); t->bFollowedBySpace = isspace(g_cxx.iChar); return TRUE; } #endif if(uInfo & CXXCharTypeDecimalDigit) { // number t->eType = CXXTokenTypeNumber; vStringPut(t->pszWord,g_cxx.iChar); for(;;) { g_cxx.iChar = cppGetc(); uInfo = (g_cxx.iChar < 0x80) ? g_aCharTable[g_cxx.iChar].uType : 0; if(!(uInfo & CXXCharTypeValidInNumber)) break; vStringPut(t->pszWord,g_cxx.iChar); } t->bFollowedBySpace = isspace(g_cxx.iChar); return TRUE; } if(uInfo & CXXCharTypeNamedSingleOrRepeatedCharToken) { t->eType = g_aCharTable[g_cxx.iChar].uSingleTokenType; vStringPut(t->pszWord,g_cxx.iChar); int iChar = g_cxx.iChar; g_cxx.iChar = cppGetc(); if(g_cxx.iChar == iChar) { t->eType = g_aCharTable[g_cxx.iChar].uMultiTokenType; // We could signal a syntax error with more than two colons // or equal signs...but we're tolerant do { vStringPut(t->pszWord,g_cxx.iChar); g_cxx.iChar = cppGetc(); } while(g_cxx.iChar == iChar); } t->bFollowedBySpace = isspace(g_cxx.iChar); return TRUE; } if(uInfo & CXXCharTypeNamedSingleOrOperatorToken) { t->eType = g_aCharTable[g_cxx.iChar].uSingleTokenType; vStringPut(t->pszWord,g_cxx.iChar); g_cxx.iChar = cppGetc(); uInfo = (g_cxx.iChar < 0x80) ? g_aCharTable[g_cxx.iChar].uType : 0; if(uInfo & (CXXCharTypeOperator | CXXCharTypeNamedSingleOrOperatorToken)) { t->eType = CXXTokenTypeOperator; do { vStringPut(t->pszWord,g_cxx.iChar); g_cxx.iChar = cppGetc(); uInfo = (g_cxx.iChar < 0x80) ? g_aCharTable[g_cxx.iChar].uType : 0; } while( uInfo & (CXXCharTypeOperator | CXXCharTypeNamedSingleOrOperatorToken) ); } t->bFollowedBySpace = isspace(g_cxx.iChar); return TRUE; } if(uInfo & CXXCharTypeNamedSingleCharToken) { t->eType = g_aCharTable[g_cxx.iChar].uSingleTokenType; vStringPut(t->pszWord,g_cxx.iChar); g_cxx.iChar = cppGetc(); t->bFollowedBySpace = isspace(g_cxx.iChar); return TRUE; } if(uInfo & CXXCharTypeOperator) { t->eType = CXXTokenTypeOperator; vStringPut(t->pszWord,g_cxx.iChar); g_cxx.iChar = cppGetc(); uInfo = (g_cxx.iChar < 0x80) ? g_aCharTable[g_cxx.iChar].uType : 0; while(uInfo & CXXCharTypeOperator) { vStringPut(t->pszWord,g_cxx.iChar); g_cxx.iChar = cppGetc(); uInfo = (g_cxx.iChar < 0x80) ? g_aCharTable[g_cxx.iChar].uType : 0; } t->bFollowedBySpace = isspace(g_cxx.iChar); return TRUE; } t->eType = CXXTokenTypeUnknown; vStringPut(t->pszWord,g_cxx.iChar); g_cxx.iChar = cppGetc(); t->bFollowedBySpace = isspace(g_cxx.iChar); return TRUE; }
static bool cxxParserParseBlockInternal(bool bExpectClosingBracket) { CXX_DEBUG_ENTER(); //char * szScopeName = cxxScopeGetFullName(); //CXX_DEBUG_PRINT("Scope name is '%s'",szScopeName ? szScopeName : ""); cxxParserNewStatement(); if(bExpectClosingBracket) { // FIXME: this cpp handling is kind of broken: // it works only because the moon is in the correct phase. cppBeginStatement(); } for(;;) { if(!cxxParserParseNextToken()) { found_eof: if(bExpectClosingBracket) { CXX_DEBUG_LEAVE_TEXT( "Syntax error: found EOF in block but a closing " \ "bracket was expected!" ); return false; } CXX_DEBUG_LEAVE_TEXT("EOF in main block"); return true; // EOF } process_token: CXX_DEBUG_PRINT( "Token '%s' of type 0x%02x", vStringValue(g_cxx.pToken->pszWord), g_cxx.pToken->eType ); switch(g_cxx.pToken->eType) { case CXXTokenTypeKeyword: { switch(g_cxx.pToken->eKeyword) { case CXXKeywordNAMESPACE: { enum CXXScopeType eScopeType = cxxScopeGetType(); if( ( // toplevel or nested within a namespace (eScopeType == CXXScopeTypeNamespace) || // namespace X = Y inside a function (eScopeType == CXXScopeTypeFunction) ) && ( // either certainly C++ g_cxx.bConfirmedCPPLanguage || // or a "sane" namespace syntax ( !cxxTokenChainPreviousTokenOfType( g_cxx.pToken, CXXTokenTypeStar | CXXTokenTypeAnd | CXXTokenTypeKeyword ) ) ) ) { if(!cxxParserParseNamespace()) { CXX_DEBUG_LEAVE_TEXT("Failed to parse namespace"); return false; } } else { // If we're pretty sure this is C++ then this is a syntax error. // If we're not sure (namely when we're in a *.h file) then // let's try to be flexible: treat the namespace keyword as an identifier. if(!g_cxx.bConfirmedCPPLanguage) { CXX_DEBUG_LEAVE_TEXT( "Found namespace in unexpected place, but we're not sure it's really C++ " "so we'll treat it as an identifier instead" ); g_cxx.pToken->eType = CXXTokenTypeIdentifier; continue; } CXX_DEBUG_LEAVE_TEXT( "Found namespace in a wrong place: we're probably out of sync" ); return false; } cxxParserNewStatement(); } break; case CXXKeywordTEMPLATE: if(!cxxParserParseTemplatePrefix()) { CXX_DEBUG_LEAVE_TEXT("Failed to parse template"); return false; } // Here we are just after the "template<parameters>" prefix. break; case CXXKeywordTYPEDEF: // Mark the next declaration as a typedef g_cxx.uKeywordState |= CXXParserKeywordStateSeenTypedef; cxxTokenChainClear(g_cxx.pTokenChain); break; case CXXKeywordENUM: if(!cxxParserParseEnum()) { CXX_DEBUG_LEAVE_TEXT("Failed to parse enum"); return false; } break; case CXXKeywordCLASS: if(!cxxParserParseClassStructOrUnion(CXXKeywordCLASS,CXXTagCPPKindCLASS,CXXScopeTypeClass)) { CXX_DEBUG_LEAVE_TEXT("Failed to parse class/struct/union"); return false; } break; case CXXKeywordSTRUCT: if(!cxxParserParseClassStructOrUnion(CXXKeywordSTRUCT,CXXTagKindSTRUCT,CXXScopeTypeStruct)) { CXX_DEBUG_LEAVE_TEXT("Failed to parse class/struct/union"); return false; } break; case CXXKeywordUNION: if(!cxxParserParseClassStructOrUnion(CXXKeywordUNION,CXXTagKindUNION,CXXScopeTypeUnion)) { CXX_DEBUG_LEAVE_TEXT("Failed to parse class/struct/union"); return false; } break; case CXXKeywordPUBLIC: case CXXKeywordPROTECTED: case CXXKeywordPRIVATE: // Note that the class keyword has its own handler // so the only possibility here is an access specifier if(!cxxParserParseAccessSpecifier()) { CXX_DEBUG_LEAVE_TEXT("Failed to parse access specifier"); return false; } break; case CXXKeywordUSING: if(!cxxParserParseUsingClause()) { CXX_DEBUG_LEAVE_TEXT("Failed to parse using clause"); return false; } cxxParserNewStatement(); break; case CXXKeywordIF: case CXXKeywordFOR: case CXXKeywordWHILE: case CXXKeywordSWITCH: if(!cxxParserParseIfForWhileSwitch()) { CXX_DEBUG_LEAVE_TEXT("Failed to parse if/for/while/switch"); return false; } cxxParserNewStatement(); // Force the cpp preprocessor to think that we're in the middle of a statement. cppBeginStatement(); break; case CXXKeywordTRY: case CXXKeywordELSE: case CXXKeywordDO: // parse as normal statement/block cxxParserNewStatement(); // Force the cpp preprocessor to think that we're in the middle of a statement. cppBeginStatement(); break; case CXXKeywordRETURN: if(cxxParserCurrentLanguageIsCPP()) { // may be followed by a lambda, otherwise it's not interesting. cxxParserNewStatement(); g_cxx.uKeywordState |= CXXParserKeywordStateSeenReturn; } else { // ignore if(!cxxParserParseUpToOneOf(CXXTokenTypeSemicolon | CXXTokenTypeEOF, false)) { CXX_DEBUG_LEAVE_TEXT("Failed to parse return"); return false; } cxxParserNewStatement(); } break; case CXXKeywordCONTINUE: case CXXKeywordBREAK: case CXXKeywordGOTO: // ignore if(!cxxParserParseUpToOneOf(CXXTokenTypeSemicolon | CXXTokenTypeEOF, false)) { CXX_DEBUG_LEAVE_TEXT("Failed to parse continue/break/goto"); return false; } cxxParserNewStatement(); break; case CXXKeywordTHROW: // ignore when inside a function if(cxxScopeGetType() == CXXScopeTypeFunction) { if(!cxxParserParseUpToOneOf(CXXTokenTypeSemicolon | CXXTokenTypeEOF, false)) { CXX_DEBUG_LEAVE_TEXT("Failed to parse return/continue/break"); return false; } cxxParserNewStatement(); } break; case CXXKeywordCASE: // ignore if(!cxxParserParseUpToOneOf( CXXTokenTypeSemicolon | CXXTokenTypeEOF | CXXTokenTypeSingleColon, false )) { CXX_DEBUG_LEAVE_TEXT("Failed to parse case keyword"); return false; } cxxParserNewStatement(); break; case CXXKeywordEXTERN: g_cxx.uKeywordState |= CXXParserKeywordStateSeenExtern; cxxTokenChainDestroyLast(g_cxx.pTokenChain); if(!cxxParserParseNextToken()) goto found_eof; if(cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeStringConstant)) { // assume extern "language" // Strictly speaking this is a C++ only syntax. // However we allow it also in C as it doesn't really hurt. cxxTokenChainDestroyLast(g_cxx.pTokenChain); // Note that extern "C" may be followed by a block with declarations // // extern "C" { ... } // // However in this case the declarations are ALSO definitions // and extern "C" is used only to specify the name mangling mode. // // extern "C" int x; <-- a declaration and not a definition // extern "C" { int x; } <-- a declaration and definition: x IS defined // here and is NOT extern. // // A variable in an extern "C" block has to be re-declared extern again // to be really treated as declaration only. // // extern "C" { extern int x; } // // So in this case we do NOT treat the inner declarations as extern // and we don't need specific handling code for this case. } else { // something else: handle it the normal way goto process_token; } break; case CXXKeywordSTATIC: g_cxx.uKeywordState |= CXXParserKeywordStateSeenStatic; cxxTokenChainDestroyLast(g_cxx.pTokenChain); break; case CXXKeywordINLINE: case CXXKeyword__INLINE: case CXXKeyword__INLINE__: case CXXKeyword__FORCEINLINE: case CXXKeyword__FORCEINLINE__: g_cxx.uKeywordState |= CXXParserKeywordStateSeenInline; cxxTokenChainDestroyLast(g_cxx.pTokenChain); break; case CXXKeywordEXPLICIT: g_cxx.uKeywordState |= CXXParserKeywordStateSeenExplicit; cxxTokenChainDestroyLast(g_cxx.pTokenChain); break; case CXXKeywordOPERATOR: g_cxx.uKeywordState |= CXXParserKeywordStateSeenOperator; break; case CXXKeywordVIRTUAL: g_cxx.uKeywordState |= CXXParserKeywordStateSeenVirtual; cxxTokenChainDestroyLast(g_cxx.pTokenChain); break; case CXXKeywordMUTABLE: g_cxx.uKeywordState |= CXXParserKeywordStateSeenMutable; cxxTokenChainDestroyLast(g_cxx.pTokenChain); break; // "const" and "volatile" are part of the type. Don't treat them specially // and don't attempt to extract an eventual typedef yet, // as there might be a struct/class/union keyword following. case CXXKeywordVOLATILE: g_cxx.uKeywordState |= CXXParserKeywordStateSeenVolatile; break; case CXXKeywordCONST: g_cxx.uKeywordState |= CXXParserKeywordStateSeenConst; break; default: if(g_cxx.uKeywordState & CXXParserKeywordStateSeenTypedef) { g_cxx.uKeywordState &= ~CXXParserKeywordStateSeenTypedef; if(!cxxParserParseGenericTypedef()) { CXX_DEBUG_LEAVE_TEXT("Failed to parse generic typedef"); return false; } cxxParserNewStatement(); } break; } } break; case CXXTokenTypeSemicolon: { if( (cxxParserCurrentLanguageIsC()) && cxxScopeIsGlobal() && (!(g_cxx.uKeywordState & CXXParserKeywordStateSeenExtern)) && (!(g_cxx.uKeywordState & CXXParserKeywordStateSeenTypedef)) ) { // Special handling of K&R style function declarations. // We might be in the following situation: // // type whatever fname(par1,par2) int par1; int par2; { // ^ // switch(cxxParserMaybeParseKnRStyleFunctionDefinition()) { case 1: // K&R parser did the job and started a new statement break; case 0: // something else cxxParserAnalyzeOtherStatement(); break; default: CXX_DEBUG_LEAVE_TEXT("Failed to check for K&R style function definition"); return false; break; } } else { // K&R style function declarations not allowed here. cxxParserAnalyzeOtherStatement(); } cxxParserNewStatement(); } break; case CXXTokenTypeSingleColon: { // label ? if( (g_cxx.pTokenChain->iCount == 2) && cxxTokenTypeIs( cxxTokenChainFirst(g_cxx.pTokenChain), CXXTokenTypeIdentifier ) ) { CXXToken * pFirst = cxxTokenChainFirst(g_cxx.pTokenChain); // assume it's label tagEntryInfo * tag = cxxTagBegin(CXXTagKindLABEL,pFirst); if(tag) { tag->isFileScope = true; cxxTagCommit(); } } else { // what is this? (default: and similar things have been handled at keyword level) } } break; case CXXTokenTypeOpeningBracket: if(!cxxParserParseBlockHandleOpeningBracket()) { CXX_DEBUG_LEAVE_TEXT("Failed to handle opening bracket"); return false; } break; case CXXTokenTypeClosingBracket: // scope finished if(!bExpectClosingBracket) { CXX_DEBUG_LEAVE_TEXT( "Found unexpected closing bracket: probably preprocessing problem" ); return false; } CXX_DEBUG_LEAVE_TEXT("Closing bracket!"); cxxParserNewStatement(); return true; break; case CXXTokenTypeOpeningParenthesis: case CXXTokenTypeOpeningSquareParenthesis: if(!cxxParserParseAndCondenseCurrentSubchain( CXXTokenTypeOpeningBracket | CXXTokenTypeOpeningParenthesis | CXXTokenTypeOpeningSquareParenthesis, true, false )) { CXX_DEBUG_LEAVE_TEXT("Parsing the parenthesis failed"); return false; } if(cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeEOF)) { if(bExpectClosingBracket) { CXX_DEBUG_LEAVE_TEXT( "Syntax error: found EOF in block but a closing bracket was expected!" ); return false; } return true; // EOF } break; case CXXTokenTypeIdentifier: if(g_cxx.uKeywordState & CXXParserKeywordStateSeenTypedef) { g_cxx.uKeywordState &= ~CXXParserKeywordStateSeenTypedef; if(!cxxParserParseGenericTypedef()) { CXX_DEBUG_LEAVE_TEXT("Failed to parse generic typedef"); return false; } cxxParserNewStatement(); } break; default: // something else we didn't handle break; } } CXX_DEBUG_LEAVE_TEXT("WARNING: Not reached"); return true; }
// // The __attribute__((...)) sequence complicates parsing quite a lot. // For this reason we attempt to "hide" it from the rest of the parser // at tokenizer level. // // Returns false if it finds an EOF. This is an important invariant required by // cxxParserParseNextToken(), the only caller. // static bool cxxParserParseNextTokenCondenseAttribute(void) { // Since cxxParserParseNextToken() returns false only when it has found // an EOF, this function must do the same. // This means that any broken input must be discarded here. CXX_DEBUG_ENTER(); CXX_DEBUG_ASSERT( cxxTokenIsKeyword(g_cxx.pToken,CXXKeyword__ATTRIBUTE__), "This function should be called only after we have parsed __attribute__" ); // Kill it cxxTokenChainDestroyLast(g_cxx.pTokenChain); // And go ahead. if(!cxxParserParseNextToken()) { CXX_DEBUG_LEAVE_TEXT("No next token after __attribute__"); return false; } if(!cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeOpeningParenthesis)) { CXX_DEBUG_LEAVE_TEXT("Something that is not an opening parenthesis"); return true; } // Do NOT accept EOF as a valid terminator as it implies broken input. if(!cxxParserParseAndCondenseCurrentSubchain( CXXTokenTypeOpeningParenthesis | CXXTokenTypeOpeningSquareParenthesis | CXXTokenTypeOpeningBracket, false, false )) { // Pasing and/or condensation of the subchain failed. This implies broken // input (mismatched parenthesis/bracket, early EOF). CXX_DEBUG_LEAVE_TEXT("Failed to parse subchains. The input is broken..."); // However our invariant (see comment at the beginning of the function) // forbids us to return false if we didn't find an EOF. So we attempt // to resume parsing anyway. If there is an EOF, cxxParserParseNextToken() // will report it. // Kill the token chain cxxTokenChainDestroyLast(g_cxx.pTokenChain); return cxxParserParseNextToken(); } CXX_DEBUG_ASSERT( cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeParenthesisChain), "Should have a parenthesis chain as last token!" ); // Try to make sense of certain kinds of __attribute__. // the proper syntax is __attribute__(()), so look at the inner chain CXXToken * pInner = cxxTokenChainFirst(g_cxx.pToken->pChain); if(pInner) { if(pInner->pNext && cxxTokenTypeIs(pInner->pNext,CXXTokenTypeParenthesisChain)) pInner = cxxTokenChainFirst(pInner->pNext->pChain); while(pInner) { if(cxxTokenTypeIs(pInner,CXXTokenTypeIdentifier)) { CXX_DEBUG_PRINT("Analyzing attribyte %s",vStringValue(pInner->pszWord)); if( (strcmp(vStringValue(pInner->pszWord),"always_inline") == 0) || (strcmp(vStringValue(pInner->pszWord),"__always_inline__") == 0) ) { CXX_DEBUG_PRINT("Found attribute 'always_inline'"); // assume "inline" has been seen. g_cxx.uKeywordState |= CXXParserKeywordStateSeenInline; } else if( (strcmp(vStringValue(pInner->pszWord),"deprecated") == 0) || (strcmp(vStringValue(pInner->pszWord),"__deprecated__") == 0) ) { CXX_DEBUG_PRINT("Found attribute 'deprecated'"); // assume "inline" has been seen. g_cxx.uKeywordState |= CXXParserKeywordStateSeenAttributeDeprecated; } } // If needed, we could attach certain attributes to previous // identifiers. But note that __attribute__ may apply to a // following identifier too. pInner = pInner->pNext; } } // Now just kill the chain. cxxTokenChainDestroyLast(g_cxx.pTokenChain); // And finally extract yet another token. CXX_DEBUG_LEAVE(); return cxxParserParseNextToken(); }
// // This function parses input until one of the specified // tokens appears. The algorithm will also build subchains of matching // pairs ([...],(...),<...>,{...}): within the subchain analysis // of uTokenTypes is completly disabled. Subchains do nest. // // Returns true if it stops before EOF or it stops at EOF and CXXTokenTypeEOF // is present in uTokenTypes. Returns false in all the other stop conditions // and when an unmatched subchain character pair is found (syntax error). // boolean cxxParserParseAndCondenseSubchainsUpToOneOf( unsigned int uTokenTypes, unsigned int uInitialSubchainMarkerTypes ) { CXX_DEBUG_ENTER_TEXT("Token types = 0x%x",uTokenTypes); if(!cxxParserParseNextToken()) { CXX_DEBUG_LEAVE_TEXT("Found EOF"); return (uTokenTypes & CXXTokenTypeEOF); // was already at EOF } // see the declaration of CXXTokenType enum. // Shifting by 4 gives the corresponding closing token type unsigned int uFinalSubchainMarkerTypes = uInitialSubchainMarkerTypes << 4; for(;;) { //CXX_DEBUG_PRINT( // "Current token is '%s' 0x%x", // vStringValue(g_cxx.pToken->pszWord), // g_cxx.pToken->eType //); if(cxxTokenTypeIsOneOf(g_cxx.pToken,uTokenTypes)) { CXX_DEBUG_LEAVE_TEXT( "Got terminator token '%s' 0x%x", vStringValue(g_cxx.pToken->pszWord), g_cxx.pToken->eType ); return TRUE; } if(cxxTokenTypeIsOneOf(g_cxx.pToken,uInitialSubchainMarkerTypes)) { // subchain CXX_DEBUG_PRINT( "Got subchain start token '%s' 0x%x", vStringValue(g_cxx.pToken->pszWord), g_cxx.pToken->eType ); CXXToken * pParenthesis; if( cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeOpeningBracket) && cxxParserCurrentLanguageIsCPP() && (pParenthesis = cxxParserOpeningBracketIsLambda()) ) { if(!cxxParserHandleLambda(pParenthesis)) { CXX_DEBUG_LEAVE_TEXT("Failed to handle lambda"); return FALSE; } } else { if(!cxxParserParseAndCondenseCurrentSubchain( uInitialSubchainMarkerTypes, (uTokenTypes & CXXTokenTypeEOF) ) ) { CXX_DEBUG_LEAVE_TEXT( "Failed to parse subchain of type 0x%x", g_cxx.pToken->eType ); return FALSE; } } if(cxxTokenTypeIsOneOf(g_cxx.pToken,uTokenTypes)) { // was looking for a subchain CXX_DEBUG_LEAVE_TEXT( "Got terminator subchain token 0x%x", g_cxx.pToken->eType ); return TRUE; } if(!cxxParserParseNextToken()) { CXX_DEBUG_LEAVE_TEXT("Found EOF(2)"); return (uTokenTypes & CXXTokenTypeEOF); // was already at EOF } continue; // jump up to avoid checking for mismatched pairs below } // Check for mismatched brackets/parenthis // Note that if we were looking for one of [({ then we would have matched // it at the top of the for if(cxxTokenTypeIsOneOf(g_cxx.pToken,uFinalSubchainMarkerTypes)) { CXX_DEBUG_LEAVE_TEXT( "Got mismatched subchain terminator 0x%x", g_cxx.pToken->eType ); return FALSE; // unmatched: syntax error } if(!cxxParserParseNextToken()) { CXX_DEBUG_LEAVE_TEXT("Found EOF(3)"); return (uTokenTypes & CXXTokenTypeEOF); // was already at EOF } } // not reached CXX_DEBUG_LEAVE_TEXT("Internal error"); return FALSE; }
bool cxxParserParseBlockHandleOpeningBracket(void) { CXX_DEBUG_ENTER(); CXX_DEBUG_ASSERT( g_cxx.pToken->eType == CXXTokenTypeOpeningBracket, "This must be called when pointing at an opening bracket!" ); enum CXXScopeType eScopeType = cxxScopeGetType(); bool bIsCPP = cxxParserCurrentLanguageIsCPP(); CXXToken * pAux; if( ( // something = {...} (g_cxx.pToken->pPrev) && cxxTokenTypeIs(g_cxx.pToken->pPrev,CXXTokenTypeAssignment) && ( (eScopeType == CXXScopeTypeFunction) || (eScopeType == CXXScopeTypeNamespace) ) ) || ( bIsCPP && (g_cxx.pToken->pPrev) && ( ( // T { arg1, arg2, ... } (1) // T object { arg1, arg2, ... } (2) // new T { arg1, arg2, ... } (3) // Class::Class() : member { arg1, arg2, ... } { (4) cxxTokenTypeIs(g_cxx.pToken->pPrev,CXXTokenTypeIdentifier) && ( // case 1 (!g_cxx.pToken->pPrev->pPrev) || // case 4 cxxTokenTypeIsOneOf( g_cxx.pToken->pPrev->pPrev, CXXTokenTypeSingleColon | CXXTokenTypeComma ) || // cases 1,2,3 but not 4 ( // more parts of typename or maybe the "new" keyword before the identifier cxxTokenTypeIsOneOf( g_cxx.pToken->pPrev->pPrev, CXXTokenTypeIdentifier | CXXTokenTypeStar | CXXTokenTypeAnd | CXXTokenTypeGreaterThanSign | CXXTokenTypeKeyword ) && // but no parenthesis (discard things like bool test() Q_DECL_NO_THROW { ... }) (!(pAux = cxxTokenChainPreviousTokenOfType( g_cxx.pToken->pPrev->pPrev, CXXTokenTypeParenthesisChain )) ) ) ) && // "override" is handled as identifier since it's a keyword only after function signatures (strcmp(vStringValue(g_cxx.pToken->pPrev->pszWord),"override") != 0) ) || ( // type var[][][]..[] { ... } // (but not '[] { ... }' which is a parameterless lambda) cxxTokenTypeIs(g_cxx.pToken->pPrev,CXXTokenTypeSquareParenthesisChain) && ( pAux = cxxTokenChainPreviousTokenNotOfType( g_cxx.pToken->pPrev, CXXTokenTypeSquareParenthesisChain ) ) && cxxTokenTypeIs(pAux,CXXTokenTypeIdentifier) ) ) ) || ( // return { } (!g_cxx.pToken->pPrev) && (g_cxx.uKeywordState & CXXParserKeywordStateSeenReturn) ) ) { // array or list-like initialisation bool bRet = cxxParserParseAndCondenseCurrentSubchain( CXXTokenTypeOpeningBracket | CXXTokenTypeOpeningParenthesis | CXXTokenTypeOpeningSquareParenthesis, false, true ); CXX_DEBUG_LEAVE_TEXT("Handled array or list-like initialisation or return"); return bRet; } // In C++ mode check for lambdas CXXToken * pParenthesis; if( bIsCPP && (pParenthesis = cxxParserOpeningBracketIsLambda()) ) { if(!cxxParserHandleLambda(pParenthesis)) { CXX_DEBUG_LEAVE_TEXT("Lambda handling failed"); return false; } // Note that here we're leaving the token chain "alive" so further parsing can be performed. CXX_DEBUG_LEAVE_TEXT("Lambda handling succeeded"); return true; } int iScopes; int iCorkQueueIndex = CORK_NIL; CXXFunctionSignatureInfo oInfo; if(eScopeType != CXXScopeTypeFunction) { // very likely a function definition // (but may be also a toplevel block, like "extern "C" { ... }") iScopes = cxxParserExtractFunctionSignatureBeforeOpeningBracket(&oInfo,&iCorkQueueIndex); // FIXME: Handle syntax (5) of list initialization: // Class::Class() : member { arg1, arg2, ... } {... } else { // some kind of other block: // - anonymous block // - block after for(),while(),foreach(),if() and other similar stuff // (note that {}-style initializers have been handled above and thus are excluded) iScopes = 0; } cxxParserNewStatement(); if(!cxxParserParseBlock(true)) { CXX_DEBUG_LEAVE_TEXT("Failed to parse nested block"); return false; } if(iScopes < 1) { CXX_DEBUG_LEAVE_TEXT("The block was not a function"); return true; } unsigned long uEndPosition = getInputLineNumber(); // If the function contained a "try" keyword before the opening bracket // then it's likely to be a function-try-block and should be followed by a catch // block that is in the same scope. if(oInfo.uFlags & CXXFunctionSignatureInfoFunctionTryBlock) { // look for the catch blocks. CXX_DEBUG_PRINT("The function is a function-try-block: looking for catch blocks"); for(;;) { CXX_DEBUG_PRINT("Looking ahead for a catch block..."); if(!cxxParserParseNextToken()) break; // EOF if(!cxxTokenIsKeyword(g_cxx.pToken,CXXKeywordCATCH)) { // No more catches. Unget and exit. CXX_DEBUG_PRINT("No more catch blocks"); cxxParserUngetCurrentToken(); break; } // assume it's a catch block. CXX_DEBUG_PRINT("Found catch block"); if(!cxxParserParseIfForWhileSwitchCatchParenthesis()) { CXX_DEBUG_LEAVE_TEXT("Failed to parse the catch parenthesis"); return false; } // the standard requires a bracket here (catch block is always a compound statement). cxxParserNewStatement(); if(!cxxParserParseNextToken()) { CXX_DEBUG_LEAVE_TEXT("Found EOF while looking for catch() block: playing nice"); break; // EOF (would be a syntax error!) } if(!cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeOpeningBracket)) { // Aargh... CXX_DEBUG_LEAVE_TEXT("Found something unexpected while looking for catch() block: playing nice"); cxxParserUngetCurrentToken(); break; // (would be a syntax error!) } if(!cxxParserParseBlock(true)) return false; uEndPosition = getInputLineNumber(); } } if(iCorkQueueIndex > CORK_NIL) cxxParserSetEndLineForTagInCorkQueue(iCorkQueueIndex,uEndPosition); while(iScopes > 0) { cxxScopePop(); iScopes--; } CXX_DEBUG_LEAVE(); return true; }
// // This is the toplevel scanning function. It's a forward-only scanner that keeps // accumulating tokens in the chain until either a characteristic token is found // or the statement ends. When a characteristic token is found it usually enters // a specialized scanning routine (e.g for classes, namespaces, structs...). // When the statement ends without finding any characteristic token the chain // is passed to an analysis routine which does a second scan pass. // boolean cxxParserParseBlock(boolean bExpectClosingBracket) { CXX_DEBUG_ENTER(); //char * szScopeName = cxxScopeGetFullName(); //CXX_DEBUG_PRINT("Scope name is '%s'",szScopeName ? szScopeName : ""); cxxParserNewStatement(); if(bExpectClosingBracket) cppBeginStatement(); // FIXME: this cpp handling is broken: it works only because the moon is in the correct phase. for(;;) { if(!cxxParserParseNextToken()) { if(bExpectClosingBracket) { CXX_DEBUG_LEAVE_TEXT("Syntax error: found EOF in block but a closing bracket was expected!"); return FALSE; } CXX_DEBUG_LEAVE_TEXT("EOF in main block"); return TRUE; // EOF } CXX_DEBUG_PRINT("Token '%s' of type 0x%02x",vStringValue(g_cxx.pToken->pszWord),g_cxx.pToken->eType); switch(g_cxx.pToken->eType) { case CXXTokenTypeKeyword: { switch(g_cxx.pToken->eKeyword) { case CXXKeywordNAMESPACE: { int iCurrentScopeKind = cxxScopeGetKind(); if(iCurrentScopeKind == CXXTagKindNAMESPACE) { // namespaces can be nested only within themselves if(!cxxParserParseNamespace()) { CXX_DEBUG_LEAVE_TEXT("Failed to parse namespace"); return FALSE; } } else { // hm... syntax error? CXX_DEBUG_LEAVE_TEXT("Found namespace in a wrong place: we're probably out of sync"); return FALSE; } //cxxParserNewStatement(); <-- already called by cxxParserParseNamespace() } break; case CXXKeywordTEMPLATE: if(!cxxParserParseTemplatePrefix()) { CXX_DEBUG_LEAVE_TEXT("Failed to parse template"); return FALSE; } // Here we are just after the "template<parameters>" prefix. break; case CXXKeywordTYPEDEF: // Mark the next declaration as a typedef g_cxx.uKeywordState |= CXXParserKeywordStateSeenTypedef; cxxTokenChainClear(g_cxx.pTokenChain); break; case CXXKeywordENUM: if(!cxxParserParseEnum()) { CXX_DEBUG_LEAVE_TEXT("Failed to parse enum"); return FALSE; } break; case CXXKeywordCLASS: if(!cxxParserParseClassStructOrUnion(CXXKeywordCLASS,CXXTagKindCLASS)) { CXX_DEBUG_LEAVE_TEXT("Failed to parse class/struct/union"); return FALSE; } break; case CXXKeywordSTRUCT: if(!cxxParserParseClassStructOrUnion(CXXKeywordSTRUCT,CXXTagKindSTRUCT)) { CXX_DEBUG_LEAVE_TEXT("Failed to parse class/struct/union"); return FALSE; } break; case CXXKeywordUNION: if(!cxxParserParseClassStructOrUnion(CXXKeywordUNION,CXXTagKindUNION)) { CXX_DEBUG_LEAVE_TEXT("Failed to parse class/struct/union"); return FALSE; } break; case CXXKeywordPUBLIC: case CXXKeywordPROTECTED: case CXXKeywordPRIVATE: // Note that the class keyword has its own handler so the only possibility here is an access specifier if(!cxxParserParseAccessSpecifier()) { CXX_DEBUG_LEAVE_TEXT("Failed to parse access specifier"); return FALSE; } break; case CXXKeywordUSING: if(!cxxParserParseUsingClause()) { CXX_DEBUG_LEAVE_TEXT("Failed to parse using clause"); return FALSE; } cxxParserNewStatement(); break; case CXXKeywordIF: case CXXKeywordFOR: case CXXKeywordWHILE: case CXXKeywordSWITCH: if(!cxxParserParseIfForWhileSwitch()) { CXX_DEBUG_LEAVE_TEXT("Failed to parse if/for/while/switch"); return FALSE; } cxxParserNewStatement(); break; case CXXKeywordTRY: case CXXKeywordELSE: case CXXKeywordDO: // parse as normal statement/block cxxParserNewStatement(); break; case CXXKeywordRETURN: if(cxxParserCurrentLanguageIsCPP()) { // may be followed by a lambda, otherwise it's not interesting. g_cxx.uKeywordState |= CXXParserKeywordStateSeenReturn; cxxParserNewStatement(); } else { // ignore if(!cxxParserParseUpToOneOf(CXXTokenTypeSemicolon | CXXTokenTypeEOF)) { CXX_DEBUG_LEAVE_TEXT("Failed to parse return"); return FALSE; } cxxParserNewStatement(); } break; case CXXKeywordCONTINUE: case CXXKeywordBREAK: case CXXKeywordGOTO: // ignore if(!cxxParserParseUpToOneOf(CXXTokenTypeSemicolon | CXXTokenTypeEOF)) { CXX_DEBUG_LEAVE_TEXT("Failed to parse continue/break/goto"); return FALSE; } cxxParserNewStatement(); break; case CXXKeywordTHROW: // ignore when inside a function if(cxxScopeGetKind() == CXXTagKindFUNCTION) { if(!cxxParserParseUpToOneOf(CXXTokenTypeSemicolon | CXXTokenTypeEOF)) { CXX_DEBUG_LEAVE_TEXT("Failed to parse return/continue/break"); return FALSE; } cxxParserNewStatement(); } break; break; case CXXKeywordCASE: // ignore if(!cxxParserParseUpToOneOf(CXXTokenTypeSemicolon | CXXTokenTypeEOF | CXXTokenTypeSingleColon)) { CXX_DEBUG_LEAVE_TEXT("Failed to parse case keyword"); return FALSE; } cxxParserNewStatement(); break; case CXXKeywordEXTERN: g_cxx.uKeywordState |= CXXParserKeywordStateSeenExtern; cxxTokenChainClear(g_cxx.pTokenChain); break; case CXXKeywordSTATIC: g_cxx.uKeywordState |= CXXParserKeywordStateSeenStatic; cxxTokenChainClear(g_cxx.pTokenChain); break; case CXXKeywordINLINE: g_cxx.uKeywordState |= CXXParserKeywordStateSeenInline; cxxTokenChainClear(g_cxx.pTokenChain); break; case CXXKeywordEXPLICIT: g_cxx.uKeywordState |= CXXParserKeywordStateSeenExplicit; cxxTokenChainClear(g_cxx.pTokenChain); break; case CXXKeywordOPERATOR: g_cxx.uKeywordState |= CXXParserKeywordStateSeenOperator; break; case CXXKeywordVIRTUAL: g_cxx.uKeywordState |= CXXParserKeywordStateSeenVirtual; break; default: if(g_cxx.uKeywordState & CXXParserKeywordStateSeenTypedef) { g_cxx.uKeywordState &= ~CXXParserKeywordStateSeenTypedef; if(!cxxParserParseGenericTypedef()) { CXX_DEBUG_LEAVE_TEXT("Failed to parse generic typedef"); return FALSE; } cxxParserNewStatement(); } break; } } break; case CXXTokenTypeSemicolon: { if( (g_cxx.eLanguage == g_cxx.eCLanguage) && cxxScopeIsGlobal() && (!(g_cxx.uKeywordState & CXXParserKeywordStateSeenExtern)) && (!(g_cxx.uKeywordState & CXXParserKeywordStateSeenTypedef)) ) { // Special handling of K&R style function declarations. // We might be in the following situation: // // type whatever fname(par1,par2) int par1; int par2; { // ^ // switch(cxxParserMaybeExtractKnRStyleFunctionDefinition()) { case 1: // got K&R style function definition, one scope was pushed. cxxParserNewStatement(); if(!cxxParserParseBlock(TRUE)) { CXX_DEBUG_LEAVE_TEXT("Failed to parse nested block"); return FALSE; } cxxScopePop(); break; case 0: // something else cxxParserAnalyzeOtherStatement(); break; default: CXX_DEBUG_LEAVE_TEXT("Failed to check for K&R style function definition"); return FALSE; break; } } else { // K&R style function declarations not allowed here. cxxParserAnalyzeOtherStatement(); } cxxParserNewStatement(); } break; case CXXTokenTypeSingleColon: { // label ? if((g_cxx.pTokenChain->iCount == 2) && cxxTokenTypeIs(cxxTokenChainFirst(g_cxx.pTokenChain),CXXTokenTypeIdentifier)) { CXXToken * pFirst = cxxTokenChainFirst(g_cxx.pTokenChain); // assume it's label tagEntryInfo * tag = cxxTagBegin(CXXTagKindLABEL,pFirst); if(tag) { tag->isFileScope = TRUE; cxxTagCommit(); } } else { // what is this? (default: and similar things have been handled at keyword level) } } break; case CXXTokenTypeOpeningBracket: if(!cxxParserParseBlockHandleOpeningBracket()) { CXX_DEBUG_LEAVE_TEXT("Failed to handle opening bracket"); return FALSE; } break; case CXXTokenTypeClosingBracket: // scope finished CXX_DEBUG_LEAVE_TEXT("Closing bracket!"); cxxParserNewStatement(); return TRUE; break; case CXXTokenTypeOpeningParenthesis: case CXXTokenTypeOpeningSquareParenthesis: if(!cxxParserParseAndCondenseCurrentSubchain( CXXTokenTypeOpeningBracket | CXXTokenTypeOpeningParenthesis | CXXTokenTypeOpeningSquareParenthesis, TRUE )) { CXX_DEBUG_LEAVE_TEXT("Parsing the parenthesis failed"); return FALSE; } if(cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeEOF)) { if(bExpectClosingBracket) { CXX_DEBUG_LEAVE_TEXT("Syntax error: found EOF in block but a closing bracket was expected!"); return FALSE; } return TRUE; // EOF } break; case CXXTokenTypeIdentifier: if(g_cxx.uKeywordState & CXXParserKeywordStateSeenTypedef) { g_cxx.uKeywordState &= ~CXXParserKeywordStateSeenTypedef; if(!cxxParserParseGenericTypedef()) { CXX_DEBUG_LEAVE_TEXT("Failed to parse generic typedef"); return FALSE; } cxxParserNewStatement(); } break; default: // something else we didn't handle break; } } CXX_DEBUG_LEAVE_TEXT("WARNING: Not reached"); return TRUE; }