void cxxTokenChainCondense(CXXTokenChain * tc,unsigned int uFlags) { if(!tc) return; if(tc->iCount <= 1) return; CXXToken * pCondensed = cxxTokenCreate(); pCondensed->eType = CXXTokenTypeUnknown; pCondensed->iLineNumber = tc->pHead->iLineNumber; pCondensed->oFilePosition = tc->pHead->oFilePosition; while(tc->iCount > 0) { CXXToken * t = cxxTokenChainTakeFirst(tc); cxxTokenAppendToString(pCondensed->pszWord,t); if( (!(uFlags & CXXTokenChainCondenseNoTrailingSpaces)) && t->bFollowedBySpace ) vStringPut (pCondensed->pszWord, ' '); pCondensed->bFollowedBySpace = t->bFollowedBySpace; cxxTokenDestroy(t); } cxxTokenChainAppend(tc,pCondensed); }
void cxxScopePop(void) { CXX_DEBUG_ASSERT( g_pScope->iCount > 0, "When popping as scope there must be a scope to pop" ); cxxTokenDestroy(cxxTokenChainTakeLast(g_pScope)); g_bScopeNameDirty = true; #ifdef CXX_DO_DEBUGGING const char * szScopeName = cxxScopeGetFullName(); CXX_DEBUG_PRINT("Popped scope: '%s'",szScopeName ? szScopeName : ""); #endif }
void cxxTokenChainDestroyRange(CXXTokenChain * pChain,CXXToken * from,CXXToken * to) { if(!from || !to) return; CXX_DEBUG_ASSERT(from,"Bad from pointer passed to cxxTokenChainDestroyRange"); CXX_DEBUG_ASSERT(to,"Bad to pointer passed to cxxTokenChainDestroyRange"); for(;;) { CXXToken * next = from->pNext; cxxTokenChainTake(pChain,from); cxxTokenDestroy(from); if(from == to) // may be compared even if invalid return; from = next; CXX_DEBUG_ASSERT(from,"Should NOT have found chain termination here"); } }
void cxxTokenChainDestroy(CXXTokenChain * tc) { CXXToken * t; CXXToken * t2; if(!tc) return; t = tc->pHead; while(t) { t2 = t->pNext; cxxTokenDestroy(t); t = t2; } eFree(tc); }
void cxxTokenChainClear(CXXTokenChain * tc) { CXXToken * t; if(!tc) return; if(tc->iCount < 1) return; while((t = cxxTokenChainTakeFirst(tc))) cxxTokenDestroy(t); Assert(tc->iCount == 0); Assert(tc->pHead == NULL); Assert(tc->pTail == NULL); }
void cxxParserUngetCurrentToken(void) { CXX_DEBUG_ASSERT( g_cxx.pToken && g_cxx.pTokenChain && (g_cxx.pTokenChain->iCount > 0), "There should be at least one token to unget" ); CXX_DEBUG_ASSERT(!g_cxx.pUngetToken,"The parser supports only one unget token at a time."); if(g_cxx.pUngetToken) cxxTokenDestroy(g_cxx.pUngetToken); g_cxx.pUngetToken = cxxTokenChainTakeLast(g_cxx.pTokenChain); CXX_DEBUG_ASSERT(g_cxx.pUngetToken == g_cxx.pToken,"Oops.. ungot a token that was not the chain tail"); g_cxx.pToken = cxxTokenChainLast(g_cxx.pTokenChain); }
// 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; }
void cxxScopePop(void) { cxxTokenDestroy(cxxScopeTakeTop()); }
// // Attempt to extract variable declarations from the chain. // Returns true if at least one variable was extracted. // Returns false if a variable declaration could not be identified. // // Recognized variable declarations are of the following kinds: // // type var; // type var1,var2; // type var[]; // type var(constructor args); // type var{list initializer}; // type var = ...; // type (*ident)(); // type var:bits; // type var: range declaration <-- (FIXME: this is only inside for!) // very complex type with modifiers() namespace::namespace::var = ...; // type<with template> namespace::var[] = { // ... // // Assumptions: // - there is a terminator at the end: either ; or { // // Notes: // - Be aware that if this function returns true then the pChain very likely has been modified // (partially destroyed) as part of the type extraction algorithm. // If the function returns false the chain has not been modified (and // to extract something else from it). // // - This function is quite tricky. // bool cxxParserExtractVariableDeclarations(CXXTokenChain * pChain,unsigned int uFlags) { CXX_DEBUG_ENTER(); if(pChain->iCount < 1) { CXX_DEBUG_LEAVE_TEXT("Chain is empty"); return false; } #ifdef CXX_DO_DEBUGGING vString * pJoinedChain = cxxTokenChainJoin(pChain,NULL,0); CXX_DEBUG_PRINT( "Looking for variable declarations in '%s'", vStringValue(pJoinedChain) ); vStringDelete(pJoinedChain); #endif // // Strategy: // - verify that the chain starts with an identifier or keyword (always present) // - run to one of : ; [] () {} = , // - ensure that the previous token is an identifier (except for special cases) // - go back to skip the eventual scope // - ensure that there is a leading type // - if we are at : [], () or {} then run to the next ; = or , // - once we have determined that a variable declaration is there // modify the chain to contain only the type name // - emit variable tag // - if we are at , then check if there are more declarations // CXXToken * t = cxxTokenChainFirst(pChain); enum CXXScopeType eScopeType = cxxScopeGetType(); CXX_DEBUG_ASSERT(t,"There should be an initial token here"); if(!cxxTokenTypeIsOneOf(t,CXXTokenTypeIdentifier | CXXTokenTypeKeyword)) { CXX_DEBUG_LEAVE_TEXT("Statement does not start with identifier or keyword"); return false; } // Handle the special case of delete/new keywords at the beginning if( cxxTokenTypeIs(t,CXXTokenTypeKeyword) && ( (t->eKeyword == CXXKeywordDELETE) || (t->eKeyword == CXXKeywordNEW) ) ) { CXX_DEBUG_LEAVE_TEXT("Statement looks like a delete or new call"); return false; } bool bGotVariable = false; // Loop over the whole statement. while(t) { // Scan up to a notable token: ()[]{}=,;:{ while(t) { if(cxxTokenTypeIsOneOf( t, CXXTokenTypeSingleColon | CXXTokenTypeParenthesisChain | CXXTokenTypeSquareParenthesisChain | CXXTokenTypeBracketChain | CXXTokenTypeAssignment | CXXTokenTypeComma | CXXTokenTypeSemicolon | CXXTokenTypeOpeningBracket )) { // Notable token reached. break; } if( cxxTokenTypeIsOneOf( t, CXXTokenTypeOperator | CXXTokenTypeMultipleAnds | CXXTokenTypePointerOperator | CXXTokenTypeStringConstant | CXXTokenTypeAngleBracketChain | CXXTokenTypeCharacterConstant | CXXTokenTypeMultipleDots | CXXTokenTypeClosingBracket | CXXTokenTypeClosingParenthesis | CXXTokenTypeClosingSquareParenthesis | CXXTokenTypeGreaterThanSign ) ) { // Something that should not appear in a variable declaration CXX_DEBUG_LEAVE_TEXT( "Found token '%s' of type 0x%02x that should " \ "not appear in the initial part of a variable declaration", vStringValue(t->pszWord), t->eType ); return bGotVariable; } if(t->eType == CXXTokenTypeSmallerThanSign) { // Must be part of template type name (so properly balanced). t = cxxTokenChainSkipToEndOfTemplateAngleBracket(t); if(!t) { CXX_DEBUG_LEAVE_TEXT("Failed to skip past angle bracket chain"); return bGotVariable; } } t = t->pNext; } // Notable token reached? if(!t) { CXX_DEBUG_LEAVE_TEXT("Nothing interesting here"); return bGotVariable; } CXX_DEBUG_PRINT( "Found notable token '%s' of type 0x%02x(%s)", vStringValue(t->pszWord), t->eType, cxxDebugTypeDecode(t->eType) ); // Now before the notable token there MUST be an identifier // (eventually hidden in a parenthesis chain) and also a typename. if(!t->pPrev) { CXX_DEBUG_LEAVE_TEXT("Nothing interesting before notable token"); return bGotVariable; } CXXToken * pIdentifier = NULL; CXXToken * pTokenBefore = NULL; // If we have to continue scanning we'll remove the tokens from here // so they don't end up being part of the type name. // If this is set to NULL then it means that we cannot determine properly // what to remove and we should stop scanning after the current variable. CXXToken * pRemoveStart = t; switch(t->eType) { case CXXTokenTypeParenthesisChain: { // At a parenthesis chain we need some additional checks. if( // check for function pointers or nasty arrays // Possible cases: // ret type (*variable)(params) // ret type (* const (variable[4]))(params) t->pNext && ( ( cxxTokenTypeIs(t->pNext,CXXTokenTypeParenthesisChain) && cxxParserTokenChainLooksLikeFunctionParameterList( t->pNext->pChain, NULL ) ) || cxxTokenTypeIs(t->pNext,CXXTokenTypeSquareParenthesisChain) ) && (pIdentifier = cxxTokenChainFirstPossiblyNestedTokenOfType( t->pChain, CXXTokenTypeIdentifier, NULL )) && // Discard function declarations with function return types // like void (*A(B))(C); ( (!pIdentifier->pNext) || (!cxxTokenTypeIs(pIdentifier->pNext,CXXTokenTypeParenthesisChain)) ) ) { // A function pointer. // There are two parentheses, skip the second too. pTokenBefore = t->pPrev; t = t->pNext->pNext; pRemoveStart = t; } else if( (t->pChain->iCount == 3) && cxxTokenTypeIs( cxxTokenChainAt(t->pChain,1), CXXTokenTypeParenthesisChain ) && t->pPrev && cxxTokenTypeIs(t->pPrev,CXXTokenTypeIdentifier) && t->pPrev->pPrev && cxxTokenTypeIs(t->pPrev->pPrev,CXXTokenTypeIdentifier) ) { CXX_DEBUG_LEAVE_TEXT("Parenthesis seems to define an __ARGS style prototype"); return bGotVariable; } else if( cxxTokenTypeIs(t->pPrev,CXXTokenTypeIdentifier) && ( (eScopeType == CXXScopeTypeNamespace) || (eScopeType == CXXScopeTypeFunction) ) && cxxParserCurrentLanguageIsCPP() && cxxParserTokenChainLooksLikeConstructorParameterSet(t->pChain) ) { // ok, *might* be variable instantiation pIdentifier = t->pPrev; pTokenBefore = pIdentifier->pPrev; } else { CXX_DEBUG_LEAVE_TEXT("No recognizable parenthesis form for a variable"); return bGotVariable; } } break; case CXXTokenTypeBracketChain: if( cxxTokenTypeIs(t->pPrev,CXXTokenTypeIdentifier) && cxxParserCurrentLanguageIsCPP() && cxxParserTokenChainLooksLikeConstructorParameterSet(t->pChain) ) { // ok, *might* be new C++ style variable initialization pIdentifier = t->pPrev; pTokenBefore = pIdentifier->pPrev; } else { CXX_DEBUG_LEAVE_TEXT("Bracket chain that doesn't look like a C++ var init"); return bGotVariable; } break; case CXXTokenTypeSingleColon: // check for bitfield if( t->pNext && cxxTokenTypeIsOneOf(t->pNext,CXXTokenTypeNumber | CXXTokenTypeIdentifier) ) { // ok, looks like a bit field if( cxxTokenTypeIs(t->pNext,CXXTokenTypeNumber) && t->pNext->pNext && cxxTokenTypeIsOneOf( t->pNext->pNext, CXXTokenTypeComma | CXXTokenTypeSemicolon | CXXTokenTypeAssignment ) ) { // keep bitfield width specification as part of type pIdentifier = t->pPrev; pTokenBefore = pIdentifier->pPrev; t = t->pNext->pNext; } else { // Too complex: strip width specification (the best we can do) pIdentifier = t->pPrev; pTokenBefore = pIdentifier->pPrev; } } else { CXX_DEBUG_LEAVE_TEXT("Single colon that doesn't look like a bit field"); return bGotVariable; } break; case CXXTokenTypeSquareParenthesisChain: // check for array // Keep the array specifier as part of type pIdentifier = t->pPrev; pTokenBefore = pIdentifier->pPrev; while(t->pNext && cxxTokenTypeIs(t->pNext,CXXTokenTypeSquareParenthesisChain)) t = t->pNext; if(!t->pNext) { CXX_DEBUG_LEAVE_TEXT("No token after []"); return bGotVariable; } // skip identifies attached to the array as attributes while(t->pNext && cxxTokenTypeIs(t->pNext, CXXTokenTypeIdentifier)) { t = t->pNext; // skip macro argument(s) if (t->pNext && cxxTokenTypeIs(t->pNext, CXXTokenTypeParenthesisChain)) t = t->pNext; } if (!t->pNext) { CXX_DEBUG_LEAVE_TEXT("No token after attribute(s) attached to []"); return bGotVariable; } if(!cxxTokenTypeIsOneOf( t->pNext, CXXTokenTypeComma | CXXTokenTypeSemicolon | CXXTokenTypeAssignment | CXXTokenTypeBracketChain )) { CXX_DEBUG_LEAVE_TEXT("No comma, semicolon, = or {} after [] (\"%s\", %s)", vStringValue (t->pNext->pszWord), cxxDebugTypeDecode (t->pNext->eType)); return bGotVariable; } t = t->pNext; pRemoveStart = t; break; default: // Must be identifier if(t->pPrev->eType != CXXTokenTypeIdentifier) { CXX_DEBUG_LEAVE_TEXT("No identifier before the notable token"); return bGotVariable; } pIdentifier = t->pPrev; pTokenBefore = pIdentifier->pPrev; break; } CXX_DEBUG_ASSERT(pIdentifier,"We should have found an identifier here"); if(!pTokenBefore) { CXX_DEBUG_LEAVE_TEXT("Identifier not preceded by a type"); // Here we can handle yet another one of the gazillion of special cases. // // MACRO(whatever) variable; // if( cxxTokenTypeIs(t,CXXTokenTypeParenthesisChain) && t->pNext && cxxTokenTypeIs(t->pNext,CXXTokenTypeIdentifier) && t->pNext->pNext && cxxTokenTypeIs(t->pNext->pNext,CXXTokenTypeSemicolon) ) { CXX_DEBUG_PRINT("Looks like the 'MACRO(whatever) variable;' special case"); pIdentifier = t->pNext; pTokenBefore = t; t = t->pNext->pNext; } else { return bGotVariable; } } CXXToken * pScopeEnd = pTokenBefore->pNext; CXXToken * pScopeStart = NULL; // Skip back to the beginning of the scope, if any while(pTokenBefore->eType == CXXTokenTypeMultipleColons) { if(!cxxParserCurrentLanguageIsCPP()) { CXX_DEBUG_LEAVE_TEXT("Syntax error: found multiple colons in C language"); return false; } pTokenBefore = pTokenBefore->pPrev; if(!pTokenBefore) { CXX_DEBUG_LEAVE_TEXT( "Identifier preceded by multiple colons " \ "but not preceded by a type" ); return bGotVariable; } if(cxxTokenTypeIs(pTokenBefore,CXXTokenTypeGreaterThanSign)) { CXXToken * pAux = cxxTokenChainSkipBackToStartOfTemplateAngleBracket(pTokenBefore); if((!pAux) || (!pAux->pPrev)) { CXX_DEBUG_LEAVE_TEXT( "Identifier preceded by multiple colons " \ "and by a >, but failed to skip back to starting <" ); return bGotVariable; } pTokenBefore = pAux->pPrev; } if(!cxxTokenTypeIs(pTokenBefore,CXXTokenTypeIdentifier)) { CXX_DEBUG_LEAVE_TEXT( "Identifier preceded by multiple colons " \ "with probable syntax error" ); return bGotVariable; } pScopeStart = pTokenBefore; pTokenBefore = pTokenBefore->pPrev; if(!pTokenBefore) { CXX_DEBUG_LEAVE_TEXT( "Identifier preceded by multiple colons " \ "but not preceded by a type" ); return bGotVariable; } } if(!bGotVariable) { // now pTokenBefore should be part of a type (either the variable type or return // type of a function in case of a function pointer) if(!cxxTokenTypeIsOneOf( pTokenBefore, CXXTokenTypeIdentifier | CXXTokenTypeKeyword | CXXTokenTypeStar | CXXTokenTypeAnd )) { if(cxxTokenTypeIs(pTokenBefore,CXXTokenTypeGreaterThanSign)) { // the < > must be balanced CXXToken * t2 = pTokenBefore->pPrev; int iLevel = 1; while(t2) { if(cxxTokenTypeIs(t2,CXXTokenTypeGreaterThanSign)) iLevel++; else if(cxxTokenTypeIs(t2,CXXTokenTypeSmallerThanSign)) iLevel--; t2 = t2->pPrev; } if(iLevel != 0) { CXX_DEBUG_LEAVE_TEXT( "The > token is unbalanced and does not " \ "seem to be part of type name" ); return bGotVariable; } } else if( // Still 'MACRO(whatever) variable;' case cxxTokenTypeIs(pTokenBefore,CXXTokenTypeParenthesisChain) && pTokenBefore->pPrev && cxxTokenTypeIs(pTokenBefore->pPrev,CXXTokenTypeIdentifier) && !pTokenBefore->pPrev->pPrev ) { CXX_DEBUG_PRINT("Type seems to be hidden in a macro"); } else { CXX_DEBUG_LEAVE_TEXT( "Token '%s' of type 0x%02x does not seem " \ "to be part of type name", vStringValue(pTokenBefore->pszWord), pTokenBefore->eType ); return bGotVariable; } } bGotVariable = true; } // Goodie. We have an identifier and almost certainly a type here. // From now on we start destroying the chain: mark the return value as true // so nobody else will try to extract stuff from it int iScopesPushed = 0; if(pScopeStart) { // Push the scopes and remove them from the chain so they are not in the way while(pScopeStart != pScopeEnd) { // This is the scope id START. It might contain // also other tokens like in ...::A<B>::... CXXToken * pPartEnd = cxxTokenChainNextTokenOfType( pScopeStart, CXXTokenTypeMultipleColons ); CXX_DEBUG_ASSERT( pPartEnd, "We should have found multiple colons here!" ); CXX_DEBUG_ASSERT( pPartEnd->pPrev, "And there should be a previous token too" ); CXXToken * pScopeId = cxxTokenChainExtractRange(pScopeStart,pPartEnd->pPrev,0); cxxScopePush( pScopeId, CXXScopeTypeClass, // WARNING: We don't know if it's really a class! (FIXME?) CXXScopeAccessUnknown ); CXXToken * pAux = pPartEnd->pNext; cxxTokenChainDestroyRange(pChain,pScopeStart,pPartEnd); pScopeStart = pAux; iScopesPushed++; } } bool bKnRStyleParameters = (uFlags & CXXExtractVariableDeclarationsKnRStyleParameters); tagEntryInfo * tag = cxxTagBegin( bKnRStyleParameters ? CXXTagKindPARAMETER : ((g_cxx.uKeywordState & CXXParserKeywordStateSeenExtern) ? CXXTagKindEXTERNVAR : cxxScopeGetVariableKind()), pIdentifier ); if(tag) { CXX_DEBUG_ASSERT(t != pIdentifier,"This should not happen"); // remove the identifier cxxTokenChainTakeRecursive(pChain,pIdentifier); // Fix square parentheses: if they contain something that is not a numeric // constant then empty them up CXXToken * pPartOfType = t->pPrev; CXX_DEBUG_ASSERT(pPartOfType,"There should be a part of type name here"); while(pPartOfType && cxxTokenTypeIs(pPartOfType,CXXTokenTypeSquareParenthesisChain)) { CXXTokenChain * pAuxChain = pPartOfType->pChain; if(pAuxChain->iCount > 2) { if( (pAuxChain->iCount > 3) || (!cxxTokenTypeIs(cxxTokenChainAt(pAuxChain,1),CXXTokenTypeNumber)) ) { cxxTokenChainDestroyRange( pAuxChain, cxxTokenChainFirst(pAuxChain)->pNext, cxxTokenChainLast(pAuxChain)->pPrev ); } } pPartOfType = pPartOfType->pPrev; } // anything that remains is part of type CXXToken * pTypeToken = cxxTagCheckAndSetTypeField(cxxTokenChainFirst(pChain),t->pPrev); tag->isFileScope = bKnRStyleParameters ? true : ( ( (eScopeType == CXXScopeTypeNamespace) && (g_cxx.uKeywordState & CXXParserKeywordStateSeenStatic) && (!isInputHeaderFile()) ) || // locals are always hidden (eScopeType == CXXScopeTypeFunction) || ( (eScopeType != CXXScopeTypeNamespace) && (eScopeType != CXXScopeTypeFunction) && (!isInputHeaderFile()) ) ); vString * pszProperties = NULL; if(cxxTagFieldEnabled(CXXTagFieldProperties)) { unsigned int uProperties = 0; if(g_cxx.uKeywordState & CXXParserKeywordStateSeenStatic) uProperties |= CXXTagPropertyStatic; if(g_cxx.uKeywordState & CXXParserKeywordStateSeenExtern) uProperties |= CXXTagPropertyExtern; if(g_cxx.uKeywordState & CXXParserKeywordStateSeenMutable) uProperties |= CXXTagPropertyMutable; if(g_cxx.uKeywordState & CXXParserKeywordStateSeenAttributeDeprecated) uProperties |= CXXTagPropertyDeprecated; // Volatile is part of the type, so we don't mark it as a property //if(g_cxx.uKeywordState & CXXParserKeywordStateSeenVolatile) // uProperties |= CXXTagPropertyVolatile; pszProperties = cxxTagSetProperties(uProperties); } cxxTagCommit(); if(pTypeToken) cxxTokenDestroy(pTypeToken); if(pszProperties) vStringDelete(pszProperties); cxxTokenDestroy(pIdentifier); } while(iScopesPushed > 0) { cxxScopePop(); iScopesPushed--; } if(!t) { CXX_DEBUG_LEAVE_TEXT("Nothing more"); return bGotVariable; } if(!cxxTokenTypeIsOneOf( t, CXXTokenTypeComma | CXXTokenTypeSemicolon | CXXTokenTypeOpeningBracket )) { t = cxxTokenChainNextTokenOfType( t, CXXTokenTypeComma | CXXTokenTypeSemicolon | CXXTokenTypeOpeningBracket ); if(!t) { CXX_DEBUG_LEAVE_TEXT("Didn't find a comma, semicolon or {"); return bGotVariable; } } if(cxxTokenTypeIsOneOf(t,CXXTokenTypeSemicolon | CXXTokenTypeOpeningBracket)) { CXX_DEBUG_LEAVE_TEXT("Noting else"); return bGotVariable; } // Comma. Might have other declarations here. CXX_DEBUG_PRINT("At a comma, might have other declarations here"); t = t->pNext; CXX_DEBUG_ASSERT(t,"There should be something after the comma here!"); if(!pRemoveStart) { CXX_DEBUG_LEAVE_TEXT("Could not properly fix type name for next token: stopping here"); return bGotVariable; } cxxTokenChainDestroyRange(pChain,pRemoveStart,t->pPrev); } CXX_DEBUG_LEAVE_TEXT("Reached end"); return bGotVariable; }
// In case of a lambda without parentheses this is the capture list token. boolean cxxParserHandleLambda(CXXToken * pParenthesis) { CXX_DEBUG_ENTER(); CXXToken * pIdentifier = cxxTokenCreateAnonymousIdentifier(CXXTagKindFUNCTION); CXXTokenChain * pSave = g_cxx.pTokenChain; CXXTokenChain * pNew = cxxTokenChainCreate(); g_cxx.pTokenChain = pNew; tagEntryInfo * tag = cxxTagBegin(CXXTagKindFUNCTION,pIdentifier); CXXToken * pAfterParenthesis = pParenthesis ? pParenthesis->pNext : NULL; CXXToken * pCaptureList = NULL; if(pParenthesis) { if(cxxTokenTypeIs(pParenthesis,CXXTokenTypeSquareParenthesisChain)) { // form (4) of lambda (see cxxParserOpeningBracketIsLambda()). pCaptureList = pParenthesis; } else if( pParenthesis->pPrev && cxxTokenTypeIs(pParenthesis->pPrev,CXXTokenTypeSquareParenthesisChain) ) { // other forms of lambda (see cxxParserOpeningBracketIsLambda()). pCaptureList = pParenthesis->pPrev; } } if( pAfterParenthesis && cxxTokenTypeIs(pAfterParenthesis,CXXTokenTypeKeyword) && (pAfterParenthesis->eKeyword == CXXKeywordCONST) ) pAfterParenthesis = pAfterParenthesis->pNext; CXXToken * pTypeStart = NULL; CXXToken * pTypeEnd; if( pAfterParenthesis && cxxTokenTypeIs(pAfterParenthesis,CXXTokenTypePointerOperator) && pAfterParenthesis->pNext && !cxxTokenTypeIs(pAfterParenthesis->pNext,CXXTokenTypeOpeningBracket) ) { pTypeStart = pAfterParenthesis->pNext; pTypeEnd = pTypeStart; while( pTypeEnd->pNext && (!cxxTokenTypeIs(pTypeEnd->pNext,CXXTokenTypeOpeningBracket)) ) pTypeEnd = pTypeEnd->pNext; while( (pTypeStart != pTypeEnd) && cxxTokenTypeIs(pTypeStart,CXXTokenTypeKeyword) && cxxKeywordExcludeFromTypeNames(pTypeStart->eKeyword) ) pTypeStart = pTypeStart->pNext; } int iCorkQueueIndex = CORK_NIL; if(tag) { tag->isFileScope = TRUE; CXXToken * pTypeName; if(pTypeStart) pTypeName = cxxTagSetTypeField(pTypeStart,pTypeEnd); else pTypeName = NULL; if(pCaptureList && cxxTagCPPFieldEnabled(CXXTagCPPFieldLambdaCaptureList)) { CXX_DEBUG_ASSERT(pCaptureList->pChain,"The capture list must be a chain"); cxxTokenChainCondense(pCaptureList->pChain,0); CXX_DEBUG_ASSERT( cxxTokenChainFirst(pCaptureList->pChain), "Condensation should have created a single token in the chain" ); cxxTagSetCPPField( CXXTagCPPFieldLambdaCaptureList, vStringValue(cxxTokenChainFirst(pCaptureList->pChain)->pszWord) ); } // FIXME: Properties? vString * pszSignature = NULL; if(cxxTokenTypeIs(pParenthesis,CXXTokenTypeParenthesisChain)) pszSignature = cxxTokenChainJoin(pParenthesis->pChain,NULL,0); if(pszSignature) tag->extensionFields.signature = vStringValue(pszSignature); iCorkQueueIndex = cxxTagCommit(); if(pTypeName) cxxTokenDestroy(pTypeName); if(pszSignature) vStringDelete(pszSignature); } cxxScopePush( pIdentifier, CXXTagKindFUNCTION, CXXScopeAccessUnknown ); if( pParenthesis && cxxTokenTypeIs(pParenthesis,CXXTokenTypeParenthesisChain) && cxxTagKindEnabled(CXXTagKindPARAMETER) ) { CXXFunctionParameterInfo oParamInfo; if(cxxParserTokenChainLooksLikeFunctionParameterList( pParenthesis->pChain,&oParamInfo )) cxxParserEmitFunctionParameterTags(&oParamInfo); } boolean bRet = cxxParserParseBlock(TRUE); if(iCorkQueueIndex > CORK_NIL) cxxParserMarkEndLineForTagInCorkQueue(iCorkQueueIndex); cxxScopePop(); pNew = g_cxx.pTokenChain; // May have been destroyed and re-created g_cxx.pTokenChain = pSave; g_cxx.pToken = pSave->pTail; // change the type of token so following parsing code is not confused too much g_cxx.pToken->eType = CXXTokenTypeAngleBracketChain; g_cxx.pToken->pChain = pNew; cxxTokenChainClear(pNew); CXXToken * t = cxxTokenCreate(); t->eType = CXXTokenTypeOpeningBracket; vStringCatS(t->pszWord,"{"); cxxTokenChainAppend(pNew,t); t = cxxTokenCreate(); t->eType = CXXTokenTypeClosingBracket; vStringCatS(t->pszWord,"}"); cxxTokenChainAppend(pNew,t); CXX_DEBUG_LEAVE(); return bRet; }
boolean cxxParserParseClassStructOrUnion( enum CXXKeyword eKeyword, enum CXXTagKind eTagKind ) { CXX_DEBUG_ENTER(); // make sure that there is only the class/struct/union keyword in the chain while(g_cxx.pTokenChain->iCount > 1) cxxTokenChainDestroyFirst(g_cxx.pTokenChain); // this may be cleared below boolean bParsingTypedef = (g_cxx.uKeywordState & CXXParserKeywordStateSeenTypedef); /* Spec is: class-key attr class-head-name base-clause { member-specification } class-key - one of class or struct. The keywords are identical except for the default member access and the default base class access. attr(C++11) - optional sequence of any number of attributes, may include alignas specifier class-head-name - the name of the class that's being defined. Optionally qualified, optionally followed by keyword final. The name may be omitted, in which case the class is unnamed (note that unnamed class cannot be final) base-clause - optional list of one or more parent classes and the model of inheritance used for each (see derived class) member-specification - list of access specifiers, member object and member function declarations and definitions (see below) */ // Skip attr and class-head-name // enable "final" keyword handling g_cxx.bParsingClassStructOrUnionDeclaration = TRUE; unsigned int uTerminatorTypes = CXXTokenTypeEOF | CXXTokenTypeSingleColon | CXXTokenTypeSemicolon | CXXTokenTypeOpeningBracket | CXXTokenTypeSmallerThanSign; if(eTagKind != CXXTagKindCLASS) uTerminatorTypes |= CXXTokenTypeParenthesisChain | CXXTokenTypeAssignment; boolean bRet; for(;;) { bRet = cxxParserParseUpToOneOf(uTerminatorTypes); if(!bRet) { g_cxx.bParsingClassStructOrUnionDeclaration = FALSE; CXX_DEBUG_LEAVE_TEXT("Could not parse class/struct/union name"); return FALSE; } if(!cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeSmallerThanSign)) break; // Probably a template specialisation // template<typename T> struct X<int> // { // } // FIXME: Should we add the specialisation arguments somewhere? // Maye as a separate field? bRet = cxxParserParseAndCondenseCurrentSubchain( CXXTokenTypeOpeningParenthesis | CXXTokenTypeOpeningBracket | CXXTokenTypeOpeningSquareParenthesis | CXXTokenTypeSmallerThanSign, FALSE ); if(!bRet) { g_cxx.bParsingClassStructOrUnionDeclaration = FALSE; CXX_DEBUG_LEAVE_TEXT("Could not parse class/struct/union name"); return FALSE; } } g_cxx.bParsingClassStructOrUnionDeclaration = FALSE; if(cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeParenthesisChain)) { // probably a function declaration/prototype // something like struct x * func().... // do not clear statement CXX_DEBUG_LEAVE_TEXT("Probably a function declaration!"); return TRUE; } // FIXME: This block is duplicated in enum if(cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeSemicolon)) { if(g_cxx.pTokenChain->iCount > 3) { // [typedef] struct X Y; <-- typedef has been removed! if(bParsingTypedef) cxxParserExtractTypedef(g_cxx.pTokenChain,TRUE); else cxxParserExtractVariableDeclarations(g_cxx.pTokenChain,0); } cxxParserNewStatement(); CXX_DEBUG_LEAVE(); return TRUE; } if(cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeAssignment)) { if(g_cxx.pTokenChain->iCount > 3) { // struct X Y = ...; cxxParserExtractVariableDeclarations(g_cxx.pTokenChain,0); } // Skip the initialization (which almost certainly contains a block) if(!cxxParserParseUpToOneOf(CXXTokenTypeEOF | CXXTokenTypeSemicolon)) { CXX_DEBUG_LEAVE_TEXT("Failed to parse up to EOF/semicolon"); return FALSE; } cxxParserNewStatement(); CXX_DEBUG_LEAVE(); return TRUE; } if(cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeEOF)) { // tolerate EOF, just ignore this cxxParserNewStatement(); CXX_DEBUG_LEAVE_TEXT("EOF: ignoring"); return TRUE; } // semicolon or opening bracket // check if we can extract a class name identifier CXXToken * pClassName = cxxTokenChainLastTokenOfType( g_cxx.pTokenChain, CXXTokenTypeIdentifier ); int iPushedScopes = 0; if(pClassName) { // good. // It may be qualified though. CXXToken * pNamespaceBegin = pClassName; CXXToken * pPrev = pClassName->pPrev; while(pPrev) { if(!cxxTokenTypeIs(pPrev,CXXTokenTypeMultipleColons)) break; pPrev = pPrev->pPrev; if(!pPrev) break; if(!cxxTokenTypeIs(pPrev,CXXTokenTypeIdentifier)) break; pNamespaceBegin = pPrev; pPrev = pPrev->pPrev; } while(pNamespaceBegin != pClassName) { CXXToken * pNext = pNamespaceBegin->pNext; cxxTokenChainTake(g_cxx.pTokenChain,pNamespaceBegin); // FIXME: We don't really know if it's a class! cxxScopePush(pNamespaceBegin,CXXTagKindCLASS,CXXScopeAccessUnknown); iPushedScopes++; pNamespaceBegin = pNext->pNext; } CXX_DEBUG_PRINT( "Class/struct/union name is %s", vStringValue(pClassName->pszWord) ); cxxTokenChainTake(g_cxx.pTokenChain,pClassName); } else { pClassName = cxxTokenCreateAnonymousIdentifier(eTagKind); CXX_DEBUG_PRINT( "Class/struct/union name is %s (anonymous)", vStringValue(pClassName->pszWord) ); } if(cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeSingleColon)) { // check for base classes cxxTokenChainClear(g_cxx.pTokenChain); if(!cxxParserParseUpToOneOf( CXXTokenTypeEOF | CXXTokenTypeSemicolon | CXXTokenTypeOpeningBracket )) { cxxTokenDestroy(pClassName); CXX_DEBUG_LEAVE_TEXT("Failed to parse base class part"); return FALSE; } if(cxxTokenTypeIsOneOf(g_cxx.pToken,CXXTokenTypeSemicolon | CXXTokenTypeEOF)) { cxxTokenDestroy(pClassName); cxxParserNewStatement(); CXX_DEBUG_LEAVE_TEXT("Syntax error: ignoring"); return TRUE; } cxxTokenChainDestroyLast(g_cxx.pTokenChain); // remove the { } else { cxxTokenChainClear(g_cxx.pTokenChain); } tagEntryInfo * tag = cxxTagBegin(eTagKind,pClassName); int iCorkQueueIndex = CORK_NIL; if(tag) { if(g_cxx.pTokenChain->iCount > 0) { // Strip inheritance type information // FIXME: This could be optional! CXXToken * t = cxxTokenChainFirst(g_cxx.pTokenChain); while(t) { if( cxxTokenTypeIs(t,CXXTokenTypeKeyword) && ( (t->eKeyword == CXXKeywordPUBLIC) || (t->eKeyword == CXXKeywordPROTECTED) || (t->eKeyword == CXXKeywordPRIVATE) || (t->eKeyword == CXXKeywordVIRTUAL) ) ) { CXXToken * pNext = t->pNext; cxxTokenChainTake(g_cxx.pTokenChain,t); cxxTokenDestroy(t); t = pNext; } else { t = t->pNext; } } if(g_cxx.pTokenChain->iCount > 0) { cxxTokenChainCondense( g_cxx.pTokenChain, CXXTokenChainCondenseNoTrailingSpaces ); tag->extensionFields.inheritance = vStringValue( g_cxx.pTokenChain->pHead->pszWord ); } } if( g_cxx.pTemplateTokenChain && (g_cxx.pTemplateTokenChain->iCount > 0) && cxxTagCPPFieldEnabled(CXXTagCPPFieldTemplate) ) { cxxTokenChainNormalizeTypeNameSpacing(g_cxx.pTemplateTokenChain); cxxTokenChainCondense(g_cxx.pTemplateTokenChain,0); cxxTagSetCPPField( CXXTagCPPFieldTemplate, vStringValue(cxxTokenChainFirst(g_cxx.pTemplateTokenChain)->pszWord) ); } tag->isFileScope = !isInputHeaderFile(); iCorkQueueIndex = cxxTagCommit(); } cxxScopePush( pClassName, eTagKind, (eTagKind == CXXTagKindCLASS) ? CXXScopeAccessPrivate : CXXScopeAccessPublic ); vString * pScopeName = cxxScopeGetFullNameAsString(); if(!cxxParserParseBlock(TRUE)) { CXX_DEBUG_LEAVE_TEXT("Failed to parse scope"); if(pScopeName) vStringDelete(pScopeName); return FALSE; } if(iCorkQueueIndex > CORK_NIL) cxxParserMarkEndLineForTagInCorkQueue(iCorkQueueIndex); iPushedScopes++; while(iPushedScopes > 0) { cxxScopePop(); iPushedScopes--; } bRet = cxxParserParseEnumStructClassOrUnionFullDeclarationTrailer( bParsingTypedef, eKeyword, eTagKind, vStringValue(pScopeName) ); if(pScopeName) vStringDelete(pScopeName); cxxParserNewStatement(); CXX_DEBUG_LEAVE(); return bRet; };