1/*
2 * Copyright (C) 2020-2022 Roy Qu (royqh1979@gmail.com)
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 */
17#ifndef CPPPREPROCESSOR_H
18#define CPPPREPROCESSOR_H
19
20#include <QObject>
21#include <QTextStream>
22#include "parserutils.h"
23
24#define MAX_DEFINE_EXPAND_DEPTH 20
25enum class DefineArgTokenType{
26 Symbol,
27 Identifier,
28 Space,
29 Sharp,
30 DSharp,
31 Other
32};
33struct DefineArgToken {
34 QString value;
35 DefineArgTokenType type;
36};
37using PDefineArgToken = std::shared_ptr<DefineArgToken>;
38
39struct ParsedFile {
40 int index; // 0-based for programming convenience
41 QString fileName; // Record filename, but not used now
42 QStringList buffer; // do not concat them all
43 int branches; //branch levels;
44 PFileIncludes fileIncludes; // includes of this file
45};
46using PParsedFile = std::shared_ptr<ParsedFile>;
47
48class CppPreprocessor
49{
50 enum class ContentType {
51 AnsiCComment,
52 CppComment,
53 String,
54 Character,
55 EscapeSequence,
56 RawStringPrefix,
57 RawString,
58 Other
59 };
60
61public:
62
63 explicit CppPreprocessor();
64 void clear();
65 void clearResult();
66 void getDefineParts(const QString& input, QString &name, QString &args, QString &value);
67 void addHardDefineByLine(const QString& line);
68 void reset(); //reset but don't clear generated defines
69 void setScanOptions(bool parseSystem, bool parseLocal);
70 void preprocess(const QString& fileName, QStringList buffer = QStringList());
71
72 void dumpDefinesTo(const QString& fileName) const;
73 void dumpIncludesListTo(const QString& fileName) const;
74 void addIncludePath(const QString& fileName);
75 void addProjectIncludePath(const QString& fileName);
76 void clearIncludePaths();
77 void clearProjectIncludePaths();
78 QStringList result() const;
79
80 QHash<QString, PFileIncludes> &includesList();
81
82 QSet<QString> &scannedFiles();
83
84 const QSet<QString> &includePaths();
85
86 const QSet<QString> &projectIncludePaths();
87
88 const DefineMap &hardDefines() const;
89
90 const QList<QString> &includePathList() const;
91
92 const QList<QString> &projectIncludePathList() const;
93private:
94 void preprocessBuffer();
95 void skipToEndOfPreprocessor();
96 void skipToPreprocessor();
97 QString getNextPreprocessor();
98 void simplify(QString& output);
99 void handleBranch(const QString& line);
100 void handleDefine(const QString& line);
101 void handleInclude(const QString& line, bool fromNext=false);
102 void handlePreprocessor(const QString& value);
103 void handleUndefine(const QString& line);
104 QString expandMacros(const QString& line, int depth);
105 void expandMacro(const QString& line, QString& newLine, QString& word, int& i, int depth);
106 QString removeGCCAttributes(const QString& line);
107 void removeGCCAttribute(const QString&line, QString& newLine, int &i, const QString& word);
108 PDefine getDefine(const QString& name);
109 // current file stuff
110 PParsedFile getInclude(int index);
111 void openInclude(const QString& fileName, QStringList bufferedText=QStringList());
112 void closeInclude();
113
114 // branch stuff
115 bool getCurrentBranch();
116 void setCurrentBranch(bool value);
117 void removeCurrentBranch();
118 // include stuff
119 PFileIncludes getFileIncludesEntry(const QString& FileName);
120 void addDefinesInFile(const QString& fileName);
121 void resetDefines();
122 void addDefineByParts(const QString& name, const QString& args,
123 const QString& value, bool hardCoded);
124 void addDefineByLine(const QString& line, bool hardCoded);
125 PDefine getHardDefine(const QString& name);
126 void invalidDefinesInFile(const QString& fileName);
127
128 void parseArgs(PDefine define);
129 QList<PDefineArgToken> tokenizeValue(const QString& value);
130
131 QStringList removeComments(const QStringList& text);
132 /*
133 * '_','a'..'z','A'..'Z','0'..'9'
134 */
135 bool isWordChar(const QChar& ch);
136 /*
137 * 'A'..'Z', '0'..'9', 'a'..'z', '_', '*', '&', '~'
138 */
139 bool isIdentChar(const QChar& ch);
140 /*
141 * '\r','\n'
142 */
143 bool isLineChar(const QChar& ch);
144 /*
145 * '\t' ' '
146 */
147 bool isSpaceChar(const QChar& ch);
148 /*
149 * '+', '-', '*', '/', '!', '=', '<', '>', '&', '|', '^'
150 */
151 bool isOperatorChar(const QChar& ch);
152
153 /*
154 * 'A'..'Z', 'a'..'z', '_'
155 */
156 bool isMacroIdentChar(const QChar& ch);
157
158 /*
159 * '0'..'9'
160 */
161 bool isDigit(const QChar& ch);
162
163 /*
164 * '0'..'9','x',X','a'..'f','A'..'F','u','U','l','L'
165 */
166 bool isNumberChar(const QChar& ch);
167
168 QString lineBreak();
169
170 bool evaluateIf(const QString& line);
171 QString expandDefines(QString line);
172 bool skipBraces(const QString&line, int& index, int step = 1);
173 QString expandFunction(PDefine define,QString args);
174 bool skipSpaces(const QString &expr, int& pos);
175 bool evalNumber(const QString &expr, int& result, int& pos);
176 bool evalTerm(const QString &expr, int& result, int& pos);
177 bool evalUnaryExpr(const QString &expr, int& result, int& pos);
178 bool evalMulExpr(const QString &expr, int& result, int& pos);
179 bool evalAddExpr(const QString &expr, int& result, int& pos);
180 bool evalShiftExpr(const QString &expr, int& result, int& pos);
181 bool evalRelationExpr(const QString &expr, int& result, int& pos);
182 bool evalEqualExpr(const QString &expr, int& result, int& pos);
183 bool evalBitAndExpr(const QString &expr, int& result, int& pos);
184 bool evalBitXorExpr(const QString &expr, int& result, int& pos);
185 bool evalBitOrExpr(const QString &expr, int& result, int& pos);
186 bool evalLogicAndExpr(const QString &expr, int& result, int& pos);
187 bool evalLogicOrExpr(const QString &expr, int& result, int& pos);
188 bool evalExpr(const QString &expr, int& result, int& pos);
189
190 int evaluateExpression(QString line);
191private:
192 int mIndex; // points to current file buffer. do not free
193 QString mFileName; // idem
194 QStringList mBuffer; // idem
195 QStringList mResult;
196 PFileIncludes mCurrentIncludes;
197 int mPreProcIndex;
198 QList<PParsedFile> mIncludes; // stack of files we've stepped into. last one is current file, first one is source file
199 QList<bool> mBranchResults;// stack of branch results (boolean). last one is current branch, first one is outermost branch
200 DefineMap mDefines; // working set, editable
201 QSet<QString> mProcessed; // dictionary to save filename already processed
202
203 //used by parser even preprocess finished
204 DefineMap mHardDefines; // set by "cpp -dM -E -xc NUL"
205 QHash<QString,PFileIncludes> mIncludesList;
206 QHash<QString, PDefineMap> mFileDefines; //dictionary to save defines for each headerfile;
207 //{ List of current project's include path }
208 QSet<QString> mProjectIncludePaths;
209 //we also need include paths in order (for #include_next)
210 QList<QString> mIncludePathList;
211 QList<QString> mProjectIncludePathList;
212 //{ List of current compiler set's include path}
213 QSet<QString> mIncludePaths;
214
215 bool mParseSystem;
216 bool mParseLocal;
217 QSet<QString> mScannedFiles;
218};
219
220#endif // CPPPREPROCESSOR_H
221