2021-12-26 23:18:28 +08:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2020-2022 Roy Qu (royqh1979@gmail.com)
|
|
|
|
*
|
|
|
|
* This program is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
*/
|
2021-08-05 23:13:21 +08:00
|
|
|
#ifndef CPPPREPROCESSOR_H
|
|
|
|
#define CPPPREPROCESSOR_H
|
|
|
|
|
|
|
|
#include <QObject>
|
2021-08-07 18:02:57 +08:00
|
|
|
#include <QTextStream>
|
2021-08-13 11:18:42 +08:00
|
|
|
#include "parserutils.h"
|
2021-08-07 18:02:57 +08:00
|
|
|
|
2021-08-10 21:27:24 +08:00
|
|
|
#define MAX_DEFINE_EXPAND_DEPTH 20
|
2021-08-27 23:51:42 +08:00
|
|
|
enum class DefineArgTokenType{
|
|
|
|
Symbol,
|
|
|
|
Identifier,
|
|
|
|
Space,
|
|
|
|
Sharp,
|
|
|
|
DSharp,
|
|
|
|
Other
|
|
|
|
};
|
|
|
|
struct DefineArgToken {
|
|
|
|
QString value;
|
|
|
|
DefineArgTokenType type;
|
|
|
|
};
|
|
|
|
using PDefineArgToken = std::shared_ptr<DefineArgToken>;
|
2021-08-10 21:27:24 +08:00
|
|
|
|
2021-08-07 18:02:57 +08:00
|
|
|
struct ParsedFile {
|
|
|
|
int index; // 0-based for programming convenience
|
|
|
|
QString fileName; // Record filename, but not used now
|
|
|
|
QStringList buffer; // do not concat them all
|
|
|
|
int branches; //branch levels;
|
2024-04-06 18:10:49 +08:00
|
|
|
PParsedFileInfo fileInfo; // info of this file
|
2021-08-07 18:02:57 +08:00
|
|
|
};
|
|
|
|
using PParsedFile = std::shared_ptr<ParsedFile>;
|
2021-08-05 23:13:21 +08:00
|
|
|
|
2021-08-14 22:52:37 +08:00
|
|
|
class CppPreprocessor
|
2021-08-05 23:13:21 +08:00
|
|
|
{
|
2021-08-12 11:29:26 +08:00
|
|
|
enum class ContentType {
|
|
|
|
AnsiCComment,
|
|
|
|
CppComment,
|
|
|
|
String,
|
|
|
|
Character,
|
|
|
|
EscapeSequence,
|
|
|
|
RawStringPrefix,
|
|
|
|
RawString,
|
|
|
|
Other
|
|
|
|
};
|
|
|
|
|
2021-08-05 23:13:21 +08:00
|
|
|
public:
|
2021-08-07 18:02:57 +08:00
|
|
|
|
2021-08-14 22:52:37 +08:00
|
|
|
explicit CppPreprocessor();
|
2023-01-12 12:07:22 +08:00
|
|
|
CppPreprocessor(const CppPreprocessor&)=delete;
|
|
|
|
CppPreprocessor& operator=(const CppPreprocessor&)=delete;
|
|
|
|
|
2021-08-08 17:22:37 +08:00
|
|
|
void clear();
|
2022-10-22 10:59:39 +08:00
|
|
|
|
|
|
|
void clearTempResults();
|
2021-08-13 11:18:42 +08:00
|
|
|
void getDefineParts(const QString& input, QString &name, QString &args, QString &value);
|
2021-11-12 02:20:13 +08:00
|
|
|
void addHardDefineByLine(const QString& line);
|
2021-08-08 17:22:37 +08:00
|
|
|
void setScanOptions(bool parseSystem, bool parseLocal);
|
2023-02-15 16:24:24 +08:00
|
|
|
void preprocess(const QString& fileName);
|
2021-08-13 11:18:42 +08:00
|
|
|
|
2021-08-21 22:15:44 +08:00
|
|
|
void dumpDefinesTo(const QString& fileName) const;
|
|
|
|
void dumpIncludesListTo(const QString& fileName) const;
|
2021-10-04 21:59:48 +08:00
|
|
|
void addIncludePath(const QString& fileName);
|
|
|
|
void addProjectIncludePath(const QString& fileName);
|
|
|
|
void clearIncludePaths();
|
|
|
|
void clearProjectIncludePaths();
|
2022-10-22 19:33:20 +08:00
|
|
|
void removeScannedFile(const QString& filename);
|
2022-10-22 10:59:39 +08:00
|
|
|
|
2024-03-06 19:35:35 +08:00
|
|
|
PDefine getDefine(const QString& name) const{
|
|
|
|
return mDefines.value(name,PDefine());
|
|
|
|
}
|
|
|
|
|
|
|
|
QString expandMacros(const QString& text, QSet<QString> usedMacros) const;
|
|
|
|
void expandMacro(const QString &text, QString &newText, const QString &word, int &i, QSet<QString> usedMacros) const;
|
|
|
|
|
2022-12-27 14:29:49 +08:00
|
|
|
const QStringList& result() const{
|
|
|
|
return mResult;
|
|
|
|
};
|
2021-08-21 22:15:44 +08:00
|
|
|
|
2024-04-06 18:10:49 +08:00
|
|
|
PParsedFileInfo findFileInfo(const QString& fileName) const {
|
|
|
|
return mFileInfos.value(fileName);
|
2023-11-05 21:00:52 +08:00
|
|
|
}
|
2021-08-21 22:15:44 +08:00
|
|
|
|
2023-11-05 21:00:52 +08:00
|
|
|
void removeFileIncludes(const QString& fileName) {
|
2024-04-06 18:10:49 +08:00
|
|
|
mFileInfos.remove(fileName);
|
2023-11-05 21:00:52 +08:00
|
|
|
}
|
2023-03-11 17:32:57 +08:00
|
|
|
|
2023-11-05 21:00:52 +08:00
|
|
|
bool fileScanned(const QString& fileName) const {
|
|
|
|
return mScannedFiles.contains(fileName);
|
|
|
|
}
|
2021-08-21 22:15:44 +08:00
|
|
|
|
2023-11-05 21:00:52 +08:00
|
|
|
const QSet<QString>& includePaths() const {
|
|
|
|
return mIncludePaths;
|
|
|
|
}
|
2021-08-21 22:15:44 +08:00
|
|
|
|
2023-11-05 21:00:52 +08:00
|
|
|
const QSet<QString>& scannedFiles() const {
|
|
|
|
return mScannedFiles;
|
|
|
|
}
|
|
|
|
|
|
|
|
const QSet<QString> &projectIncludePaths() {
|
|
|
|
return mProjectIncludePaths;
|
|
|
|
}
|
2021-08-21 22:15:44 +08:00
|
|
|
|
2021-08-22 23:48:00 +08:00
|
|
|
const DefineMap &hardDefines() const;
|
|
|
|
|
2021-10-04 22:32:34 +08:00
|
|
|
const QList<QString> &includePathList() const;
|
|
|
|
|
|
|
|
const QList<QString> &projectIncludePathList() const;
|
2022-10-22 19:33:20 +08:00
|
|
|
void setOnGetFileStream(const GetFileStreamCallBack &newOnGetFileStream);
|
|
|
|
|
2022-12-11 19:47:43 +08:00
|
|
|
static QList<PDefineArgToken> tokenizeValue(const QString& value);
|
|
|
|
|
2021-08-07 18:02:57 +08:00
|
|
|
private:
|
2023-10-01 10:45:43 +08:00
|
|
|
|
|
|
|
enum class BranchResult {
|
|
|
|
isTrue, /* This branch is true */
|
|
|
|
isFalse_but_trued, /* This branch is false, but a previous branch is true */
|
|
|
|
isFalse, /* This branch and all previous branches is false */
|
|
|
|
parentIsFalse
|
|
|
|
};
|
|
|
|
|
2024-03-06 19:35:35 +08:00
|
|
|
static QString expandFunction(PDefine define,const QString &args);
|
2021-08-07 18:02:57 +08:00
|
|
|
void preprocessBuffer();
|
|
|
|
void skipToEndOfPreprocessor();
|
|
|
|
void skipToPreprocessor();
|
|
|
|
QString getNextPreprocessor();
|
|
|
|
void handleBranch(const QString& line);
|
2021-08-08 17:22:37 +08:00
|
|
|
void handleDefine(const QString& line);
|
2021-10-04 21:59:48 +08:00
|
|
|
void handleInclude(const QString& line, bool fromNext=false);
|
2021-08-08 17:22:37 +08:00
|
|
|
void handlePreprocessor(const QString& value);
|
|
|
|
void handleUndefine(const QString& line);
|
2023-07-01 18:06:39 +08:00
|
|
|
QString expandMacros();
|
2024-03-06 19:35:35 +08:00
|
|
|
void expandMacro(QString &newLine, const QString &word, int& i, QSet<QString> usedMacros);
|
2021-08-07 18:02:57 +08:00
|
|
|
QString removeGCCAttributes(const QString& line);
|
2021-08-12 21:25:13 +08:00
|
|
|
void removeGCCAttribute(const QString&line, QString& newLine, int &i, const QString& word);
|
2024-03-06 19:35:35 +08:00
|
|
|
|
2021-08-07 18:02:57 +08:00
|
|
|
// current file stuff
|
2022-12-27 14:34:57 +08:00
|
|
|
PParsedFile getInclude(int index) const {
|
|
|
|
return mIncludes[index];
|
|
|
|
}
|
2023-10-28 09:20:01 +08:00
|
|
|
void openInclude(QString fileName);
|
2021-08-07 18:02:57 +08:00
|
|
|
void closeInclude();
|
2021-08-07 23:30:01 +08:00
|
|
|
|
2021-08-07 18:02:57 +08:00
|
|
|
// branch stuff
|
2023-10-01 10:45:43 +08:00
|
|
|
BranchResult getCurrentBranch(){
|
2022-12-27 14:34:57 +08:00
|
|
|
if (!mBranchResults.isEmpty())
|
|
|
|
return mBranchResults.last();
|
|
|
|
else
|
2023-10-01 10:45:43 +08:00
|
|
|
return BranchResult::isTrue;
|
|
|
|
}
|
|
|
|
BranchResult calcElseBranchResult(BranchResult oldResult);
|
|
|
|
bool sameResultWithCurrentBranch(BranchResult value) {
|
|
|
|
return (getCurrentBranch()==BranchResult::isTrue && value == BranchResult::isTrue)
|
|
|
|
|| (getCurrentBranch()!=BranchResult::isTrue && value != BranchResult::isTrue);
|
2022-12-27 14:34:57 +08:00
|
|
|
}
|
2023-10-01 10:45:43 +08:00
|
|
|
void setCurrentBranch(BranchResult value){
|
|
|
|
if (!sameResultWithCurrentBranch(value)) {
|
2024-04-06 18:10:49 +08:00
|
|
|
mCurrentFileInfo->branches.insert(mIndex+1,value==BranchResult::isTrue);
|
2023-08-17 13:24:08 +08:00
|
|
|
}
|
2022-12-27 14:34:57 +08:00
|
|
|
mBranchResults.append(value);
|
|
|
|
}
|
|
|
|
void removeCurrentBranch(){
|
2023-10-01 10:45:43 +08:00
|
|
|
BranchResult value = getCurrentBranch();
|
2023-08-17 13:24:08 +08:00
|
|
|
if (mBranchResults.size()>0) {
|
2022-12-27 14:34:57 +08:00
|
|
|
mBranchResults.pop_back();
|
2023-08-17 13:24:08 +08:00
|
|
|
}
|
2023-10-01 10:45:43 +08:00
|
|
|
if (!sameResultWithCurrentBranch(value)) {
|
2024-04-06 18:10:49 +08:00
|
|
|
mCurrentFileInfo->branches.insert(mIndex,getCurrentBranch()==BranchResult::isTrue);
|
2023-08-17 13:24:08 +08:00
|
|
|
}
|
2023-10-01 10:45:43 +08:00
|
|
|
}
|
2021-08-07 18:02:57 +08:00
|
|
|
void addDefinesInFile(const QString& fileName);
|
2021-11-12 02:20:13 +08:00
|
|
|
void addDefineByParts(const QString& name, const QString& args,
|
|
|
|
const QString& value, bool hardCoded);
|
|
|
|
void addDefineByLine(const QString& line, bool hardCoded);
|
2022-12-27 14:34:57 +08:00
|
|
|
PDefine getHardDefine(const QString& name){
|
|
|
|
return mHardDefines.value(name,PDefine());
|
|
|
|
};
|
2021-11-12 02:20:13 +08:00
|
|
|
void invalidDefinesInFile(const QString& fileName);
|
2021-08-07 23:30:01 +08:00
|
|
|
|
2021-08-12 21:25:13 +08:00
|
|
|
void parseArgs(PDefine define);
|
|
|
|
|
2021-08-12 11:29:26 +08:00
|
|
|
QStringList removeComments(const QStringList& text);
|
2021-08-09 09:11:10 +08:00
|
|
|
/*
|
|
|
|
* '_','a'..'z','A'..'Z','0'..'9'
|
|
|
|
*/
|
2022-12-11 19:47:43 +08:00
|
|
|
static bool isWordChar(const QChar& ch);
|
2021-08-09 09:11:10 +08:00
|
|
|
/*
|
|
|
|
* 'A'..'Z', '0'..'9', 'a'..'z', '_', '*', '&', '~'
|
|
|
|
*/
|
2022-12-11 19:47:43 +08:00
|
|
|
static bool isIdentChar(const QChar& ch);
|
2021-08-09 09:47:36 +08:00
|
|
|
/*
|
|
|
|
* '\r','\n'
|
|
|
|
*/
|
2022-12-11 19:47:43 +08:00
|
|
|
static bool isLineChar(const QChar& ch);
|
2021-08-09 09:47:36 +08:00
|
|
|
/*
|
|
|
|
* '\t' ' '
|
|
|
|
*/
|
2022-12-11 19:47:43 +08:00
|
|
|
static bool isSpaceChar(const QChar& ch);
|
2021-08-09 09:47:36 +08:00
|
|
|
/*
|
|
|
|
* '+', '-', '*', '/', '!', '=', '<', '>', '&', '|', '^'
|
|
|
|
*/
|
2023-05-24 13:42:46 +08:00
|
|
|
//static bool isOperatorChar(const QChar& ch);
|
2021-08-09 09:47:36 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 'A'..'Z', 'a'..'z', '_'
|
|
|
|
*/
|
2022-12-11 19:47:43 +08:00
|
|
|
static bool isMacroIdentChar(const QChar& ch);
|
2021-08-08 17:22:37 +08:00
|
|
|
|
2021-08-10 12:09:48 +08:00
|
|
|
/*
|
|
|
|
* '0'..'9'
|
|
|
|
*/
|
2022-12-11 19:47:43 +08:00
|
|
|
static bool isDigit(const QChar& ch);
|
2021-08-10 12:09:48 +08:00
|
|
|
|
2021-08-10 17:23:15 +08:00
|
|
|
/*
|
|
|
|
* '0'..'9','x',X','a'..'f','A'..'F','u','U','l','L'
|
|
|
|
*/
|
2022-12-11 19:47:43 +08:00
|
|
|
static bool isNumberChar(const QChar& ch);
|
2021-08-10 17:23:15 +08:00
|
|
|
|
2021-08-08 17:22:37 +08:00
|
|
|
QString lineBreak();
|
2021-08-09 09:11:10 +08:00
|
|
|
|
|
|
|
bool evaluateIf(const QString& line);
|
2021-08-10 12:09:48 +08:00
|
|
|
QString expandDefines(QString line);
|
2023-10-25 11:49:25 +08:00
|
|
|
bool skipParenthesis(const QString&line, int& index, int step = 1);
|
2021-08-10 12:09:48 +08:00
|
|
|
bool skipSpaces(const QString &expr, int& pos);
|
2021-08-10 17:23:15 +08:00
|
|
|
bool evalNumber(const QString &expr, int& result, int& pos);
|
2021-08-10 12:09:48 +08:00
|
|
|
bool evalTerm(const QString &expr, int& result, int& pos);
|
|
|
|
bool evalUnaryExpr(const QString &expr, int& result, int& pos);
|
|
|
|
bool evalMulExpr(const QString &expr, int& result, int& pos);
|
|
|
|
bool evalAddExpr(const QString &expr, int& result, int& pos);
|
|
|
|
bool evalShiftExpr(const QString &expr, int& result, int& pos);
|
|
|
|
bool evalRelationExpr(const QString &expr, int& result, int& pos);
|
|
|
|
bool evalEqualExpr(const QString &expr, int& result, int& pos);
|
|
|
|
bool evalBitAndExpr(const QString &expr, int& result, int& pos);
|
|
|
|
bool evalBitXorExpr(const QString &expr, int& result, int& pos);
|
|
|
|
bool evalBitOrExpr(const QString &expr, int& result, int& pos);
|
|
|
|
bool evalLogicAndExpr(const QString &expr, int& result, int& pos);
|
|
|
|
bool evalLogicOrExpr(const QString &expr, int& result, int& pos);
|
|
|
|
bool evalExpr(const QString &expr, int& result, int& pos);
|
|
|
|
|
2021-08-10 17:23:15 +08:00
|
|
|
int evaluateExpression(QString line);
|
2021-08-07 18:02:57 +08:00
|
|
|
private:
|
2022-10-22 10:44:10 +08:00
|
|
|
//temporary data when preprocessing single file
|
|
|
|
int mIndex; // points to current file buffer.
|
|
|
|
QString mFileName;
|
|
|
|
QStringList mBuffer;
|
2021-08-07 18:02:57 +08:00
|
|
|
QStringList mResult;
|
2024-04-06 18:10:49 +08:00
|
|
|
PParsedFileInfo mCurrentFileInfo;
|
2022-10-22 10:44:10 +08:00
|
|
|
int mPreProcIndex;
|
2021-08-07 18:02:57 +08:00
|
|
|
QList<PParsedFile> mIncludes; // stack of files we've stepped into. last one is current file, first one is source file
|
2023-10-01 10:45:43 +08:00
|
|
|
QList<BranchResult> mBranchResults;// stack of branch results (boolean). last one is current branch, first one is outermost branch
|
2021-11-12 02:20:13 +08:00
|
|
|
DefineMap mDefines; // working set, editable
|
|
|
|
QSet<QString> mProcessed; // dictionary to save filename already processed
|
|
|
|
|
2022-10-22 10:44:10 +08:00
|
|
|
|
|
|
|
//Result across processings.
|
2021-11-12 02:20:13 +08:00
|
|
|
//used by parser even preprocess finished
|
2024-04-06 18:10:49 +08:00
|
|
|
QHash<QString, PParsedFileInfo> mFileInfos;
|
2021-11-12 02:20:13 +08:00
|
|
|
QHash<QString, PDefineMap> mFileDefines; //dictionary to save defines for each headerfile;
|
2024-03-26 16:33:22 +08:00
|
|
|
QHash<QString, PDefineMap> mFileUndefines; //dictionary to save defines for each headerfile;
|
2022-10-22 10:44:10 +08:00
|
|
|
QSet<QString> mScannedFiles;
|
|
|
|
|
|
|
|
//option data for the parser
|
2021-08-21 22:15:44 +08:00
|
|
|
//{ List of current project's include path }
|
2022-10-22 10:44:10 +08:00
|
|
|
DefineMap mHardDefines; // set by "cpp -dM -E -xc NUL"
|
2021-08-19 23:49:23 +08:00
|
|
|
QSet<QString> mProjectIncludePaths;
|
2021-10-04 21:59:48 +08:00
|
|
|
//we also need include paths in order (for #include_next)
|
|
|
|
QList<QString> mIncludePathList;
|
2021-10-04 22:32:34 +08:00
|
|
|
QList<QString> mProjectIncludePathList;
|
2021-11-12 02:20:13 +08:00
|
|
|
//{ List of current compiler set's include path}
|
|
|
|
QSet<QString> mIncludePaths;
|
2021-08-21 22:15:44 +08:00
|
|
|
|
2021-08-07 18:02:57 +08:00
|
|
|
bool mParseSystem;
|
|
|
|
bool mParseLocal;
|
2022-10-22 19:33:20 +08:00
|
|
|
|
|
|
|
GetFileStreamCallBack mOnGetFileStream;
|
2021-08-05 23:13:21 +08:00
|
|
|
};
|
|
|
|
|
2022-10-18 12:24:59 +08:00
|
|
|
using PCppPreprocessor = std::shared_ptr<CppPreprocessor>;
|
|
|
|
|
2021-08-05 23:13:21 +08:00
|
|
|
#endif // CPPPREPROCESSOR_H
|