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#include "cpppreprocessor.h"
18#include "../utils.h"
19
20#include <QFile>
21#include <QTextCodec>
22#include <QDebug>
23#include <QMessageBox>
24
25CppPreprocessor::CppPreprocessor()
26{
27}
28
29void CppPreprocessor::clear()
30{
31 mIncludes.clear();
32 mDefines.clear();
33 mHardDefines.clear();
34 mProcessed.clear();
35 mFileDefines.clear();
36 mBranchResults.clear();
37 mResult.clear();
38 mCurrentIncludes.reset();
39}
40
41void CppPreprocessor::clearResult()
42{
43 mFileName.clear();
44 mBuffer.clear();
45 mResult.clear();
46 mCurrentIncludes = nullptr;
47 mIncludes.clear(); // stack of files we've stepped into. last one is current file, first one is source file
48 mBranchResults.clear();// stack of branch results (boolean). last one is current branch, first one is outermost branch
49 mDefines.clear(); // working set, editable
50 mProcessed.clear(); // dictionary to save filename already processed
51}
52
53void CppPreprocessor::addDefineByParts(const QString &name, const QString &args, const QString &value, bool hardCoded)
54{
55 // Check for duplicates
56 PDefine define = std::make_shared<Define>();
57 define->name = name;
58 define->args = args;
59 define->value = value;
60 define->filename = mFileName;
61 //define->argList;
62 define->formatValue = value;
63 define->hardCoded = hardCoded;
64 if (!args.isEmpty())
65 parseArgs(define);
66 if (hardCoded)
67 mHardDefines.insert(name,define);
68 else {
69 PDefineMap defineMap = mFileDefines.value(mFileName,PDefineMap());
70 if (!defineMap) {
71 defineMap = std::make_shared<DefineMap>();
72 mFileDefines.insert(mFileName,defineMap);
73 }
74 defineMap->insert(define->name,define);
75 mDefines.insert(name,define);
76 }
77}
78
79void CppPreprocessor::getDefineParts(const QString &input, QString &name, QString &args, QString &value)
80{
81 QString s = input.trimmed();
82 name = "";
83 args = "";
84 value = "";
85
86 // Rules:
87 // When the character before the first opening brace is nonblank, a function is defined.
88 // After that point, switch from name to args
89 // The value starts after the first blank character outside of the outermost () pair
90
91 int i = 0;
92 int level = 0;
93 bool isFunction = false;
94 int argStart = 0;
95 while (i < s.length()) {
96 // When we find the first opening brace, check if this is a function define
97 if (s[i] == '(') {
98 level++;
99 if ((level == 1) && (!isFunction)) { // found a function define!
100 name = s.mid(0,i);
101 argStart = i;
102 isFunction = true;
103 }
104 } else if (s[i]==')') {
105 level--;
106 } else if (isSpaceChar(s[i]) && (level == 0)) {
107 break;
108 }
109 i++;
110 }
111 if (isFunction) {
112 // Name has already been found
113 args = s.mid(argStart,i-argStart);
114 //todo: expand macro (if already have)
115 } else {
116 name = s.mid(0,i);
117 args = "";
118 }
119 value = removeGCCAttributes(s.mid(i+1).trimmed());
120}
121
122void CppPreprocessor::addHardDefineByLine(const QString &line)
123{
124 addDefineByLine(line,true);
125}
126
127void CppPreprocessor::addDefineByLine(const QString &line, bool hardCoded)
128{
129 // Remove define
130 constexpr int DEFINE_LEN=6;
131 QString s = line.mid(DEFINE_LEN).trimmed();
132
133 QString name, args, value;
134 // Get parts from generalized function
135 getDefineParts(s, name, args, value);
136
137 // Add to the list
138 addDefineByParts(name, args, value, hardCoded);
139}
140
141PDefine CppPreprocessor::getDefine(const QString &name)
142{
143 return mDefines.value(name,PDefine());
144}
145
146PDefine CppPreprocessor::getHardDefine(const QString &name)
147{
148 return mHardDefines.value(name,PDefine());
149}
150
151void CppPreprocessor::reset()
152{
153 mResult.clear();
154
155 // Clear extracted data
156 mIncludes.clear();
157 mBranchResults.clear();
158 mCurrentIncludes.reset();
159 mProcessed.clear();
160 resetDefines(); // do not throw away hardcoded
161}
162
163void CppPreprocessor::resetDefines()
164{
165 mDefines = mHardDefines;
166// mDefines.clear();
167
168// mDefines.insert(mHardDefines);
169}
170
171void CppPreprocessor::setScanOptions(bool parseSystem, bool parseLocal)
172{
173 mParseSystem = parseSystem;
174 mParseLocal=parseLocal;
175}
176
177void CppPreprocessor::preprocess(const QString &fileName, QStringList buffer)
178{
179 mFileName = fileName;
180 reset();
181 openInclude(fileName, buffer);
182 // StringsToFile(mBuffer,"f:\\buffer.txt");
183 preprocessBuffer();
184 // StringsToFile(mBuffer,"f:\\buffer.txt");
185 // StringsToFile(mResult,"f:\\log.txt");
186}
187
188void CppPreprocessor::invalidDefinesInFile(const QString &fileName)
189{
190 PDefineMap defineMap = mFileDefines.value(fileName,PDefineMap());
191 if (defineMap) {
192 foreach (const PDefine& define, *defineMap) {
193 const PDefine& p = mDefines.value(define->name);
194 if (p == define) {
195 mDefines.remove(define->name);
196 }
197 }
198 mFileDefines.remove(fileName);
199 }
200}
201
202void CppPreprocessor::dumpDefinesTo(const QString &fileName) const
203{
204 QFile file(fileName);
205 if (file.open(QIODevice::WriteOnly|QIODevice::Truncate)) {
206 QTextStream stream(&file);
207 for (const PDefine& define:mDefines) {
208 stream<<QString("%1 %2 %3 %4 %5\n")
209 .arg(define->name,define->args,define->value)
210 .arg(define->hardCoded).arg(define->formatValue)
211#if QT_VERSION >= QT_VERSION_CHECK(5,15,0)
212 <<Qt::endl;
213#else
214 <<endl;
215#endif
216 }
217 }
218}
219
220void CppPreprocessor::dumpIncludesListTo(const QString &fileName) const
221{
222 QFile file(fileName);
223 if (file.open(QIODevice::WriteOnly|QIODevice::Truncate)) {
224 QTextStream stream(&file);
225 for (const PFileIncludes& fileIncludes:mIncludesList) {
226 stream<<fileIncludes->baseFile<<" : "
227 #if QT_VERSION >= QT_VERSION_CHECK(5,15,0)
228 <<Qt::endl;
229 #else
230 <<endl;
231 #endif
232 stream<<"\t**includes:**"
233 #if QT_VERSION >= QT_VERSION_CHECK(5,15,0)
234 <<Qt::endl;
235 #else
236 <<endl;
237 #endif
238 foreach (const QString& s,fileIncludes->includeFiles.keys()) {
239 stream<<"\t--"+s
240 #if QT_VERSION >= QT_VERSION_CHECK(5,15,0)
241 <<Qt::endl;
242 #else
243 <<endl;
244 #endif
245 }
246 stream<<"\t**depends on:**"
247 #if QT_VERSION >= QT_VERSION_CHECK(5,15,0)
248 <<Qt::endl;
249 #else
250 <<endl;
251 #endif
252 foreach (const QString& s,fileIncludes->dependingFiles) {
253 stream<<"\t^^"+s
254 #if QT_VERSION >= QT_VERSION_CHECK(5,15,0)
255 <<Qt::endl;
256 #else
257 <<endl;
258 #endif
259 }
260 stream<<"\t**depended by:**"
261 #if QT_VERSION >= QT_VERSION_CHECK(5,15,0)
262 <<Qt::endl;
263 #else
264 <<endl;
265 #endif
266 foreach (const QString& s,fileIncludes->dependedFiles) {
267 stream<<"\t&&"+s
268 #if QT_VERSION >= QT_VERSION_CHECK(5,15,0)
269 <<Qt::endl;
270 #else
271 <<endl;
272 #endif
273 }
274 stream<<"\t**using:**"
275 #if QT_VERSION >= QT_VERSION_CHECK(5,15,0)
276 <<Qt::endl;
277 #else
278 <<endl;
279 #endif
280 foreach (const QString& s,fileIncludes->usings) {
281 stream<<"\t++"+s
282 #if QT_VERSION >= QT_VERSION_CHECK(5,15,0)
283 <<Qt::endl;
284 #else
285 <<endl;
286 #endif
287 }
288 stream<<"\t**statements:**"
289 #if QT_VERSION >= QT_VERSION_CHECK(5,15,0)
290 <<Qt::endl;
291 #else
292 <<endl;
293 #endif
294 foreach (const PStatement& statement,fileIncludes->statements) {
295 if (statement) {
296 stream<<QString("\t**%1 , %2")
297 .arg(statement->command,statement->fullName)
298 #if QT_VERSION >= QT_VERSION_CHECK(5,15,0)
299 <<Qt::endl;
300 #else
301 <<endl;
302 #endif
303 }
304 }
305 }
306 }
307}
308
309void CppPreprocessor::addIncludePath(const QString &fileName)
310{
311 if (!mIncludePaths.contains(fileName)) {
312 mIncludePaths.insert(fileName);
313 mIncludePathList.append(fileName);
314 }
315}
316
317void CppPreprocessor::addProjectIncludePath(const QString &fileName)
318{
319 if (!mProjectIncludePaths.contains(fileName)) {
320 mProjectIncludePaths.insert(fileName);
321 mProjectIncludePathList.append(fileName);
322 }
323}
324
325void CppPreprocessor::clearIncludePaths()
326{
327 mIncludePaths.clear();
328 mIncludePathList.clear();
329}
330
331void CppPreprocessor::clearProjectIncludePaths()
332{
333 mProjectIncludePaths.clear();
334 mProjectIncludePathList.clear();
335}
336
337QString CppPreprocessor::getNextPreprocessor()
338{
339 skipToPreprocessor(); // skip until # at start of line
340 int preProcFrom = mIndex;
341 if (preProcFrom >= mBuffer.count())
342 return ""; // we've gone past the final #preprocessor line. Yay
343 skipToEndOfPreprocessor();
344 int preProcTo = mIndex;
345
346 // Calculate index to insert defines in in result file
347 mPreProcIndex = (mResult.count() - 1) + 1; // offset by one for #include rootfile
348
349 // Assemble whole line, convert newlines to space
350 QString result;
351 for (int i=preProcFrom;i<=preProcTo;i++) {
352 if (mBuffer[i].endsWith('\\')) {
353 result+=mBuffer[i].mid(0,mBuffer[i].size()-1)+' ';
354 } else {
355 result+=mBuffer[i]+' ';
356 }
357 mResult.append("");// defines resolve into empty files, except #define and #include
358 }
359 // Step over
360 mIndex++;
361 return result;
362}
363
364void CppPreprocessor::simplify(QString &output)
365{
366 // Remove #
367 output = output.mid(1).trimmed();
368}
369
370void CppPreprocessor::handleBranch(const QString &line)
371{
372 if (line.startsWith("ifdef")) {
373// // if a branch that is not at our level is false, current branch is false too;
374// for (int i=0;i<=mBranchResults.count()-2;i++) {
375// if (!mBranchResults[i]) {
376// setCurrentBranch(false);
377// return;
378// }
379// }
380 if (!getCurrentBranch()) {
381 setCurrentBranch(false);
382 } else {
383 constexpr int IFDEF_LEN = 5; //length of ifdef;
384 QString name = line.mid(IFDEF_LEN).trimmed();
385 setCurrentBranch( getDefine(name)!=nullptr );
386
387 }
388 } else if (line.startsWith("ifndef")) {
389// // if a branch that is not at our level is false, current branch is false too;
390// for (int i=0;i<=mBranchResults.count()-2;i++) {
391// if (!mBranchResults[i]) {
392// setCurrentBranch(false);
393// return;
394// }
395// }
396 if (!getCurrentBranch()) {
397 setCurrentBranch(false);
398 } else {
399 constexpr int IFNDEF_LEN = 6; //length of ifndef;
400 QString name = line.mid(IFNDEF_LEN).trimmed();
401 setCurrentBranch( getDefine(name)==nullptr );
402 }
403 } else if (line.startsWith("if")) {
404 // // if a branch that is not at our level is false, current branch is false too;
405 // for (int i=0;i<=mBranchResults.count()-2;i++) {
406 // if (!mBranchResults[i]) {
407 // setCurrentBranch(false);
408 // return;
409 // }
410 // }
411 if (!getCurrentBranch()) {// we are already inside an if that is NOT being taken
412 setCurrentBranch(false);// so don't take this one either
413 } else {
414 constexpr int IF_LEN = 2; //length of if;
415 QString ifLine = line.mid(IF_LEN).trimmed();
416
417 bool testResult = evaluateIf(ifLine);
418 setCurrentBranch(testResult);
419 }
420 } else if (line.startsWith("else")) {
421 bool oldResult = getCurrentBranch(); // take either if or else
422 removeCurrentBranch();
423 setCurrentBranch(!oldResult);
424 } else if (line.startsWith("elif")) {
425 bool oldResult = getCurrentBranch(); // take either if or else
426 removeCurrentBranch();
427 if (oldResult) { // don't take this one, if previous has been taken
428 setCurrentBranch(false);
429 } else {
430 constexpr int ELIF_LEN = 4; //length of if;
431 QString ifLine = line.mid(ELIF_LEN).trimmed();
432 bool testResult = evaluateIf(ifLine);
433 setCurrentBranch(testResult);
434 }
435 } else if (line.startsWith("endif")) {
436 removeCurrentBranch();
437 }
438}
439
440void CppPreprocessor::handleDefine(const QString &line)
441{
442 if (getCurrentBranch()) {
443 addDefineByLine(line, false);
444 mResult[mPreProcIndex] = '#' + line; // add define to result file so the parser can handle it
445 }
446}
447
448void CppPreprocessor::handleInclude(const QString &line, bool fromNext)
449{
450 if (!getCurrentBranch()) // we're skipping due to a branch failure
451 return;
452
453 PParsedFile file = mIncludes.back();
454 QString fileName;
455 // Get full header file name
456 QString currentDir = includeTrailingPathDelimiter(extractFileDir(file->fileName));
457 QStringList includes;
458 QStringList projectIncludes;
459 bool found;
460 if (fromNext && mIncludePaths.contains(currentDir))
461 found = false;
462 else
463 found = true;
464 foreach(const QString& s, mIncludePathList) {
465 if (found) {
466 includes.append(s);
467 }
468 if (s == currentDir)
469 found = true;
470 }
471 if (fromNext && mProjectIncludePaths.contains(currentDir))
472 found = false;
473 else
474 found = true;
475 foreach(const QString& s, mProjectIncludePathList) {
476 if (found)
477 projectIncludes.append(s);
478 if (s == currentDir)
479 found = true;
480 }
481 fileName = getHeaderFilename(
482 file->fileName,
483 line,
484 includes,
485 projectIncludes);
486
487 if (fileName.isEmpty())
488 return;
489
490 PFileIncludes oldCurrentIncludes = mCurrentIncludes;
491 openInclude(fileName);
492 //oldCurrentIncludes->includeFiles.insert(fileName,true);
493}
494
495void CppPreprocessor::handlePreprocessor(const QString &value)
496{
497 if (value.startsWith("define"))
498 handleDefine(value);
499 else if (value.startsWith("undef"))
500 handleUndefine(value);
501 else if (value.startsWith("if")
502 || value.startsWith("else") || value.startsWith("elif")
503 || value.startsWith("endif"))
504 handleBranch(value);
505 else if (value.startsWith("include_next"))
506 handleInclude(value,true);
507 else if (value.startsWith("include"))
508 handleInclude(value);
509}
510
511void CppPreprocessor::handleUndefine(const QString &line)
512{
513 // Remove undef
514 constexpr int UNDEF_LEN = 5;
515 QString name = line.mid(UNDEF_LEN).trimmed();
516
517// //may be defined many times
518// while (true) {
519 PDefine define = getDefine(name);
520 if (define) {
521 //remove the define from defines set
522 mDefines.remove(name);
523 //remove the define form the file where it defines
524 if (define->filename == mFileName) {
525 PDefineMap defineMap = mFileDefines.value(mFileName);
526 if (defineMap) {
527 defineMap->remove(name);
528 }
529 }
530 }
531}
532
533QString CppPreprocessor::expandMacros(const QString &line, int depth)
534{
535 //prevent infinit recursion
536 if (depth > MAX_DEFINE_EXPAND_DEPTH)
537 return line;
538 QString word;
539 QString newLine;
540 int lenLine = line.length();
541 int i=0;
542 while (i< lenLine) {
543 QChar ch=line[i];
544 if (isWordChar(ch)) {
545 word += ch;
546 } else {
547 if (!word.isEmpty()) {
548 expandMacro(line,newLine,word,i,depth);
549 }
550 word = "";
551 if (i< lenLine) {
552 newLine += line[i];
553 }
554 }
555 i++;
556 }
557 if (!word.isEmpty()) {
558 expandMacro(line,newLine,word,i,depth);
559 }
560 return newLine;
561}
562
563void CppPreprocessor::expandMacro(const QString &line, QString &newLine, QString &word, int &i, int depth)
564{
565 int lenLine = line.length();
566 if (word.startsWith("__")
567 && word.endsWith("__")) {
568// if (word == "__attribute__") {
569 //skip gcc __attribute__
570 while ((i<lenLine) && (line[i] == ' ' || line[i]=='\t'))
571 i++;
572 if ((i<lenLine) && (line[i]=="(")) {
573 int level=0;
574 while (i<lenLine) {
575 switch(line[i].unicode()) {
576 case '(':
577 level++;
578 break;
579 case ')':
580 level--;
581 break;
582 }
583 i++;
584 if (level==0)
585 break;
586 }
587 }
588 } else {
589 PDefine define = getDefine(word);
590 if (define && define->args=="" ) {
591 //newLine:=newLine+RemoveGCCAttributes(define^.Value);
592 if (define->value != word )
593 newLine += expandMacros(define->value,depth+1);
594 else
595 newLine += word;
596
597 } else if (define && (define->args!="")) {
598 while ((i<lenLine) && (line[i] == ' ' || line[i]=='\t'))
599 i++;
600 int argStart=-1;
601 int argEnd=-1;
602 if ((i<lenLine) && (line[i]=='(')) {
603 argStart =i+1;
604 int level=0;
605 while (i<lenLine) {
606 switch(line[i].unicode()) {
607 case '(':
608 level++;
609 break;
610 case ')':
611 level--;
612 break;
613 }
614 i++;
615 if (level==0)
616 break;
617 }
618 if (level==0) {
619 argEnd = i-2;
620 QString args = line.mid(argStart,argEnd-argStart+1).trimmed();
621 QString formattedValue = expandFunction(define,args);
622 newLine += expandMacros(formattedValue,depth+1);
623 }
624 }
625 } else {
626 newLine += word;
627 }
628 }
629}
630
631QString CppPreprocessor::removeGCCAttributes(const QString &line)
632{
633 QString newLine = "";
634 QString word = "";
635 int lenLine = line.length();
636 int i=0;
637 while(i< lenLine) {
638 if (isWordChar(line[i])) {
639 word += line[i];
640 } else {
641 if (!word.isEmpty()) {
642 removeGCCAttribute(line,newLine,i,word);
643 }
644 word = "";
645 if (i<lenLine) {
646 newLine = newLine+line[i];
647 }
648 }
649 i++;
650 }
651 if (!word.isEmpty())
652 removeGCCAttribute(line,newLine,i,word);
653 return newLine;
654}
655
656void CppPreprocessor::removeGCCAttribute(const QString &line, QString &newLine, int &i, const QString &word)
657{
658 int lenLine = line.length();
659 int level = 0;
660 if (word=="__attribute__") {
661 while ( (i<lenLine) && isSpaceChar(line[i]))
662 i++;
663 if ((i<lenLine) && (line[i]=='(')) {
664 level=0;
665 while (i<lenLine) {
666 switch(line[i].unicode()) {
667 case '(': level++;
668 break;
669 case ')': level--;
670 break;
671 }
672 i++;
673 if (level==0)
674 break;
675 }
676 }
677 } else {
678 newLine += word;
679 }
680}
681
682PParsedFile CppPreprocessor::getInclude(int index)
683{
684 return mIncludes[index];
685}
686
687void CppPreprocessor::openInclude(const QString &fileName, QStringList bufferedText)
688{
689 if (mIncludes.size()>0) {
690 PParsedFile topFile = mIncludes.front();
691 if (topFile->fileIncludes->includeFiles.contains(fileName)) {
692 return; //already included
693 }
694 for (PParsedFile& parsedFile:mIncludes) {
695 parsedFile->fileIncludes->includeFiles.insert(fileName,false);
696 }
697 // Backup old position if we're entering a new file
698 PParsedFile innerMostFile = mIncludes.back();
699 innerMostFile->index = mIndex;
700 innerMostFile->branches = mBranchResults.count();
701
702 innerMostFile->fileIncludes->includeFiles.insert(fileName,true);
703 innerMostFile->fileIncludes->directIncludes.append(fileName);
704 }
705
706// // Add the new file to the includes of the current file
707// // Only add items to the include list of the given file if the file hasn't been scanned yet
708// // The above is fixed by checking for duplicates.
709// // The proper way would be to do backtracking of files we have FINISHED scanned.
710// // These are not the same files as the ones in fScannedFiles. We have STARTED scanning these.
711// {
712// if Assigned(fCurrentIncludes) then
713// with fCurrentIncludes^ do
714// if not ContainsText(IncludeFiles, FileName) then
715// IncludeFiles := IncludeFiles + AnsiQuotedStr(FileName, '"') + ',';
716// }
717
718 // Create and add new buffer/position
719 PParsedFile parsedFile = std::make_shared<ParsedFile>();
720 parsedFile->index = 0;
721 parsedFile->fileName = fileName;
722 parsedFile->branches = 0;
723 // parsedFile->buffer; it's auto initialized
724
725
726 // Keep track of files we include here
727 // Only create new items for files we have NOT scanned yet
728 mCurrentIncludes = getFileIncludesEntry(fileName);
729 if (!mCurrentIncludes) {
730 // do NOT create a new item for a file that's already in the list
731 mCurrentIncludes = std::make_shared<FileIncludes>();
732 mCurrentIncludes->baseFile = fileName;
733 //mCurrentIncludes->includeFiles;
734 //mCurrentIncludes->usings;
735 //mCurrentIncludes->statements;
736 //mCurrentIncludes->declaredStatements;
737 //mCurrentIncludes->scopes;
738 //mCurrentIncludes->dependedFiles;
739 //mCurrentIncludes->dependingFiles;
740 mIncludesList.insert(fileName,mCurrentIncludes);
741 }
742
743 parsedFile->fileIncludes = mCurrentIncludes;
744
745 // Don't parse stuff we have already parsed
746 if ((!bufferedText.isEmpty()) || !mScannedFiles.contains(fileName)) {
747 // Parse ONCE
748 //if not Assigned(Stream) then
749 mScannedFiles.insert(fileName);
750
751 // Only load up the file if we are allowed to parse it
752 bool isSystemFile = isSystemHeaderFile(fileName, mIncludePaths);
753 if ((mParseSystem && isSystemFile) || (mParseLocal && !isSystemFile)) {
754 if (!bufferedText.isEmpty()) {
755 parsedFile->buffer = bufferedText;
756 } else {
757 parsedFile->buffer = readFileToLines(fileName);
758 }
759 }
760 } else {
761 //add defines of already parsed including headers;
762 addDefinesInFile(fileName);
763 PFileIncludes fileIncludes = getFileIncludesEntry(fileName);
764 for (PParsedFile& file:mIncludes) {
765 foreach (const QString& incFile,fileIncludes->includeFiles.keys()) {
766 file->fileIncludes->includeFiles.insert(incFile,false);
767 }
768// file->fileIncludes->includeFiles =
769// file->fileIncludes->includeFiles.unite(fileIncludes->includeFiles);
770 // file->fileIncludes->includeFiles.insert(fileIncludes->includeFiles);
771 }
772 }
773 mIncludes.append(parsedFile);
774
775 // Process it
776 mIndex = parsedFile->index;
777 mFileName = parsedFile->fileName;
778 parsedFile->buffer = removeComments(parsedFile->buffer);
779 mBuffer = parsedFile->buffer;
780
781// for (int i=0;i<mBuffer.count();i++) {
782// mBuffer[i] = mBuffer[i].trimmed();
783// }
784
785 // Update result file
786 QString includeLine = "#include " + fileName + ":1";
787 if (mIncludes.count()>1) { // include from within a file
788 mResult[mPreProcIndex] = includeLine;
789 } else {
790 mResult.append(includeLine);
791 }
792}
793
794
795void CppPreprocessor::closeInclude()
796{
797 if (mIncludes.isEmpty())
798 return;
799 mIncludes.pop_back();
800
801 if (mIncludes.isEmpty())
802 return;
803 PParsedFile parsedFile = mIncludes.back();
804
805 // Continue where we left off
806 mIndex = parsedFile->index;
807 mFileName = parsedFile->fileName;
808 // Point to previous buffer and start past the include we walked into
809 mBuffer = parsedFile->buffer;
810 while (mBranchResults.count()>parsedFile->branches) {
811 mBranchResults.pop_back();
812 }
813
814
815 // Start augmenting previous include list again
816 //fCurrentIncludes := GetFileIncludesEntry(fFileName);
817 mCurrentIncludes = parsedFile->fileIncludes;
818
819 // Update result file (we've left the previous file)
820 mResult.append(
821 QString("#include %1:%2").arg(parsedFile->fileName)
822 .arg(parsedFile->index+1));
823}
824
825bool CppPreprocessor::getCurrentBranch()
826{
827 if (!mBranchResults.isEmpty())
828 return mBranchResults.last();
829 else
830 return true;
831}
832
833void CppPreprocessor::setCurrentBranch(bool value)
834{
835 mBranchResults.append(value);
836}
837
838void CppPreprocessor::removeCurrentBranch()
839{
840 if (mBranchResults.size()>0)
841 mBranchResults.pop_back();
842}
843
844QStringList CppPreprocessor::result() const
845{
846 return mResult;
847}
848
849PFileIncludes CppPreprocessor::getFileIncludesEntry(const QString &fileName)
850{
851 return mIncludesList.value(fileName,PFileIncludes());
852}
853
854void CppPreprocessor::addDefinesInFile(const QString &fileName)
855{
856 if (mProcessed.contains(fileName))
857 return;
858 mProcessed.insert(fileName);
859
860 //todo: why test this?
861 if (!mScannedFiles.contains(fileName))
862 return;
863
864 //May be redefined, so order is important
865 //first add the defines in the files it included
866 PFileIncludes fileIncludes = getFileIncludesEntry(fileName);
867 if (fileIncludes) {
868 foreach (const QString& s,fileIncludes->includeFiles.keys()) {
869 addDefinesInFile(s);
870 }
871 }
872
873 // then add the defines defined in it
874 PDefineMap defineList = mFileDefines.value(fileName, PDefineMap());
875 if (defineList) {
876 foreach (const PDefine& define, defineList->values()) {
877 mDefines.insert(define->name,define);
878 }
879 }
880}
881
882void CppPreprocessor::parseArgs(PDefine define)
883{
884 QString args=define->args.mid(1,define->args.length()-2).trimmed(); // remove '(' ')'
885
886 if(args=="")
887 return ;
888 define->argList = args.split(',');
889 for (int i=0;i<define->argList.size();i++) {
890 define->argList[i]=define->argList[i].trimmed();
891 define->argUsed.append(false);
892 }
893 QList<PDefineArgToken> tokens = tokenizeValue(define->value);
894
895 QString formatStr = "";
896 DefineArgTokenType lastTokenType=DefineArgTokenType::Other;
897 int index;
898 foreach (const PDefineArgToken& token, tokens) {
899 switch(token->type) {
900 case DefineArgTokenType::Identifier:
901 index = define->argList.indexOf(token->value);
902 if (index>=0) {
903 define->argUsed[index] = true;
904 if (lastTokenType == DefineArgTokenType::Sharp) {
905 formatStr+= "\"%"+QString("%1").arg(index+1)+"\"";
906 break;
907 } else {
908 formatStr+= "%"+QString("%1").arg(index+1);
909 break;
910 }
911 }
912 formatStr += token->value;
913 break;
914 case DefineArgTokenType::DSharp:
915 case DefineArgTokenType::Sharp:
916 break;
917 case DefineArgTokenType::Space:
918 case DefineArgTokenType::Symbol:
919 formatStr+=token->value;
920 break;
921 default:
922 break;
923 }
924 lastTokenType = token->type;
925 }
926 define->formatValue = formatStr;
927}
928
929QList<PDefineArgToken> CppPreprocessor::tokenizeValue(const QString &value)
930{
931 int i=0;
932 PDefineArgToken token = std::make_shared<DefineArgToken>();
933 token->type = DefineArgTokenType::Other;
934 QList<PDefineArgToken> tokens;
935 bool skipSpaces=false;
936 while (i<value.length()) {
937 QChar ch = value[i];
938 if (isSpaceChar(ch)) {
939 if (token->type==DefineArgTokenType::Other) {
940 token->value = " ";
941 token->type = DefineArgTokenType::Space;
942 } else if (token->type!=DefineArgTokenType::Space) {
943 tokens.append(token);
944 token = std::make_shared<DefineArgToken>();
945 token->value = " ";
946 token->type = DefineArgTokenType::Space;
947 }
948 i++;
949 } else if (ch=='#') {
950 if (token->type!=DefineArgTokenType::Other
951 && token->type!=DefineArgTokenType::Space) {
952 tokens.append(token);
953 token = std::make_shared<DefineArgToken>();
954 }
955 if ((i+1<value.length()) && (value[i+1]=='#')) {
956 i+=2;
957 token->value = "##";
958 token->type = DefineArgTokenType::DSharp;
959 } else {
960 i++;
961 token->value = "#";
962 token->type = DefineArgTokenType::Sharp;
963 }
964 skipSpaces=true;
965 tokens.append(token);
966 token = std::make_shared<DefineArgToken>();
967 token->value = "";
968 token->type = DefineArgTokenType::Other;
969 } else if (isWordChar(ch)) {
970 if (token->type==DefineArgTokenType::Other) {
971 token->value = ch ;
972 token->type = DefineArgTokenType::Identifier;
973 } else if (token->type==DefineArgTokenType::Identifier) {
974 token->value+=ch;
975 } else if (skipSpaces && token->type==DefineArgTokenType::Space) {
976 //dont use space;
977 token->value = ch ;
978 token->type = DefineArgTokenType::Identifier;
979 } else {
980 tokens.append(token);
981 token = std::make_shared<DefineArgToken>();
982 token->value = ch ;
983 token->type = DefineArgTokenType::Identifier;
984 }
985 skipSpaces=false;
986 i++;
987 } else {
988 if (skipSpaces && token->type==DefineArgTokenType::Space) {
989 //dont use space;
990 } else if (token->type!=DefineArgTokenType::Other) {
991 tokens.append(token);
992 token = std::make_shared<DefineArgToken>();
993 }
994 skipSpaces=false;
995 token->value = ch ;
996 token->type = DefineArgTokenType::Symbol;
997 i++;
998 }
999 }
1000 if(token->type!=DefineArgTokenType::Other)
1001 tokens.append(token);
1002 return tokens;
1003}
1004
1005QStringList CppPreprocessor::removeComments(const QStringList &text)
1006{
1007 QStringList result;
1008 ContentType currentType = ContentType::Other;
1009 QString delimiter;
1010
1011 for (const QString& line:text) {
1012 QString s;
1013 int pos = 0;
1014 bool stopProcess=false;
1015 while (pos<line.length()) {
1016 QChar ch =line[pos];
1017 if (currentType == ContentType::AnsiCComment) {
1018 if (ch=='*' && (pos+1<line.length()) && line[pos+1]=='/') {
1019 pos+=2;
1020 currentType = ContentType::Other;
1021 } else {
1022 pos+=1;
1023 }
1024 continue;
1025 }
1026 switch (ch.unicode()) {
1027 case '"':
1028 switch (currentType) {
1029 case ContentType::String:
1030 currentType=ContentType::Other;
1031 break;
1032 case ContentType::RawString:
1033 if (line.midRef(0,pos).endsWith(')'+delimiter))
1034 currentType = ContentType::Other;
1035 break;
1036 case ContentType::Other:
1037 currentType=ContentType::String;
1038 break;
1039 case ContentType::RawStringPrefix:
1040 delimiter+=ch;
1041 break;
1042 default:
1043 break;
1044 }
1045 s+=ch;
1046 break;
1047 case '\'':
1048 switch (currentType) {
1049 case ContentType::Character:
1050 currentType=ContentType::Other;
1051 break;
1052 case ContentType::Other:
1053 currentType=ContentType::Character;
1054 break;
1055 case ContentType::RawStringPrefix:
1056 delimiter+=ch;
1057 break;
1058 default:
1059 break;
1060 }
1061 s+=ch;
1062 break;
1063 case 'R':
1064 if (currentType == ContentType::Other && pos+1<line.length() && line[pos+1]=='"') {
1065 s+=ch;
1066 pos++;
1067 ch = line[pos];
1068 currentType=ContentType::RawStringPrefix;
1069 delimiter = "";
1070 }
1071 if (currentType == ContentType::RawStringPrefix ) {
1072 delimiter += ch;
1073 }
1074 s+=ch;
1075 break;
1076 case '(':
1077 switch(currentType) {
1078 case ContentType::RawStringPrefix:
1079 currentType = ContentType::RawString;
1080 break;
1081 default:
1082 break;
1083 }
1084 s+=ch;
1085 break;
1086 case '/':
1087 if (currentType == ContentType::Other) {
1088 if (pos+1<line.length() && line[pos+1]=='/') {
1089 // line comment , skip all remainings of the current line
1090 stopProcess = true;
1091 break;
1092 } else if (pos+1<line.length() && line[pos+1]=='*') {
1093 /* ansi c comment */
1094 pos++;
1095 currentType = ContentType::AnsiCComment;
1096 break;
1097 }
1098 }
1099 s+=ch;
1100 break;
1101 case '\\':
1102 switch (currentType) {
1103 case ContentType::String:
1104 case ContentType::Character:
1105 pos++;
1106 s+=ch;
1107 if (pos<line.length()) {
1108 ch = line[pos];
1109 s+=ch;
1110 }
1111 break;
1112 default:
1113 s+=ch;
1114 }
1115 break;
1116 default:
1117 s+=ch;
1118 }
1119 if (stopProcess)
1120 break;
1121 pos++;
1122 }
1123 result.append(s.trimmed());
1124 }
1125 return result;
1126}
1127
1128void CppPreprocessor::preprocessBuffer()
1129{
1130 while (mIncludes.count() > 0) {
1131 QString s;
1132 do {
1133 s = getNextPreprocessor();
1134 if (s.startsWith('#')) {
1135 simplify(s);
1136 if (!s.isEmpty()) {
1137 handlePreprocessor(s);
1138 }
1139 }
1140 } while (!s.isEmpty());
1141 closeInclude();
1142 }
1143}
1144
1145void CppPreprocessor::skipToEndOfPreprocessor()
1146{
1147 // Skip until last char of line is NOT \ anymore
1148 while ((mIndex < mBuffer.count()) && mBuffer[mIndex].endsWith('\\'))
1149 mIndex++;
1150}
1151
1152void CppPreprocessor::skipToPreprocessor()
1153{
1154
1155// Increment until a line begins with a #
1156 while ((mIndex < mBuffer.count()) && !mBuffer[mIndex].startsWith('#')) {
1157 if (getCurrentBranch()) // if not skipping, expand current macros
1158 mResult.append(expandMacros(mBuffer[mIndex],1));
1159 else // If skipping due to a failed branch, clear line
1160 mResult.append("");
1161 mIndex++;
1162 }
1163}
1164
1165bool CppPreprocessor::isWordChar(const QChar &ch)
1166{
1167 if (ch=='_'
1168 // || (ch>='a' && ch<='z') || (ch>='A' && ch<='Z')
1169 || ch.isLetter()
1170 || (ch>='0' && ch<='9')) {
1171 return true;
1172 }
1173 return false;
1174}
1175
1176bool CppPreprocessor::isIdentChar(const QChar &ch)
1177{
1178 if (ch=='_' || ch == '*' || ch == '&' || ch == '~' ||
1179 ch.isLetter()
1180 //(ch>='a' && ch<='z') || (ch>='A' && ch<='Z')
1181 || (ch>='0' && ch<='9')) {
1182 return true;
1183 }
1184 return false;
1185}
1186
1187bool CppPreprocessor::isLineChar(const QChar &ch)
1188{
1189 return ch=='\r' || ch == '\n';
1190}
1191
1192bool CppPreprocessor::isSpaceChar(const QChar &ch)
1193{
1194 return ch == ' ' || ch == '\t';
1195}
1196
1197bool CppPreprocessor::isOperatorChar(const QChar &ch)
1198{
1199
1200 switch(ch.unicode()) {
1201 case '+':
1202 case '-':
1203 case '*':
1204 case '/':
1205 case '!':
1206 case '=':
1207 case '<':
1208 case '>':
1209 case '&':
1210 case '|':
1211 case '^':
1212 return true;
1213 default:
1214 return false;
1215 }
1216}
1217
1218bool CppPreprocessor::isMacroIdentChar(const QChar &ch)
1219{
1220 //return (ch>='A' && ch<='Z') || (ch>='a' && ch<='z')
1221 return ch.isLetter()
1222 || ch == '_';
1223}
1224
1225bool CppPreprocessor::isDigit(const QChar &ch)
1226{
1227 return (ch>='0' && ch<='9');
1228}
1229
1230bool CppPreprocessor::isNumberChar(const QChar &ch)
1231{
1232 if (ch>='0' && ch<='9')
1233 return true;
1234 if (ch>='a' && ch<='f')
1235 return true;
1236 if (ch>='A' && ch<='F')
1237 return true;
1238 switch(ch.unicode()) {
1239 case 'x':
1240 case 'X':
1241 case 'u':
1242 case 'U':
1243 case 'l':
1244 case 'L':
1245 return true;
1246 default:
1247 return false;
1248 }
1249}
1250
1251QString CppPreprocessor::lineBreak()
1252{
1253 return "\n";
1254}
1255
1256bool CppPreprocessor::evaluateIf(const QString &line)
1257{
1258 QString newLine = expandDefines(line); // replace FOO by numerical value of FOO
1259 return evaluateExpression(newLine);
1260}
1261
1262QString CppPreprocessor::expandDefines(QString line)
1263{
1264 int searchPos = 0;
1265 while (searchPos < line.length()) {
1266 // We have found an identifier. It is not a number suffix. Try to expand it
1267 if (isMacroIdentChar(line[searchPos]) && (
1268 (searchPos == 0) || !isDigit(line[searchPos - 1]))) {
1269 int head = searchPos;
1270 int tail = searchPos;
1271
1272 // Get identifier name (numbers are allowed, but not at the start
1273 while ((tail < line.length()) && (isMacroIdentChar(line[tail]) || isDigit(line[head])))
1274 tail++;
1275// qDebug()<<"1 "<<head<<tail<<line;
1276 QString name = line.mid(head,tail-head);
1277 int nameStart = head;
1278 int nameEnd = tail;
1279
1280 if (name == "defined") {
1281 //expand define
1282 //tail = searchPos + name.length();
1283 while ((tail < line.length()) && isSpaceChar(line[tail]))
1284 tail++; // skip spaces
1285 int defineStart;
1286
1287 // Skip over its arguments
1288 if ((tail < line.length()) && (line[tail]=='(')) {
1289 //braced argument (next word)
1290 defineStart = tail+1;
1291 if (!skipBraces(line, tail)) {
1292 line = ""; // broken line
1293 break;
1294 }
1295 } else {
1296 //none braced argument (next word)
1297 defineStart = tail;
1298 if ((tail>=line.length()) || !isMacroIdentChar(line[defineStart])) {
1299 line = ""; // broken line
1300 break;
1301 }
1302 while ((tail < line.length()) && (isMacroIdentChar(line[tail]) || isDigit(line[tail])))
1303 tail++;
1304 }
1305// qDebug()<<"2 "<<defineStart<<tail<<line;
1306 name = line.mid(defineStart, tail - defineStart);
1307 PDefine define = getDefine(name);
1308 QString insertValue;
1309 if (!define) {
1310 insertValue = "0";
1311 } else {
1312 insertValue = "1";
1313 }
1314 // Insert found value at place
1315 line.remove(searchPos, tail-searchPos+1);
1316 line.insert(searchPos,insertValue);
1317 } else if ((name == "and") || (name == "or")) {
1318 searchPos = tail; // Skip logical operators
1319 } else {
1320 // We have found a regular define. Replace it by its value
1321 // Does it exist in the database?
1322 PDefine define = getDefine(name);
1323 QString insertValue;
1324 if (!define) {
1325 insertValue = "0";
1326 } else {
1327 while ((tail < line.length()) && isSpaceChar(line[tail]))
1328 tail++;// skip spaces
1329 // It is a function. Expand arguments
1330 if ((tail < line.length()) && (line[tail] == '(')) {
1331 head=tail;
1332 if (skipBraces(line, tail)) {
1333// qDebug()<<"3 "<<line<<head<<tail;
1334 QString args = line.mid(head,tail-head+1);
1335 insertValue = expandFunction(define,args);
1336 nameEnd = tail+1;
1337 } else {
1338 line = "";// broken line
1339 break;
1340 }
1341 // Replace regular define
1342 } else {
1343 if (!define->value.isEmpty())
1344 insertValue = define->value;
1345 else
1346 insertValue = "0";
1347 }
1348 }
1349 // Insert found value at place
1350 line.remove(nameStart, nameEnd - nameStart);
1351 line.insert(searchPos,insertValue);
1352 }
1353 } else {
1354 searchPos ++ ;
1355 }
1356 }
1357 return line;
1358}
1359
1360bool CppPreprocessor::skipBraces(const QString &line, int &index, int step)
1361{
1362 int level = 0;
1363 while ((index >= 0) && (index < line.length())) { // Find the corresponding opening brace
1364 if (line[index] == '(') {
1365 level++;
1366 } else if (line[index] == ')') {
1367 level--;
1368 }
1369 if (level == 0)
1370 return true;
1371 index+=step;
1372 }
1373 return false;
1374}
1375
1376QString CppPreprocessor::expandFunction(PDefine define, QString args)
1377{
1378 // Replace function by this string
1379 QString result = define->formatValue;
1380 if (args.startsWith('(') && args.endsWith(')')) {
1381 args = args.mid(1,args.length()-2);
1382 }
1383
1384 QStringList argValues = args.split(",");
1385 if (argValues.length() == define->argList.length()
1386 && argValues.length()>0) {
1387 for (int i=0;i<argValues.length();i++) {
1388 if (define->argUsed[i]) {
1389 QString argValue = argValues[i];
1390 result=result.arg(argValue.trimmed());
1391 }
1392 }
1393 }
1394 result.replace("%%","%");
1395
1396 return result;
1397}
1398
1399bool CppPreprocessor::skipSpaces(const QString &expr, int &pos)
1400{
1401 while (pos<expr.length() && isSpaceChar(expr[pos]))
1402 pos++;
1403 return pos<expr.length();
1404}
1405
1406bool CppPreprocessor::evalNumber(const QString &expr, int &result, int &pos)
1407{
1408 if (!skipSpaces(expr,pos))
1409 return false;
1410 QString s;
1411 while (pos<expr.length() && isNumberChar(expr[pos])) {
1412 s+=expr[pos];
1413 pos++;
1414 }
1415 bool ok;
1416
1417 if (s.endsWith("LL",Qt::CaseInsensitive)) {
1418 s.remove(s.length()-2,2);
1419 result = s.toLongLong(&ok);
1420 } else if (s.endsWith("L",Qt::CaseInsensitive)) {
1421 s.remove(s.length()-1,1);
1422 result = s.toLong(&ok);
1423 } else if (s.endsWith("ULL",Qt::CaseInsensitive)) {
1424 s.remove(s.length()-3,3);
1425 result = s.toULongLong(&ok);
1426 } else if (s.endsWith("UL",Qt::CaseInsensitive)) {
1427 s.remove(s.length()-2,2);
1428 result = s.toULong(&ok);
1429 } else if (s.endsWith("U",Qt::CaseInsensitive)) {
1430 s.remove(s.length()-1,1);
1431 result = s.toUInt(&ok);
1432 } else {
1433 result = s.toInt(&ok);
1434 }
1435 return ok;
1436}
1437
1438bool CppPreprocessor::evalTerm(const QString &expr, int &result, int &pos)
1439{
1440 if (!skipSpaces(expr,pos))
1441 return false;
1442 if (expr[pos]=='(') {
1443 pos++;
1444 if (!evalExpr(expr,result,pos))
1445 return false;
1446 if (!skipSpaces(expr,pos))
1447 return false;
1448 if (expr[pos]!=')')
1449 return false;
1450 pos++;
1451 return true;
1452 } else {
1453 return evalNumber(expr,result,pos);
1454 }
1455}
1456
1457/*
1458 * unary_expr = term
1459 | '+' term
1460 | '-' term
1461 | '!' term
1462 | '~' term
1463 */
1464bool CppPreprocessor::evalUnaryExpr(const QString &expr, int &result, int &pos)
1465{
1466 if (!skipSpaces(expr,pos))
1467 return false;
1468 if (expr[pos]=='+') {
1469 pos++;
1470 if (!evalTerm(expr,result,pos))
1471 return false;
1472 } else if (expr[pos]=='-') {
1473 pos++;
1474 if (!evalTerm(expr,result,pos))
1475 return false;
1476 result = -result;
1477 } else if (expr[pos]=='~') {
1478 pos++;
1479 if (!evalTerm(expr,result,pos))
1480 return false;
1481 result = ~result;
1482 } else if (expr[pos]=='!') {
1483 pos++;
1484 if (!evalTerm(expr,result,pos))
1485 return false;
1486 result = !result;
1487 } else {
1488 return evalTerm(expr,result,pos);
1489 }
1490 return true;
1491}
1492
1493/*
1494 * mul_expr = unary_expr
1495 | mul_expr '*' unary_expr
1496 | mul_expr '/' unary_expr
1497 | mul_expr '%' unary_expr
1498 */
1499bool CppPreprocessor::evalMulExpr(const QString &expr, int &result, int &pos)
1500{
1501 if (!evalUnaryExpr(expr,result,pos))
1502 return false;
1503 while (true) {
1504 if (!skipSpaces(expr,pos))
1505 break;
1506 int rightResult;
1507 if (expr[pos]=='*') {
1508 pos++;
1509 if (!evalUnaryExpr(expr,rightResult,pos))
1510 return false;
1511 result *= rightResult;
1512 } else if (expr[pos]=='/') {
1513 pos++;
1514 if (!evalUnaryExpr(expr,rightResult,pos))
1515 return false;
1516 result /= rightResult;
1517 } else if (expr[pos]=='%') {
1518 pos++;
1519 if (!evalUnaryExpr(expr,rightResult,pos))
1520 return false;
1521 result %= rightResult;
1522 } else {
1523 break;
1524 }
1525 }
1526 return true;
1527}
1528
1529/*
1530 * add_expr = mul_expr
1531 | add_expr '+' mul_expr
1532 | add_expr '-' mul_expr
1533 */
1534bool CppPreprocessor::evalAddExpr(const QString &expr, int &result, int &pos)
1535{
1536 if (!evalMulExpr(expr,result,pos))
1537 return false;
1538 while (true) {
1539 if (!skipSpaces(expr,pos))
1540 break;
1541 int rightResult;
1542 if (expr[pos]=='+') {
1543 pos++;
1544 if (!evalMulExpr(expr,rightResult,pos))
1545 return false;
1546 result += rightResult;
1547 } else if (expr[pos]=='-') {
1548 pos++;
1549 if (!evalMulExpr(expr,rightResult,pos))
1550 return false;
1551 result -= rightResult;
1552 } else {
1553 break;
1554 }
1555 }
1556 return true;
1557}
1558
1559/*
1560 * shift_expr = add_expr
1561 | shift_expr "<<" add_expr
1562 | shift_expr ">>" add_expr
1563 */
1564bool CppPreprocessor::evalShiftExpr(const QString &expr, int &result, int &pos)
1565{
1566 if (!evalAddExpr(expr,result,pos))
1567 return false;
1568 while (true) {
1569 if (!skipSpaces(expr,pos))
1570 break;
1571 int rightResult;
1572 if (pos+1<expr.length() && expr[pos] == '<' && expr[pos+1]=='<') {
1573 pos += 2;
1574 if (!evalAddExpr(expr,rightResult,pos))
1575 return false;
1576 result = (result << rightResult);
1577 } else if (pos+1<expr.length() && expr[pos] == '>' && expr[pos+1]=='>') {
1578 pos += 2;
1579 if (!evalAddExpr(expr,rightResult,pos))
1580 return false;
1581 result = (result >> rightResult);
1582 } else {
1583 break;
1584 }
1585 }
1586 return true;
1587}
1588
1589/*
1590 * relation_expr = shift_expr
1591 | relation_expr ">=" shift_expr
1592 | relation_expr ">" shift_expr
1593 | relation_expr "<=" shift_expr
1594 | relation_expr "<" shift_expr
1595 */
1596bool CppPreprocessor::evalRelationExpr(const QString &expr, int &result, int &pos)
1597{
1598 if (!evalShiftExpr(expr,result,pos))
1599 return false;
1600 while (true) {
1601 if (!skipSpaces(expr,pos))
1602 break;
1603 int rightResult;
1604 if (expr[pos]=='<') {
1605 if (pos+1<expr.length() && expr[pos+1]=='=') {
1606 pos+=2;
1607 if (!evalShiftExpr(expr,rightResult,pos))
1608 return false;
1609 result = (result <= rightResult);
1610 } else {
1611 pos++;
1612 if (!evalShiftExpr(expr,rightResult,pos))
1613 return false;
1614 result = (result < rightResult);
1615 }
1616 } else if (expr[pos]=='>') {
1617 if (pos+1<expr.length() && expr[pos+1]=='=') {
1618 pos+=2;
1619 if (!evalShiftExpr(expr,rightResult,pos))
1620 return false;
1621 result = (result >= rightResult);
1622 } else {
1623 pos++;
1624 if (!evalShiftExpr(expr,rightResult,pos))
1625 return false;
1626 result = (result > rightResult);
1627 }
1628 } else {
1629 break;
1630 }
1631 }
1632 return true;
1633}
1634
1635/*
1636 * equal_expr = relation_expr
1637 | equal_expr "==" relation_expr
1638 | equal_expr "!=" relation_expr
1639 */
1640bool CppPreprocessor::evalEqualExpr(const QString &expr, int &result, int &pos)
1641{
1642 if (!evalRelationExpr(expr,result,pos))
1643 return false;
1644 while (true) {
1645 if (!skipSpaces(expr,pos))
1646 break;
1647 if (pos+1<expr.length() && expr[pos]=='!' && expr[pos+1]=='=') {
1648 pos+=2;
1649 int rightResult;
1650 if (!evalRelationExpr(expr,rightResult,pos))
1651 return false;
1652 result = (result != rightResult);
1653 } else if (pos+1<expr.length() && expr[pos]=='=' && expr[pos+1]=='=') {
1654 pos+=2;
1655 int rightResult;
1656 if (!evalRelationExpr(expr,rightResult,pos))
1657 return false;
1658 result = (result == rightResult);
1659 } else {
1660 break;
1661 }
1662 }
1663 return true;
1664}
1665
1666/*
1667 * bit_and_expr = equal_expr
1668 | bit_and_expr "&" equal_expr
1669 */
1670bool CppPreprocessor::evalBitAndExpr(const QString &expr, int &result, int &pos)
1671{
1672 if (!evalEqualExpr(expr,result,pos))
1673 return false;
1674 while (true) {
1675 if (!skipSpaces(expr,pos))
1676 break;
1677 if (expr[pos]=='&'
1678 && (pos == expr.length()
1679 || expr[pos+1]!='&')) {
1680 pos++;
1681 int rightResult;
1682 if (!evalEqualExpr(expr,rightResult,pos))
1683 return false;
1684 result = result & rightResult;
1685 } else {
1686 break;
1687 }
1688 }
1689 return true;
1690}
1691
1692/*
1693 * bit_xor_expr = bit_and_expr
1694 | bit_xor_expr "^" bit_and_expr
1695 */
1696bool CppPreprocessor::evalBitXorExpr(const QString &expr, int &result, int &pos)
1697{
1698 if (!evalBitAndExpr(expr,result,pos))
1699 return false;
1700 while (true) {
1701 if (!skipSpaces(expr,pos))
1702 break;
1703 if (expr[pos]=='^') {
1704 pos++;
1705 int rightResult;
1706 if (!evalBitAndExpr(expr,rightResult,pos))
1707 return false;
1708 result = result ^ rightResult;
1709 } else {
1710 break;
1711 }
1712 }
1713 return true;
1714}
1715
1716/*
1717 * bit_or_expr = bit_xor_expr
1718 | bit_or_expr "|" bit_xor_expr
1719 */
1720bool CppPreprocessor::evalBitOrExpr(const QString &expr, int &result, int &pos)
1721{
1722 if (!evalBitXorExpr(expr,result,pos))
1723 return false;
1724 while (true) {
1725 if (!skipSpaces(expr,pos))
1726 break;
1727 if (expr[pos] == '|'
1728 && (pos == expr.length()
1729 || expr[pos+1]!='|')) {
1730 pos++;
1731 int rightResult;
1732 if (!evalBitXorExpr(expr,rightResult,pos))
1733 return false;
1734 result = result | rightResult;
1735 } else {
1736 break;
1737 }
1738 }
1739 return true;
1740}
1741
1742/*
1743 * logic_and_expr = bit_or_expr
1744 | logic_and_expr "&&" bit_or_expr
1745 */
1746bool CppPreprocessor::evalLogicAndExpr(const QString &expr, int &result, int &pos)
1747{
1748 if (!evalBitOrExpr(expr,result,pos))
1749 return false;
1750 while (true) {
1751 if (!skipSpaces(expr,pos))
1752 break;
1753 if (pos+1<expr.length() && expr[pos]=='&' && expr[pos+1] =='&') {
1754 pos+=2;
1755 int rightResult;
1756 if (!evalBitOrExpr(expr,rightResult,pos))
1757 return false;
1758 result = result && rightResult;
1759 } else {
1760 break;
1761 }
1762 }
1763 return true;
1764}
1765
1766/*
1767 * logic_or_expr = logic_and_expr
1768 | logic_or_expr "||" logic_and_expr
1769 */
1770bool CppPreprocessor::evalLogicOrExpr(const QString &expr, int &result, int &pos)
1771{
1772 if (!evalLogicAndExpr(expr,result,pos))
1773 return false;
1774 while (true) {
1775 if (!skipSpaces(expr,pos))
1776 break;
1777 if (pos+1<expr.length() && expr[pos]=='|' && expr[pos+1] =='|') {
1778 pos+=2;
1779 int rightResult;
1780 if (!evalLogicAndExpr(expr,rightResult,pos))
1781 return false;
1782 result = result || rightResult;
1783 } else {
1784 break;
1785 }
1786 }
1787 return true;
1788}
1789
1790bool CppPreprocessor::evalExpr(const QString &expr, int &result, int &pos)
1791{
1792 return evalLogicOrExpr(expr,result,pos);
1793}
1794
1795/* BNF for C constant expression evaluation
1796 * term = number
1797 | '(' expression ')'
1798unary_expr = term
1799 | '+' term
1800 | '-' term
1801 | '!' term
1802 | '~' term
1803mul_expr = term
1804 | mul_expr '*' term
1805 | mul_expr '/' term
1806 | mul_expr '%' term
1807add_expr = mul_expr
1808 | add_expr '+' mul_expr
1809 | add_expr '-' mul_expr
1810shift_expr = add_expr
1811 | shift_expr "<<" add_expr
1812 | shift_expr ">>" add_expr
1813relation_expr = shift_expr
1814 | relation_expr ">=" shift_expr
1815 | relation_expr ">" shift_expr
1816 | relation_expr "<=" shift_expr
1817 | relation_expr "<" shift_expr
1818equal_expr = relation_expr
1819 | equal_expr "==" relation_expr
1820 | equal_expr "!=" relation_expr
1821bit_and_expr = equal_expr
1822 | bit_and_expr "&" equal_expr
1823bit_xor_expr = bit_and_expr
1824 | bit_xor_expr "^" bit_and_expr
1825bit_or_expr = bit_xor_expr
1826 | bit_or_expr "|" bit_xor_expr
1827logic_and_expr = bit_or_expr
1828 | logic_and_expr "&&" bit_or_expr
1829logic_or_expr = logic_and_expr
1830 | logic_or_expr "||" logic_and_expr
1831 */
1832
1833int CppPreprocessor::evaluateExpression(QString line)
1834{
1835 int pos = 0;
1836 int result;
1837 bool ok = evalExpr(line,result,pos);
1838 if (!ok)
1839 return -1;
1840 //expr not finished
1841 if (skipSpaces(line,pos))
1842 return -1;
1843 return result;
1844}
1845
1846const QList<QString> &CppPreprocessor::projectIncludePathList() const
1847{
1848 return mProjectIncludePathList;
1849}
1850
1851const QList<QString> &CppPreprocessor::includePathList() const
1852{
1853 return mIncludePathList;
1854}
1855
1856const DefineMap &CppPreprocessor::hardDefines() const
1857{
1858 return mHardDefines;
1859}
1860
1861const QSet<QString> &CppPreprocessor::projectIncludePaths()
1862{
1863 return mProjectIncludePaths;
1864}
1865
1866const QSet<QString> &CppPreprocessor::includePaths()
1867{
1868 return mIncludePaths;
1869}
1870
1871QSet<QString> &CppPreprocessor::scannedFiles()
1872{
1873 return mScannedFiles;
1874}
1875
1876QHash<QString, PFileIncludes> &CppPreprocessor::includesList()
1877{
1878 return mIncludesList;
1879}
1880
1881