RedPanda-CPP/RedPandaIDE/parser/cpppreprocessor.h

311 lines
10 KiB
C
Raw Permalink Normal View History

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>
#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;
PParsedFileInfo fileInfo; // info of this file
2021-08-07 18:02:57 +08:00
};
2024-04-09 17:15:27 +08:00
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
{
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();
CppPreprocessor(const CppPreprocessor&)=delete;
CppPreprocessor& operator=(const CppPreprocessor&)=delete;
2021-08-08 17:22:37 +08:00
void clear();
void clearTempResults();
void getDefineParts(const QString& input, QString &name, QString &args, QString &value);
void addHardDefineByLine(const QString& line) { addDefineByLine(line,true); }
void setScanOptions(bool parseSystem, bool parseLocal) {
mParseSystem = parseSystem;
mParseLocal=parseLocal;
}
void preprocess(const QString& fileName);
2021-08-21 22:15:44 +08:00
void dumpDefinesTo(const QString& fileName) const;
void dumpIncludesListTo(const QString& fileName) const;
void addIncludePath(const QString& fileName);
void addProjectIncludePath(const QString& fileName);
void clearIncludePaths() {
mIncludePaths.clear();
mIncludePathList.clear();
}
void clearProjectIncludePaths() {
mProjectIncludePaths.clear();
mProjectIncludePathList.clear();
}
void removeScannedFile(const QString& filename);
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
PParsedFileInfo findFileInfo(const QString& fileName) const {
return mFileInfos.value(fileName);
}
2021-08-21 22:15:44 +08:00
2024-04-09 19:39:35 +08:00
void removeFileInfo(const QString& fileName) {
mFileInfos.remove(fileName);
}
bool fileScanned(const QString& fileName) const {
return mScannedFiles.contains(fileName);
}
2021-08-21 22:15:44 +08:00
const QSet<QString>& includePaths() const {
return mIncludePaths;
}
2021-08-21 22:15:44 +08:00
const QSet<QString>& scannedFiles() const {
return mScannedFiles;
}
2024-05-28 19:01:18 +08:00
const QSet<QString> &projectIncludePaths() const {
return mProjectIncludePaths;
}
2021-08-21 22:15:44 +08:00
const DefineMap &hardDefines() const { return mHardDefines; }
2021-08-22 23:48:00 +08:00
const QList<QString> &includePathList() const { return mIncludePathList; }
2021-10-04 22:32:34 +08:00
const QList<QString> &projectIncludePathList() const { return mProjectIncludePathList; }
void setOnGetFileStream(const GetFileStreamCallBack &newOnGetFileStream) { mOnGetFileStream = newOnGetFileStream; }
static QList<PDefineArgToken> tokenizeValue(const QString& value);
2021-08-07 18:02:57 +08:00
private:
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
};
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);
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);
QString expandMacros();
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);
2021-08-07 18:02:57 +08:00
// current file stuff
2022-12-27 14:34:57 +08:00
PParsedFile getInclude(int index) const {
2024-04-09 19:39:35 +08:00
return mIncludeStack[index];
2022-12-27 14:34:57 +08:00
}
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
BranchResult getCurrentBranch(){
2022-12-27 14:34:57 +08:00
if (!mBranchResults.isEmpty())
return mBranchResults.last();
else
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
}
void setCurrentBranch(BranchResult value){
if (!sameResultWithCurrentBranch(value)) {
2024-04-09 18:10:44 +08:00
mCurrentFileInfo->insertBranch(mIndex+1,value==BranchResult::isTrue);
}
2022-12-27 14:34:57 +08:00
mBranchResults.append(value);
}
void removeCurrentBranch(){
BranchResult value = getCurrentBranch();
if (mBranchResults.size()>0) {
2022-12-27 14:34:57 +08:00
mBranchResults.pop_back();
}
if (!sameResultWithCurrentBranch(value)) {
2024-04-09 18:10:44 +08:00
mCurrentFileInfo->insertBranch(mIndex,getCurrentBranch()==BranchResult::isTrue);
}
}
2021-08-07 18:02:57 +08:00
void addDefinesInFile(const QString& fileName);
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());
};
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);
QStringList removeComments(const QStringList& text);
2021-08-09 09:11:10 +08:00
/*
* '_','a'..'z','A'..'Z','0'..'9'
*/
static bool isWordChar(const QChar& ch) {
return (ch=='_'
|| ch.isLetter()
|| (ch>='0' && ch<='9'));
}
2021-08-09 09:11:10 +08:00
/*
* 'A'..'Z', '0'..'9', 'a'..'z', '_', '*', '&', '~'
*/
static bool isIdentChar(const QChar& ch) {
return (ch=='_' || ch == '*' || ch == '&' || ch == '~' ||
ch.isLetter()
|| (ch>='0' && ch<='9'));
}
2021-08-09 09:47:36 +08:00
/*
* '\r','\n'
*/
static bool isLineChar(const QChar& ch) { return ch=='\r' || ch == '\n'; }
2021-08-09 09:47:36 +08:00
/*
* '\t' ' '
*/
static bool isSpaceChar(const QChar& ch) { return ch == ' ' || ch == '\t'; }
2021-08-09 09:47:36 +08:00
/*
* 'A'..'Z', 'a'..'z', '_'
*/
static bool isMacroIdentChar(const QChar& ch) { return ch.isLetter() || ch == '_'; }
2021-08-08 17:22:37 +08:00
2021-08-10 12:09:48 +08:00
/*
* '0'..'9'
*/
static bool isDigit(const QChar& ch) { return (ch>='0' && ch<='9'); }
2021-08-10 12:09:48 +08:00
/*
* '0'..'9','x',X','a'..'f','A'..'F','u','U','l','L'
*/
static bool isNumberChar(const QChar& ch);
QString lineBreak() { return "\n"; }
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);
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);
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;
PParsedFileInfo mCurrentFileInfo;
2022-10-22 10:44:10 +08:00
int mPreProcIndex;
2024-04-09 19:39:35 +08:00
QList<PParsedFile> mIncludeStack; // stack of files we've stepped into. last one is current file, first one is source file
QList<BranchResult> mBranchResults;// stack of branch results (boolean). last one is current branch, first one is outermost branch
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.
//used by parser even preprocess finished
QHash<QString, PParsedFileInfo> mFileInfos;
QHash<QString, PDefineMap> mFileDefines; //dictionary to save defines for each headerfile;
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;
//we also need include paths in order (for #include_next)
QList<QString> mIncludePathList;
2021-10-04 22:32:34 +08:00
QList<QString> mProjectIncludePathList;
//{ 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;
GetFileStreamCallBack mOnGetFileStream;
2021-08-05 23:13:21 +08:00
};
using PCppPreprocessor = std::shared_ptr<CppPreprocessor>;
2021-08-05 23:13:21 +08:00
#endif // CPPPREPROCESSOR_H