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; }
// // 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; }
static rescanReason cxxParserMain(const unsigned int passCount) { cxxScopeClear(); cxxTokenAPINewFile(); cxxParserNewStatement(); kindOption * kind_for_define = cxxTagGetKindOptions() + CXXTagKindMACRO; kindOption * kind_for_header = cxxTagGetKindOptions() + CXXTagKindINCLUDE; int role_for_macro_undef = CR_MACRO_UNDEF; int role_for_header_system = CR_HEADER_SYSTEM; int role_for_header_local = CR_HEADER_LOCAL; int end_field_type = cxxParserCurrentLanguageIsCPP()? cxxTagGetCPPFieldSpecifiers () [CXXTagCPPFieldEndLine].ftype: cxxTagGetCFieldSpecifiers () [CXXTagCFieldEndLine].ftype; Assert(passCount < 3); cppInit( (boolean) (passCount > 1), FALSE, TRUE, // raw literals FALSE, kind_for_define, role_for_macro_undef, kind_for_header, role_for_header_system, role_for_header_local, end_field_type ); g_cxx.iChar = ' '; boolean bRet = cxxParserParseBlock(FALSE); cppTerminate (); cxxTokenChainClear(g_cxx.pTokenChain); if(g_cxx.pTemplateTokenChain) cxxTokenChainClear(g_cxx.pTemplateTokenChain); if(!bRet && (passCount == 1)) { CXX_DEBUG_PRINT("Processing failed: trying to rescan"); return RESCAN_FAILED; } return RESCAN_NONE; }
// // 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); }
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; }
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; }
static bool cxxParserParseBlockInternal(bool bExpectClosingBracket) { CXX_DEBUG_ENTER(); //char * szScopeName = cxxScopeGetFullName(); //CXX_DEBUG_PRINT("Scope name is '%s'",szScopeName ? szScopeName : ""); cxxParserNewStatement(); if(bExpectClosingBracket) { // FIXME: this cpp handling is kind of broken: // it works only because the moon is in the correct phase. cppBeginStatement(); } for(;;) { if(!cxxParserParseNextToken()) { found_eof: if(bExpectClosingBracket) { CXX_DEBUG_LEAVE_TEXT( "Syntax error: found EOF in block but a closing " \ "bracket was expected!" ); return false; } CXX_DEBUG_LEAVE_TEXT("EOF in main block"); return true; // EOF } process_token: CXX_DEBUG_PRINT( "Token '%s' of type 0x%02x", vStringValue(g_cxx.pToken->pszWord), g_cxx.pToken->eType ); switch(g_cxx.pToken->eType) { case CXXTokenTypeKeyword: { switch(g_cxx.pToken->eKeyword) { case CXXKeywordNAMESPACE: { enum CXXScopeType eScopeType = cxxScopeGetType(); if( ( // toplevel or nested within a namespace (eScopeType == CXXScopeTypeNamespace) || // namespace X = Y inside a function (eScopeType == CXXScopeTypeFunction) ) && ( // either certainly C++ g_cxx.bConfirmedCPPLanguage || // or a "sane" namespace syntax ( !cxxTokenChainPreviousTokenOfType( g_cxx.pToken, CXXTokenTypeStar | CXXTokenTypeAnd | CXXTokenTypeKeyword ) ) ) ) { if(!cxxParserParseNamespace()) { CXX_DEBUG_LEAVE_TEXT("Failed to parse namespace"); return false; } } else { // If we're pretty sure this is C++ then this is a syntax error. // If we're not sure (namely when we're in a *.h file) then // let's try to be flexible: treat the namespace keyword as an identifier. if(!g_cxx.bConfirmedCPPLanguage) { CXX_DEBUG_LEAVE_TEXT( "Found namespace in unexpected place, but we're not sure it's really C++ " "so we'll treat it as an identifier instead" ); g_cxx.pToken->eType = CXXTokenTypeIdentifier; continue; } CXX_DEBUG_LEAVE_TEXT( "Found namespace in a wrong place: we're probably out of sync" ); return false; } cxxParserNewStatement(); } break; case CXXKeywordTEMPLATE: if(!cxxParserParseTemplatePrefix()) { CXX_DEBUG_LEAVE_TEXT("Failed to parse template"); return false; } // Here we are just after the "template<parameters>" prefix. break; case CXXKeywordTYPEDEF: // Mark the next declaration as a typedef g_cxx.uKeywordState |= CXXParserKeywordStateSeenTypedef; cxxTokenChainClear(g_cxx.pTokenChain); break; case CXXKeywordENUM: if(!cxxParserParseEnum()) { CXX_DEBUG_LEAVE_TEXT("Failed to parse enum"); return false; } break; case CXXKeywordCLASS: if(!cxxParserParseClassStructOrUnion(CXXKeywordCLASS,CXXTagCPPKindCLASS,CXXScopeTypeClass)) { CXX_DEBUG_LEAVE_TEXT("Failed to parse class/struct/union"); return false; } break; case CXXKeywordSTRUCT: if(!cxxParserParseClassStructOrUnion(CXXKeywordSTRUCT,CXXTagKindSTRUCT,CXXScopeTypeStruct)) { CXX_DEBUG_LEAVE_TEXT("Failed to parse class/struct/union"); return false; } break; case CXXKeywordUNION: if(!cxxParserParseClassStructOrUnion(CXXKeywordUNION,CXXTagKindUNION,CXXScopeTypeUnion)) { CXX_DEBUG_LEAVE_TEXT("Failed to parse class/struct/union"); return false; } break; case CXXKeywordPUBLIC: case CXXKeywordPROTECTED: case CXXKeywordPRIVATE: // Note that the class keyword has its own handler // so the only possibility here is an access specifier if(!cxxParserParseAccessSpecifier()) { CXX_DEBUG_LEAVE_TEXT("Failed to parse access specifier"); return false; } break; case CXXKeywordUSING: if(!cxxParserParseUsingClause()) { CXX_DEBUG_LEAVE_TEXT("Failed to parse using clause"); return false; } cxxParserNewStatement(); break; case CXXKeywordIF: case CXXKeywordFOR: case CXXKeywordWHILE: case CXXKeywordSWITCH: if(!cxxParserParseIfForWhileSwitch()) { CXX_DEBUG_LEAVE_TEXT("Failed to parse if/for/while/switch"); return false; } cxxParserNewStatement(); // Force the cpp preprocessor to think that we're in the middle of a statement. cppBeginStatement(); break; case CXXKeywordTRY: case CXXKeywordELSE: case CXXKeywordDO: // parse as normal statement/block cxxParserNewStatement(); // Force the cpp preprocessor to think that we're in the middle of a statement. cppBeginStatement(); break; case CXXKeywordRETURN: if(cxxParserCurrentLanguageIsCPP()) { // may be followed by a lambda, otherwise it's not interesting. cxxParserNewStatement(); g_cxx.uKeywordState |= CXXParserKeywordStateSeenReturn; } else { // ignore if(!cxxParserParseUpToOneOf(CXXTokenTypeSemicolon | CXXTokenTypeEOF, false)) { CXX_DEBUG_LEAVE_TEXT("Failed to parse return"); return false; } cxxParserNewStatement(); } break; case CXXKeywordCONTINUE: case CXXKeywordBREAK: case CXXKeywordGOTO: // ignore if(!cxxParserParseUpToOneOf(CXXTokenTypeSemicolon | CXXTokenTypeEOF, false)) { CXX_DEBUG_LEAVE_TEXT("Failed to parse continue/break/goto"); return false; } cxxParserNewStatement(); break; case CXXKeywordTHROW: // ignore when inside a function if(cxxScopeGetType() == CXXScopeTypeFunction) { if(!cxxParserParseUpToOneOf(CXXTokenTypeSemicolon | CXXTokenTypeEOF, false)) { CXX_DEBUG_LEAVE_TEXT("Failed to parse return/continue/break"); return false; } cxxParserNewStatement(); } break; case CXXKeywordCASE: // ignore if(!cxxParserParseUpToOneOf( CXXTokenTypeSemicolon | CXXTokenTypeEOF | CXXTokenTypeSingleColon, false )) { CXX_DEBUG_LEAVE_TEXT("Failed to parse case keyword"); return false; } cxxParserNewStatement(); break; case CXXKeywordEXTERN: g_cxx.uKeywordState |= CXXParserKeywordStateSeenExtern; cxxTokenChainDestroyLast(g_cxx.pTokenChain); if(!cxxParserParseNextToken()) goto found_eof; if(cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeStringConstant)) { // assume extern "language" // Strictly speaking this is a C++ only syntax. // However we allow it also in C as it doesn't really hurt. cxxTokenChainDestroyLast(g_cxx.pTokenChain); // Note that extern "C" may be followed by a block with declarations // // extern "C" { ... } // // However in this case the declarations are ALSO definitions // and extern "C" is used only to specify the name mangling mode. // // extern "C" int x; <-- a declaration and not a definition // extern "C" { int x; } <-- a declaration and definition: x IS defined // here and is NOT extern. // // A variable in an extern "C" block has to be re-declared extern again // to be really treated as declaration only. // // extern "C" { extern int x; } // // So in this case we do NOT treat the inner declarations as extern // and we don't need specific handling code for this case. } else { // something else: handle it the normal way goto process_token; } break; case CXXKeywordSTATIC: g_cxx.uKeywordState |= CXXParserKeywordStateSeenStatic; cxxTokenChainDestroyLast(g_cxx.pTokenChain); break; case CXXKeywordINLINE: case CXXKeyword__INLINE: case CXXKeyword__INLINE__: case CXXKeyword__FORCEINLINE: case CXXKeyword__FORCEINLINE__: g_cxx.uKeywordState |= CXXParserKeywordStateSeenInline; cxxTokenChainDestroyLast(g_cxx.pTokenChain); break; case CXXKeywordEXPLICIT: g_cxx.uKeywordState |= CXXParserKeywordStateSeenExplicit; cxxTokenChainDestroyLast(g_cxx.pTokenChain); break; case CXXKeywordOPERATOR: g_cxx.uKeywordState |= CXXParserKeywordStateSeenOperator; break; case CXXKeywordVIRTUAL: g_cxx.uKeywordState |= CXXParserKeywordStateSeenVirtual; cxxTokenChainDestroyLast(g_cxx.pTokenChain); break; case CXXKeywordMUTABLE: g_cxx.uKeywordState |= CXXParserKeywordStateSeenMutable; cxxTokenChainDestroyLast(g_cxx.pTokenChain); break; // "const" and "volatile" are part of the type. Don't treat them specially // and don't attempt to extract an eventual typedef yet, // as there might be a struct/class/union keyword following. case CXXKeywordVOLATILE: g_cxx.uKeywordState |= CXXParserKeywordStateSeenVolatile; break; case CXXKeywordCONST: g_cxx.uKeywordState |= CXXParserKeywordStateSeenConst; break; default: if(g_cxx.uKeywordState & CXXParserKeywordStateSeenTypedef) { g_cxx.uKeywordState &= ~CXXParserKeywordStateSeenTypedef; if(!cxxParserParseGenericTypedef()) { CXX_DEBUG_LEAVE_TEXT("Failed to parse generic typedef"); return false; } cxxParserNewStatement(); } break; } } break; case CXXTokenTypeSemicolon: { if( (cxxParserCurrentLanguageIsC()) && cxxScopeIsGlobal() && (!(g_cxx.uKeywordState & CXXParserKeywordStateSeenExtern)) && (!(g_cxx.uKeywordState & CXXParserKeywordStateSeenTypedef)) ) { // Special handling of K&R style function declarations. // We might be in the following situation: // // type whatever fname(par1,par2) int par1; int par2; { // ^ // switch(cxxParserMaybeParseKnRStyleFunctionDefinition()) { case 1: // K&R parser did the job and started a new statement break; case 0: // something else cxxParserAnalyzeOtherStatement(); break; default: CXX_DEBUG_LEAVE_TEXT("Failed to check for K&R style function definition"); return false; break; } } else { // K&R style function declarations not allowed here. cxxParserAnalyzeOtherStatement(); } cxxParserNewStatement(); } break; case CXXTokenTypeSingleColon: { // label ? if( (g_cxx.pTokenChain->iCount == 2) && cxxTokenTypeIs( cxxTokenChainFirst(g_cxx.pTokenChain), CXXTokenTypeIdentifier ) ) { CXXToken * pFirst = cxxTokenChainFirst(g_cxx.pTokenChain); // assume it's label tagEntryInfo * tag = cxxTagBegin(CXXTagKindLABEL,pFirst); if(tag) { tag->isFileScope = true; cxxTagCommit(); } } else { // what is this? (default: and similar things have been handled at keyword level) } } break; case CXXTokenTypeOpeningBracket: if(!cxxParserParseBlockHandleOpeningBracket()) { CXX_DEBUG_LEAVE_TEXT("Failed to handle opening bracket"); return false; } break; case CXXTokenTypeClosingBracket: // scope finished if(!bExpectClosingBracket) { CXX_DEBUG_LEAVE_TEXT( "Found unexpected closing bracket: probably preprocessing problem" ); return false; } CXX_DEBUG_LEAVE_TEXT("Closing bracket!"); cxxParserNewStatement(); return true; break; case CXXTokenTypeOpeningParenthesis: case CXXTokenTypeOpeningSquareParenthesis: if(!cxxParserParseAndCondenseCurrentSubchain( CXXTokenTypeOpeningBracket | CXXTokenTypeOpeningParenthesis | CXXTokenTypeOpeningSquareParenthesis, true, false )) { CXX_DEBUG_LEAVE_TEXT("Parsing the parenthesis failed"); return false; } if(cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeEOF)) { if(bExpectClosingBracket) { CXX_DEBUG_LEAVE_TEXT( "Syntax error: found EOF in block but a closing bracket was expected!" ); return false; } return true; // EOF } break; case CXXTokenTypeIdentifier: if(g_cxx.uKeywordState & CXXParserKeywordStateSeenTypedef) { g_cxx.uKeywordState &= ~CXXParserKeywordStateSeenTypedef; if(!cxxParserParseGenericTypedef()) { CXX_DEBUG_LEAVE_TEXT("Failed to parse generic typedef"); return false; } cxxParserNewStatement(); } break; default: // something else we didn't handle break; } } CXX_DEBUG_LEAVE_TEXT("WARNING: Not reached"); return true; }
// In case of a lambda without parentheses this is the capture list token. boolean cxxParserHandleLambda(CXXToken * pParenthesis) { CXX_DEBUG_ENTER(); CXX_DEBUG_ASSERT(cxxParserCurrentLanguageIsCPP(),"C++ only"); 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 && cxxTagFieldEnabled(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" ); cxxTagSetField( 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, CXXScopeTypeFunction, 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; }
// // This function parses input until one of the specified // tokens appears. The algorithm will also build subchains of matching // pairs ([...],(...),<...>,{...}): within the subchain analysis // of uTokenTypes is completly disabled. Subchains do nest. // // Returns true if it stops before EOF or it stops at EOF and CXXTokenTypeEOF // is present in uTokenTypes. Returns false in all the other stop conditions // and when an unmatched subchain character pair is found (syntax error). // boolean cxxParserParseAndCondenseSubchainsUpToOneOf( unsigned int uTokenTypes, unsigned int uInitialSubchainMarkerTypes ) { CXX_DEBUG_ENTER_TEXT("Token types = 0x%x",uTokenTypes); if(!cxxParserParseNextToken()) { CXX_DEBUG_LEAVE_TEXT("Found EOF"); return (uTokenTypes & CXXTokenTypeEOF); // was already at EOF } // see the declaration of CXXTokenType enum. // Shifting by 4 gives the corresponding closing token type unsigned int uFinalSubchainMarkerTypes = uInitialSubchainMarkerTypes << 4; for(;;) { //CXX_DEBUG_PRINT( // "Current token is '%s' 0x%x", // vStringValue(g_cxx.pToken->pszWord), // g_cxx.pToken->eType //); if(cxxTokenTypeIsOneOf(g_cxx.pToken,uTokenTypes)) { CXX_DEBUG_LEAVE_TEXT( "Got terminator token '%s' 0x%x", vStringValue(g_cxx.pToken->pszWord), g_cxx.pToken->eType ); return TRUE; } if(cxxTokenTypeIsOneOf(g_cxx.pToken,uInitialSubchainMarkerTypes)) { // subchain CXX_DEBUG_PRINT( "Got subchain start token '%s' 0x%x", vStringValue(g_cxx.pToken->pszWord), g_cxx.pToken->eType ); CXXToken * pParenthesis; if( cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeOpeningBracket) && cxxParserCurrentLanguageIsCPP() && (pParenthesis = cxxParserOpeningBracketIsLambda()) ) { if(!cxxParserHandleLambda(pParenthesis)) { CXX_DEBUG_LEAVE_TEXT("Failed to handle lambda"); return FALSE; } } else { if(!cxxParserParseAndCondenseCurrentSubchain( uInitialSubchainMarkerTypes, (uTokenTypes & CXXTokenTypeEOF) ) ) { CXX_DEBUG_LEAVE_TEXT( "Failed to parse subchain of type 0x%x", g_cxx.pToken->eType ); return FALSE; } } if(cxxTokenTypeIsOneOf(g_cxx.pToken,uTokenTypes)) { // was looking for a subchain CXX_DEBUG_LEAVE_TEXT( "Got terminator subchain token 0x%x", g_cxx.pToken->eType ); return TRUE; } if(!cxxParserParseNextToken()) { CXX_DEBUG_LEAVE_TEXT("Found EOF(2)"); return (uTokenTypes & CXXTokenTypeEOF); // was already at EOF } continue; // jump up to avoid checking for mismatched pairs below } // Check for mismatched brackets/parenthis // Note that if we were looking for one of [({ then we would have matched // it at the top of the for if(cxxTokenTypeIsOneOf(g_cxx.pToken,uFinalSubchainMarkerTypes)) { CXX_DEBUG_LEAVE_TEXT( "Got mismatched subchain terminator 0x%x", g_cxx.pToken->eType ); return FALSE; // unmatched: syntax error } if(!cxxParserParseNextToken()) { CXX_DEBUG_LEAVE_TEXT("Found EOF(3)"); return (uTokenTypes & CXXTokenTypeEOF); // was already at EOF } } // not reached CXX_DEBUG_LEAVE_TEXT("Internal error"); return FALSE; }
bool cxxParserParseBlockHandleOpeningBracket(void) { CXX_DEBUG_ENTER(); CXX_DEBUG_ASSERT( g_cxx.pToken->eType == CXXTokenTypeOpeningBracket, "This must be called when pointing at an opening bracket!" ); enum CXXScopeType eScopeType = cxxScopeGetType(); bool bIsCPP = cxxParserCurrentLanguageIsCPP(); CXXToken * pAux; if( ( // something = {...} (g_cxx.pToken->pPrev) && cxxTokenTypeIs(g_cxx.pToken->pPrev,CXXTokenTypeAssignment) && ( (eScopeType == CXXScopeTypeFunction) || (eScopeType == CXXScopeTypeNamespace) ) ) || ( bIsCPP && (g_cxx.pToken->pPrev) && ( ( // T { arg1, arg2, ... } (1) // T object { arg1, arg2, ... } (2) // new T { arg1, arg2, ... } (3) // Class::Class() : member { arg1, arg2, ... } { (4) cxxTokenTypeIs(g_cxx.pToken->pPrev,CXXTokenTypeIdentifier) && ( // case 1 (!g_cxx.pToken->pPrev->pPrev) || // case 4 cxxTokenTypeIsOneOf( g_cxx.pToken->pPrev->pPrev, CXXTokenTypeSingleColon | CXXTokenTypeComma ) || // cases 1,2,3 but not 4 ( // more parts of typename or maybe the "new" keyword before the identifier cxxTokenTypeIsOneOf( g_cxx.pToken->pPrev->pPrev, CXXTokenTypeIdentifier | CXXTokenTypeStar | CXXTokenTypeAnd | CXXTokenTypeGreaterThanSign | CXXTokenTypeKeyword ) && // but no parenthesis (discard things like bool test() Q_DECL_NO_THROW { ... }) (!(pAux = cxxTokenChainPreviousTokenOfType( g_cxx.pToken->pPrev->pPrev, CXXTokenTypeParenthesisChain )) ) ) ) && // "override" is handled as identifier since it's a keyword only after function signatures (strcmp(vStringValue(g_cxx.pToken->pPrev->pszWord),"override") != 0) ) || ( // type var[][][]..[] { ... } // (but not '[] { ... }' which is a parameterless lambda) cxxTokenTypeIs(g_cxx.pToken->pPrev,CXXTokenTypeSquareParenthesisChain) && ( pAux = cxxTokenChainPreviousTokenNotOfType( g_cxx.pToken->pPrev, CXXTokenTypeSquareParenthesisChain ) ) && cxxTokenTypeIs(pAux,CXXTokenTypeIdentifier) ) ) ) || ( // return { } (!g_cxx.pToken->pPrev) && (g_cxx.uKeywordState & CXXParserKeywordStateSeenReturn) ) ) { // array or list-like initialisation bool bRet = cxxParserParseAndCondenseCurrentSubchain( CXXTokenTypeOpeningBracket | CXXTokenTypeOpeningParenthesis | CXXTokenTypeOpeningSquareParenthesis, false, true ); CXX_DEBUG_LEAVE_TEXT("Handled array or list-like initialisation or return"); return bRet; } // In C++ mode check for lambdas CXXToken * pParenthesis; if( bIsCPP && (pParenthesis = cxxParserOpeningBracketIsLambda()) ) { if(!cxxParserHandleLambda(pParenthesis)) { CXX_DEBUG_LEAVE_TEXT("Lambda handling failed"); return false; } // Note that here we're leaving the token chain "alive" so further parsing can be performed. CXX_DEBUG_LEAVE_TEXT("Lambda handling succeeded"); return true; } int iScopes; int iCorkQueueIndex = CORK_NIL; CXXFunctionSignatureInfo oInfo; if(eScopeType != CXXScopeTypeFunction) { // very likely a function definition // (but may be also a toplevel block, like "extern "C" { ... }") iScopes = cxxParserExtractFunctionSignatureBeforeOpeningBracket(&oInfo,&iCorkQueueIndex); // FIXME: Handle syntax (5) of list initialization: // Class::Class() : member { arg1, arg2, ... } {... } else { // some kind of other block: // - anonymous block // - block after for(),while(),foreach(),if() and other similar stuff // (note that {}-style initializers have been handled above and thus are excluded) iScopes = 0; } cxxParserNewStatement(); if(!cxxParserParseBlock(true)) { CXX_DEBUG_LEAVE_TEXT("Failed to parse nested block"); return false; } if(iScopes < 1) { CXX_DEBUG_LEAVE_TEXT("The block was not a function"); return true; } unsigned long uEndPosition = getInputLineNumber(); // If the function contained a "try" keyword before the opening bracket // then it's likely to be a function-try-block and should be followed by a catch // block that is in the same scope. if(oInfo.uFlags & CXXFunctionSignatureInfoFunctionTryBlock) { // look for the catch blocks. CXX_DEBUG_PRINT("The function is a function-try-block: looking for catch blocks"); for(;;) { CXX_DEBUG_PRINT("Looking ahead for a catch block..."); if(!cxxParserParseNextToken()) break; // EOF if(!cxxTokenIsKeyword(g_cxx.pToken,CXXKeywordCATCH)) { // No more catches. Unget and exit. CXX_DEBUG_PRINT("No more catch blocks"); cxxParserUngetCurrentToken(); break; } // assume it's a catch block. CXX_DEBUG_PRINT("Found catch block"); if(!cxxParserParseIfForWhileSwitchCatchParenthesis()) { CXX_DEBUG_LEAVE_TEXT("Failed to parse the catch parenthesis"); return false; } // the standard requires a bracket here (catch block is always a compound statement). cxxParserNewStatement(); if(!cxxParserParseNextToken()) { CXX_DEBUG_LEAVE_TEXT("Found EOF while looking for catch() block: playing nice"); break; // EOF (would be a syntax error!) } if(!cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeOpeningBracket)) { // Aargh... CXX_DEBUG_LEAVE_TEXT("Found something unexpected while looking for catch() block: playing nice"); cxxParserUngetCurrentToken(); break; // (would be a syntax error!) } if(!cxxParserParseBlock(true)) return false; uEndPosition = getInputLineNumber(); } } if(iCorkQueueIndex > CORK_NIL) cxxParserSetEndLineForTagInCorkQueue(iCorkQueueIndex,uEndPosition); while(iScopes > 0) { cxxScopePop(); iScopes--; } CXX_DEBUG_LEAVE(); return true; }
static boolean cxxParserParseBlockHandleOpeningBracket(void) { CXX_DEBUG_ENTER(); CXX_DEBUG_ASSERT(g_cxx.pToken->eType == CXXTokenTypeOpeningBracket,"This must be called when pointing at an opening bracket!"); enum CXXTagKind eScopeKind = cxxScopeGetKind(); if( (g_cxx.pToken->pPrev) && cxxTokenTypeIs(g_cxx.pToken->pPrev,CXXTokenTypeAssignment) && ( (eScopeKind == CXXTagKindFUNCTION) || (eScopeKind == CXXTagKindNAMESPACE) ) ) { // array or C++11-list-like initialisation boolean bRet = cxxParserParseAndCondenseCurrentSubchain( CXXTokenTypeOpeningBracket | CXXTokenTypeOpeningParenthesis | CXXTokenTypeOpeningSquareParenthesis, FALSE ); CXX_DEBUG_LEAVE_TEXT("Handled array or C++11-list-like initialisation"); return bRet; } int iScopes; if(eScopeKind != CXXTagKindFUNCTION) { // very likely a function definition iScopes = cxxParserExtractFunctionSignatureBeforeOpeningBracket(); } 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( cxxParserCurrentLanguageIsCPP() && (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; } while(iScopes > 0) { cxxScopePop(); iScopes--; } CXX_DEBUG_LEAVE(); return TRUE; }
// // This is the toplevel scanning function. It's a forward-only scanner that keeps // accumulating tokens in the chain until either a characteristic token is found // or the statement ends. When a characteristic token is found it usually enters // a specialized scanning routine (e.g for classes, namespaces, structs...). // When the statement ends without finding any characteristic token the chain // is passed to an analysis routine which does a second scan pass. // boolean cxxParserParseBlock(boolean bExpectClosingBracket) { CXX_DEBUG_ENTER(); //char * szScopeName = cxxScopeGetFullName(); //CXX_DEBUG_PRINT("Scope name is '%s'",szScopeName ? szScopeName : ""); cxxParserNewStatement(); if(bExpectClosingBracket) cppBeginStatement(); // FIXME: this cpp handling is broken: it works only because the moon is in the correct phase. for(;;) { if(!cxxParserParseNextToken()) { if(bExpectClosingBracket) { CXX_DEBUG_LEAVE_TEXT("Syntax error: found EOF in block but a closing bracket was expected!"); return FALSE; } CXX_DEBUG_LEAVE_TEXT("EOF in main block"); return TRUE; // EOF } CXX_DEBUG_PRINT("Token '%s' of type 0x%02x",vStringValue(g_cxx.pToken->pszWord),g_cxx.pToken->eType); switch(g_cxx.pToken->eType) { case CXXTokenTypeKeyword: { switch(g_cxx.pToken->eKeyword) { case CXXKeywordNAMESPACE: { int iCurrentScopeKind = cxxScopeGetKind(); if(iCurrentScopeKind == CXXTagKindNAMESPACE) { // namespaces can be nested only within themselves if(!cxxParserParseNamespace()) { CXX_DEBUG_LEAVE_TEXT("Failed to parse namespace"); return FALSE; } } else { // hm... syntax error? CXX_DEBUG_LEAVE_TEXT("Found namespace in a wrong place: we're probably out of sync"); return FALSE; } //cxxParserNewStatement(); <-- already called by cxxParserParseNamespace() } break; case CXXKeywordTEMPLATE: if(!cxxParserParseTemplatePrefix()) { CXX_DEBUG_LEAVE_TEXT("Failed to parse template"); return FALSE; } // Here we are just after the "template<parameters>" prefix. break; case CXXKeywordTYPEDEF: // Mark the next declaration as a typedef g_cxx.uKeywordState |= CXXParserKeywordStateSeenTypedef; cxxTokenChainClear(g_cxx.pTokenChain); break; case CXXKeywordENUM: if(!cxxParserParseEnum()) { CXX_DEBUG_LEAVE_TEXT("Failed to parse enum"); return FALSE; } break; case CXXKeywordCLASS: if(!cxxParserParseClassStructOrUnion(CXXKeywordCLASS,CXXTagKindCLASS)) { CXX_DEBUG_LEAVE_TEXT("Failed to parse class/struct/union"); return FALSE; } break; case CXXKeywordSTRUCT: if(!cxxParserParseClassStructOrUnion(CXXKeywordSTRUCT,CXXTagKindSTRUCT)) { CXX_DEBUG_LEAVE_TEXT("Failed to parse class/struct/union"); return FALSE; } break; case CXXKeywordUNION: if(!cxxParserParseClassStructOrUnion(CXXKeywordUNION,CXXTagKindUNION)) { CXX_DEBUG_LEAVE_TEXT("Failed to parse class/struct/union"); return FALSE; } break; case CXXKeywordPUBLIC: case CXXKeywordPROTECTED: case CXXKeywordPRIVATE: // Note that the class keyword has its own handler so the only possibility here is an access specifier if(!cxxParserParseAccessSpecifier()) { CXX_DEBUG_LEAVE_TEXT("Failed to parse access specifier"); return FALSE; } break; case CXXKeywordUSING: if(!cxxParserParseUsingClause()) { CXX_DEBUG_LEAVE_TEXT("Failed to parse using clause"); return FALSE; } cxxParserNewStatement(); break; case CXXKeywordIF: case CXXKeywordFOR: case CXXKeywordWHILE: case CXXKeywordSWITCH: if(!cxxParserParseIfForWhileSwitch()) { CXX_DEBUG_LEAVE_TEXT("Failed to parse if/for/while/switch"); return FALSE; } cxxParserNewStatement(); break; case CXXKeywordTRY: case CXXKeywordELSE: case CXXKeywordDO: // parse as normal statement/block cxxParserNewStatement(); break; case CXXKeywordRETURN: if(cxxParserCurrentLanguageIsCPP()) { // may be followed by a lambda, otherwise it's not interesting. g_cxx.uKeywordState |= CXXParserKeywordStateSeenReturn; cxxParserNewStatement(); } else { // ignore if(!cxxParserParseUpToOneOf(CXXTokenTypeSemicolon | CXXTokenTypeEOF)) { CXX_DEBUG_LEAVE_TEXT("Failed to parse return"); return FALSE; } cxxParserNewStatement(); } break; case CXXKeywordCONTINUE: case CXXKeywordBREAK: case CXXKeywordGOTO: // ignore if(!cxxParserParseUpToOneOf(CXXTokenTypeSemicolon | CXXTokenTypeEOF)) { CXX_DEBUG_LEAVE_TEXT("Failed to parse continue/break/goto"); return FALSE; } cxxParserNewStatement(); break; case CXXKeywordTHROW: // ignore when inside a function if(cxxScopeGetKind() == CXXTagKindFUNCTION) { if(!cxxParserParseUpToOneOf(CXXTokenTypeSemicolon | CXXTokenTypeEOF)) { CXX_DEBUG_LEAVE_TEXT("Failed to parse return/continue/break"); return FALSE; } cxxParserNewStatement(); } break; break; case CXXKeywordCASE: // ignore if(!cxxParserParseUpToOneOf(CXXTokenTypeSemicolon | CXXTokenTypeEOF | CXXTokenTypeSingleColon)) { CXX_DEBUG_LEAVE_TEXT("Failed to parse case keyword"); return FALSE; } cxxParserNewStatement(); break; case CXXKeywordEXTERN: g_cxx.uKeywordState |= CXXParserKeywordStateSeenExtern; cxxTokenChainClear(g_cxx.pTokenChain); break; case CXXKeywordSTATIC: g_cxx.uKeywordState |= CXXParserKeywordStateSeenStatic; cxxTokenChainClear(g_cxx.pTokenChain); break; case CXXKeywordINLINE: g_cxx.uKeywordState |= CXXParserKeywordStateSeenInline; cxxTokenChainClear(g_cxx.pTokenChain); break; case CXXKeywordEXPLICIT: g_cxx.uKeywordState |= CXXParserKeywordStateSeenExplicit; cxxTokenChainClear(g_cxx.pTokenChain); break; case CXXKeywordOPERATOR: g_cxx.uKeywordState |= CXXParserKeywordStateSeenOperator; break; case CXXKeywordVIRTUAL: g_cxx.uKeywordState |= CXXParserKeywordStateSeenVirtual; break; default: if(g_cxx.uKeywordState & CXXParserKeywordStateSeenTypedef) { g_cxx.uKeywordState &= ~CXXParserKeywordStateSeenTypedef; if(!cxxParserParseGenericTypedef()) { CXX_DEBUG_LEAVE_TEXT("Failed to parse generic typedef"); return FALSE; } cxxParserNewStatement(); } break; } } break; case CXXTokenTypeSemicolon: { if( (g_cxx.eLanguage == g_cxx.eCLanguage) && cxxScopeIsGlobal() && (!(g_cxx.uKeywordState & CXXParserKeywordStateSeenExtern)) && (!(g_cxx.uKeywordState & CXXParserKeywordStateSeenTypedef)) ) { // Special handling of K&R style function declarations. // We might be in the following situation: // // type whatever fname(par1,par2) int par1; int par2; { // ^ // switch(cxxParserMaybeExtractKnRStyleFunctionDefinition()) { case 1: // got K&R style function definition, one scope was pushed. cxxParserNewStatement(); if(!cxxParserParseBlock(TRUE)) { CXX_DEBUG_LEAVE_TEXT("Failed to parse nested block"); return FALSE; } cxxScopePop(); break; case 0: // something else cxxParserAnalyzeOtherStatement(); break; default: CXX_DEBUG_LEAVE_TEXT("Failed to check for K&R style function definition"); return FALSE; break; } } else { // K&R style function declarations not allowed here. cxxParserAnalyzeOtherStatement(); } cxxParserNewStatement(); } break; case CXXTokenTypeSingleColon: { // label ? if((g_cxx.pTokenChain->iCount == 2) && cxxTokenTypeIs(cxxTokenChainFirst(g_cxx.pTokenChain),CXXTokenTypeIdentifier)) { CXXToken * pFirst = cxxTokenChainFirst(g_cxx.pTokenChain); // assume it's label tagEntryInfo * tag = cxxTagBegin(CXXTagKindLABEL,pFirst); if(tag) { tag->isFileScope = TRUE; cxxTagCommit(); } } else { // what is this? (default: and similar things have been handled at keyword level) } } break; case CXXTokenTypeOpeningBracket: if(!cxxParserParseBlockHandleOpeningBracket()) { CXX_DEBUG_LEAVE_TEXT("Failed to handle opening bracket"); return FALSE; } break; case CXXTokenTypeClosingBracket: // scope finished CXX_DEBUG_LEAVE_TEXT("Closing bracket!"); cxxParserNewStatement(); return TRUE; break; case CXXTokenTypeOpeningParenthesis: case CXXTokenTypeOpeningSquareParenthesis: if(!cxxParserParseAndCondenseCurrentSubchain( CXXTokenTypeOpeningBracket | CXXTokenTypeOpeningParenthesis | CXXTokenTypeOpeningSquareParenthesis, TRUE )) { CXX_DEBUG_LEAVE_TEXT("Parsing the parenthesis failed"); return FALSE; } if(cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeEOF)) { if(bExpectClosingBracket) { CXX_DEBUG_LEAVE_TEXT("Syntax error: found EOF in block but a closing bracket was expected!"); return FALSE; } return TRUE; // EOF } break; case CXXTokenTypeIdentifier: if(g_cxx.uKeywordState & CXXParserKeywordStateSeenTypedef) { g_cxx.uKeywordState &= ~CXXParserKeywordStateSeenTypedef; if(!cxxParserParseGenericTypedef()) { CXX_DEBUG_LEAVE_TEXT("Failed to parse generic typedef"); return FALSE; } cxxParserNewStatement(); } break; default: // something else we didn't handle break; } } CXX_DEBUG_LEAVE_TEXT("WARNING: Not reached"); return TRUE; }