void cxxTokenChainNormalizeTypeNameSpacingInRange(CXXToken * pFrom,CXXToken * pTo) { if(!pFrom || !pTo) return; // Goals: // int // unsigned short int // int * // unsigned short int ** // const Class & // Class && // int (*)(type &,type *) // unsigned short int[3]; // ClassA<ClassB<type *,type>> <-- fixme: not sure about the trailing >> // Class<Something> (*)(type[]) CXXToken * t = pFrom; for(;;) { if(cxxTokenTypeIsOneOf( t, CXXTokenTypeParenthesisChain | CXXTokenTypeSquareParenthesisChain )) { cxxTokenChainNormalizeTypeNameSpacing(t->pChain); t->bFollowedBySpace = false; } else if(cxxTokenTypeIsOneOf(t, CXXTokenTypeIdentifier | CXXTokenTypeKeyword | CXXTokenTypeGreaterThanSign | CXXTokenTypeAnd | CXXTokenTypeMultipleAnds )) { t->bFollowedBySpace = t->pNext && cxxTokenTypeIsOneOf( t->pNext, CXXTokenTypeParenthesisChain | CXXTokenTypeIdentifier | CXXTokenTypeKeyword | CXXTokenTypeStar | CXXTokenTypeAnd | CXXTokenTypeMultipleAnds ); } else if(cxxTokenTypeIs(t,CXXTokenTypeStar)) { t->bFollowedBySpace = t->pNext && (!cxxTokenTypeIsOneOf( t->pNext, CXXTokenTypeStar | CXXTokenTypeComma | CXXTokenTypeClosingParenthesis )); } else { t->bFollowedBySpace = false; } if(t == pTo) break; t = t->pNext; } // Finally the chain has no space at end pTo->bFollowedBySpace = false; }
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; };