📄 gcppparser.cpp
字号:
/* Copyright (C) 2006, Mike Gashler This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. see http://www.gnu.org/copyleft/lesser.html*/#include "GCppParser.h"#include <string.h>#include <stdio.h>#ifdef WIN32#include <direct.h>#include <io.h>#else // WIN32#include <unistd.h>#endif // !WIN32#include "GArray.h"#include "GMacros.h"#include "GFile.h"#include "GHashTable.h"#include "GQueue.h"#include <stdlib.h>GCppObject::GCppObject(){ m_szFilename = NULL; m_nLineNumber = 0; m_pComment = NULL;}/*virtual*/ GCppObject::~GCppObject(){ delete(m_pComment);}// -------------------------------------------------------------------GCppScope::GCppScope(): GCppObject(){ m_pStringHeap = NULL; m_pTypes = new GConstStringHashTable(7, true); m_pVariables = new GConstStringHashTable(7, true); m_pMethods = new GConstStringHashTable(7, true); m_eCurrentAccess = PUBLIC;}#define DeleteHashTableContents(pht, typ) \{\ GHashTable htUnique(pht->GetCount() * 2);\ void* pValue;\ void* pTemp;\ GHashTableEnumerator e(pht);\ while(true)\ {\ const char* pKey = e.GetNext(&pValue);\ if(!pKey)\ break;\ if(!htUnique.Get(pValue, &pTemp))\ {\ htUnique.Add(pValue, NULL);\ delete((typ)pValue);\ }\ }\}GCppScope::~GCppScope(){ DeleteHashTableContents(m_pTypes, GCppType*); delete(m_pTypes); DeleteHashTableContents(m_pVariables, GCppVariable*); delete(m_pVariables); DeleteHashTableContents(m_pMethods, GCppMethod*); delete(m_pMethods); delete(m_pStringHeap);}// -------------------------------------------------------------------GCppType::GCppType(const char* szName): GCppScope(){ m_szName = szName; m_bPrimitive = false; m_bDeclaredInProjectFile = false;}GCppType::~GCppType(){}// -------------------------------------------------------------------const char* g_pPrimitiveTypes[] ={ "void", "int", "bool", "short", "long", "float", "double", "char", "__int64", "PCONTEXT", // winnt.h "HGDIOBJ", // wingdi.h "HPALETTE", // wingdi.h "HDC", // wingdi.h "HBITMAP", // wingdi.h "HBRUSH", // wingdi.h "HFONT", // wingdi.h "HPEN", // wingdi.h "HMENU", // wingdi.h "HACCEL", // wingdi.h};#define PRIMITIVE_TYPE_COUNT (sizeof(g_pPrimitiveTypes) / sizeof(const char*))// -------------------------------------------------------------------GCppMethod::GCppMethod(GCppType* pReturnType, GCppScope* pScope, const char* szName, GCppAccess eAccess, GCppMethodModifier eModifiers) : GCppDeclaration(pReturnType, szName, eAccess){ m_eModifiers = eModifiers; m_pParameters = new GPointerArray(4); m_pLocals = NULL; m_pCalls = NULL; m_pScope = pScope;}GCppMethod::~GCppMethod(){ int n; int nCount = m_pParameters->GetSize(); for(n = 0; n < nCount; n++) delete((GCppVariable*)m_pParameters->GetPointer(n)); delete(m_pParameters); if(m_pLocals) { DeleteHashTableContents(m_pLocals, GCppVariable*); delete(m_pLocals); } delete(m_pCalls);}void GCppMethod::AddParameter(GCppVariable* pVar){ m_pParameters->AddPointer(pVar);}int GCppMethod::GetParameterCount(){ return m_pParameters->GetSize();}GCppVariable* GCppMethod::GetParameter(int n){ return (GCppVariable*)m_pParameters->GetPointer(n);}int GCppMethod::GetCallCount(){ return m_pCalls->GetSize();}GCppMethod* GCppMethod::GetCall(int n){ return (GCppMethod*)m_pCalls->GetPointer(n);}// -------------------------------------------------------------------void GCppToken::Reset(GCppFile* pFile, int nStart, int nLength, int nLine, int nCol){ if(pFile) pFile->AddRef(); if(m_pFile) m_pFile->Release(); m_pFile = pFile; m_nStart = nStart; m_nLength = nLength; m_nLine = nLine; m_nCol = nCol;}bool GCppToken::StartsWith(const char* szString){ int n; for(n = 0; n < m_nLength && szString[n] != '\0'; n++) { if(m_pFile->m_pFile[m_nStart + n] != szString[n]) return false; } return true;}bool GCppToken::Equals(const char* szString){ int n; for(n = 0; n < m_nLength; n++) { if(m_pFile->m_pFile[m_nStart + n] != szString[n]) return false; } if(szString[n] != '\0') return false; return true;}// -------------------------------------------------------------------GCppFile::GCppFile(char* pFile, const char* szOldDir, bool bProjectFile, const char* szFilename){ m_nFilePos = 0; m_nLine = 1; m_nLineStart = 0; m_pFile = pFile; m_nRefs = 0; m_bProjectFile = bProjectFile; m_szFilename = szFilename; m_pPrevDir = new char[strlen(szOldDir) + 1]; strcpy(m_pPrevDir, szOldDir);}// -------------------------------------------------------------------GCppParser::GCppParser(){ m_eMethodModifiers = 0; m_eVarModifiers = 0; m_nFileStackPos = 0; m_nTokenPos = 0; m_nActiveTokens = 0; m_nIfDefSkips = 0; m_nErrorCount = 0; m_nRecoveries = 0; m_pIncludePaths = new GPointerArray(8); m_pStringHeap = new GStringHeap(1024); m_pDefines = new GConstStringHashTable(31, true); m_pGlobalScope = new GCppScope(); m_pScopeStack[0] = m_pGlobalScope; m_nScopeStackPointer = 1; m_bRetainComments = false; m_pComment = NULL; // Add primitive types to the global scope int n; for(n = 0; n < (int)PRIMITIVE_TYPE_COUNT; n++) { GCppClass* pPrimitive = new GCppClass(g_pPrimitiveTypes[n]); pPrimitive->m_bPrimitive = true; m_pGlobalScope->m_pTypes->Add(g_pPrimitiveTypes[n], pPrimitive); }}GCppParser::~GCppParser(){ delete(m_pIncludePaths); delete(m_pDefines); delete(m_pStringHeap); delete(m_pGlobalScope); delete(m_pComment);}void GCppParser::AddIncludePath(const char* szPath){ char* pPath = m_pStringHeap->Add(szPath); m_pIncludePaths->AddPointer(pPath);}void GCppParser::AddDefine(const char* szDefine, const char* szValue){ GAssert(szDefine, "invalid parameter"); char* pDefine = m_pStringHeap->Add(szDefine); char* pValue = NULL; if(szValue) pValue = m_pStringHeap->Add(szValue); m_pDefines->Add(pDefine, pValue);}char* LoadFileToBuffer(const char* szFilename){ FILE* pFile = fopen(szFilename, "rb"); if(!pFile) return NULL; int nFileSize = filelength(fileno(pFile)); ArrayHolder<char*> hBuffer(new char[nFileSize + 1]); if(!hBuffer.Get()) { fclose(pFile); return NULL; } int nBytesRead = fread(hBuffer.Get(), sizeof(char), nFileSize, pFile); int err = ferror(pFile); if(err != 0 || nBytesRead != nFileSize) { fclose(pFile); return NULL; } hBuffer.Get()[nFileSize] = '\0'; fclose(pFile); return hBuffer.Drop();}GCppScope* GCppParser::ParseCppFile(const char* szFilename){ // Parse the file if(!PushFile(szFilename, true)) return NULL; while(true) { GCppToken* pTok = GetToken(0); if(pTok->GetLength() <= 0) break; ParseScope(); } // Flush all the tokens to make sure they aren't holding on to any files int n; for(n = 0; n < MAX_LOOK_AHEAD; n++) Advance(); // Return the global scope GCppScope* pScope = m_pGlobalScope; m_pGlobalScope = NULL; pScope->m_pStringHeap = m_pStringHeap; m_pStringHeap = NULL; return pScope;}bool GCppParser::OnError(GCppToken* pTok, const char* szMessage){ m_nErrorCount++; //GAssert(!GetCurrentFile()->IsProjectFile(), "break"); return false;}bool GCppParser::ParseScope(){ int n = m_nScopeStackPointer; while(m_nScopeStackPointer >= n) { GCppToken* pTok = GetToken(0); if(pTok->GetLength() <= 0) break; if(pTok->m_nLine == 284) { do {} while(false); //GAssert(false, "Break"); } if(!ParseScopeItem()) { // Attempt to recover by skipping to the next ';' or '}' or '#" m_nRecoveries++; GAssert(m_nRecoveries == m_nErrorCount, "Someone didn't call OnError when an error was found!"); Advance(); int nScopeNests = 0; while(true) { pTok = GetToken(0); if(pTok->Equals("{")) nScopeNests++; else if(pTok->Equals("}")) nScopeNests--; if(nScopeNests <= 0) { if(pTok->GetLength() <= 0 || pTok->StartsWith("#")) break; if(pTok->Equals(";") || pTok->Equals("}")) { Advance(); break; } } else if(pTok->GetLength() <= 0) nScopeNests--; Advance(); } } } return true;}bool GCppParser::PushFile(const char* szFilename, bool bProjectFile){ // Separate the file and directory char szDrive[32]; char szDir[256]; char szFile[256]; char szExt[256]; _splitpath(szFilename, szDrive, szDir, szFile, szExt); char szNewDir[256]; char szNewFile[256]; _makepath(szNewDir, szDrive, szDir, NULL, NULL); _makepath(szNewFile, NULL, NULL, szFile, szExt); // Record the old directory and change to the new one char szOldDir[256]; getcwd(szOldDir, 256); chdir(szNewDir); // Load the file ArrayHolder<char*> hFile(LoadFileToBuffer(szNewFile)); if(!hFile.Get()) { chdir(szOldDir); OnError(GetToken(0), "file not found"); return false; } const char* szFN = m_pStringHeap->Add(szFilename); PushBuffer(hFile.Drop(), szOldDir, bProjectFile, szFN); return true;}// This takes ownership of szBufvoid GCppParser::PushBuffer(char* szBuf, const char* szOldDir, bool bProjectFile, const char* szFilename){ GAssert(m_nFileStackPos < MAX_INCLUDE_NESTING - 10, "deep #include nesting"); if(m_nFileStackPos >= MAX_INCLUDE_NESTING) { GAssert(false, "#include nesting too deep"); return; } // Push the new file on the stack m_pFileStack[m_nFileStackPos] = new GCppFile(szBuf, szOldDir, bProjectFile, szFilename); m_pFileStack[m_nFileStackPos]->AddRef(); m_nFileStackPos++;}bool GCppParser::PopFile(){ GAssert(m_nFileStackPos > 0, "Nothing to pop"); m_nFileStackPos--; chdir(m_pFileStack[m_nFileStackPos]->GetOldDir()); m_pFileStack[m_nFileStackPos]->Release(); if(m_nFileStackPos <= 0) return false; // indicates that the file stack is now empty return true;}void GCppParser::PushScope(GCppScope* pNewScope){ GAssert(m_nScopeStackPointer < MAX_SCOPE_NESTING, "nesting that deep not supported"); m_pScopeStack[m_nScopeStackPointer] = pNewScope; m_nScopeStackPointer++; m_eMethodModifiers = 0; m_eVarModifiers = 0;}bool GCppParser::PopScope(){ if(m_nScopeStackPointer <= 0) return OnError(GetToken(0), "\"}\" without matching \"{\""); m_nScopeStackPointer--; m_eMethodModifiers = 0; m_eVarModifiers = 0; return true;}GCppToken* GCppParser::GetToken(int n){ GAssert(n < MAX_LOOK_AHEAD, "can't look that far ahead"); while(n >= m_nActiveTokens) GetNextToken(); int nPos = m_nTokenPos + n; if(nPos >= MAX_LOOK_AHEAD) nPos -= MAX_LOOK_AHEAD; return &m_tokens[nPos];}void GCppParser::Advance(){ if(m_nActiveTokens == 0) GetNextToken(); m_nTokenPos++; if(m_nTokenPos >= MAX_LOOK_AHEAD) m_nTokenPos = 0; m_nActiveTokens--;}void GCppParser::GetNextToken(){ GAssert(m_nActiveTokens < MAX_LOOK_AHEAD, "got all the tokens we can hold"); if(m_nFileStackPos <= 0) { int nNextToken = m_nTokenPos + m_nActiveTokens; if(nNextToken >= MAX_LOOK_AHEAD) nNextToken -= MAX_LOOK_AHEAD; m_tokens[nNextToken].Reset(NULL, 0, 0, 0, 0); m_nActiveTokens++; return; } int nStart, nLength, nLine, nCol; nLength = 0; while(true) { FindNextToken(&nStart, &nLength, &nLine, &nCol); // Check for macros GCppFile* pFile = GetCurrentFile(); GAssert(pFile, "FindNextToken shouldn't pop files"); // If we hit the end of the file, see if there's still one on the stack if(nLength <= 0) { if(PopFile()) continue; else pFile = NULL; } // Set the token value int nNextToken = m_nTokenPos + m_nActiveTokens; if(nNextToken >= MAX_LOOK_AHEAD) nNextToken -= MAX_LOOK_AHEAD; m_tokens[nNextToken].Reset(pFile, nStart, nLength, nLine, nCol); m_nActiveTokens++; // Do any necessary macro substitutions const char* szMacro; if(pFile && GetMacro(&pFile->m_pFile[nStart], nLength, &szMacro)) { if(!ParseMacro(szMacro)) m_nRecoveries++; continue; } // It's just a normal token, so we're done break; }}void GCppParser::OnNewLine(int nPos){ GCppFile* pFile = GetCurrentFile(); pFile->m_nLine++; pFile->m_nLineStart = nPos + 1;}void GCppParser::FindNextToken(int* pnStart, int* pnLength, int* pnLine, int* pnCol){ // Find the start of the token GAssert(m_nFileStackPos > 0, "No files on stack"); GCppFile* pCppFile = GetCurrentFile(); const char* pFile = pCppFile->m_pFile; int nPos = pCppFile->m_nFilePos; // Skip whitespace and comments, and process compiler directives while(pFile[nPos] != '\0')
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -