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 | |
25 | CppPreprocessor::CppPreprocessor() |
26 | { |
27 | } |
28 | |
29 | void 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 | |
41 | void 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 | |
53 | void 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 | |
79 | void 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 | |
122 | void CppPreprocessor::addHardDefineByLine(const QString &line) |
123 | { |
124 | addDefineByLine(line,true); |
125 | } |
126 | |
127 | void 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 | |
141 | PDefine CppPreprocessor::getDefine(const QString &name) |
142 | { |
143 | return mDefines.value(name,PDefine()); |
144 | } |
145 | |
146 | PDefine CppPreprocessor::getHardDefine(const QString &name) |
147 | { |
148 | return mHardDefines.value(name,PDefine()); |
149 | } |
150 | |
151 | void 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 | |
163 | void CppPreprocessor::resetDefines() |
164 | { |
165 | mDefines = mHardDefines; |
166 | // mDefines.clear(); |
167 | |
168 | // mDefines.insert(mHardDefines); |
169 | } |
170 | |
171 | void CppPreprocessor::setScanOptions(bool parseSystem, bool parseLocal) |
172 | { |
173 | mParseSystem = parseSystem; |
174 | mParseLocal=parseLocal; |
175 | } |
176 | |
177 | void 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 | |
188 | void 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 | |
202 | void 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 | |
220 | void 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 | |
309 | void CppPreprocessor::addIncludePath(const QString &fileName) |
310 | { |
311 | if (!mIncludePaths.contains(fileName)) { |
312 | mIncludePaths.insert(fileName); |
313 | mIncludePathList.append(fileName); |
314 | } |
315 | } |
316 | |
317 | void CppPreprocessor::addProjectIncludePath(const QString &fileName) |
318 | { |
319 | if (!mProjectIncludePaths.contains(fileName)) { |
320 | mProjectIncludePaths.insert(fileName); |
321 | mProjectIncludePathList.append(fileName); |
322 | } |
323 | } |
324 | |
325 | void CppPreprocessor::clearIncludePaths() |
326 | { |
327 | mIncludePaths.clear(); |
328 | mIncludePathList.clear(); |
329 | } |
330 | |
331 | void CppPreprocessor::clearProjectIncludePaths() |
332 | { |
333 | mProjectIncludePaths.clear(); |
334 | mProjectIncludePathList.clear(); |
335 | } |
336 | |
337 | QString 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 | |
364 | void CppPreprocessor::simplify(QString &output) |
365 | { |
366 | // Remove # |
367 | output = output.mid(1).trimmed(); |
368 | } |
369 | |
370 | void 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 | |
440 | void 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 | |
448 | void 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 | |
495 | void 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 | |
511 | void 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 | |
533 | QString 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 | |
563 | void 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 | |
631 | QString 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 | |
656 | void 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 | |
682 | PParsedFile CppPreprocessor::getInclude(int index) |
683 | { |
684 | return mIncludes[index]; |
685 | } |
686 | |
687 | void 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 | |
795 | void 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 | |
825 | bool CppPreprocessor::getCurrentBranch() |
826 | { |
827 | if (!mBranchResults.isEmpty()) |
828 | return mBranchResults.last(); |
829 | else |
830 | return true; |
831 | } |
832 | |
833 | void CppPreprocessor::setCurrentBranch(bool value) |
834 | { |
835 | mBranchResults.append(value); |
836 | } |
837 | |
838 | void CppPreprocessor::removeCurrentBranch() |
839 | { |
840 | if (mBranchResults.size()>0) |
841 | mBranchResults.pop_back(); |
842 | } |
843 | |
844 | QStringList CppPreprocessor::result() const |
845 | { |
846 | return mResult; |
847 | } |
848 | |
849 | PFileIncludes CppPreprocessor::getFileIncludesEntry(const QString &fileName) |
850 | { |
851 | return mIncludesList.value(fileName,PFileIncludes()); |
852 | } |
853 | |
854 | void 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 | |
882 | void 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 | |
929 | QList<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 | |
1005 | QStringList CppPreprocessor::(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 | |
1128 | void 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 | |
1145 | void 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 | |
1152 | void 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 | |
1165 | bool 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 | |
1176 | bool 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 | |
1187 | bool CppPreprocessor::isLineChar(const QChar &ch) |
1188 | { |
1189 | return ch=='\r' || ch == '\n'; |
1190 | } |
1191 | |
1192 | bool CppPreprocessor::isSpaceChar(const QChar &ch) |
1193 | { |
1194 | return ch == ' ' || ch == '\t'; |
1195 | } |
1196 | |
1197 | bool 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 | |
1218 | bool CppPreprocessor::isMacroIdentChar(const QChar &ch) |
1219 | { |
1220 | //return (ch>='A' && ch<='Z') || (ch>='a' && ch<='z') |
1221 | return ch.isLetter() |
1222 | || ch == '_'; |
1223 | } |
1224 | |
1225 | bool CppPreprocessor::isDigit(const QChar &ch) |
1226 | { |
1227 | return (ch>='0' && ch<='9'); |
1228 | } |
1229 | |
1230 | bool 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 | |
1251 | QString CppPreprocessor::lineBreak() |
1252 | { |
1253 | return "\n" ; |
1254 | } |
1255 | |
1256 | bool CppPreprocessor::evaluateIf(const QString &line) |
1257 | { |
1258 | QString newLine = expandDefines(line); // replace FOO by numerical value of FOO |
1259 | return evaluateExpression(newLine); |
1260 | } |
1261 | |
1262 | QString 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 | |
1360 | bool 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 | |
1376 | QString 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 | |
1399 | bool 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 | |
1406 | bool 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 | |
1438 | bool 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 | */ |
1464 | bool 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 | */ |
1499 | bool 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 | */ |
1534 | bool 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 | */ |
1564 | bool 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 | */ |
1596 | bool 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 | */ |
1640 | bool 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 | */ |
1670 | bool 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 | */ |
1696 | bool 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 | */ |
1720 | bool 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 | */ |
1746 | bool 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 | */ |
1770 | bool 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 | |
1790 | bool 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 ')' |
1798 | unary_expr = term |
1799 | | '+' term |
1800 | | '-' term |
1801 | | '!' term |
1802 | | '~' term |
1803 | mul_expr = term |
1804 | | mul_expr '*' term |
1805 | | mul_expr '/' term |
1806 | | mul_expr '%' term |
1807 | add_expr = mul_expr |
1808 | | add_expr '+' mul_expr |
1809 | | add_expr '-' mul_expr |
1810 | shift_expr = add_expr |
1811 | | shift_expr "<<" add_expr |
1812 | | shift_expr ">>" add_expr |
1813 | relation_expr = shift_expr |
1814 | | relation_expr ">=" shift_expr |
1815 | | relation_expr ">" shift_expr |
1816 | | relation_expr "<=" shift_expr |
1817 | | relation_expr "<" shift_expr |
1818 | equal_expr = relation_expr |
1819 | | equal_expr "==" relation_expr |
1820 | | equal_expr "!=" relation_expr |
1821 | bit_and_expr = equal_expr |
1822 | | bit_and_expr "&" equal_expr |
1823 | bit_xor_expr = bit_and_expr |
1824 | | bit_xor_expr "^" bit_and_expr |
1825 | bit_or_expr = bit_xor_expr |
1826 | | bit_or_expr "|" bit_xor_expr |
1827 | logic_and_expr = bit_or_expr |
1828 | | logic_and_expr "&&" bit_or_expr |
1829 | logic_or_expr = logic_and_expr |
1830 | | logic_or_expr "||" logic_and_expr |
1831 | */ |
1832 | |
1833 | int 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 | |
1846 | const QList<QString> &CppPreprocessor::projectIncludePathList() const |
1847 | { |
1848 | return mProjectIncludePathList; |
1849 | } |
1850 | |
1851 | const QList<QString> &CppPreprocessor::includePathList() const |
1852 | { |
1853 | return mIncludePathList; |
1854 | } |
1855 | |
1856 | const DefineMap &CppPreprocessor::hardDefines() const |
1857 | { |
1858 | return mHardDefines; |
1859 | } |
1860 | |
1861 | const QSet<QString> &CppPreprocessor::projectIncludePaths() |
1862 | { |
1863 | return mProjectIncludePaths; |
1864 | } |
1865 | |
1866 | const QSet<QString> &CppPreprocessor::includePaths() |
1867 | { |
1868 | return mIncludePaths; |
1869 | } |
1870 | |
1871 | QSet<QString> &CppPreprocessor::scannedFiles() |
1872 | { |
1873 | return mScannedFiles; |
1874 | } |
1875 | |
1876 | QHash<QString, PFileIncludes> &CppPreprocessor::includesList() |
1877 | { |
1878 | return mIncludesList; |
1879 | } |
1880 | |
1881 | |