static rescanReason cxxParserMain(const unsigned int passCount) { cxxScopeClear(); cxxTokenAPINewFile(); cxxParserNewStatement(); kindOption * kind_for_define = cxxTagGetKindOptions() + CXXTagKindMACRO; kindOption * kind_for_header = cxxTagGetKindOptions() + CXXTagKindINCLUDE; int role_for_macro_undef = CR_MACRO_UNDEF; int role_for_header_system = CR_HEADER_SYSTEM; int role_for_header_local = CR_HEADER_LOCAL; int end_field_type = cxxParserCurrentLanguageIsCPP()? cxxTagGetCPPFieldSpecifiers () [CXXTagCPPFieldEndLine].ftype: cxxTagGetCFieldSpecifiers () [CXXTagCFieldEndLine].ftype; Assert(passCount < 3); cppInit( (boolean) (passCount > 1), FALSE, TRUE, // raw literals FALSE, kind_for_define, role_for_macro_undef, kind_for_header, role_for_header_system, role_for_header_local, end_field_type ); g_cxx.iChar = ' '; boolean bRet = cxxParserParseBlock(FALSE); cppTerminate (); cxxTokenChainClear(g_cxx.pTokenChain); if(g_cxx.pTemplateTokenChain) cxxTokenChainClear(g_cxx.pTemplateTokenChain); if(!bRet && (passCount == 1)) { CXX_DEBUG_PRINT("Processing failed: trying to rescan"); return RESCAN_FAILED; } return RESCAN_NONE; }
void cxxScopeClear(void) { if(g_pScope) cxxTokenChainClear(g_pScope); if(g_szScopeName) { vStringDelete(g_szScopeName); g_szScopeName = NULL; } }
void cxxTokenChainMoveEntries(CXXTokenChain * src,CXXTokenChain * dest) { if(dest->iCount > 0) cxxTokenChainClear(dest); dest->iCount = src->iCount; dest->pHead = src->pHead; dest->pTail = src->pTail; src->iCount = 0; src->pHead = NULL; src->pTail = NULL; }
// 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; }
// // Reset parser state: // - Clear the token chain // - Reset "seen" keywords // void cxxParserNewStatement(void) { cxxTokenChainClear(g_cxx.pTokenChain); if(g_cxx.pTemplateTokenChain) { cxxTokenChainDestroy(g_cxx.pTemplateTokenChain); g_cxx.pTemplateTokenChain = NULL; } g_cxx.uKeywordState = 0; // FIXME: this cpp handling of end/statement is kind of broken: // it works only because the moon is in the correct phase. cppEndStatement(); }
// 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; }
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 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; }
// // 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; }