work done: refactor cpp parser for project

This commit is contained in:
Roy Qu 2022-10-22 19:33:20 +08:00
parent dbf34548d8
commit d1d68758aa
6 changed files with 119 additions and 126 deletions

View File

@ -55,6 +55,8 @@ CppParser::CppParser(QObject *parent) : QObject(parent),
mCppTypeKeywords = CppTypeKeywords; mCppTypeKeywords = CppTypeKeywords;
mEnabled = true; mEnabled = true;
internalClear();
//mNamespaces; //mNamespaces;
//mBlockBeginSkips; //mBlockBeginSkips;
//mBlockEndSkips; //mBlockEndSkips;
@ -812,8 +814,10 @@ void CppParser::parseFile(const QString &fileName, bool inProject, bool onlyIfNo
if (onlyIfNotParsed && mPreprocessor.scannedFiles().contains(fName)) if (onlyIfNotParsed && mPreprocessor.scannedFiles().contains(fName))
return; return;
QSet<QString> files = calculateFilesToBeReparsed(fileName); QSet<QString> filesToReparsed = calculateFilesToBeReparsed(fileName);
internalInvalidateFiles(files); internalInvalidateFiles(filesToReparsed);
QStringList files = sortFilesByIncludeRelations(filesToReparsed);
if (inProject) if (inProject)
mProjectFiles.insert(fileName); mProjectFiles.insert(fileName);
@ -825,9 +829,7 @@ void CppParser::parseFile(const QString &fileName, bool inProject, bool onlyIfNo
mFilesToScanCount = files.count(); mFilesToScanCount = files.count();
mFilesScannedCount = 0; mFilesScannedCount = 0;
// parse header files in the first parse
foreach (const QString& file,files) { foreach (const QString& file,files) {
if (isHFile(file)) {
mFilesScannedCount++; mFilesScannedCount++;
emit onProgress(file,mFilesToScanCount,mFilesScannedCount); emit onProgress(file,mFilesToScanCount,mFilesScannedCount);
if (!mPreprocessor.scannedFiles().contains(file)) { if (!mPreprocessor.scannedFiles().contains(file)) {
@ -835,17 +837,6 @@ void CppParser::parseFile(const QString &fileName, bool inProject, bool onlyIfNo
} }
} }
} }
//we only parse CFile in the second parse
foreach (const QString& file,files) {
if (!isHFile(file)) {
mFilesScannedCount++;
emit onProgress(file,mFilesToScanCount,mFilesScannedCount);
if (!mPreprocessor.scannedFiles().contains(file)) {
internalParse(file);
}
}
}
}
} }
void CppParser::parseFileList(bool updateView) void CppParser::parseFileList(bool updateView)
@ -873,26 +864,16 @@ void CppParser::parseFileList(bool updateView)
// Support stopping of parsing when files closes unexpectedly // Support stopping of parsing when files closes unexpectedly
mFilesScannedCount = 0; mFilesScannedCount = 0;
mFilesToScanCount = mFilesToScan.count(); mFilesToScanCount = mFilesToScan.count();
QStringList files = sortFilesByIncludeRelations(mFilesToScan);
// parse header files in the first parse // parse header files in the first parse
foreach (const QString& file, mFilesToScan) { foreach (const QString& file, files) {
if (isHFile(file)) {
mFilesScannedCount++; mFilesScannedCount++;
emit onProgress(mCurrentFile,mFilesToScanCount,mFilesScannedCount); emit onProgress(mCurrentFile,mFilesToScanCount,mFilesScannedCount);
if (!mPreprocessor.scannedFiles().contains(file)) { if (!mPreprocessor.scannedFiles().contains(file)) {
internalParse(file); internalParse(file);
} }
} }
}
//we only parse CFile in the second parse
foreach (const QString& file,mFilesToScan) {
if (isCFile(file)) {
mFilesScannedCount++;
emit onProgress(mCurrentFile,mFilesToScanCount,mFilesScannedCount);
if (!mPreprocessor.scannedFiles().contains(file)) {
internalParse(file);
}
}
}
mFilesToScan.clear(); mFilesToScan.clear();
} }
} }
@ -1210,15 +1191,10 @@ PStatement CppParser::addStatement(const PStatement& parent,
if (oldStatement && !oldStatement->hasDefinition) { if (oldStatement && !oldStatement->hasDefinition) {
oldStatement->hasDefinition = true; oldStatement->hasDefinition = true;
if (oldStatement->fileName!=fileName) { if (oldStatement->fileName!=fileName) {
PFileIncludes fileIncludes1=mPreprocessor.includesList().value(fileName); PFileIncludes fileIncludes=mPreprocessor.includesList().value(fileName);
if (fileIncludes1) { if (fileIncludes) {
fileIncludes1->statements.insert(oldStatement->fullName, fileIncludes->statements.insert(oldStatement->fullName,
oldStatement); oldStatement);
fileIncludes1->dependingFiles.insert(oldStatement->fileName);
PFileIncludes fileIncludes2=mPreprocessor.includesList().value(oldStatement->fileName);
if (fileIncludes2) {
fileIncludes2->dependedFiles.insert(fileName);
}
} }
} }
oldStatement->definitionLine = line; oldStatement->definitionLine = line;
@ -1275,7 +1251,6 @@ PStatement CppParser::addStatement(const PStatement& parent,
PFileIncludes fileIncludes = mPreprocessor.includesList().value(fileName); PFileIncludes fileIncludes = mPreprocessor.includesList().value(fileName);
if (fileIncludes) { if (fileIncludes) {
fileIncludes->statements.insert(result->fullName,result); fileIncludes->statements.insert(result->fullName,result);
fileIncludes->declaredStatements.insert(result->fullName,result);
} }
} }
return result; return result;
@ -1385,7 +1360,6 @@ void CppParser::removeScopeLevel(int line)
mStatementList.deleteStatement(currentScope); mStatementList.deleteStatement(currentScope);
} else { } else {
fileIncludes->statements.insert(currentScope->fullName,currentScope); fileIncludes->statements.insert(currentScope->fullName,currentScope);
fileIncludes->declaredStatements.insert(currentScope->fullName,currentScope);
} }
} }
mCurrentScope.pop_back(); mCurrentScope.pop_back();
@ -1453,6 +1427,55 @@ void CppParser::internalClear()
mInlineNamespaceEndSkips.clear(); mInlineNamespaceEndSkips.clear();
} }
QStringList CppParser::sortFilesByIncludeRelations(const QSet<QString> &files)
{
QStringList result;
//rebuild file include relations
foreach(const QString& file, files) {
//already removed in interalInvalidateFiles
//mPreprocessor.removeScannedFile(file);
QStringList buffer;
if (mOnGetFileStream) {
mOnGetFileStream(file,buffer);
}
mPreprocessor.setScanOptions(mParseGlobalHeaders, mParseLocalHeaders);
mPreprocessor.preprocess(file);
mPreprocessor.clearTempResults();
}
QSet<QString> fileSet=files;
while (!fileSet.isEmpty()) {
bool found=false;
foreach (const QString& file,fileSet) {
PFileIncludes fileIncludes = mPreprocessor.includesList().value(file);
bool hasInclude=false;
foreach(const QString& inc,fileIncludes->includeFiles.keys()) {
if (fileSet.contains(inc)) {
hasInclude=true;
break;
}
}
if (!hasInclude) {
result.push_front(file);
fileSet.remove(file);
found=true;
break;
}
}
if (!found) {
foreach (const QString& file,fileSet) {
result.push_front(file);
fileSet.remove(file);
}
}
}
foreach(const QString& file, files) {
mPreprocessor.removeScannedFile(file);
}
return result;
}
bool CppParser::checkForCatchBlock() bool CppParser::checkForCatchBlock()
{ {
// return mIndex < mTokenizer.tokenCount() && // return mIndex < mTokenizer.tokenCount() &&
@ -2581,7 +2604,7 @@ void CppParser::handlePreprocessor()
emit onProgress(mCurrentFile,mFilesToScanCount,mFilesScannedCount); emit onProgress(mCurrentFile,mFilesToScanCount,mFilesScannedCount);
} }
} }
} else if (text.startsWith("define") || text.startsWith("define")) { } else if (text.startsWith("define")) {
// format: #define A B, remove define keyword // format: #define A B, remove define keyword
QString s = text.mid(QString("define").length()); QString s = text.mid(QString("define").length());
@ -2592,6 +2615,7 @@ void CppParser::handlePreprocessor()
QString name,args,value; QString name,args,value;
mPreprocessor.getDefineParts(s,name,args,value); mPreprocessor.getDefineParts(s,name,args,value);
qDebug()<<"add define "<<name<<mCurrentFile<<mTokenizer[mIndex]->line<<mIndex;
addStatement( addStatement(
nullptr, // defines don't belong to any scope nullptr, // defines don't belong to any scope
mCurrentFile, mCurrentFile,
@ -3271,13 +3295,13 @@ void CppParser::internalParse(const QString &fileName)
mPreprocessor.preprocess(fileName, buffer); mPreprocessor.preprocess(fileName, buffer);
QStringList preprocessResult = mPreprocessor.result(); QStringList preprocessResult = mPreprocessor.result();
//reduce memory usage
mPreprocessor.clearTempResults();
#ifdef QT_DEBUG #ifdef QT_DEBUG
// stringsToFile(mPreprocessor.result(),"r:\\preprocess.txt"); // stringsToFile(mPreprocessor.result(),QString("r:\\preprocess-%1.txt").arg(extractFileName(fileName)));
// mPreprocessor.dumpDefinesTo("r:\\defines.txt"); // mPreprocessor.dumpDefinesTo("r:\\defines.txt");
// mPreprocessor.dumpIncludesListTo("r:\\includes.txt"); // mPreprocessor.dumpIncludesListTo("r:\\includes.txt");
#endif #endif
//reduce memory usage
mPreprocessor.clearTempResults();
// Tokenize the preprocessed buffer file // Tokenize the preprocessed buffer file
mTokenizer.tokenize(preprocessResult); mTokenizer.tokenize(preprocessResult);
@ -3287,7 +3311,6 @@ void CppParser::internalParse(const QString &fileName)
return; return;
// Process the token list // Process the token list
internalClear();
while(true) { while(true) {
if (!handleStatement()) if (!handleStatement())
break; break;
@ -3299,21 +3322,12 @@ void CppParser::internalParse(const QString &fileName)
// //
// mStatementList.dumpAll("r:\\all-stats.txt"); // mStatementList.dumpAll("r:\\all-stats.txt");
#endif #endif
//reduce memory usage
mTokenizer.clear();
} }
} }
void CppParser::inheritClassStatement(const PStatement& derived, bool isStruct, void CppParser::inheritClassStatement(const PStatement& derived, bool isStruct,
const PStatement& base, StatementClassScope access) const PStatement& base, StatementClassScope access)
{ {
PFileIncludes fileIncludes1=mPreprocessor.includesList().value(derived->fileName);
PFileIncludes fileIncludes2=mPreprocessor.includesList().value(base->fileName);
if (fileIncludes1 && fileIncludes2) {
//derived class depeneds on base class
fileIncludes1->dependingFiles.insert(base->fileName);
fileIncludes2->dependedFiles.insert(derived->fileName);
}
//differentiate class and struct //differentiate class and struct
if (access == StatementClassScope::scsNone) { if (access == StatementClassScope::scsNone) {
if (isStruct) if (isStruct)
@ -4271,8 +4285,6 @@ void CppParser::internalInvalidateFile(const QString &fileName)
mNamespaces.remove(key); mNamespaces.remove(key);
} }
} }
// delete it from scannedfiles
mPreprocessor.scannedFiles().remove(fileName);
// remove its include files list // remove its include files list
PFileIncludes p = findFileIncludes(fileName, true); PFileIncludes p = findFileIncludes(fileName, true);
@ -4281,25 +4293,12 @@ void CppParser::internalInvalidateFile(const QString &fileName)
//p->includeFiles.clear(); //p->includeFiles.clear();
//p->usings.clear(); //p->usings.clear();
for (PStatement& statement:p->statements) { for (PStatement& statement:p->statements) {
if ((statement->kind == StatementKind::skFunction
|| statement->kind == StatementKind::skConstructor
|| statement->kind == StatementKind::skDestructor
|| statement->kind == StatementKind::skVariable)
&& (fileName != statement->fileName)) {
statement->hasDefinition = false;
}
}
for (PStatement& statement:p->declaredStatements) {
mStatementList.deleteStatement(statement); mStatementList.deleteStatement(statement);
} }
p->statements.clear();
//p->declaredStatements.clear();
//p->statements.clear();
//p->scopes.clear();
//p->dependedFiles.clear();
//p->dependingFiles.clear();
} }
// delete it from scannedfiles
mPreprocessor.removeScannedFile(fileName);
} }
void CppParser::internalInvalidateFiles(const QSet<QString> &files) void CppParser::internalInvalidateFiles(const QSet<QString> &files)
@ -4312,22 +4311,14 @@ QSet<QString> CppParser::calculateFilesToBeReparsed(const QString &fileName)
{ {
if (fileName.isEmpty()) if (fileName.isEmpty())
return QSet<QString>(); return QSet<QString>();
QQueue<QString> queue; QSet<QString> result;
QSet<QString> processed; result.insert(fileName);
queue.enqueue(fileName); foreach (const QString& file,mPreprocessor.includesList().keys()) {
while (!queue.isEmpty()) { PFileIncludes fileIncludes = mPreprocessor.includesList()[file];
QString name = queue.dequeue(); if (fileIncludes->includeFiles.contains(fileName))
processed.insert(name); result.insert(file);
PFileIncludes p=mPreprocessor.includesList().value(name);
if (!p)
continue;
foreach (const QString& s,p->dependedFiles) {
if (!processed.contains(s)) {
queue.enqueue(s);
} }
} return result;
}
return processed;
} }
int CppParser::calcKeyLenForStruct(const QString &word) int CppParser::calcKeyLenForStruct(const QString &word)
@ -4738,6 +4729,7 @@ int CppParser::parserId() const
void CppParser::setOnGetFileStream(const GetFileStreamCallBack &newOnGetFileStream) void CppParser::setOnGetFileStream(const GetFileStreamCallBack &newOnGetFileStream)
{ {
mOnGetFileStream = newOnGetFileStream; mOnGetFileStream = newOnGetFileStream;
mPreprocessor.setOnGetFileStream(newOnGetFileStream);
} }
const QSet<QString> &CppParser::filesToScan() const const QSet<QString> &CppParser::filesToScan() const

View File

@ -29,7 +29,6 @@ class CppParser : public QObject
{ {
Q_OBJECT Q_OBJECT
using GetFileStreamCallBack = std::function<bool (const QString&, QStringList&)>;
public: public:
explicit CppParser(QObject *parent = nullptr); explicit CppParser(QObject *parent = nullptr);
~CppParser(); ~CppParser();
@ -190,6 +189,8 @@ private:
void internalClear(); void internalClear();
QStringList sortFilesByIncludeRelations(const QSet<QString> &files);
bool checkForCatchBlock(); bool checkForCatchBlock();
bool checkForEnum(); bool checkForEnum();
bool checkForForBlock(); bool checkForForBlock();

View File

@ -249,28 +249,12 @@ void CppPreprocessor::dumpIncludesListTo(const QString &fileName) const
#else #else
<<endl; <<endl;
#endif #endif
foreach (const QString& s,fileIncludes->dependingFiles) {
stream<<"\t^^"+s
#if QT_VERSION >= QT_VERSION_CHECK(5,15,0)
<<Qt::endl;
#else
<<endl;
#endif
}
stream<<"\t**depended by:**" stream<<"\t**depended by:**"
#if QT_VERSION >= QT_VERSION_CHECK(5,15,0) #if QT_VERSION >= QT_VERSION_CHECK(5,15,0)
<<Qt::endl; <<Qt::endl;
#else #else
<<endl; <<endl;
#endif #endif
foreach (const QString& s,fileIncludes->dependedFiles) {
stream<<"\t&&"+s
#if QT_VERSION >= QT_VERSION_CHECK(5,15,0)
<<Qt::endl;
#else
<<endl;
#endif
}
stream<<"\t**using:**" stream<<"\t**using:**"
#if QT_VERSION >= QT_VERSION_CHECK(5,15,0) #if QT_VERSION >= QT_VERSION_CHECK(5,15,0)
<<Qt::endl; <<Qt::endl;
@ -490,7 +474,11 @@ void CppPreprocessor::handleInclude(const QString &line, bool fromNext)
return; return;
PFileIncludes oldCurrentIncludes = mCurrentIncludes; PFileIncludes oldCurrentIncludes = mCurrentIncludes;
openInclude(fileName); QStringList buffer;
if (mOnGetFileStream) {
mOnGetFileStream(fileName,buffer);
}
openInclude(fileName, buffer);
//oldCurrentIncludes->includeFiles.insert(fileName,true); //oldCurrentIncludes->includeFiles.insert(fileName,true);
} }
@ -745,7 +733,6 @@ void CppPreprocessor::openInclude(const QString &fileName, QStringList bufferedT
//mCurrentIncludes->includeFiles; //mCurrentIncludes->includeFiles;
//mCurrentIncludes->usings; //mCurrentIncludes->usings;
//mCurrentIncludes->statements; //mCurrentIncludes->statements;
//mCurrentIncludes->declaredStatements;
//mCurrentIncludes->scopes; //mCurrentIncludes->scopes;
//mCurrentIncludes->dependedFiles; //mCurrentIncludes->dependedFiles;
//mCurrentIncludes->dependingFiles; //mCurrentIncludes->dependingFiles;
@ -755,7 +742,7 @@ void CppPreprocessor::openInclude(const QString &fileName, QStringList bufferedT
parsedFile->fileIncludes = mCurrentIncludes; parsedFile->fileIncludes = mCurrentIncludes;
// Don't parse stuff we have already parsed // Don't parse stuff we have already parsed
if ((!bufferedText.isEmpty()) || !mScannedFiles.contains(fileName)) { if (!mScannedFiles.contains(fileName)) {
// Parse ONCE // Parse ONCE
//if not Assigned(Stream) then //if not Assigned(Stream) then
mScannedFiles.insert(fileName); mScannedFiles.insert(fileName);
@ -1842,6 +1829,11 @@ int CppPreprocessor::evaluateExpression(QString line)
return result; return result;
} }
void CppPreprocessor::setOnGetFileStream(const GetFileStreamCallBack &newOnGetFileStream)
{
mOnGetFileStream = newOnGetFileStream;
}
const QList<QString> &CppPreprocessor::projectIncludePathList() const const QList<QString> &CppPreprocessor::projectIncludePathList() const
{ {
return mProjectIncludePathList; return mProjectIncludePathList;

View File

@ -75,6 +75,7 @@ public:
void addProjectIncludePath(const QString& fileName); void addProjectIncludePath(const QString& fileName);
void clearIncludePaths(); void clearIncludePaths();
void clearProjectIncludePaths(); void clearProjectIncludePaths();
void removeScannedFile(const QString& filename);
QStringList result() const; QStringList result() const;
@ -91,6 +92,8 @@ public:
const QList<QString> &includePathList() const; const QList<QString> &includePathList() const;
const QList<QString> &projectIncludePathList() const; const QList<QString> &projectIncludePathList() const;
void setOnGetFileStream(const GetFileStreamCallBack &newOnGetFileStream);
private: private:
void preprocessBuffer(); void preprocessBuffer();
void skipToEndOfPreprocessor(); void skipToEndOfPreprocessor();
@ -221,6 +224,8 @@ private:
bool mParseSystem; bool mParseSystem;
bool mParseLocal; bool mParseLocal;
GetFileStreamCallBack mOnGetFileStream;
}; };
using PCppPreprocessor = std::shared_ptr<CppPreprocessor>; using PCppPreprocessor = std::shared_ptr<CppPreprocessor>;

View File

@ -666,3 +666,15 @@ bool isCppControlKeyword(const QString &word)
{ {
return CppControlKeyWords.contains(word); return CppControlKeyWords.contains(word);
} }
static int counter=0;
Statement::Statement()
{
counter++;
}
Statement::~Statement()
{
counter--;
qDebug()<<"statement deleted:"<<counter<<fullName<<kind<<extractFileName(fileName)<<line;
}

View File

@ -22,6 +22,8 @@
#include <QVector> #include <QVector>
#include <memory> #include <memory>
using GetFileStreamCallBack = std::function<bool (const QString&, QStringList&)>;
enum class ParserLanguage { enum class ParserLanguage {
C, C,
CPlusPlus CPlusPlus
@ -111,15 +113,6 @@ enum class MemberOperatorType {
otOther otOther
}; };
struct RemovedStatement{
QString type; // type "int"
QString command; // identifier/name of statement "foo"
int definitionLine; // definition
QString definitionFileName; // definition
QString fullName; // fullname(including class and namespace)
QString noNameArgs; // Args without name
};
enum class EvalStatementKind { enum class EvalStatementKind {
Namespace, Namespace,
Type, Type,
@ -128,8 +121,6 @@ enum class EvalStatementKind {
Function Function
}; };
using PRemovedStatement = std::shared_ptr<RemovedStatement>;
struct StatementMatchPosition{ struct StatementMatchPosition{
int start; int start;
int end; int end;
@ -143,6 +134,8 @@ using StatementList = QList<PStatement>;
using PStatementList = std::shared_ptr<StatementList>; using PStatementList = std::shared_ptr<StatementList>;
using StatementMap = QMultiMap<QString, PStatement>; using StatementMap = QMultiMap<QString, PStatement>;
struct Statement { struct Statement {
Statement();
~Statement();
std::weak_ptr<Statement> parentScope; // parent class/struct/namespace scope, don't use auto pointer to prevent circular reference std::weak_ptr<Statement> parentScope; // parent class/struct/namespace scope, don't use auto pointer to prevent circular reference
QString type; // type "int" QString type; // type "int"
QString command; // identifier/name of statement "foo" QString command; // identifier/name of statement "foo"
@ -234,8 +227,6 @@ struct FileIncludes {
StatementMap statements; // but we don't save temporary statements (full name as key) StatementMap statements; // but we don't save temporary statements (full name as key)
StatementMap declaredStatements; // statements declared in this file (full name as key) StatementMap declaredStatements; // statements declared in this file (full name as key)
CppScopes scopes; // int is start line of the statement scope CppScopes scopes; // int is start line of the statement scope
QSet<QString> dependingFiles; // The files I depeneds on
QSet<QString> dependedFiles; // the files depends on me
}; };
using PFileIncludes = std::shared_ptr<FileIncludes>; using PFileIncludes = std::shared_ptr<FileIncludes>;