rescanReason cxxCppParserMain(const unsigned int passCount) { CXX_DEBUG_ENTER(); g_cxx.eLanguage = g_cxx.eCPPLanguage; rescanReason r = cxxParserMain(passCount); CXX_DEBUG_LEAVE(); return r; }
static bool qtMocParseProperty(void) { char *pszPropType; CXX_DEBUG_ENTER(); if(!cxxParserParseNextToken()) { CXX_DEBUG_LEAVE_TEXT("EOF in cxxParserParseNextToken"); return false; } if (!cxxTokenTypeIs(g_cxx.pToken, CXXTokenTypeOpeningParenthesis)) { CXX_DEBUG_LEAVE_TEXT("Found no Opening Parenthesis after Q_PROPERTY"); return false; } if (!cxxParserParseNextToken()) { CXX_DEBUG_LEAVE_TEXT("EOF in cxxParserParseNextToken"); return false; } if (!(cxxTokenTypeIs(g_cxx.pToken, CXXTokenTypeIdentifier) || (cxxTokenTypeIs(g_cxx.pToken, CXXTokenTypeKeyword) && cxxKeywordMayBePartOfTypeName (g_cxx.pToken->eKeyword)))) { CXX_DEBUG_LEAVE_TEXT("Found no identifier after Q_PROPERTY("); cxxParserSkipToClosingParenthesisOrEOF (); return false; } pszPropType = vStringStrdup (g_cxx.pToken->pszWord); if(!cxxParserParseNextToken()) { CXX_DEBUG_LEAVE_TEXT("EOF in cxxParserParseNextToken"); eFree (pszPropType); return false; } if (!cxxTokenTypeIs(g_cxx.pToken, CXXTokenTypeIdentifier)) { CXX_DEBUG_LEAVE_TEXT("Found no identifier after Q_PROPERTY(%s", pszPropType); cxxParserSkipToClosingParenthesisOrEOF (); eFree (pszPropType); return false; } qtMocMakeTagForProperty (g_cxx.pToken, pszPropType); eFree (pszPropType); cxxParserSkipToClosingParenthesisOrEOF (); CXX_DEBUG_LEAVE(); return true; }
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(); }
// 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; }
// 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; }
// // 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; }
// // 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; }
// // 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; }
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; }
// 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; }
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; }
bool cxxParserParseBlockHandleOpeningBracket(void) { CXX_DEBUG_ENTER(); CXX_DEBUG_ASSERT( g_cxx.pToken->eType == CXXTokenTypeOpeningBracket, "This must be called when pointing at an opening bracket!" ); enum CXXScopeType eScopeType = cxxScopeGetType(); bool bIsCPP = cxxParserCurrentLanguageIsCPP(); CXXToken * pAux; if( ( // something = {...} (g_cxx.pToken->pPrev) && cxxTokenTypeIs(g_cxx.pToken->pPrev,CXXTokenTypeAssignment) && ( (eScopeType == CXXScopeTypeFunction) || (eScopeType == CXXScopeTypeNamespace) ) ) || ( bIsCPP && (g_cxx.pToken->pPrev) && ( ( // T { arg1, arg2, ... } (1) // T object { arg1, arg2, ... } (2) // new T { arg1, arg2, ... } (3) // Class::Class() : member { arg1, arg2, ... } { (4) cxxTokenTypeIs(g_cxx.pToken->pPrev,CXXTokenTypeIdentifier) && ( // case 1 (!g_cxx.pToken->pPrev->pPrev) || // case 4 cxxTokenTypeIsOneOf( g_cxx.pToken->pPrev->pPrev, CXXTokenTypeSingleColon | CXXTokenTypeComma ) || // cases 1,2,3 but not 4 ( // more parts of typename or maybe the "new" keyword before the identifier cxxTokenTypeIsOneOf( g_cxx.pToken->pPrev->pPrev, CXXTokenTypeIdentifier | CXXTokenTypeStar | CXXTokenTypeAnd | CXXTokenTypeGreaterThanSign | CXXTokenTypeKeyword ) && // but no parenthesis (discard things like bool test() Q_DECL_NO_THROW { ... }) (!(pAux = cxxTokenChainPreviousTokenOfType( g_cxx.pToken->pPrev->pPrev, CXXTokenTypeParenthesisChain )) ) ) ) && // "override" is handled as identifier since it's a keyword only after function signatures (strcmp(vStringValue(g_cxx.pToken->pPrev->pszWord),"override") != 0) ) || ( // type var[][][]..[] { ... } // (but not '[] { ... }' which is a parameterless lambda) cxxTokenTypeIs(g_cxx.pToken->pPrev,CXXTokenTypeSquareParenthesisChain) && ( pAux = cxxTokenChainPreviousTokenNotOfType( g_cxx.pToken->pPrev, CXXTokenTypeSquareParenthesisChain ) ) && cxxTokenTypeIs(pAux,CXXTokenTypeIdentifier) ) ) ) || ( // return { } (!g_cxx.pToken->pPrev) && (g_cxx.uKeywordState & CXXParserKeywordStateSeenReturn) ) ) { // array or list-like initialisation bool bRet = cxxParserParseAndCondenseCurrentSubchain( CXXTokenTypeOpeningBracket | CXXTokenTypeOpeningParenthesis | CXXTokenTypeOpeningSquareParenthesis, false, true ); CXX_DEBUG_LEAVE_TEXT("Handled array or list-like initialisation or return"); return bRet; } // In C++ mode check for lambdas CXXToken * pParenthesis; if( bIsCPP && (pParenthesis = cxxParserOpeningBracketIsLambda()) ) { if(!cxxParserHandleLambda(pParenthesis)) { CXX_DEBUG_LEAVE_TEXT("Lambda handling failed"); return false; } // Note that here we're leaving the token chain "alive" so further parsing can be performed. CXX_DEBUG_LEAVE_TEXT("Lambda handling succeeded"); return true; } int iScopes; int iCorkQueueIndex = CORK_NIL; CXXFunctionSignatureInfo oInfo; if(eScopeType != CXXScopeTypeFunction) { // very likely a function definition // (but may be also a toplevel block, like "extern "C" { ... }") iScopes = cxxParserExtractFunctionSignatureBeforeOpeningBracket(&oInfo,&iCorkQueueIndex); // FIXME: Handle syntax (5) of list initialization: // Class::Class() : member { arg1, arg2, ... } {... } else { // some kind of other block: // - anonymous block // - block after for(),while(),foreach(),if() and other similar stuff // (note that {}-style initializers have been handled above and thus are excluded) iScopes = 0; } cxxParserNewStatement(); if(!cxxParserParseBlock(true)) { CXX_DEBUG_LEAVE_TEXT("Failed to parse nested block"); return false; } if(iScopes < 1) { CXX_DEBUG_LEAVE_TEXT("The block was not a function"); return true; } unsigned long uEndPosition = getInputLineNumber(); // If the function contained a "try" keyword before the opening bracket // then it's likely to be a function-try-block and should be followed by a catch // block that is in the same scope. if(oInfo.uFlags & CXXFunctionSignatureInfoFunctionTryBlock) { // look for the catch blocks. CXX_DEBUG_PRINT("The function is a function-try-block: looking for catch blocks"); for(;;) { CXX_DEBUG_PRINT("Looking ahead for a catch block..."); if(!cxxParserParseNextToken()) break; // EOF if(!cxxTokenIsKeyword(g_cxx.pToken,CXXKeywordCATCH)) { // No more catches. Unget and exit. CXX_DEBUG_PRINT("No more catch blocks"); cxxParserUngetCurrentToken(); break; } // assume it's a catch block. CXX_DEBUG_PRINT("Found catch block"); if(!cxxParserParseIfForWhileSwitchCatchParenthesis()) { CXX_DEBUG_LEAVE_TEXT("Failed to parse the catch parenthesis"); return false; } // the standard requires a bracket here (catch block is always a compound statement). cxxParserNewStatement(); if(!cxxParserParseNextToken()) { CXX_DEBUG_LEAVE_TEXT("Found EOF while looking for catch() block: playing nice"); break; // EOF (would be a syntax error!) } if(!cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeOpeningBracket)) { // Aargh... CXX_DEBUG_LEAVE_TEXT("Found something unexpected while looking for catch() block: playing nice"); cxxParserUngetCurrentToken(); break; // (would be a syntax error!) } if(!cxxParserParseBlock(true)) return false; uEndPosition = getInputLineNumber(); } } if(iCorkQueueIndex > CORK_NIL) cxxParserSetEndLineForTagInCorkQueue(iCorkQueueIndex,uEndPosition); while(iScopes > 0) { cxxScopePop(); iScopes--; } CXX_DEBUG_LEAVE(); return true; }
// // This is the toplevel scanning function. It's a forward-only scanner that keeps // accumulating tokens in the chain until either a characteristic token is found // or the statement ends. When a characteristic token is found it usually enters // a specialized scanning routine (e.g for classes, namespaces, structs...). // When the statement ends without finding any characteristic token the chain // is passed to an analysis routine which does a second scan pass. // boolean cxxParserParseBlock(boolean bExpectClosingBracket) { CXX_DEBUG_ENTER(); //char * szScopeName = cxxScopeGetFullName(); //CXX_DEBUG_PRINT("Scope name is '%s'",szScopeName ? szScopeName : ""); cxxParserNewStatement(); if(bExpectClosingBracket) cppBeginStatement(); // FIXME: this cpp handling is broken: it works only because the moon is in the correct phase. for(;;) { if(!cxxParserParseNextToken()) { if(bExpectClosingBracket) { CXX_DEBUG_LEAVE_TEXT("Syntax error: found EOF in block but a closing bracket was expected!"); return FALSE; } CXX_DEBUG_LEAVE_TEXT("EOF in main block"); return TRUE; // EOF } CXX_DEBUG_PRINT("Token '%s' of type 0x%02x",vStringValue(g_cxx.pToken->pszWord),g_cxx.pToken->eType); switch(g_cxx.pToken->eType) { case CXXTokenTypeKeyword: { switch(g_cxx.pToken->eKeyword) { case CXXKeywordNAMESPACE: { int iCurrentScopeKind = cxxScopeGetKind(); if(iCurrentScopeKind == CXXTagKindNAMESPACE) { // namespaces can be nested only within themselves if(!cxxParserParseNamespace()) { CXX_DEBUG_LEAVE_TEXT("Failed to parse namespace"); return FALSE; } } else { // hm... syntax error? CXX_DEBUG_LEAVE_TEXT("Found namespace in a wrong place: we're probably out of sync"); return FALSE; } //cxxParserNewStatement(); <-- already called by cxxParserParseNamespace() } break; case CXXKeywordTEMPLATE: if(!cxxParserParseTemplatePrefix()) { CXX_DEBUG_LEAVE_TEXT("Failed to parse template"); return FALSE; } // Here we are just after the "template<parameters>" prefix. break; case CXXKeywordTYPEDEF: // Mark the next declaration as a typedef g_cxx.uKeywordState |= CXXParserKeywordStateSeenTypedef; cxxTokenChainClear(g_cxx.pTokenChain); break; case CXXKeywordENUM: if(!cxxParserParseEnum()) { CXX_DEBUG_LEAVE_TEXT("Failed to parse enum"); return FALSE; } break; case CXXKeywordCLASS: if(!cxxParserParseClassStructOrUnion(CXXKeywordCLASS,CXXTagKindCLASS)) { CXX_DEBUG_LEAVE_TEXT("Failed to parse class/struct/union"); return FALSE; } break; case CXXKeywordSTRUCT: if(!cxxParserParseClassStructOrUnion(CXXKeywordSTRUCT,CXXTagKindSTRUCT)) { CXX_DEBUG_LEAVE_TEXT("Failed to parse class/struct/union"); return FALSE; } break; case CXXKeywordUNION: if(!cxxParserParseClassStructOrUnion(CXXKeywordUNION,CXXTagKindUNION)) { CXX_DEBUG_LEAVE_TEXT("Failed to parse class/struct/union"); return FALSE; } break; case CXXKeywordPUBLIC: case CXXKeywordPROTECTED: case CXXKeywordPRIVATE: // Note that the class keyword has its own handler so the only possibility here is an access specifier if(!cxxParserParseAccessSpecifier()) { CXX_DEBUG_LEAVE_TEXT("Failed to parse access specifier"); return FALSE; } break; case CXXKeywordUSING: if(!cxxParserParseUsingClause()) { CXX_DEBUG_LEAVE_TEXT("Failed to parse using clause"); return FALSE; } cxxParserNewStatement(); break; case CXXKeywordIF: case CXXKeywordFOR: case CXXKeywordWHILE: case CXXKeywordSWITCH: if(!cxxParserParseIfForWhileSwitch()) { CXX_DEBUG_LEAVE_TEXT("Failed to parse if/for/while/switch"); return FALSE; } cxxParserNewStatement(); break; case CXXKeywordTRY: case CXXKeywordELSE: case CXXKeywordDO: // parse as normal statement/block cxxParserNewStatement(); break; case CXXKeywordRETURN: if(cxxParserCurrentLanguageIsCPP()) { // may be followed by a lambda, otherwise it's not interesting. g_cxx.uKeywordState |= CXXParserKeywordStateSeenReturn; cxxParserNewStatement(); } else { // ignore if(!cxxParserParseUpToOneOf(CXXTokenTypeSemicolon | CXXTokenTypeEOF)) { CXX_DEBUG_LEAVE_TEXT("Failed to parse return"); return FALSE; } cxxParserNewStatement(); } break; case CXXKeywordCONTINUE: case CXXKeywordBREAK: case CXXKeywordGOTO: // ignore if(!cxxParserParseUpToOneOf(CXXTokenTypeSemicolon | CXXTokenTypeEOF)) { CXX_DEBUG_LEAVE_TEXT("Failed to parse continue/break/goto"); return FALSE; } cxxParserNewStatement(); break; case CXXKeywordTHROW: // ignore when inside a function if(cxxScopeGetKind() == CXXTagKindFUNCTION) { if(!cxxParserParseUpToOneOf(CXXTokenTypeSemicolon | CXXTokenTypeEOF)) { CXX_DEBUG_LEAVE_TEXT("Failed to parse return/continue/break"); return FALSE; } cxxParserNewStatement(); } break; break; case CXXKeywordCASE: // ignore if(!cxxParserParseUpToOneOf(CXXTokenTypeSemicolon | CXXTokenTypeEOF | CXXTokenTypeSingleColon)) { CXX_DEBUG_LEAVE_TEXT("Failed to parse case keyword"); return FALSE; } cxxParserNewStatement(); break; case CXXKeywordEXTERN: g_cxx.uKeywordState |= CXXParserKeywordStateSeenExtern; cxxTokenChainClear(g_cxx.pTokenChain); break; case CXXKeywordSTATIC: g_cxx.uKeywordState |= CXXParserKeywordStateSeenStatic; cxxTokenChainClear(g_cxx.pTokenChain); break; case CXXKeywordINLINE: g_cxx.uKeywordState |= CXXParserKeywordStateSeenInline; cxxTokenChainClear(g_cxx.pTokenChain); break; case CXXKeywordEXPLICIT: g_cxx.uKeywordState |= CXXParserKeywordStateSeenExplicit; cxxTokenChainClear(g_cxx.pTokenChain); break; case CXXKeywordOPERATOR: g_cxx.uKeywordState |= CXXParserKeywordStateSeenOperator; break; case CXXKeywordVIRTUAL: g_cxx.uKeywordState |= CXXParserKeywordStateSeenVirtual; break; default: if(g_cxx.uKeywordState & CXXParserKeywordStateSeenTypedef) { g_cxx.uKeywordState &= ~CXXParserKeywordStateSeenTypedef; if(!cxxParserParseGenericTypedef()) { CXX_DEBUG_LEAVE_TEXT("Failed to parse generic typedef"); return FALSE; } cxxParserNewStatement(); } break; } } break; case CXXTokenTypeSemicolon: { if( (g_cxx.eLanguage == g_cxx.eCLanguage) && cxxScopeIsGlobal() && (!(g_cxx.uKeywordState & CXXParserKeywordStateSeenExtern)) && (!(g_cxx.uKeywordState & CXXParserKeywordStateSeenTypedef)) ) { // Special handling of K&R style function declarations. // We might be in the following situation: // // type whatever fname(par1,par2) int par1; int par2; { // ^ // switch(cxxParserMaybeExtractKnRStyleFunctionDefinition()) { case 1: // got K&R style function definition, one scope was pushed. cxxParserNewStatement(); if(!cxxParserParseBlock(TRUE)) { CXX_DEBUG_LEAVE_TEXT("Failed to parse nested block"); return FALSE; } cxxScopePop(); break; case 0: // something else cxxParserAnalyzeOtherStatement(); break; default: CXX_DEBUG_LEAVE_TEXT("Failed to check for K&R style function definition"); return FALSE; break; } } else { // K&R style function declarations not allowed here. cxxParserAnalyzeOtherStatement(); } cxxParserNewStatement(); } break; case CXXTokenTypeSingleColon: { // label ? if((g_cxx.pTokenChain->iCount == 2) && cxxTokenTypeIs(cxxTokenChainFirst(g_cxx.pTokenChain),CXXTokenTypeIdentifier)) { CXXToken * pFirst = cxxTokenChainFirst(g_cxx.pTokenChain); // assume it's label tagEntryInfo * tag = cxxTagBegin(CXXTagKindLABEL,pFirst); if(tag) { tag->isFileScope = TRUE; cxxTagCommit(); } } else { // what is this? (default: and similar things have been handled at keyword level) } } break; case CXXTokenTypeOpeningBracket: if(!cxxParserParseBlockHandleOpeningBracket()) { CXX_DEBUG_LEAVE_TEXT("Failed to handle opening bracket"); return FALSE; } break; case CXXTokenTypeClosingBracket: // scope finished CXX_DEBUG_LEAVE_TEXT("Closing bracket!"); cxxParserNewStatement(); return TRUE; break; case CXXTokenTypeOpeningParenthesis: case CXXTokenTypeOpeningSquareParenthesis: if(!cxxParserParseAndCondenseCurrentSubchain( CXXTokenTypeOpeningBracket | CXXTokenTypeOpeningParenthesis | CXXTokenTypeOpeningSquareParenthesis, TRUE )) { CXX_DEBUG_LEAVE_TEXT("Parsing the parenthesis failed"); return FALSE; } if(cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeEOF)) { if(bExpectClosingBracket) { CXX_DEBUG_LEAVE_TEXT("Syntax error: found EOF in block but a closing bracket was expected!"); return FALSE; } return TRUE; // EOF } break; case CXXTokenTypeIdentifier: if(g_cxx.uKeywordState & CXXParserKeywordStateSeenTypedef) { g_cxx.uKeywordState &= ~CXXParserKeywordStateSeenTypedef; if(!cxxParserParseGenericTypedef()) { CXX_DEBUG_LEAVE_TEXT("Failed to parse generic typedef"); return FALSE; } cxxParserNewStatement(); } break; default: // something else we didn't handle break; } } CXX_DEBUG_LEAVE_TEXT("WARNING: Not reached"); return TRUE; }
// 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(); }
// // This is called at block level, upon encountering a semicolon, an unbalanced // closing bracket or EOF.The current token is something like: // static const char * variable; // int i = .... // const QString & function(whatever) const; // QString szText("ascii"); // QString(...) // // Notable facts: // - several special statements never end up here: this includes class, // struct, union, enum, namespace, typedef, case, try, catch and other // similar stuff. // - the terminator is always at the end. It's either a semicolon, a closing // bracket or an EOF // - the parentheses and brackets are always condensed in subchains // (unless unbalanced). // // int __attribute__() function(); // | | // ("whatever") (int var1,type var2) // // const char * strings[] = {} // | | // [10] { "string","string",.... } // // This function tries to extract variable declarations and function prototypes. // // Yes, it's complex: it's because C/C++ is complex. // void cxxParserAnalyzeOtherStatement(void) { CXX_DEBUG_ENTER(); #ifdef CXX_DO_DEBUGGING vString * pChain = cxxTokenChainJoin(g_cxx.pTokenChain,NULL,0); CXX_DEBUG_PRINT("Analyzing statement '%s'",vStringValue(pChain)); vStringDelete(pChain); #endif CXX_DEBUG_ASSERT( g_cxx.pTokenChain->iCount > 0, "There should be at least the terminator here!" ); if(g_cxx.pTokenChain->iCount < 2) { CXX_DEBUG_LEAVE_TEXT("Empty statement"); return; } if(g_cxx.uKeywordState & CXXParserKeywordStateSeenReturn) { CXX_DEBUG_LEAVE_TEXT("Statement after a return is not interesting"); return; } // Everything we can make sense of starts with an identifier or keyword. // This is usually a type name (eventually decorated by some attributes // and modifiers) with the notable exception of constructor/destructor // declarations (which are still identifiers tho). CXXToken * t = cxxTokenChainFirst(g_cxx.pTokenChain); if(!cxxTokenTypeIsOneOf(t,CXXTokenTypeIdentifier | CXXTokenTypeKeyword)) { CXX_DEBUG_LEAVE_TEXT("Statement does not start with an identifier or keyword"); return; } enum CXXTagKind eScopeKind = cxxScopeGetKind(); CXXFunctionSignatureInfo oInfo; // kinda looks like a function or variable instantiation... maybe if(eScopeKind == CXXTagKindFUNCTION) { // prefer variable declarations. // if none found then try function prototype if(cxxParserExtractVariableDeclarations(g_cxx.pTokenChain,0)) { CXX_DEBUG_LEAVE_TEXT("Found variable declarations"); return; } // FIXME: This *COULD* work but we should first rule out the possibility // of simple function calls like func(a). The function signature search // should be far stricter here. //if(cxxParserLookForFunctionSignature(g_cxx.pTokenChain,&oInfo,NULL)) // cxxParserEmitFunctionTags(&oInfo,CXXTagKindPROTOTYPE,0); CXX_DEBUG_LEAVE(); return; } // prefer function. if(cxxParserLookForFunctionSignature(g_cxx.pTokenChain,&oInfo,NULL)) { int iScopesPushed = cxxParserEmitFunctionTags(&oInfo,CXXTagKindPROTOTYPE,CXXEmitFunctionTagsPushScopes,NULL); while(iScopesPushed > 0) { cxxScopePop(); iScopesPushed--; } CXX_DEBUG_LEAVE_TEXT("Found function prototypes"); return; } if( g_cxx.uKeywordState & ( CXXParserKeywordStateSeenInline | CXXParserKeywordStateSeenExplicit | CXXParserKeywordStateSeenOperator | CXXParserKeywordStateSeenVirtual ) ) { // must be function! CXX_DEBUG_LEAVE_TEXT( "WARNING: Was expecting to find a function prototype " \ "but did not find one" ); return; } cxxParserExtractVariableDeclarations(g_cxx.pTokenChain,0); CXX_DEBUG_LEAVE_TEXT("Nothing else"); }
boolean cxxParserParseClassStructOrUnion( enum CXXKeyword eKeyword, enum CXXTagKind eTagKind ) { CXX_DEBUG_ENTER(); // make sure that there is only the class/struct/union keyword in the chain while(g_cxx.pTokenChain->iCount > 1) cxxTokenChainDestroyFirst(g_cxx.pTokenChain); // this may be cleared below boolean bParsingTypedef = (g_cxx.uKeywordState & CXXParserKeywordStateSeenTypedef); /* Spec is: class-key attr class-head-name base-clause { member-specification } class-key - one of class or struct. The keywords are identical except for the default member access and the default base class access. attr(C++11) - optional sequence of any number of attributes, may include alignas specifier class-head-name - the name of the class that's being defined. Optionally qualified, optionally followed by keyword final. The name may be omitted, in which case the class is unnamed (note that unnamed class cannot be final) base-clause - optional list of one or more parent classes and the model of inheritance used for each (see derived class) member-specification - list of access specifiers, member object and member function declarations and definitions (see below) */ // Skip attr and class-head-name // enable "final" keyword handling g_cxx.bParsingClassStructOrUnionDeclaration = TRUE; unsigned int uTerminatorTypes = CXXTokenTypeEOF | CXXTokenTypeSingleColon | CXXTokenTypeSemicolon | CXXTokenTypeOpeningBracket | CXXTokenTypeSmallerThanSign; if(eTagKind != CXXTagKindCLASS) uTerminatorTypes |= CXXTokenTypeParenthesisChain | CXXTokenTypeAssignment; boolean bRet; for(;;) { bRet = cxxParserParseUpToOneOf(uTerminatorTypes); if(!bRet) { g_cxx.bParsingClassStructOrUnionDeclaration = FALSE; CXX_DEBUG_LEAVE_TEXT("Could not parse class/struct/union name"); return FALSE; } if(!cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeSmallerThanSign)) break; // Probably a template specialisation // template<typename T> struct X<int> // { // } // FIXME: Should we add the specialisation arguments somewhere? // Maye as a separate field? bRet = cxxParserParseAndCondenseCurrentSubchain( CXXTokenTypeOpeningParenthesis | CXXTokenTypeOpeningBracket | CXXTokenTypeOpeningSquareParenthesis | CXXTokenTypeSmallerThanSign, FALSE ); if(!bRet) { g_cxx.bParsingClassStructOrUnionDeclaration = FALSE; CXX_DEBUG_LEAVE_TEXT("Could not parse class/struct/union name"); return FALSE; } } g_cxx.bParsingClassStructOrUnionDeclaration = FALSE; if(cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeParenthesisChain)) { // probably a function declaration/prototype // something like struct x * func().... // do not clear statement CXX_DEBUG_LEAVE_TEXT("Probably a function declaration!"); return TRUE; } // FIXME: This block is duplicated in enum if(cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeSemicolon)) { if(g_cxx.pTokenChain->iCount > 3) { // [typedef] struct X Y; <-- typedef has been removed! if(bParsingTypedef) cxxParserExtractTypedef(g_cxx.pTokenChain,TRUE); else cxxParserExtractVariableDeclarations(g_cxx.pTokenChain,0); } cxxParserNewStatement(); CXX_DEBUG_LEAVE(); return TRUE; } if(cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeAssignment)) { if(g_cxx.pTokenChain->iCount > 3) { // struct X Y = ...; cxxParserExtractVariableDeclarations(g_cxx.pTokenChain,0); } // Skip the initialization (which almost certainly contains a block) if(!cxxParserParseUpToOneOf(CXXTokenTypeEOF | CXXTokenTypeSemicolon)) { CXX_DEBUG_LEAVE_TEXT("Failed to parse up to EOF/semicolon"); return FALSE; } cxxParserNewStatement(); CXX_DEBUG_LEAVE(); return TRUE; } if(cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeEOF)) { // tolerate EOF, just ignore this cxxParserNewStatement(); CXX_DEBUG_LEAVE_TEXT("EOF: ignoring"); return TRUE; } // semicolon or opening bracket // check if we can extract a class name identifier CXXToken * pClassName = cxxTokenChainLastTokenOfType( g_cxx.pTokenChain, CXXTokenTypeIdentifier ); int iPushedScopes = 0; if(pClassName) { // good. // It may be qualified though. CXXToken * pNamespaceBegin = pClassName; CXXToken * pPrev = pClassName->pPrev; while(pPrev) { if(!cxxTokenTypeIs(pPrev,CXXTokenTypeMultipleColons)) break; pPrev = pPrev->pPrev; if(!pPrev) break; if(!cxxTokenTypeIs(pPrev,CXXTokenTypeIdentifier)) break; pNamespaceBegin = pPrev; pPrev = pPrev->pPrev; } while(pNamespaceBegin != pClassName) { CXXToken * pNext = pNamespaceBegin->pNext; cxxTokenChainTake(g_cxx.pTokenChain,pNamespaceBegin); // FIXME: We don't really know if it's a class! cxxScopePush(pNamespaceBegin,CXXTagKindCLASS,CXXScopeAccessUnknown); iPushedScopes++; pNamespaceBegin = pNext->pNext; } CXX_DEBUG_PRINT( "Class/struct/union name is %s", vStringValue(pClassName->pszWord) ); cxxTokenChainTake(g_cxx.pTokenChain,pClassName); } else { pClassName = cxxTokenCreateAnonymousIdentifier(eTagKind); CXX_DEBUG_PRINT( "Class/struct/union name is %s (anonymous)", vStringValue(pClassName->pszWord) ); } if(cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeSingleColon)) { // check for base classes cxxTokenChainClear(g_cxx.pTokenChain); if(!cxxParserParseUpToOneOf( CXXTokenTypeEOF | CXXTokenTypeSemicolon | CXXTokenTypeOpeningBracket )) { cxxTokenDestroy(pClassName); CXX_DEBUG_LEAVE_TEXT("Failed to parse base class part"); return FALSE; } if(cxxTokenTypeIsOneOf(g_cxx.pToken,CXXTokenTypeSemicolon | CXXTokenTypeEOF)) { cxxTokenDestroy(pClassName); cxxParserNewStatement(); CXX_DEBUG_LEAVE_TEXT("Syntax error: ignoring"); return TRUE; } cxxTokenChainDestroyLast(g_cxx.pTokenChain); // remove the { } else { cxxTokenChainClear(g_cxx.pTokenChain); } tagEntryInfo * tag = cxxTagBegin(eTagKind,pClassName); int iCorkQueueIndex = CORK_NIL; if(tag) { if(g_cxx.pTokenChain->iCount > 0) { // Strip inheritance type information // FIXME: This could be optional! CXXToken * t = cxxTokenChainFirst(g_cxx.pTokenChain); while(t) { if( cxxTokenTypeIs(t,CXXTokenTypeKeyword) && ( (t->eKeyword == CXXKeywordPUBLIC) || (t->eKeyword == CXXKeywordPROTECTED) || (t->eKeyword == CXXKeywordPRIVATE) || (t->eKeyword == CXXKeywordVIRTUAL) ) ) { CXXToken * pNext = t->pNext; cxxTokenChainTake(g_cxx.pTokenChain,t); cxxTokenDestroy(t); t = pNext; } else { t = t->pNext; } } if(g_cxx.pTokenChain->iCount > 0) { cxxTokenChainCondense( g_cxx.pTokenChain, CXXTokenChainCondenseNoTrailingSpaces ); tag->extensionFields.inheritance = vStringValue( g_cxx.pTokenChain->pHead->pszWord ); } } if( g_cxx.pTemplateTokenChain && (g_cxx.pTemplateTokenChain->iCount > 0) && cxxTagCPPFieldEnabled(CXXTagCPPFieldTemplate) ) { cxxTokenChainNormalizeTypeNameSpacing(g_cxx.pTemplateTokenChain); cxxTokenChainCondense(g_cxx.pTemplateTokenChain,0); cxxTagSetCPPField( CXXTagCPPFieldTemplate, vStringValue(cxxTokenChainFirst(g_cxx.pTemplateTokenChain)->pszWord) ); } tag->isFileScope = !isInputHeaderFile(); iCorkQueueIndex = cxxTagCommit(); } cxxScopePush( pClassName, eTagKind, (eTagKind == CXXTagKindCLASS) ? CXXScopeAccessPrivate : CXXScopeAccessPublic ); vString * pScopeName = cxxScopeGetFullNameAsString(); if(!cxxParserParseBlock(TRUE)) { CXX_DEBUG_LEAVE_TEXT("Failed to parse scope"); if(pScopeName) vStringDelete(pScopeName); return FALSE; } if(iCorkQueueIndex > CORK_NIL) cxxParserMarkEndLineForTagInCorkQueue(iCorkQueueIndex); iPushedScopes++; while(iPushedScopes > 0) { cxxScopePop(); iPushedScopes--; } bRet = cxxParserParseEnumStructClassOrUnionFullDeclarationTrailer( bParsingTypedef, eKeyword, eTagKind, vStringValue(pScopeName) ); if(pScopeName) vStringDelete(pScopeName); cxxParserNewStatement(); CXX_DEBUG_LEAVE(); return bRet; };
boolean cxxParserParseEnum(void) { CXX_DEBUG_ENTER(); // may be cleared below boolean bParsingTypedef = (g_cxx.uKeywordState & CXXParserKeywordStateSeenTypedef); /* Spec is: enum-key attr(optional) identifier(optional) enum-base(optional) { enumerator-list(optional) } (1) enum-key attr(optional) identifier enum-base(optional) ; (2) (since C++11) */ // Skip attr and class-head-name if(!cxxParserParseUpToOneOf( CXXTokenTypeEOF | CXXTokenTypeSemicolon | CXXTokenTypeParenthesisChain | CXXTokenTypeOpeningBracket )) { CXX_DEBUG_LEAVE_TEXT("Could not parse enum name"); return FALSE; } if(cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeParenthesisChain)) { // probably a function declaration/prototype // something like enum x func().... // do not clear statement CXX_DEBUG_LEAVE_TEXT("Probably a function declaration!"); return TRUE; } // FIXME: This block is duplicated in struct/union/class if(cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeSemicolon)) { if(g_cxx.pTokenChain->iCount > 3) { // [typedef] struct X Y; <-- typedef has been removed! if(g_cxx.uKeywordState & CXXParserKeywordStateSeenTypedef) cxxParserExtractTypedef(g_cxx.pTokenChain,TRUE); else cxxParserExtractVariableDeclarations(g_cxx.pTokenChain,0); } cxxParserNewStatement(); CXX_DEBUG_LEAVE(); return TRUE; } if(cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeEOF)) { // tolerate EOF, treat as forward declaration cxxParserNewStatement(); CXX_DEBUG_LEAVE_TEXT("EOF before enum block: treating as forward declaration"); return TRUE; } // semicolon or opening bracket // check if we can extract a class name identifier CXXToken * pEnumName = cxxTokenChainLastTokenOfType( g_cxx.pTokenChain, CXXTokenTypeIdentifier ); int iPushedScopes = 0; if(pEnumName) { // good. // It may be qualified though. CXXToken * pNamespaceBegin = pEnumName; CXXToken * pPrev = pEnumName->pPrev; while(pPrev) { if(!cxxTokenTypeIs(pPrev,CXXTokenTypeMultipleColons)) break; pPrev = pPrev->pPrev; if(!pPrev) break; if(!cxxTokenTypeIs(pPrev,CXXTokenTypeIdentifier)) break; pNamespaceBegin = pPrev; pPrev = pPrev->pPrev; } while(pNamespaceBegin != pEnumName) { CXXToken * pNext = pNamespaceBegin->pNext; cxxTokenChainTake(g_cxx.pTokenChain,pNamespaceBegin); // FIXME: We don't really know if it's a class! cxxScopePush(pNamespaceBegin,CXXTagKindCLASS,CXXScopeAccessUnknown); iPushedScopes++; pNamespaceBegin = pNext->pNext; } CXX_DEBUG_PRINT("Enum name is %s",vStringValue(pEnumName->pszWord)); cxxTokenChainTake(g_cxx.pTokenChain,pEnumName); } else { pEnumName = cxxTokenCreateAnonymousIdentifier(CXXTagKindENUM); CXX_DEBUG_PRINT( "Enum name is %s (anonymous)", vStringValue(pEnumName->pszWord) ); } tagEntryInfo * tag = cxxTagBegin(CXXTagKindENUM,pEnumName); int iCorkQueueIndex = CORK_NIL; if(tag) { // FIXME: this is debatable tag->isFileScope = !isInputHeaderFile(); iCorkQueueIndex = cxxTagCommit(); } cxxScopePush(pEnumName,CXXTagKindENUM,CXXScopeAccessPublic); iPushedScopes++; vString * pScopeName = cxxScopeGetFullNameAsString(); // Special kind of block for(;;) { cxxTokenChainClear(g_cxx.pTokenChain); if(!cxxParserParseUpToOneOf( CXXTokenTypeComma | CXXTokenTypeClosingBracket | CXXTokenTypeEOF )) { CXX_DEBUG_LEAVE_TEXT("Failed to parse enum contents"); if(pScopeName) vStringDelete(pScopeName); return FALSE; } CXXToken * pFirst = cxxTokenChainFirst(g_cxx.pTokenChain); // enumerator. if( (g_cxx.pTokenChain->iCount > 1) && cxxTokenTypeIs(pFirst,CXXTokenTypeIdentifier) ) { tag = cxxTagBegin(CXXTagKindENUMERATOR,pFirst); if(tag) { tag->isFileScope = !isInputHeaderFile(); cxxTagCommit(); } } if(cxxTokenTypeIsOneOf( g_cxx.pToken, CXXTokenTypeEOF | CXXTokenTypeClosingBracket )) break; } if(iCorkQueueIndex > CORK_NIL) cxxParserMarkEndLineForTagInCorkQueue(iCorkQueueIndex); while(iPushedScopes > 0) { cxxScopePop(); iPushedScopes--; } boolean bRet = cxxParserParseEnumStructClassOrUnionFullDeclarationTrailer( bParsingTypedef, CXXKeywordENUM, CXXTagKindENUM, vStringValue(pScopeName) ); if(pScopeName) vStringDelete(pScopeName); cxxParserNewStatement(); CXX_DEBUG_LEAVE(); return bRet; };
// // This is called after a full enum/struct/class/union declaration // that ends with a closing bracket. // static boolean cxxParserParseEnumStructClassOrUnionFullDeclarationTrailer( boolean bParsingTypedef, enum CXXKeyword eTagKeyword, enum CXXTagKind eTagKind, const char * szTypeName ) { CXX_DEBUG_ENTER(); cxxTokenChainClear(g_cxx.pTokenChain); CXX_DEBUG_PRINT( "Parse enum/struct/class/union trailer, typename is '%s'", szTypeName ); MIOPos oFilePosition = getInputFilePosition(); int iFileLine = getInputLineNumber(); if(!cxxParserParseUpToOneOf(CXXTokenTypeEOF | CXXTokenTypeSemicolon)) { CXX_DEBUG_LEAVE_TEXT("Failed to parse up to EOF/semicolon"); return FALSE; } if(cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeEOF)) { // It's a syntax error, but we can be tolerant here. CXX_DEBUG_LEAVE_TEXT("Got EOF after enum/class/struct/union block"); return TRUE; } if(g_cxx.pTokenChain->iCount < 2) { CXX_DEBUG_LEAVE_TEXT("Nothing interesting after enum/class/struct block"); return TRUE; } // fake the initial two tokens CXXToken * pIdentifier = cxxTokenCreate(); pIdentifier->oFilePosition = oFilePosition; pIdentifier->iLineNumber = iFileLine; pIdentifier->eType = CXXTokenTypeIdentifier; pIdentifier->bFollowedBySpace = TRUE; vStringCatS(pIdentifier->pszWord,szTypeName); cxxTokenChainPrepend(g_cxx.pTokenChain,pIdentifier); CXXToken * pKeyword = cxxTokenCreate(); pKeyword->oFilePosition = oFilePosition; pKeyword->iLineNumber = iFileLine; pKeyword->eType = CXXTokenTypeKeyword; pKeyword->eKeyword = eTagKeyword; pKeyword->bFollowedBySpace = TRUE; vStringCatS(pKeyword->pszWord,cxxTagGetKindOptions()[eTagKind].name); cxxTokenChainPrepend(g_cxx.pTokenChain,pKeyword); if(bParsingTypedef) cxxParserExtractTypedef(g_cxx.pTokenChain,TRUE); else cxxParserExtractVariableDeclarations(g_cxx.pTokenChain,0); CXX_DEBUG_LEAVE(); return TRUE; }
boolean cxxParserParseNamespace(void) { CXX_DEBUG_ENTER(); CXX_DEBUG_ASSERT(cxxParserCurrentLanguageIsCPP(),"This should be called only in C++"); /* Spec is: namespace ns_name { declarations } (1) inline namespace ns_name { declarations } (2) (since C++11) namespace { declarations } (3) namespace name = qualified-namespace ; (7) namespace ns_name::name { (8) (since C++17) Note that the using clauses have their own parsing routine and do not end up here. */ // namespace <X> { // namespace <X>::<Y>::<Z> { // namespace <X>::<Y>::<Z>; // namespace <X>; // namespace; unsigned int uProperties = 0; if(cxxTagFieldEnabled(CXXTagFieldProperties)) { if(g_cxx.uKeywordState & CXXParserKeywordStateSeenInline) uProperties |= CXXTagPropertyInline; } cxxParserNewStatement(); // always a new statement int iScopeCount = 0; int i; int aCorkQueueIndices[MAX_NESTED_NAMESPACES]; for(i=0;i<MAX_NESTED_NAMESPACES;i++) aCorkQueueIndices[i] = CORK_NIL; if(!cxxParserParseNextToken()) { // syntax error, but we tolerate this CXX_DEBUG_LEAVE_TEXT("Implicit EOF in cxxParserParseNextToken"); return TRUE; // EOF } if(cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeIdentifier)) { // OK, check next token to see what's coming after CXX_DEBUG_PRINT("Got identifier %s",g_cxx.pToken->pszWord->buffer); CXXToken * pFirstIdentifier = g_cxx.pToken; CXXToken * pLastIdentifier = g_cxx.pToken; if(!cxxParserParseNextToken()) { // syntax error, but we tolerate this CXX_DEBUG_LEAVE_TEXT("EOF in cxxParserParseNextToken"); return TRUE; // EOF } switch(g_cxx.pToken->eType) { case CXXTokenTypeAssignment: { // probably namespace alias CXX_DEBUG_PRINT("Found assignment"); if(!cxxParserParseNextToken()) { // syntax error, but we tolerate this CXX_DEBUG_LEAVE_TEXT("EOF in cxxParserParseNextToken"); return TRUE; // EOF } if(!cxxTokenTypeIsOneOf( g_cxx.pToken, CXXTokenTypeIdentifier | CXXTokenTypeMultipleColons )) { CXX_DEBUG_LEAVE_TEXT("Some kind of syntax error here"); return cxxParserSkipToSemicolonOrEOF(); } CXXToken * pAlias = pFirstIdentifier; pFirstIdentifier = g_cxx.pToken; if(!cxxParserParseToEndOfQualifedName()) { CXX_DEBUG_LEAVE_TEXT("Failed to parse the aliased name"); return cxxParserSkipToSemicolonOrEOF(); } pLastIdentifier = g_cxx.pToken->pPrev; tagEntryInfo * tag = cxxTagBegin(CXXTagCPPKindALIAS,pAlias); if(tag) { // This is highly questionable but well.. it's how old ctags did, so we do. tag->isFileScope = !isInputHeaderFile(); CXXToken * pAliasedName = cxxTokenChainExtractRange( pFirstIdentifier, pLastIdentifier, CXXTokenChainExtractRangeNoTrailingSpaces ); cxxTagSetField( CXXTagCPPFieldAliasedName, vStringValue(pAliasedName->pszWord) ); cxxTagCommit(); cxxTokenDestroy(pAliasedName); } CXX_DEBUG_LEAVE_TEXT("Finished parsing namespace alias"); return cxxParserSkipToSemicolonOrEOF(); } break; case CXXTokenTypeMultipleColons: // multi-namespace CXX_DEBUG_PRINT("Found multiple colons"); if(!cxxParserParseToEndOfQualifedName()) { CXX_DEBUG_LEAVE_TEXT("Failed to parse the namespace name"); return cxxParserSkipToSemicolonOrEOF(); } pLastIdentifier = g_cxx.pToken->pPrev; CXX_DEBUG_ASSERT( pFirstIdentifier != pLastIdentifier, "We expected multiple identifiers here" ); if(!cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeOpeningBracket)) { if(!cxxParserParseUpToOneOf( CXXTokenTypeOpeningBracket | CXXTokenTypeSemicolon | CXXTokenTypeEOF )) { CXX_DEBUG_LEAVE_TEXT("Failed to parse up to an opening bracket"); return FALSE; } if(!cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeOpeningBracket)) { // tolerate syntax error CXX_DEBUG_LEAVE_TEXT("Found semicolon just after namespace declaration"); return TRUE; } CXX_DEBUG_LEAVE_TEXT("Was expecting an opening bracket here"); // FIXME: Maybe we could attempt to recover here? return TRUE; } break; case CXXTokenTypeOpeningBracket: // single name namespace CXX_DEBUG_PRINT("Found opening bracket"); break; case CXXTokenTypeSemicolon: // tolerate syntax error CXX_DEBUG_LEAVE_TEXT("Found semicolon just after namespace declaration"); return TRUE; break; default: CXX_DEBUG_LEAVE_TEXT("Some kind of syntax error here"); return cxxParserSkipToSemicolonOrEOF(); break; } CXX_DEBUG_ASSERT( cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeOpeningBracket), "Should have an opening bracket here!" ); CXX_DEBUG_PRINT("Found regular namespace start"); CXXToken * t = pFirstIdentifier; while(t) { tagEntryInfo * tag = cxxTagBegin(CXXTagCPPKindNAMESPACE,t); if(tag) { // This is highly questionable but well.. it's how old ctags did, so we do. tag->isFileScope = !isInputHeaderFile(); vString * pszProperties = uProperties ? cxxTagSetProperties(uProperties) : NULL; int iCorkQueueIndex = cxxTagCommit(); if(iScopeCount < MAX_NESTED_NAMESPACES) aCorkQueueIndices[iScopeCount] = iCorkQueueIndex; if(pszProperties) vStringDelete(pszProperties); } CXXToken * pNext = (t == pLastIdentifier) ? NULL : t->pNext->pNext; cxxTokenChainTake(g_cxx.pTokenChain,t); cxxScopePush( t, CXXScopeTypeNamespace, CXXScopeAccessUnknown ); iScopeCount++; t = pNext; } } else if(cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeOpeningBracket)) { // anonymous namespace CXX_DEBUG_PRINT("Found anonymous namespace start"); CXXToken * t = cxxTokenCreateAnonymousIdentifier(CXXTagCPPKindNAMESPACE); tagEntryInfo * tag = cxxTagBegin(CXXTagCPPKindNAMESPACE,t); if(tag) { tag->isFileScope = !isInputHeaderFile(); vString * pszProperties = uProperties ? cxxTagSetProperties(uProperties) : NULL; aCorkQueueIndices[0] = cxxTagCommit(); if(pszProperties) vStringDelete(pszProperties); } cxxScopePush(t,CXXScopeTypeNamespace,CXXScopeAccessUnknown); iScopeCount++; } else { CXX_DEBUG_LEAVE_TEXT("Some kind of syntax error after namespace declaration"); return cxxParserSkipToSemicolonOrEOF(); } CXX_DEBUG_ASSERT( cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeOpeningBracket), "Should have an opening bracket here!" ); // Here we certainly got an opening bracket: namespace block if(!cxxParserParseBlock(TRUE)) { CXX_DEBUG_LEAVE_TEXT("Failed to parse namespace block"); return FALSE; } while(iScopeCount > 0) { cxxScopePop(); iScopeCount--; if(aCorkQueueIndices[iScopeCount] > CORK_NIL) cxxParserMarkEndLineForTagInCorkQueue(aCorkQueueIndices[iScopeCount]); } CXX_DEBUG_LEAVE_TEXT("Finished parsing namespace"); return TRUE; }
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; }
boolean cxxParserParseIfForWhileSwitch(void) { CXX_DEBUG_ENTER(); if(!cxxParserParseUpToOneOf( CXXTokenTypeParenthesisChain | CXXTokenTypeSemicolon | CXXTokenTypeOpeningBracket | CXXTokenTypeEOF )) { CXX_DEBUG_LEAVE_TEXT("Failed to parse if/for/while/switch up to parenthesis"); return FALSE; } if(cxxTokenTypeIsOneOf(g_cxx.pToken,CXXTokenTypeEOF | CXXTokenTypeSemicolon)) { CXX_DEBUG_LEAVE_TEXT("Found EOF/semicolon while parsing if/for/while/switch"); return TRUE; } if(cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeParenthesisChain)) { // Extract variables from the parenthesis chain // We handle only simple cases. CXXTokenChain * pChain = g_cxx.pToken->pChain; CXX_DEBUG_ASSERT( pChain->iCount >= 2, "The parenthesis chain must have initial and final parenthesis" ); // Simple check for cases like if(a & b), if(a * b). // If there is &, && or * then we expect there to be also a =. if( // & && * not present !cxxTokenChainFirstTokenOfType( pChain, CXXTokenTypeAnd | CXXTokenTypeMultipleAnds | CXXTokenTypeStar ) || // or = present cxxTokenChainFirstTokenOfType( pChain, CXXTokenTypeAssignment ) ) { // Kill the initial parenthesis cxxTokenChainDestroyFirst(pChain); // Fake the final semicolon CXXToken * t = cxxTokenChainLast(pChain); t->eType = CXXTokenTypeSemicolon; vStringClear(t->pszWord); vStringPut(t->pszWord,';'); // and extract variable declarations if possible cxxParserExtractVariableDeclarations(pChain,0); } CXX_DEBUG_LEAVE_TEXT("Found if/for/while/switch parenthesis chain"); return TRUE; } // must be opening bracket: parse it here. boolean bRet = cxxParserParseBlock(TRUE); CXX_DEBUG_LEAVE(); return bRet; }
// // The __attribute__((...)) sequence complicates parsing quite a lot. // For this reason we attempt to "hide" it from the rest of the parser // at tokenizer level. // // Returns false if it finds an EOF. This is an important invariant required by // cxxParserParseNextToken(), the only caller. // static bool cxxParserParseNextTokenCondenseAttribute(void) { // Since cxxParserParseNextToken() returns false only when it has found // an EOF, this function must do the same. // This means that any broken input must be discarded here. CXX_DEBUG_ENTER(); CXX_DEBUG_ASSERT( cxxTokenIsKeyword(g_cxx.pToken,CXXKeyword__ATTRIBUTE__), "This function should be called only after we have parsed __attribute__" ); // Kill it cxxTokenChainDestroyLast(g_cxx.pTokenChain); // And go ahead. if(!cxxParserParseNextToken()) { CXX_DEBUG_LEAVE_TEXT("No next token after __attribute__"); return false; } if(!cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeOpeningParenthesis)) { CXX_DEBUG_LEAVE_TEXT("Something that is not an opening parenthesis"); return true; } // Do NOT accept EOF as a valid terminator as it implies broken input. if(!cxxParserParseAndCondenseCurrentSubchain( CXXTokenTypeOpeningParenthesis | CXXTokenTypeOpeningSquareParenthesis | CXXTokenTypeOpeningBracket, false, false )) { // Pasing and/or condensation of the subchain failed. This implies broken // input (mismatched parenthesis/bracket, early EOF). CXX_DEBUG_LEAVE_TEXT("Failed to parse subchains. The input is broken..."); // However our invariant (see comment at the beginning of the function) // forbids us to return false if we didn't find an EOF. So we attempt // to resume parsing anyway. If there is an EOF, cxxParserParseNextToken() // will report it. // Kill the token chain cxxTokenChainDestroyLast(g_cxx.pTokenChain); return cxxParserParseNextToken(); } CXX_DEBUG_ASSERT( cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeParenthesisChain), "Should have a parenthesis chain as last token!" ); // Try to make sense of certain kinds of __attribute__. // the proper syntax is __attribute__(()), so look at the inner chain CXXToken * pInner = cxxTokenChainFirst(g_cxx.pToken->pChain); if(pInner) { if(pInner->pNext && cxxTokenTypeIs(pInner->pNext,CXXTokenTypeParenthesisChain)) pInner = cxxTokenChainFirst(pInner->pNext->pChain); while(pInner) { if(cxxTokenTypeIs(pInner,CXXTokenTypeIdentifier)) { CXX_DEBUG_PRINT("Analyzing attribyte %s",vStringValue(pInner->pszWord)); if( (strcmp(vStringValue(pInner->pszWord),"always_inline") == 0) || (strcmp(vStringValue(pInner->pszWord),"__always_inline__") == 0) ) { CXX_DEBUG_PRINT("Found attribute 'always_inline'"); // assume "inline" has been seen. g_cxx.uKeywordState |= CXXParserKeywordStateSeenInline; } else if( (strcmp(vStringValue(pInner->pszWord),"deprecated") == 0) || (strcmp(vStringValue(pInner->pszWord),"__deprecated__") == 0) ) { CXX_DEBUG_PRINT("Found attribute 'deprecated'"); // assume "inline" has been seen. g_cxx.uKeywordState |= CXXParserKeywordStateSeenAttributeDeprecated; } } // If needed, we could attach certain attributes to previous // identifiers. But note that __attribute__ may apply to a // following identifier too. pInner = pInner->pNext; } } // Now just kill the chain. cxxTokenChainDestroyLast(g_cxx.pTokenChain); // And finally extract yet another token. CXX_DEBUG_LEAVE(); return cxxParserParseNextToken(); }
static boolean cxxTagCheckTypeField( CXXToken * pTypeStart, CXXToken * pTypeEnd ) { CXX_DEBUG_ENTER(); if(!pTypeStart || !pTypeEnd) { CXX_DEBUG_LEAVE_TEXT("One of the pointers is NULL"); return FALSE; } int iTotalCount = 0; int iParenthesisCount = 0; int iIdentifierOrKeywordCount = 0; int iConsecutiveIdentifiers = 0; while(pTypeStart) { iTotalCount++; if(iTotalCount > 30) { CXX_DEBUG_LEAVE_TEXT("The chain is really too long to be a type name"); return FALSE; } if(cxxTokenTypeIs(pTypeStart,CXXTokenTypeIdentifier)) { iConsecutiveIdentifiers++; iIdentifierOrKeywordCount++; if(iConsecutiveIdentifiers > 4) { // Probably many macros inside. Too many. CXX_DEBUG_LEAVE_TEXT("Too many consecutive identifiers for a type name"); return FALSE; } } else { iConsecutiveIdentifiers = 0; if(cxxTokenTypeIs(pTypeStart,CXXTokenTypeParenthesisChain)) { iParenthesisCount++; if(iParenthesisCount > 3) { CXX_DEBUG_LEAVE_TEXT("Too many non-nested parentheses for a type name"); return FALSE; } if( (iTotalCount > 1) && cxxTokenTypeIs(pTypeStart->pPrev,CXXTokenTypeIdentifier) && pTypeStart != pTypeEnd && pTypeStart->pNext && cxxTokenTypeIs(pTypeStart->pNext,CXXTokenTypeIdentifier) ) { // identifier () identifier // Looks suspicious, might be macros gathered by mistake CXX_DEBUG_LEAVE_TEXT("Identifier-parenthesis-identifier pattern: looks suspicious"); return FALSE; } } else if(cxxTokenTypeIs(pTypeStart,CXXTokenTypeKeyword)) { iIdentifierOrKeywordCount++; } } if(pTypeStart == pTypeEnd) break; pTypeStart = pTypeStart->pNext; } if(iIdentifierOrKeywordCount < 1) { CXX_DEBUG_LEAVE_TEXT("Type does not seem to contains identifiers or keywords, can't be a type name"); return FALSE; } if(!pTypeStart) { CXX_DEBUG_LEAVE_TEXT("Type tokens do not belong to the same chain!"); return FALSE; } CXX_DEBUG_LEAVE(); return TRUE; }