unsigned int cxxScopeGetKind(void) { CXX_DEBUG_ASSERT(g_pScope->iCount >= 0,"Must not be called in global scope"); switch(g_pScope->pTail->uInternalScopeType) { case CXXScopeTypeNamespace: CXX_DEBUG_ASSERT(cxxParserCurrentLanguageIsCPP(),"C++ only"); return CXXTagCPPKindNAMESPACE; case CXXScopeTypeClass: CXX_DEBUG_ASSERT(cxxParserCurrentLanguageIsCPP(),"C++ only"); return CXXTagCPPKindCLASS; case CXXScopeTypeEnum: return CXXTagKindENUM; case CXXScopeTypeFunction: return CXXTagKindFUNCTION; case CXXScopeTypeStruct: return CXXTagKindSTRUCT; case CXXScopeTypeUnion: return CXXTagKindUNION; default: CXX_DEBUG_ASSERT(false,"Unhandled scope type!"); break; } return CXXTagKindFUNCTION; }
void cxxScopePush( CXXToken * t, enum CXXScopeType eScopeType, enum CXXScopeAccess eInitialAccess ) { CXX_DEBUG_ASSERT( t->eType == CXXTokenTypeIdentifier, "The scope name must be an identifer" ); CXX_DEBUG_ASSERT( t->pszWord, "The scope name should have a text" ); cxxTokenChainAppend(g_pScope,t); t->uInternalScopeType = (unsigned char)eScopeType; t->uInternalScopeAccess = (unsigned char)eInitialAccess; g_bScopeNameDirty = true; #ifdef CXX_DO_DEBUGGING const char * szScopeName = cxxScopeGetFullName(); CXX_DEBUG_PRINT("Pushed scope: '%s'",szScopeName ? szScopeName : ""); #endif }
static void cxxParserParseNextTokenApplyReplacement( const cppMacroInfo * pInfo, CXXToken * pParameterChainToken ) { CXX_DEBUG_ENTER(); CXX_DEBUG_ASSERT(pInfo,"Info must be not null"); CXX_DEBUG_ASSERT(pInfo->replacements,"There should be a replacement"); if(!pInfo->hasParameterList) { CXX_DEBUG_ASSERT(!pParameterChainToken,"This shouldn't have been extracted"); } CXXTokenChain * pParameters = NULL; const char ** aParameters = NULL; int iParameterCount = 0; if(pInfo->hasParameterList && pParameterChainToken && (pParameterChainToken->pChain->iCount >= 3)) { // kill parenthesis cxxTokenChainDestroyFirst(pParameterChainToken->pChain); cxxTokenChainDestroyLast(pParameterChainToken->pChain); pParameters = cxxTokenChainSplitOnComma( pParameterChainToken->pChain ); aParameters = (const char **)eMalloc(sizeof(const char *) * pParameters->iCount); CXXToken * pParam = cxxTokenChainFirst(pParameters); while(pParam) { aParameters[iParameterCount] = vStringValue(pParam->pszWord); iParameterCount++; pParam = pParam->pNext; } CXX_DEBUG_ASSERT(iParameterCount == pParameters->iCount,"Bad number of parameters found"); } vString * pReplacement = cppBuildMacroReplacement(pInfo,aParameters,iParameterCount); if(pParameters) { cxxTokenChainDestroy(pParameters); eFree(aParameters); } CXX_DEBUG_PRINT("Applying complex replacement '%s'",vStringValue(pReplacement)); cppUngetString(vStringValue(pReplacement),vStringLength(pReplacement)); vStringDelete(pReplacement); CXX_DEBUG_LEAVE(); }
// 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; }
void cxxTagSetCorkQueueField( int iIndex, unsigned int uField, const char * szValue ) { CXX_DEBUG_ASSERT( uField < g_cxx.uFieldOptionCount, "The field must be associated to the current language!" ); CXX_DEBUG_ASSERT(g_cxx.pFieldOptions[uField].enabled,"The field must be enabled!"); attachParserFieldToCorkEntry(iIndex,g_cxx.pFieldOptions[uField].ftype,szValue); }
CXXToken * cxxTokenChainSkipBackToStartOfTemplateAngleBracket(CXXToken * t) { if(!t) return NULL; CXX_DEBUG_ASSERT( t->eType == CXXTokenTypeGreaterThanSign, "This function must be called when pointing to a >" ); int iLevel = 1; t = t->pPrev; while(t) { if(cxxTokenTypeIs(t,CXXTokenTypeGreaterThanSign)) { iLevel++; } else if(cxxTokenTypeIs(t,CXXTokenTypeSmallerThanSign)) { if(iLevel == 1) return t; iLevel--; } t = t->pPrev; } // invalid return NULL; }
boolean cxxTagFieldEnabled(unsigned int uField) { CXX_DEBUG_ASSERT( uField < g_cxx.uFieldOptionCount, "The field must be associated to the current language!" ); return g_cxx.pFieldOptions[uField].enabled; }
boolean cxxTagKindEnabled(unsigned int uKind) { CXX_DEBUG_ASSERT( uKind < g_cxx.uKindOptionCount, "The kind must be associated to the current language!" ); return g_cxx.pKindOptions[uKind].enabled; }
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"); } }
// // This has to be called when pointing at an opening bracket in function scope. // Returns NULL if it does not look to be a lambda invocation. // Returns the lambda parameter parenthesis chain token if it DOES look like a // lambda invocation. // CXXToken * cxxParserOpeningBracketIsLambda(void) { // [ capture-list ] ( params ) mutable(opt) exception attr -> ret {} (1) // [ capture-list ] ( params ) -> ret { body } (2) // [ capture-list ] ( params ) { body } (3) // [ capture-list ] { body } (4) CXX_DEBUG_ASSERT(cxxParserCurrentLanguageIsCPP(),"C++ only"); CXXToken * t = g_cxx.pToken->pPrev; if(!t) return NULL; // not a lambda // Check simple cases first // case 4 if(cxxTokenTypeIs(t,CXXTokenTypeSquareParenthesisChain)) { // very likely parameterless lambda return t; } // case 3 if(cxxTokenTypeIs(t,CXXTokenTypeParenthesisChain)) { t = t->pPrev; if(!t) return NULL; // can't be if(cxxTokenTypeIs(t,CXXTokenTypeSquareParenthesisChain)) return t->pNext; return NULL; } // Stop also at commas, so in very large structures we will not be searching far t = cxxTokenChainPreviousTokenOfType( t, CXXTokenTypeSquareParenthesisChain | CXXTokenTypeComma ); if(!t) return NULL; if(!cxxTokenTypeIs(t,CXXTokenTypeSquareParenthesisChain)) return NULL; t = t->pNext; if(cxxTokenTypeIs(t,CXXTokenTypeParenthesisChain)) return t; return NULL; }
void cxxScopePushTop(CXXToken * t) { CXX_DEBUG_ASSERT( t->eType == CXXTokenTypeIdentifier, "The scope name must be an identifier" ); CXX_DEBUG_ASSERT( t->pszWord, "The scope name should have a text" ); cxxTokenChainAppend(g_pScope,t); g_bScopeNameDirty = true; #ifdef CXX_DO_DEBUGGING const char * szScopeName = cxxScopeGetFullName(); CXX_DEBUG_PRINT("Pushed scope: '%s'",szScopeName ? szScopeName : ""); #endif }
void cxxTagSetField(unsigned int uField,const char * szValue) { CXX_DEBUG_ASSERT( uField < g_cxx.uFieldOptionCount, "The field must be associated to the current language!" ); if(!g_cxx.pFieldOptions[uField].enabled) return; attachParserField(&g_oCXXTag,g_cxx.pFieldOptions[uField].ftype,szValue); }
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); }
// This is called when we encounter a "public", "protected" or "private" keyword // that is NOT in the class declaration header line. boolean cxxParserParseAccessSpecifier(void) { CXX_DEBUG_ENTER(); enum CXXTagKind eScopeKind = cxxScopeGetKind(); if( (eScopeKind != CXXTagKindCLASS) && (eScopeKind != CXXTagKindSTRUCT) && (eScopeKind != CXXTagKindUNION) ) { // this is a syntax error: we're in the wrong scope. CXX_DEBUG_LEAVE_TEXT( "Access specified in wrong context (%d): " "bailing out to avoid reporting broken structure", eScopeKind ); return FALSE; } switch(g_cxx.pToken->eKeyword) { case CXXKeywordPUBLIC: cxxScopeSetAccess(CXXScopeAccessPublic); break; case CXXKeywordPRIVATE: cxxScopeSetAccess(CXXScopeAccessPrivate); break; case CXXKeywordPROTECTED: cxxScopeSetAccess(CXXScopeAccessProtected); break; default: CXX_DEBUG_ASSERT(FALSE,"Bad keyword in cxxParserParseAccessSpecifier!"); break; } // skip to the next :, without leaving scope. if(!cxxParserParseUpToOneOf( CXXTokenTypeSingleColon | CXXTokenTypeSemicolon | CXXTokenTypeClosingBracket | CXXTokenTypeEOF )) { CXX_DEBUG_LEAVE_TEXT("Failed to parse up to the next ;"); return FALSE; } cxxTokenChainClear(g_cxx.pTokenChain); CXX_DEBUG_LEAVE(); return TRUE; }
void cxxTokenChainMoveEntryRange( CXXTokenChain * src, CXXToken * start, CXXToken * end, CXXTokenChain * dest ) { if(!src || !dest || !start || !end) return; CXX_DEBUG_ASSERT( cxxTokenChainFindToken(src,start) >= 0, "The start token must be in the source chain!" ); CXX_DEBUG_ASSERT( cxxTokenChainFindToken(src,end) >= 0, "The end token must be in the source chain!" ); CXX_DEBUG_ASSERT( cxxTokenChainFindToken(src,start) <= cxxTokenChainFindToken(src,end), "The start token must come before the end token" ); CXXToken * t = start; for(;;) { CXXToken * next = t->pNext; cxxTokenChainTake(src,t); cxxTokenChainAppend(dest,t); if(t == end) break; t = next; } }
CXXToken * cxxTagCheckAndSetTypeField( CXXToken * pTypeStart, CXXToken * pTypeEnd ) { CXX_DEBUG_ASSERT(pTypeStart && pTypeEnd,"Non null parameters are expected"); const char * szTypeRef0; // "typename" is debatable since it's not really // allowed by C++ for unqualified types. However I haven't been able // to come up with something better... so "typename" it is for now. static const char * szTypename = "typename"; if(pTypeStart != pTypeEnd) { // Note that this does not work for types like "const enum X" // But that's not backward compatible anyway, so we live with it. if( cxxTokenTypeIs(pTypeStart,CXXTokenTypeKeyword) && cxxKeywordIsTypeRefMarker(pTypeStart->eKeyword) ) { szTypeRef0 = cxxKeywordName(pTypeStart->eKeyword); pTypeStart = pTypeStart->pNext; } else { szTypeRef0 = szTypename; } } else { szTypeRef0 = szTypename; } if(!cxxTagCheckTypeField(pTypeStart,pTypeEnd)) { CXX_DEBUG_PRINT("Type name looks suspicious: refusing to emit it"); return NULL; } cxxTokenChainNormalizeTypeNameSpacingInRange(pTypeStart,pTypeEnd); CXXToken * pTypeName = cxxTokenChainExtractRange(pTypeStart,pTypeEnd,0); CXX_DEBUG_PRINT("Type name is '%s'",vStringValue(pTypeName->pszWord)); g_oCXXTag.extensionFields.typeRef[0] = szTypeRef0; g_oCXXTag.extensionFields.typeRef[1] = vStringValue(pTypeName->pszWord); return pTypeName; }
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 cxxTokenAppendToString(vString * s,CXXToken * t) { switch(t->eType) { case CXXTokenTypeParenthesisChain: case CXXTokenTypeSquareParenthesisChain: case CXXTokenTypeBracketChain: case CXXTokenTypeAngleBracketChain: CXX_DEBUG_ASSERT(t->pChain,"This token should have a nested chain!"); cxxTokenChainJoinInString(t->pChain,s,NULL,0); break; default: vStringCat(s,t->pszWord); break; } }
void cxxTokenForceDestroy(CXXToken * t) { if(!t) return; if(t->pChain) { cxxTokenChainDestroy(t->pChain); t->pChain = NULL; } CXX_DEBUG_ASSERT(t->pszWord,"There should be a word here"); vStringDelete(t->pszWord); eFree(t); }
void cxxTagInitForLanguage(langType eLanguage) { g_cxx.eLanguage = eLanguage; if(g_cxx.eLanguage == g_cxx.eCLanguage) { g_cxx.pKindOptions = g_aCXXCKinds; g_cxx.uKindOptionCount = sizeof(g_aCXXCKinds) / sizeof(kindOption); g_cxx.pFieldOptions = g_aCXXCFields; g_cxx.uFieldOptionCount = sizeof(g_aCXXCFields) / sizeof(fieldSpec); } else if(g_cxx.eLanguage == g_cxx.eCPPLanguage) { g_cxx.pKindOptions = g_aCXXCPPKinds; g_cxx.uKindOptionCount = sizeof(g_aCXXCPPKinds) / sizeof(kindOption); g_cxx.pFieldOptions = g_aCXXCPPFields; g_cxx.uFieldOptionCount = sizeof(g_aCXXCPPFields) / sizeof(fieldSpec); } else { CXX_DEBUG_ASSERT(FALSE,"Invalid language passed to cxxTagInitForLanguage()"); } }
// // Parses a template<anything> prefix. // The parsed template parameter definition is stored in a separate token chain. // boolean cxxParserParseTemplatePrefix(void) { CXX_DEBUG_ENTER(); CXX_DEBUG_ASSERT(cxxTokenTypeIs(cxxTokenChainLast(g_cxx.pTokenChain),CXXTokenTypeKeyword),"We should be pointing at the template keyword here"); cxxTokenChainDestroyLast(g_cxx.pTokenChain); // kill the template keyword if(!cxxParserParseUpToOneOf(CXXTokenTypeSmallerThanSign | CXXTokenTypeEOF | CXXTokenTypeSemicolon)) { CXX_DEBUG_LEAVE_TEXT("Failed to parse up to the < sign"); return FALSE; } if(cxxTokenTypeIsOneOf(g_cxx.pToken,CXXTokenTypeEOF | CXXTokenTypeSemicolon)) { CXX_DEBUG_LEAVE_TEXT("Found EOF or semicolon: assuming this is unparseable"); cxxParserNewStatement(); return TRUE; // tolerate syntax error } CXXTokenChain * pSave = g_cxx.pTokenChain; g_cxx.pTokenChain = cxxTokenChainCreate(); cxxTokenChainAppend(g_cxx.pTokenChain,cxxTokenChainTakeLast(pSave)); if(!cxxParserParseTemplatePrefixAngleBrackets()) { CXX_DEBUG_LEAVE_TEXT("Failed to parse angle brackets"); cxxTokenChainDestroy(pSave); return FALSE; } if(g_cxx.pTemplateTokenChain) cxxTokenChainDestroy(g_cxx.pTemplateTokenChain); g_cxx.pTemplateTokenChain = g_cxx.pTokenChain; g_cxx.pTokenChain = pSave; CXX_DEBUG_LEAVE(); return TRUE; }
// // Attach the current position of input file as "end" field of // the specified tag in the cork queue // void cxxParserMarkEndLineForTagInCorkQueue(int iCorkQueueIndex) { CXX_DEBUG_ASSERT(iCorkQueueIndex > CORK_NIL,"The cork queue index is not valid"); char buf[16]; if(cxxParserCurrentLanguageIsCPP()) { if(!cxxTagCPPFieldEnabled(CXXTagCPPFieldEndLine)) return; sprintf(buf,"%ld",getInputLineNumber()); cxxTagSetCorkQueueCPPField(iCorkQueueIndex,CXXTagCPPFieldEndLine,buf); return; } if(!cxxTagCFieldEnabled(CXXTagCFieldEndLine)) return; sprintf(buf,"%ld",getInputLineNumber()); cxxTagSetCorkQueueCField(iCorkQueueIndex,CXXTagCFieldEndLine,buf); }
CXXToken * cxxTokenCreate(void) { CXXToken * t; if(g_pTokenPool->iCount > 0) { t = cxxTokenChainTakeFirst(g_pTokenPool); CXX_DEBUG_ASSERT(t->pszWord,"The string shouldn't have been destroyed"); } else { t = xMalloc(sizeof(CXXToken),CXXToken); // we almost always want a string, and since this token // is being reused..well.. we always want it t->pszWord = vStringNew(); } t->bFollowedBySpace = FALSE; t->pChain = NULL; t->pNext = NULL; t->pPrev = NULL; return t; }
void cxxTokenDestroy(CXXToken * t) { if(!t) return; if(t->pChain) { cxxTokenChainDestroy(t->pChain); t->pChain = NULL; } CXX_DEBUG_ASSERT(t->pszWord,"There should be a word here"); if(g_pTokenPool->iCount < CXX_TOKEN_POOL_MAXIMUM_SIZE) { // this won't actually release memory (but we're taking care // to do not create very large strings) vStringClear(t->pszWord); cxxTokenChainAppend(g_pTokenPool,t); } else { vStringDelete(t->pszWord); eFree(t); } }
// 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(); }
// 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 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; }
// // 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; }
// // 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; }
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) && ( (!g_cxx.pToken->pPrev->pPrev) || (cxxTokenTypeIsOneOf( g_cxx.pToken->pPrev->pPrev, CXXTokenTypeIdentifier | CXXTokenTypeStar | CXXTokenTypeAnd | CXXTokenTypeGreaterThanSign | CXXTokenTypeKeyword | // FIXME: This check could be made stricter? CXXTokenTypeSingleColon | CXXTokenTypeComma )) ) && // "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; } int iScopes; // FIXME: Why the invalid cork queue entry index is CORK_NIL? int iCorkQueueIndex = CORK_NIL; if(eScopeType != CXXScopeTypeFunction) { // very likely a function definition iScopes = cxxParserExtractFunctionSignatureBeforeOpeningBracket(&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 // - lambda // check for lambdas CXXToken * pParenthesis; if( bIsCPP && (pParenthesis = cxxParserOpeningBracketIsLambda()) ) { CXX_DEBUG_LEAVE_TEXT("Will handle lambda"); return cxxParserHandleLambda(pParenthesis); } iScopes = 0; } cxxParserNewStatement(); if(!cxxParserParseBlock(true)) { CXX_DEBUG_LEAVE_TEXT("Failed to parse nested block"); return false; } if(iCorkQueueIndex > CORK_NIL) cxxParserMarkEndLineForTagInCorkQueue(iCorkQueueIndex); while(iScopes > 0) { cxxScopePop(); iScopes--; } CXX_DEBUG_LEAVE(); return true; }