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 CPPPARSER_H
18#define CPPPARSER_H
19
20#include <QMutex>
21#include <QObject>
22#include <QThread>
23#include <QVector>
24#include "statementmodel.h"
25#include "cpptokenizer.h"
26#include "cpppreprocessor.h"
27
28class CppParser : public QObject
29{
30 Q_OBJECT
31
32 using GetFileStreamCallBack = std::function<bool (const QString&, QStringList&)>;
33public:
34 explicit CppParser(QObject *parent = nullptr);
35 ~CppParser();
36
37 void addHardDefineByLine(const QString& line);
38 void addFileToScan(const QString& value, bool inProject = false);
39 void addIncludePath(const QString& value);
40 void addProjectIncludePath(const QString& value);
41 void clearIncludePaths();
42 void clearProjectIncludePaths();
43 void clearProjectFiles();
44 QList<PStatement> getListOfFunctions(const QString& fileName,
45 const QString& phrase,
46 int line);
47 PStatement findAndScanBlockAt(const QString& filename, int line);
48 PFileIncludes findFileIncludes(const QString &filename, bool deleteIt = false);
49 QString findFirstTemplateParamOf(const QString& fileName,
50 const QString& phrase,
51 const PStatement& currentScope);
52 PStatement findFunctionAt(const QString& fileName,
53 int line);
54 int findLastOperator(const QString& phrase) const;
55 PStatementList findNamespace(const QString& name); // return a list of PSTATEMENTS (of the namespace)
56 PStatement findStatement(const QString& fullname);
57 PStatement findStatementOf(const QString& fileName,
58 const QString& phrase,
59 int line);
60 PStatement findStatementOf(const QString& fileName,
61 const QString& phrase,
62 const PStatement& currentScope,
63 PStatement& parentScopeType,
64 bool force = false);
65 PStatement findStatementOf(const QString& fileName,
66 const QString& phrase,
67 const PStatement& currentClass,
68 bool force = false);
69
70 PStatement findStatementOf(const QString& fileName,
71 const QStringList& expression,
72 const PStatement& currentScope);
73 PStatement findStatementOf(const QString& fileName,
74 const QStringList& expression,
75 int line);
76 PStatement findAliasedStatement(const PStatement& statement);
77
78 /**
79 * @brief evaluate the expression
80 * @param fileName
81 * @param expression
82 * @param currentScope
83 * @return the statement of the evaluation result
84 */
85 PEvalStatement evalExpression(const QString& fileName,
86 const QStringList& expression,
87 const PStatement& currentScope);
88 PStatement findTypeDefinitionOf(const QString& fileName,
89 const QString& aType,
90 const PStatement& currentClass);
91 PStatement findTypeDef(const PStatement& statement,
92 const QString& fileName);
93 bool freeze(); // Freeze/Lock (stop reparse while searching)
94 bool freeze(const QString& serialId); // Freeze/Lock (stop reparse while searching)
95 QStringList getClassesList();
96 QStringList getFileDirectIncludes(const QString& filename);
97 QSet<QString> getFileIncludes(const QString& filename);
98 QSet<QString> getFileUsings(const QString& filename);
99
100 QString getHeaderFileName(const QString& relativeTo, const QString& line);// both
101
102 void invalidateFile(const QString& fileName);
103 bool isIncludeLine(const QString &line);
104 bool isProjectHeaderFile(const QString& fileName);
105 bool isSystemHeaderFile(const QString& fileName);
106 void parseFile(const QString& fileName, bool inProject,
107 bool onlyIfNotParsed = false, bool updateView = true);
108 void parseFileList(bool updateView = true);
109 void parseHardDefines();
110 bool parsing() const;
111 void reset();
112 void unFreeze(); // UnFree/UnLock (reparse while searching)
113 QSet<QString> scannedFiles();
114
115 bool isFileParsed(const QString& filename);
116
117 QString prettyPrintStatement(const PStatement& statement, const QString& filename, int line = -1);
118
119 bool enabled() const;
120 void setEnabled(bool newEnabled);
121
122 const QSet<QString> &filesToScan() const;
123 void setFilesToScan(const QSet<QString> &newFilesToScan);
124
125 void setOnGetFileStream(const GetFileStreamCallBack &newOnGetFileStream);
126
127 int parserId() const;
128
129 const QString &serialId() const;
130
131 bool parseLocalHeaders() const;
132 void setParseLocalHeaders(bool newParseLocalHeaders);
133
134 bool parseGlobalHeaders() const;
135 void setParseGlobalHeaders(bool newParseGlobalHeaders);
136
137 const QSet<QString>& includePaths();
138 const QSet<QString>& projectIncludePaths();
139
140 const StatementModel &statementList() const;
141
142 ParserLanguage language() const;
143 void setLanguage(ParserLanguage newLanguage);
144
145signals:
146 void onProgress(const QString& fileName, int total, int current);
147 void onBusy();
148 void onStartParsing();
149 void onEndParsing(int total, int updateView);
150private:
151 PStatement addInheritedStatement(
152 const PStatement& derived,
153 const PStatement& inherit,
154 StatementClassScope access);
155
156 PStatement addChildStatement(
157 // support for multiple parents (only typedef struct/union use multiple parents)
158 const PStatement& parent,
159 const QString& fileName,
160 const QString& aType, // "Type" is already in use
161 const QString& command,
162 const QString& args,
163 const QString& value,
164 int line,
165 StatementKind kind,
166 const StatementScope& scope,
167 const StatementClassScope& classScope,
168 bool isDefinition,
169 bool isStatic); // TODO: InheritanceList not supported
170 PStatement addStatement(
171 const PStatement& parent,
172 const QString &fileName,
173 const QString &aType, // "Type" is already in use
174 const QString &command,
175 const QString &args,
176 const QString& value,
177 int line,
178 StatementKind kind,
179 const StatementScope& scope,
180 const StatementClassScope& classScope,
181 bool isDefinition,
182 bool isStatic);
183 void setInheritance(int index, const PStatement& classStatement, bool isStruct);
184 bool isCurrentScope(const QString& command);
185 void addSoloScopeLevel(PStatement& statement, int line, bool shouldResetBlock = true); // adds new solo level
186 void removeScopeLevel(int line); // removes level
187 int skipBraces(int startAt);
188 int skipBracket(int startAt);
189
190 void internalClear();
191
192 bool checkForCatchBlock();
193 bool checkForEnum();
194 bool checkForForBlock();
195 bool checkForKeyword();
196 bool checkForMethod(QString &sType, QString &sName, QString &sArgs,
197 bool &isStatic, bool &isFriend); // caching of results
198 bool checkForNamespace();
199 bool checkForPreprocessor();
200 bool checkForScope();
201 void checkForSkipStatement();
202 bool checkForStructs();
203 bool checkForTypedef();
204 bool checkForTypedefEnum();
205 bool checkForTypedefStruct();
206 bool checkForUsing();
207 bool checkForVar();
208 QString expandMacroType(const QString& name);
209 //{procedure ResetDefines;}
210 void fillListOfFunctions(const QString& fileName, int line,
211 const PStatement& statement,
212 const PStatement& scopeStatement, QStringList& list);
213 QList<PStatement> getListOfFunctions(const QString& fileName, int line,
214 const PStatement& statement,
215 const PStatement& scopeStatement);
216 PStatement findMemberOfStatement(
217 const QString& phrase,
218 const PStatement& scopeStatement);
219 QList<PStatement> findMembersOfStatement(const QString& phrase,
220 const PStatement& scopeStatement);
221 PStatement findStatementInScope(
222 const QString& name,
223 const QString& noNameArgs,
224 StatementKind kind,
225 const PStatement& scope);
226 PStatement findStatementInScope(
227 const QString& name,
228 const PStatement& scope);
229 PStatement findStatementInNamespace(
230 const QString& name,
231 const QString& namespaceName);
232
233 //{Find statement starting from startScope}
234 PStatement findStatementStartingFrom(const QString& fileName,
235 const QString& phrase,
236 const PStatement& startScope);
237
238 /**
239 * @brief evaluate the expression (starting from pos) in the scope
240 * @param fileName
241 * @param phraseExpression
242 * @param pos
243 * @param scope
244 * @param previousResult the result of evalution for expression from 0 to pos-1
245 * @param freeScoped if the expression left is
246 * @return
247 */
248 PEvalStatement doEvalExpression(const QString& fileName,
249 const QStringList& phraseExpression,
250 int &pos,
251 const PStatement& scope,
252 const PEvalStatement& previousResult,
253 bool freeScoped);
254
255 PEvalStatement doEvalPointerArithmetic(
256 const QString& fileName,
257 const QStringList& phraseExpression,
258 int &pos,
259 const PStatement& scope,
260 const PEvalStatement& previousResult,
261 bool freeScoped);
262 PEvalStatement doEvalPointerToMembers(
263 const QString& fileName,
264 const QStringList& phraseExpression,
265 int &pos,
266 const PStatement& scope,
267 const PEvalStatement& previousResult,
268 bool freeScoped);
269 PEvalStatement doEvalCCast(
270 const QString& fileName,
271 const QStringList& phraseExpression,
272 int &pos,
273 const PStatement& scope,
274 const PEvalStatement& previousResult,
275 bool freeScoped);
276 PEvalStatement doEvalMemberAccess(
277 const QString& fileName,
278 const QStringList& phraseExpression,
279 int &pos,
280 const PStatement& scope,
281 const PEvalStatement& previousResult,
282 bool freeScoped);
283 PEvalStatement doEvalScopeResolution(
284 const QString& fileName,
285 const QStringList& phraseExpression,
286 int &pos,
287 const PStatement& scope,
288 const PEvalStatement& previousResult,
289 bool freeScoped);
290 PEvalStatement doEvalTerm(
291 const QString& fileName,
292 const QStringList& phraseExpression,
293 int &pos,
294 const PStatement& scope,
295 const PEvalStatement& previousResult,
296 bool freeScoped);
297
298 PEvalStatement doCreateEvalNamespace(const PStatement& namespaceStatement);
299
300 PEvalStatement doCreateEvalType(const QString& fileName,const PStatement& typeStatement);
301 PEvalStatement doCreateEvalType(const QString& primitiveType);
302
303 PEvalStatement doCreateEvalVariable(const QString& fileName, PStatement varStatement);
304 PEvalStatement doCreateEvalFunction(const QString& fileName, PStatement funcStatement);
305 PEvalStatement doCreateEvalLiteral(const QString& type);
306 void doSkipInExpression(const QStringList& expression, int&pos, const QString& startSymbol, const QString& endSymbol);
307
308 bool isIdentifier(const QString& token) const {
309 return (!token.isEmpty() && isLetterChar(token.front())
310 && !token.contains('\"'));
311 }
312
313 bool isIntegerLiteral(const QString& token) const {
314 if (token.isEmpty())
315 return false;
316 QChar ch = token.front();
317 return (ch>='0' && ch<='9' && !token.contains(".") && !token.contains("e"));
318 }
319 bool isFloatLiteral(const QString& token) const {
320 if (token.isEmpty())
321 return false;
322 QChar ch = token.front();
323 return (ch>='0' && ch<='9' && (token.contains(".") || token.contains("e")));
324 }
325 bool isStringLiteral(const QString& token) const {
326 if (token.isEmpty())
327 return false;
328 return (!token.startsWith('\'') && token.contains('"'));
329 }
330
331 bool isCharLiteral(const QString& token) const{
332 if (token.isEmpty())
333 return false;
334 return (token.startsWith('\''));
335 }
336 PStatement doParseEvalTypeInfo(
337 const QString& fileName,
338 const PStatement& scope,
339 const QString& type,
340 QString& baseType,
341 int& pointerLevel);
342
343 int getBracketEnd(const QString& s, int startAt);
344 StatementClassScope getClassScope(int index);
345 int getCurrentBlockBeginSkip();
346 int getCurrentBlockEndSkip();
347 int getCurrentInlineNamespaceEndSkip();
348 PStatement getCurrentScope(); // gets last item from last level
349 QString getFirstTemplateParam(const PStatement& statement, const QString& filename,
350 const QString& phrase, const PStatement& currentScope);
351 int getFirstTemplateParamEnd(const QString& s, int startAt);
352
353 void getFullNamespace(
354 const QString& phrase,
355 QString& sNamespace,
356 QString& member);
357 QString getFullStatementName(
358 const QString& command,
359 const PStatement& parent);
360 PStatement getIncompleteClass(
361 const QString& command,
362 const PStatement& parentScope);
363 QString getScopePrefix(const PStatement& statement);
364 StatementScope getScope();
365 QString getStatementKey(const QString& sName,
366 const QString& sType,
367 const QString& sNoNameArgs);
368 PStatement getTypeDef(const PStatement& statement,
369 const QString& fileName, const QString& aType);
370 void handleCatchBlock();
371 void handleEnum();
372 void handleForBlock();
373 void handleKeyword();
374 void handleMethod(
375 const QString& sType,
376 const QString& sName,
377 const QString& sArgs,
378 bool isStatic,
379 bool isFriend);
380 void handleNamespace();
381 void handleOtherTypedefs();
382 void handlePreprocessor();
383 void handleScope();
384 bool handleStatement();
385 void handleStructs(bool isTypedef = false);
386 void handleUsing();
387 void handleVar();
388 void internalParse(const QString& fileName);
389// function FindMacroDefine(const Command: AnsiString): PStatement;
390 void inheritClassStatement(
391 const PStatement& derived,
392 bool isStruct,
393 const PStatement& base,
394 StatementClassScope access);
395 PStatement doFindStatementInScope(const QString& name,
396 const QString& noNameArgs,
397 StatementKind kind,
398 const PStatement& scope);
399 void internalInvalidateFile(const QString& fileName);
400 void internalInvalidateFiles(const QSet<QString>& files);
401 QSet<QString> calculateFilesToBeReparsed(const QString& fileName);
402 int calcKeyLenForStruct(const QString& word);
403// {
404// function GetClass(const Phrase: AnsiString): AnsiString;
405// function GetMember(const Phrase: AnsiString): AnsiString;
406// function GetOperator(const Phrase: AnsiString): AnsiString;
407// function GetRemainder(const Phrase: AnsiString): AnsiString;
408// }
409 void scanMethodArgs(
410 const PStatement& functionStatement,
411 const QString& argStr);
412 QString splitPhrase(const QString& phrase, QString& sClazz,
413 QString& sOperator, QString &sMember);
414
415 QString removeArgNames(const QString& args);
416
417 bool isSpaceChar(const QChar& ch) const {
418 return ch==' ' || ch =='\t';
419 }
420
421 bool isWordChar(const QChar& ch) const {
422 return ch.isLetter()
423 || ch == '_'
424 || ch == '*'
425 || ch == '&';
426 }
427
428 bool isLetterChar(const QChar& ch) const {
429 return ch.isLetter()
430 || ch == '_';
431 }
432
433 bool isDigitChar(const QChar& ch) const {
434 return (ch>='0' && ch<='9');
435 }
436
437 /*'(', ';', ':', '{', '}', '#' */
438 bool isSeperator(const QChar& ch) const {
439 switch(ch.unicode()){
440 case '(':
441 case ';':
442 case ':':
443 case '{':
444 case '}':
445 case '#':
446 return true;
447 default:
448 return false;
449 }
450 }
451
452 /*';', '{', '}'*/
453 bool isblockChar(const QChar& ch) const {
454 switch(ch.unicode()){
455 case ';':
456 case '{':
457 case '}':
458 return true;
459 default:
460 return false;
461 }
462 }
463
464 /* '#', ',', ';', ':', '{', '}', '!', '/', '+', '-', '<', '>' */
465 bool isInvalidVarPrefixChar(const QChar& ch) const {
466 switch (ch.unicode()) {
467 case '#':
468 case ',':
469 case ';':
470 case ':':
471 case '{':
472 case '}':
473 case '!':
474 case '/':
475 case '+':
476 case '-':
477 case '<':
478 case '>':
479 return true;
480 default:
481 return false;
482 }
483 }
484
485 /*'{', '}' */
486 bool isBraceChar(const QChar& ch) const {
487 return ch == '{' || ch =='}';
488 }
489
490 bool isLineChar(const QChar& ch) const {
491 return ch=='\n' || ch=='\r';
492 }
493
494 bool isNotFuncArgs(const QString& args);
495
496 /**
497 * @brief Test if a statement is a class/struct/union/namespace/function
498 * @param kind
499 * @return
500 */
501 bool isNamedScope(StatementKind kind) const;
502
503 /**
504 * @brief Test if a statement is a class/struct/union/enum/enum class/typedef
505 * @param kind
506 * @return
507 */
508 bool isTypeStatement(StatementKind kind) const;
509
510 void updateSerialId();
511
512
513private:
514 int mParserId;
515 ParserLanguage mLanguage;
516 int mSerialCount;
517 QString mSerialId;
518 int mUniqId;
519 bool mEnabled;
520 int mIndex;
521 bool mIsHeader;
522 bool mIsSystemHeader;
523 QString mCurrentFile;
524// stack list , each element is a list of one/many scopes(like intypedef struct s1,s2;
525// It's used for store scope nesting infos
526 QVector<PStatement> mCurrentScope;
527 QVector<StatementClassScope> mCurrentClassScope;
528
529// the start index in tokens to skip to ; when parsing typedef struct we need to skip
530// the names after the closing bracket because we have processed it
531 QVector<int> mSkipList; // TList<Integer>
532 StatementClassScope mClassScope;
533 StatementModel mStatementList;
534 //It's used in preprocessor, so we can't use fIncludeList instead
535
536 CppTokenizer mTokenizer;
537 CppPreprocessor mPreprocessor;
538 //{ List of current project's file }
539 QSet<QString> mProjectFiles;
540 QVector<int> mBlockBeginSkips; //list of for/catch block begin token index;
541 QVector<int> mBlockEndSkips; //list of for/catch block end token index;
542 QVector<int> mInlineNamespaceEndSkips; // list for inline namespace end token index;
543 QSet<QString> mFilesToScan; // list of base files to scan
544 int mFilesScannedCount; // count of files that have been scanned
545 int mFilesToScanCount; // count of files and files included in files that have to be scanned
546 bool mParseLocalHeaders;
547 bool mParseGlobalHeaders;
548 bool mIsProjectFile;
549 //fMacroDefines : TList;
550 int mLockCount; // lock(don't reparse) when we need to find statements in a batch
551 bool mParsing;
552 QHash<QString,PStatementList> mNamespaces; //TStringList<String,List<Statement>> namespace and the statements in its scope
553 QSet<QString> mInlineNamespaces;
554 //fRemovedStatements: THashedStringList; //THashedStringList<String,PRemovedStatements>
555
556 QMutex mMutex;
557 GetFileStreamCallBack mOnGetFileStream;
558 QMap<QString,SkipType> mCppKeywords;
559 QSet<QString> mCppTypeKeywords;
560};
561using PCppParser = std::shared_ptr<CppParser>;
562
563class CppFileParserThread : public QThread {
564 Q_OBJECT
565public:
566 explicit CppFileParserThread(
567 PCppParser parser,
568 QString fileName,
569 bool inProject,
570 bool onlyIfNotParsed = false,
571 bool updateView = true,
572 QObject *parent = nullptr);
573
574private:
575 PCppParser mParser;
576 QString mFileName;
577 bool mInProject;
578 bool mOnlyIfNotParsed;
579 bool mUpdateView;
580
581 // QThread interface
582protected:
583 void run() override;
584};
585using PCppParserThread = std::shared_ptr<CppFileParserThread>;
586
587class CppFileListParserThread: public QThread {
588 Q_OBJECT
589public:
590 explicit CppFileListParserThread(
591 PCppParser parser,
592 bool updateView = true,
593 QObject *parent = nullptr);
594private:
595 PCppParser mParser;
596 bool mUpdateView;
597 // QThread interface
598protected:
599 void run() override;
600};
601
602void parseFile(
603 PCppParser parser,
604 const QString& fileName,
605 bool inProject,
606 bool onlyIfNotParsed = false,
607 bool updateView = true);
608
609void parseFileList(
610 PCppParser parser,
611 bool updateView = true);
612
613
614#endif // CPPPARSER_H
615