/src/cppcheck/externals/simplecpp/simplecpp.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * simplecpp - A simple and high-fidelity C/C++ preprocessor library |
3 | | * Copyright (C) 2016-2022 Daniel Marjamäki. |
4 | | * |
5 | | * This library is free software: you can redistribute it and/or |
6 | | * modify it under the terms of the GNU Lesser General Public |
7 | | * License as published by the Free Software Foundation, either |
8 | | * version 3 of the License, or (at your option) any later version. |
9 | | * |
10 | | * This library is distributed in the hope that it will be useful, |
11 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | | * Lesser General Public License for more details. |
14 | | * |
15 | | * You should have received a copy of the GNU Lesser General Public |
16 | | * License along with this library. If not, see <http://www.gnu.org/licenses/>. |
17 | | */ |
18 | | |
19 | | #if defined(_WIN32) || defined(__CYGWIN__) || defined(__MINGW32__) |
20 | | #define SIMPLECPP_WINDOWS |
21 | | #define NOMINMAX |
22 | | #endif |
23 | | |
24 | | #include "simplecpp.h" |
25 | | |
26 | | #include <algorithm> |
27 | | #include <cassert> |
28 | | #include <cctype> |
29 | | #include <climits> |
30 | | #include <cstddef> |
31 | | #include <cstdio> |
32 | | #include <cstdlib> |
33 | | #include <cstring> |
34 | | #include <ctime> |
35 | | #include <exception> |
36 | | #include <fstream> // IWYU pragma: keep |
37 | | #include <iostream> |
38 | | #include <limits> |
39 | | #include <list> |
40 | | #include <map> |
41 | | #include <set> |
42 | | #include <sstream> // IWYU pragma: keep |
43 | | #include <stack> |
44 | | #include <stdexcept> |
45 | | #include <string> |
46 | | #if __cplusplus >= 201103L |
47 | | #ifdef SIMPLECPP_WINDOWS |
48 | | #include <mutex> |
49 | | #endif |
50 | | #include <unordered_map> |
51 | | #endif |
52 | | #include <utility> |
53 | | #include <vector> |
54 | | |
55 | | #ifdef SIMPLECPP_WINDOWS |
56 | | #include <windows.h> |
57 | | #undef ERROR |
58 | | #endif |
59 | | |
60 | | #if __cplusplus >= 201103L |
61 | | #define OVERRIDE override |
62 | | #define EXPLICIT explicit |
63 | | #else |
64 | | #define OVERRIDE |
65 | | #define EXPLICIT |
66 | | #endif |
67 | | |
68 | | #if (__cplusplus < 201103L) && !defined(__APPLE__) |
69 | | #define nullptr NULL |
70 | | #endif |
71 | | |
72 | | static bool isHex(const std::string &s) |
73 | 21.8k | { |
74 | 21.8k | return s.size()>2 && (s.compare(0,2,"0x")==0 || s.compare(0,2,"0X")==0); |
75 | 21.8k | } |
76 | | |
77 | | static bool isOct(const std::string &s) |
78 | 10.9k | { |
79 | 10.9k | return s.size()>1 && (s[0]=='0') && (s[1] >= '0') && (s[1] < '8'); |
80 | 10.9k | } |
81 | | |
82 | | // TODO: added an undercore since this conflicts with a function of the same name in utils.h from Cppcheck source when building Cppcheck with MSBuild |
83 | | static bool isStringLiteral_(const std::string &s) |
84 | 0 | { |
85 | 0 | return s.size() > 1 && (s[0]=='\"') && (*s.rbegin()=='\"'); |
86 | 0 | } |
87 | | |
88 | | // TODO: added an undercore since this conflicts with a function of the same name in utils.h from Cppcheck source when building Cppcheck with MSBuild |
89 | | static bool isCharLiteral_(const std::string &s) |
90 | 0 | { |
91 | | // char literal patterns can include 'a', '\t', '\000', '\xff', 'abcd', and maybe '' |
92 | | // This only checks for the surrounding '' but doesn't parse the content. |
93 | 0 | return s.size() > 1 && (s[0]=='\'') && (*s.rbegin()=='\''); |
94 | 0 | } |
95 | | |
96 | | static const simplecpp::TokenString DEFINE("define"); |
97 | | static const simplecpp::TokenString UNDEF("undef"); |
98 | | |
99 | | static const simplecpp::TokenString INCLUDE("include"); |
100 | | |
101 | | static const simplecpp::TokenString ERROR("error"); |
102 | | static const simplecpp::TokenString WARNING("warning"); |
103 | | |
104 | | static const simplecpp::TokenString IF("if"); |
105 | | static const simplecpp::TokenString IFDEF("ifdef"); |
106 | | static const simplecpp::TokenString IFNDEF("ifndef"); |
107 | | static const simplecpp::TokenString DEFINED("defined"); |
108 | | static const simplecpp::TokenString ELSE("else"); |
109 | | static const simplecpp::TokenString ELIF("elif"); |
110 | | static const simplecpp::TokenString ENDIF("endif"); |
111 | | |
112 | | static const simplecpp::TokenString PRAGMA("pragma"); |
113 | | static const simplecpp::TokenString ONCE("once"); |
114 | | |
115 | | static const simplecpp::TokenString HAS_INCLUDE("__has_include"); |
116 | | |
117 | | template<class T> static std::string toString(T t) |
118 | 0 | { |
119 | | // NOLINTNEXTLINE(misc-const-correctness) - false positive |
120 | 0 | std::ostringstream ostr; |
121 | 0 | ostr << t; |
122 | 0 | return ostr.str(); |
123 | 0 | } Unexecuted instantiation: simplecpp.cpp:std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > toString<unsigned int>(unsigned int) Unexecuted instantiation: simplecpp.cpp:std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > toString<unsigned long>(unsigned long) Unexecuted instantiation: simplecpp.cpp:std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > toString<unsigned long long>(unsigned long long) Unexecuted instantiation: simplecpp.cpp:std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > toString<long long>(long long) Unexecuted instantiation: simplecpp.cpp:std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > toString<int>(int) |
124 | | |
125 | | static long long stringToLL(const std::string &s) |
126 | 0 | { |
127 | 0 | long long ret; |
128 | 0 | const bool hex = isHex(s); |
129 | 0 | const bool oct = isOct(s); |
130 | 0 | std::istringstream istr(hex ? s.substr(2) : oct ? s.substr(1) : s); |
131 | 0 | if (hex) |
132 | 0 | istr >> std::hex; |
133 | 0 | else if (oct) |
134 | 0 | istr >> std::oct; |
135 | 0 | istr >> ret; |
136 | 0 | return ret; |
137 | 0 | } |
138 | | |
139 | | static unsigned long long stringToULL(const std::string &s) |
140 | 0 | { |
141 | 0 | unsigned long long ret; |
142 | 0 | const bool hex = isHex(s); |
143 | 0 | const bool oct = isOct(s); |
144 | 0 | std::istringstream istr(hex ? s.substr(2) : oct ? s.substr(1) : s); |
145 | 0 | if (hex) |
146 | 0 | istr >> std::hex; |
147 | 0 | else if (oct) |
148 | 0 | istr >> std::oct; |
149 | 0 | istr >> ret; |
150 | 0 | return ret; |
151 | 0 | } |
152 | | |
153 | | static bool endsWith(const std::string &s, const std::string &e) |
154 | 4.18k | { |
155 | 4.18k | return (s.size() >= e.size()) && std::equal(e.rbegin(), e.rend(), s.rbegin()); |
156 | 4.18k | } |
157 | | |
158 | | static bool sameline(const simplecpp::Token *tok1, const simplecpp::Token *tok2) |
159 | 197k | { |
160 | 197k | return tok1 && tok2 && tok1->location.sameline(tok2->location); |
161 | 197k | } |
162 | | |
163 | | static bool isAlternativeBinaryOp(const simplecpp::Token *tok, const std::string &alt) |
164 | 0 | { |
165 | 0 | return (tok->name && |
166 | 0 | tok->str() == alt && |
167 | 0 | tok->previous && |
168 | 0 | tok->next && |
169 | 0 | (tok->previous->number || tok->previous->name || tok->previous->op == ')') && |
170 | 0 | (tok->next->number || tok->next->name || tok->next->op == '(')); |
171 | 0 | } |
172 | | |
173 | | static bool isAlternativeUnaryOp(const simplecpp::Token *tok, const std::string &alt) |
174 | 0 | { |
175 | 0 | return ((tok->name && tok->str() == alt) && |
176 | 0 | (!tok->previous || tok->previous->op == '(') && |
177 | 0 | (tok->next && (tok->next->name || tok->next->number))); |
178 | 0 | } |
179 | | |
180 | | static std::string replaceAll(std::string s, const std::string& from, const std::string& to) |
181 | 0 | { |
182 | 0 | for (size_t pos = s.find(from); pos != std::string::npos; pos = s.find(from, pos + to.size())) |
183 | 0 | s.replace(pos, from.size(), to); |
184 | 0 | return s; |
185 | 0 | } |
186 | | |
187 | | const std::string simplecpp::Location::emptyFileName; |
188 | | |
189 | | void simplecpp::Location::adjust(const std::string &str) |
190 | 99.2k | { |
191 | 99.2k | if (strpbrk(str.c_str(), "\r\n") == nullptr) { |
192 | 99.2k | col += str.size(); |
193 | 99.2k | return; |
194 | 99.2k | } |
195 | | |
196 | 0 | for (std::size_t i = 0U; i < str.size(); ++i) { |
197 | 0 | col++; |
198 | 0 | if (str[i] == '\n' || str[i] == '\r') { |
199 | 0 | col = 1; |
200 | 0 | line++; |
201 | 0 | if (str[i] == '\r' && (i+1)<str.size() && str[i+1]=='\n') |
202 | 0 | ++i; |
203 | 0 | } |
204 | 0 | } |
205 | 0 | } |
206 | | |
207 | | bool simplecpp::Token::isOneOf(const char ops[]) const |
208 | 9.59k | { |
209 | 9.59k | return (op != '\0') && (std::strchr(ops, op) != nullptr); |
210 | 9.59k | } |
211 | | |
212 | | bool simplecpp::Token::startsWithOneOf(const char c[]) const |
213 | 0 | { |
214 | 0 | return std::strchr(c, string[0]) != nullptr; |
215 | 0 | } |
216 | | |
217 | | bool simplecpp::Token::endsWithOneOf(const char c[]) const |
218 | 0 | { |
219 | 0 | return std::strchr(c, string[string.size() - 1U]) != nullptr; |
220 | 0 | } |
221 | | |
222 | | void simplecpp::Token::printAll() const |
223 | 0 | { |
224 | 0 | const Token *tok = this; |
225 | 0 | while (tok->previous) |
226 | 0 | tok = tok->previous; |
227 | 0 | for (; tok; tok = tok->next) { |
228 | 0 | if (tok->previous) { |
229 | 0 | std::cout << (sameline(tok, tok->previous) ? ' ' : '\n'); |
230 | 0 | } |
231 | 0 | std::cout << tok->str(); |
232 | 0 | } |
233 | 0 | std::cout << std::endl; |
234 | 0 | } |
235 | | |
236 | | void simplecpp::Token::printOut() const |
237 | 0 | { |
238 | 0 | for (const Token *tok = this; tok; tok = tok->next) { |
239 | 0 | if (tok != this) { |
240 | 0 | std::cout << (sameline(tok, tok->previous) ? ' ' : '\n'); |
241 | 0 | } |
242 | 0 | std::cout << tok->str(); |
243 | 0 | } |
244 | 0 | std::cout << std::endl; |
245 | 0 | } |
246 | | |
247 | | // cppcheck-suppress noConstructor - we call init() in the inherited to initialize the private members |
248 | | class simplecpp::TokenList::Stream { |
249 | | public: |
250 | 9.53k | virtual ~Stream() {} |
251 | | |
252 | | virtual int get() = 0; |
253 | | virtual int peek() = 0; |
254 | | virtual void unget() = 0; |
255 | | virtual bool good() = 0; |
256 | | |
257 | | unsigned char readChar() |
258 | 499k | { |
259 | 499k | unsigned char ch = static_cast<unsigned char>(get()); |
260 | | |
261 | | // For UTF-16 encoded files the BOM is 0xfeff/0xfffe. If the |
262 | | // character is non-ASCII character then replace it with 0xff |
263 | 499k | if (isUtf16) { |
264 | 0 | const unsigned char ch2 = static_cast<unsigned char>(get()); |
265 | 0 | const int ch16 = makeUtf16Char(ch, ch2); |
266 | 0 | ch = static_cast<unsigned char>(((ch16 >= 0x80) ? 0xff : ch16)); |
267 | 0 | } |
268 | | |
269 | | // Handling of newlines.. |
270 | 499k | if (ch == '\r') { |
271 | 0 | ch = '\n'; |
272 | |
|
273 | 0 | int ch2 = get(); |
274 | 0 | if (isUtf16) { |
275 | 0 | const int c2 = get(); |
276 | 0 | ch2 = makeUtf16Char(ch2, c2); |
277 | 0 | } |
278 | |
|
279 | 0 | if (ch2 != '\n') |
280 | 0 | ungetChar(); |
281 | 0 | } |
282 | | |
283 | 499k | return ch; |
284 | 499k | } |
285 | | |
286 | | unsigned char peekChar() |
287 | 540 | { |
288 | 540 | unsigned char ch = static_cast<unsigned char>(peek()); |
289 | | |
290 | | // For UTF-16 encoded files the BOM is 0xfeff/0xfffe. If the |
291 | | // character is non-ASCII character then replace it with 0xff |
292 | 540 | if (isUtf16) { |
293 | 0 | (void)get(); |
294 | 0 | const unsigned char ch2 = static_cast<unsigned char>(peek()); |
295 | 0 | unget(); |
296 | 0 | const int ch16 = makeUtf16Char(ch, ch2); |
297 | 0 | ch = static_cast<unsigned char>(((ch16 >= 0x80) ? 0xff : ch16)); |
298 | 0 | } |
299 | | |
300 | | // Handling of newlines.. |
301 | 540 | if (ch == '\r') |
302 | 0 | ch = '\n'; |
303 | | |
304 | 540 | return ch; |
305 | 540 | } |
306 | | |
307 | | void ungetChar() |
308 | 52.1k | { |
309 | 52.1k | unget(); |
310 | 52.1k | if (isUtf16) |
311 | 0 | unget(); |
312 | 52.1k | } |
313 | | |
314 | | protected: |
315 | 9.53k | void init() { |
316 | | // initialize since we use peek() in getAndSkipBOM() |
317 | 9.53k | isUtf16 = false; |
318 | 9.53k | bom = getAndSkipBOM(); |
319 | 9.53k | isUtf16 = (bom == 0xfeff || bom == 0xfffe); |
320 | 9.53k | } |
321 | | |
322 | | private: |
323 | | inline int makeUtf16Char(const unsigned char ch, const unsigned char ch2) const |
324 | 0 | { |
325 | 0 | return (bom == 0xfeff) ? (ch<<8 | ch2) : (ch2<<8 | ch); |
326 | 0 | } |
327 | | |
328 | | unsigned short getAndSkipBOM() |
329 | 9.53k | { |
330 | 9.53k | const int ch1 = peek(); |
331 | | |
332 | | // The UTF-16 BOM is 0xfffe or 0xfeff. |
333 | 9.53k | if (ch1 >= 0xfe) { |
334 | 0 | (void)get(); |
335 | 0 | const unsigned short byte = (static_cast<unsigned char>(ch1) << 8); |
336 | 0 | if (peek() >= 0xfe) |
337 | 0 | return byte | static_cast<unsigned char>(get()); |
338 | 0 | unget(); |
339 | 0 | return 0; |
340 | 0 | } |
341 | | |
342 | | // Skip UTF-8 BOM 0xefbbbf |
343 | 9.53k | if (ch1 == 0xef) { |
344 | 0 | (void)get(); |
345 | 0 | if (peek() == 0xbb) { |
346 | 0 | (void)get(); |
347 | 0 | if (peek() == 0xbf) { |
348 | 0 | (void)get(); |
349 | 0 | return 0; |
350 | 0 | } |
351 | 0 | unget(); |
352 | 0 | } |
353 | 0 | unget(); |
354 | 0 | } |
355 | | |
356 | 9.53k | return 0; |
357 | 9.53k | } |
358 | | |
359 | | unsigned short bom; |
360 | | protected: |
361 | | bool isUtf16; |
362 | | }; |
363 | | |
364 | | class StdIStream : public simplecpp::TokenList::Stream { |
365 | | public: |
366 | | // cppcheck-suppress uninitDerivedMemberVar - we call Stream::init() to initialize the private members |
367 | | EXPLICIT StdIStream(std::istream &istr) |
368 | | : istr(istr) |
369 | 9.53k | { |
370 | 9.53k | assert(istr.good()); |
371 | 0 | init(); |
372 | 9.53k | } |
373 | | |
374 | 499k | virtual int get() OVERRIDE { |
375 | 499k | return istr.get(); |
376 | 499k | } |
377 | 10.0k | virtual int peek() OVERRIDE { |
378 | 10.0k | return istr.peek(); |
379 | 10.0k | } |
380 | 52.1k | virtual void unget() OVERRIDE { |
381 | 52.1k | istr.unget(); |
382 | 52.1k | } |
383 | 769k | virtual bool good() OVERRIDE { |
384 | 769k | return istr.good(); |
385 | 769k | } |
386 | | |
387 | | private: |
388 | | std::istream &istr; |
389 | | }; |
390 | | |
391 | | class FileStream : public simplecpp::TokenList::Stream { |
392 | | public: |
393 | | // cppcheck-suppress uninitDerivedMemberVar - we call Stream::init() to initialize the private members |
394 | | EXPLICIT FileStream(const std::string &filename) |
395 | | : file(fopen(filename.c_str(), "rb")) |
396 | | , lastCh(0) |
397 | | , lastStatus(0) |
398 | 0 | { |
399 | 0 | assert(file != nullptr); |
400 | 0 | init(); |
401 | 0 | } |
402 | | |
403 | 0 | ~FileStream() OVERRIDE { |
404 | 0 | fclose(file); |
405 | 0 | file = nullptr; |
406 | 0 | } |
407 | | |
408 | 0 | virtual int get() OVERRIDE { |
409 | 0 | lastStatus = lastCh = fgetc(file); |
410 | 0 | return lastCh; |
411 | 0 | } |
412 | 0 | virtual int peek() OVERRIDE{ |
413 | | // keep lastCh intact |
414 | 0 | const int ch = fgetc(file); |
415 | 0 | unget_internal(ch); |
416 | 0 | return ch; |
417 | 0 | } |
418 | 0 | virtual void unget() OVERRIDE { |
419 | 0 | unget_internal(lastCh); |
420 | 0 | } |
421 | 0 | virtual bool good() OVERRIDE { |
422 | 0 | return lastStatus != EOF; |
423 | 0 | } |
424 | | |
425 | | private: |
426 | 0 | void unget_internal(int ch) { |
427 | 0 | if (isUtf16) { |
428 | | // TODO: use ungetc() as well |
429 | | // UTF-16 has subsequent unget() calls |
430 | 0 | fseek(file, -1, SEEK_CUR); |
431 | 0 | } |
432 | 0 | else |
433 | 0 | ungetc(ch, file); |
434 | 0 | } |
435 | | |
436 | | FileStream(const FileStream&); |
437 | | FileStream &operator=(const FileStream&); |
438 | | |
439 | | FILE *file; |
440 | | int lastCh; |
441 | | int lastStatus; |
442 | | }; |
443 | | |
444 | 106k | simplecpp::TokenList::TokenList(std::vector<std::string> &filenames) : frontToken(nullptr), backToken(nullptr), files(filenames) {} |
445 | | |
446 | | simplecpp::TokenList::TokenList(std::istream &istr, std::vector<std::string> &filenames, const std::string &filename, OutputList *outputList) |
447 | | : frontToken(nullptr), backToken(nullptr), files(filenames) |
448 | 1.36k | { |
449 | 1.36k | StdIStream stream(istr); |
450 | 1.36k | readfile(stream,filename,outputList); |
451 | 1.36k | } |
452 | | |
453 | | simplecpp::TokenList::TokenList(const std::string &filename, std::vector<std::string> &filenames, OutputList *outputList) |
454 | | : frontToken(nullptr), backToken(nullptr), files(filenames) |
455 | 0 | { |
456 | 0 | FileStream stream(filename); |
457 | 0 | readfile(stream,filename,outputList); |
458 | 0 | } |
459 | | |
460 | | simplecpp::TokenList::TokenList(const TokenList &other) : frontToken(nullptr), backToken(nullptr), files(other.files) |
461 | 0 | { |
462 | 0 | *this = other; |
463 | 0 | } |
464 | | |
465 | | #if __cplusplus >= 201103L |
466 | | simplecpp::TokenList::TokenList(TokenList &&other) : frontToken(nullptr), backToken(nullptr), files(other.files) |
467 | 0 | { |
468 | 0 | *this = std::move(other); |
469 | 0 | } |
470 | | #endif |
471 | | |
472 | | simplecpp::TokenList::~TokenList() |
473 | 107k | { |
474 | 107k | clear(); |
475 | 107k | } |
476 | | |
477 | | simplecpp::TokenList &simplecpp::TokenList::operator=(const TokenList &other) |
478 | 16.3k | { |
479 | 16.3k | if (this != &other) { |
480 | 16.3k | clear(); |
481 | 16.3k | files = other.files; |
482 | 49.0k | for (const Token *tok = other.cfront(); tok; tok = tok->next) |
483 | 32.6k | push_back(new Token(*tok)); |
484 | 16.3k | sizeOfType = other.sizeOfType; |
485 | 16.3k | } |
486 | 16.3k | return *this; |
487 | 16.3k | } |
488 | | |
489 | | #if __cplusplus >= 201103L |
490 | | simplecpp::TokenList &simplecpp::TokenList::operator=(TokenList &&other) |
491 | 0 | { |
492 | 0 | if (this != &other) { |
493 | 0 | clear(); |
494 | 0 | frontToken = other.frontToken; |
495 | 0 | other.frontToken = nullptr; |
496 | 0 | backToken = other.backToken; |
497 | 0 | other.backToken = nullptr; |
498 | 0 | files = other.files; |
499 | 0 | sizeOfType = std::move(other.sizeOfType); |
500 | 0 | } |
501 | 0 | return *this; |
502 | 0 | } |
503 | | #endif |
504 | | |
505 | | void simplecpp::TokenList::clear() |
506 | 124k | { |
507 | 124k | backToken = nullptr; |
508 | 254k | while (frontToken) { |
509 | 130k | Token * const next = frontToken->next; |
510 | 130k | delete frontToken; |
511 | 130k | frontToken = next; |
512 | 130k | } |
513 | 124k | sizeOfType.clear(); |
514 | 124k | } |
515 | | |
516 | | void simplecpp::TokenList::push_back(Token *tok) |
517 | 212k | { |
518 | 212k | if (!frontToken) |
519 | 106k | frontToken = tok; |
520 | 106k | else |
521 | 106k | backToken->next = tok; |
522 | 212k | tok->previous = backToken; |
523 | 212k | backToken = tok; |
524 | 212k | } |
525 | | |
526 | | void simplecpp::TokenList::dump() const |
527 | 0 | { |
528 | 0 | std::cout << stringify() << std::endl; |
529 | 0 | } |
530 | | |
531 | | std::string simplecpp::TokenList::stringify() const |
532 | 0 | { |
533 | 0 | std::ostringstream ret; |
534 | 0 | Location loc(files); |
535 | 0 | for (const Token *tok = cfront(); tok; tok = tok->next) { |
536 | 0 | if (tok->location.line < loc.line || tok->location.fileIndex != loc.fileIndex) { |
537 | 0 | ret << "\n#line " << tok->location.line << " \"" << tok->location.file() << "\"\n"; |
538 | 0 | loc = tok->location; |
539 | 0 | } |
540 | |
|
541 | 0 | while (tok->location.line > loc.line) { |
542 | 0 | ret << '\n'; |
543 | 0 | loc.line++; |
544 | 0 | } |
545 | |
|
546 | 0 | if (sameline(tok->previous, tok)) |
547 | 0 | ret << ' '; |
548 | |
|
549 | 0 | ret << tok->str(); |
550 | |
|
551 | 0 | loc.adjust(tok->str()); |
552 | 0 | } |
553 | |
|
554 | 0 | return ret.str(); |
555 | 0 | } |
556 | | |
557 | | static bool isNameChar(unsigned char ch) |
558 | 406k | { |
559 | 406k | return std::isalnum(ch) || ch == '_' || ch == '$'; |
560 | 406k | } |
561 | | |
562 | | static std::string escapeString(const std::string &str) |
563 | 0 | { |
564 | 0 | std::ostringstream ostr; |
565 | 0 | ostr << '\"'; |
566 | 0 | for (std::size_t i = 1U; i < str.size() - 1; ++i) { |
567 | 0 | const char c = str[i]; |
568 | 0 | if (c == '\\' || c == '\"' || c == '\'') |
569 | 0 | ostr << '\\'; |
570 | 0 | ostr << c; |
571 | 0 | } |
572 | 0 | ostr << '\"'; |
573 | 0 | return ostr.str(); |
574 | 0 | } |
575 | | |
576 | | static void portabilityBackslash(simplecpp::OutputList *outputList, const std::vector<std::string> &files, const simplecpp::Location &location) |
577 | 0 | { |
578 | 0 | if (!outputList) |
579 | 0 | return; |
580 | 0 | simplecpp::Output err(files); |
581 | 0 | err.type = simplecpp::Output::PORTABILITY_BACKSLASH; |
582 | 0 | err.location = location; |
583 | 0 | err.msg = "Combination 'backslash space newline' is not portable."; |
584 | 0 | outputList->push_back(err); |
585 | 0 | } |
586 | | |
587 | | static bool isStringLiteralPrefix(const std::string &str) |
588 | 2.72k | { |
589 | 2.72k | return str == "u" || str == "U" || str == "L" || str == "u8" || |
590 | 2.72k | str == "R" || str == "uR" || str == "UR" || str == "LR" || str == "u8R"; |
591 | 2.72k | } |
592 | | |
593 | | void simplecpp::TokenList::lineDirective(unsigned int fileIndex, unsigned int line, Location *location) |
594 | 0 | { |
595 | 0 | if (fileIndex != location->fileIndex || line >= location->line) { |
596 | 0 | location->fileIndex = fileIndex; |
597 | 0 | location->line = line; |
598 | 0 | return; |
599 | 0 | } |
600 | | |
601 | 0 | if (line + 2 >= location->line) { |
602 | 0 | location->line = line; |
603 | 0 | while (cback()->op != '#') |
604 | 0 | deleteToken(back()); |
605 | 0 | deleteToken(back()); |
606 | 0 | return; |
607 | 0 | } |
608 | 0 | } |
609 | | |
610 | | static const std::string COMMENT_END("*/"); |
611 | | |
612 | | void simplecpp::TokenList::readfile(Stream &stream, const std::string &filename, OutputList *outputList) |
613 | 9.53k | { |
614 | 9.53k | std::stack<simplecpp::Location> loc; |
615 | | |
616 | 9.53k | unsigned int multiline = 0U; |
617 | | |
618 | 9.53k | const Token *oldLastToken = nullptr; |
619 | | |
620 | 9.53k | Location location(files); |
621 | 9.53k | location.fileIndex = fileIndex(filename); |
622 | 9.53k | location.line = 1U; |
623 | 9.53k | location.col = 1U; |
624 | 215k | while (stream.good()) { |
625 | 210k | unsigned char ch = stream.readChar(); |
626 | 210k | if (!stream.good()) |
627 | 4.08k | break; |
628 | 206k | if (ch < ' ' && ch != '\t' && ch != '\n' && ch != '\r') |
629 | 0 | ch = ' '; |
630 | | |
631 | 206k | if (ch >= 0x80) { |
632 | 0 | if (outputList) { |
633 | 0 | simplecpp::Output err(files); |
634 | 0 | err.type = simplecpp::Output::UNHANDLED_CHAR_ERROR; |
635 | 0 | err.location = location; |
636 | 0 | std::ostringstream s; |
637 | 0 | s << static_cast<int>(ch); |
638 | 0 | err.msg = "The code contains unhandled character(s) (character code=" + s.str() + "). Neither unicode nor extended ascii is supported."; |
639 | 0 | outputList->push_back(err); |
640 | 0 | } |
641 | 0 | clear(); |
642 | 0 | return; |
643 | 0 | } |
644 | | |
645 | 206k | if (ch == '\n') { |
646 | 22.7k | if (cback() && cback()->op == '\\') { |
647 | 0 | if (location.col > cback()->location.col + 1U) |
648 | 0 | portabilityBackslash(outputList, files, cback()->location); |
649 | 0 | ++multiline; |
650 | 0 | deleteToken(back()); |
651 | 22.7k | } else { |
652 | 22.7k | location.line += multiline + 1; |
653 | 22.7k | multiline = 0U; |
654 | 22.7k | } |
655 | 22.7k | if (!multiline) |
656 | 22.7k | location.col = 1; |
657 | | |
658 | 22.7k | if (oldLastToken != cback()) { |
659 | 21.0k | oldLastToken = cback(); |
660 | 21.0k | if (!isLastLinePreprocessor()) |
661 | 21.0k | continue; |
662 | 0 | const std::string lastline(lastLine()); |
663 | 0 | if (lastline == "# file %str%") { |
664 | 0 | const Token *strtok = cback(); |
665 | 0 | while (strtok->comment) |
666 | 0 | strtok = strtok->previous; |
667 | 0 | loc.push(location); |
668 | 0 | location.fileIndex = fileIndex(strtok->str().substr(1U, strtok->str().size() - 2U)); |
669 | 0 | location.line = 1U; |
670 | 0 | } else if (lastline == "# line %num%") { |
671 | 0 | const Token *numtok = cback(); |
672 | 0 | while (numtok->comment) |
673 | 0 | numtok = numtok->previous; |
674 | 0 | lineDirective(location.fileIndex, std::atol(numtok->str().c_str()), &location); |
675 | 0 | } else if (lastline == "# %num% %str%" || lastline == "# line %num% %str%") { |
676 | 0 | const Token *strtok = cback(); |
677 | 0 | while (strtok->comment) |
678 | 0 | strtok = strtok->previous; |
679 | 0 | const Token *numtok = strtok->previous; |
680 | 0 | while (numtok->comment) |
681 | 0 | numtok = numtok->previous; |
682 | 0 | lineDirective(fileIndex(replaceAll(strtok->str().substr(1U, strtok->str().size() - 2U),"\\\\","\\")), |
683 | 0 | std::atol(numtok->str().c_str()), &location); |
684 | 0 | } |
685 | | // #endfile |
686 | 0 | else if (lastline == "# endfile" && !loc.empty()) { |
687 | 0 | location = loc.top(); |
688 | 0 | loc.pop(); |
689 | 0 | } |
690 | 0 | } |
691 | | |
692 | 1.74k | continue; |
693 | 22.7k | } |
694 | | |
695 | 183k | if (std::isspace(ch)) { |
696 | 84.0k | location.col++; |
697 | 84.0k | continue; |
698 | 84.0k | } |
699 | | |
700 | 99.2k | TokenString currentToken; |
701 | | |
702 | 99.2k | if (cback() && cback()->location.line == location.line && cback()->previous && cback()->previous->op == '#' && isLastLinePreprocessor() && (lastLine() == "# error" || lastLine() == "# warning")) { |
703 | 0 | char prev = ' '; |
704 | 0 | while (stream.good() && (prev == '\\' || (ch != '\r' && ch != '\n'))) { |
705 | 0 | currentToken += ch; |
706 | 0 | prev = ch; |
707 | 0 | ch = stream.readChar(); |
708 | 0 | } |
709 | 0 | stream.ungetChar(); |
710 | 0 | push_back(new Token(currentToken, location)); |
711 | 0 | location.adjust(currentToken); |
712 | 0 | continue; |
713 | 0 | } |
714 | | |
715 | | // number or name |
716 | 99.2k | if (isNameChar(ch)) { |
717 | 52.1k | const bool num = std::isdigit(ch); |
718 | 312k | while (stream.good() && isNameChar(ch)) { |
719 | 260k | currentToken += ch; |
720 | 260k | ch = stream.readChar(); |
721 | 260k | if (num && ch=='\'' && isNameChar(stream.peekChar())) |
722 | 0 | ch = stream.readChar(); |
723 | 260k | } |
724 | | |
725 | 52.1k | stream.ungetChar(); |
726 | 52.1k | } |
727 | | |
728 | | // comment |
729 | 47.1k | else if (ch == '/' && stream.peekChar() == '/') { |
730 | 0 | while (stream.good() && ch != '\r' && ch != '\n') { |
731 | 0 | currentToken += ch; |
732 | 0 | ch = stream.readChar(); |
733 | 0 | } |
734 | 0 | const std::string::size_type pos = currentToken.find_last_not_of(" \t"); |
735 | 0 | if (pos < currentToken.size() - 1U && currentToken[pos] == '\\') |
736 | 0 | portabilityBackslash(outputList, files, location); |
737 | 0 | if (currentToken[currentToken.size() - 1U] == '\\') { |
738 | 0 | ++multiline; |
739 | 0 | currentToken.erase(currentToken.size() - 1U); |
740 | 0 | } else { |
741 | 0 | stream.ungetChar(); |
742 | 0 | } |
743 | 0 | } |
744 | | |
745 | | // comment |
746 | 47.1k | else if (ch == '/' && stream.peekChar() == '*') { |
747 | 0 | currentToken = "/*"; |
748 | 0 | (void)stream.readChar(); |
749 | 0 | ch = stream.readChar(); |
750 | 0 | while (stream.good()) { |
751 | 0 | currentToken += ch; |
752 | 0 | if (currentToken.size() >= 4U && endsWith(currentToken, COMMENT_END)) |
753 | 0 | break; |
754 | 0 | ch = stream.readChar(); |
755 | 0 | } |
756 | | // multiline.. |
757 | |
|
758 | 0 | std::string::size_type pos = 0; |
759 | 0 | while ((pos = currentToken.find("\\\n",pos)) != std::string::npos) { |
760 | 0 | currentToken.erase(pos,2); |
761 | 0 | ++multiline; |
762 | 0 | } |
763 | 0 | if (multiline || isLastLinePreprocessor()) { |
764 | 0 | pos = 0; |
765 | 0 | while ((pos = currentToken.find('\n',pos)) != std::string::npos) { |
766 | 0 | currentToken.erase(pos,1); |
767 | 0 | ++multiline; |
768 | 0 | } |
769 | 0 | } |
770 | 0 | } |
771 | | |
772 | | // string / char literal |
773 | 47.1k | else if (ch == '\"' || ch == '\'') { |
774 | 2.72k | std::string prefix; |
775 | 2.72k | if (cback() && cback()->name && isStringLiteralPrefix(cback()->str()) && |
776 | 2.72k | ((cback()->location.col + cback()->str().size()) == location.col) && |
777 | 2.72k | (cback()->location.line == location.line)) { |
778 | 0 | prefix = cback()->str(); |
779 | 0 | } |
780 | | // C++11 raw string literal |
781 | 2.72k | if (ch == '\"' && !prefix.empty() && *cback()->str().rbegin() == 'R') { |
782 | 0 | std::string delim; |
783 | 0 | currentToken = ch; |
784 | 0 | prefix.resize(prefix.size() - 1); |
785 | 0 | ch = stream.readChar(); |
786 | 0 | while (stream.good() && ch != '(' && ch != '\n') { |
787 | 0 | delim += ch; |
788 | 0 | ch = stream.readChar(); |
789 | 0 | } |
790 | 0 | if (!stream.good() || ch == '\n') { |
791 | 0 | if (outputList) { |
792 | 0 | Output err(files); |
793 | 0 | err.type = Output::SYNTAX_ERROR; |
794 | 0 | err.location = location; |
795 | 0 | err.msg = "Invalid newline in raw string delimiter."; |
796 | 0 | outputList->push_back(err); |
797 | 0 | } |
798 | 0 | return; |
799 | 0 | } |
800 | 0 | const std::string endOfRawString(')' + delim + currentToken); |
801 | 0 | while (stream.good() && !(endsWith(currentToken, endOfRawString) && currentToken.size() > 1)) |
802 | 0 | currentToken += stream.readChar(); |
803 | 0 | if (!endsWith(currentToken, endOfRawString)) { |
804 | 0 | if (outputList) { |
805 | 0 | Output err(files); |
806 | 0 | err.type = Output::SYNTAX_ERROR; |
807 | 0 | err.location = location; |
808 | 0 | err.msg = "Raw string missing terminating delimiter."; |
809 | 0 | outputList->push_back(err); |
810 | 0 | } |
811 | 0 | return; |
812 | 0 | } |
813 | 0 | currentToken.erase(currentToken.size() - endOfRawString.size(), endOfRawString.size() - 1U); |
814 | 0 | currentToken = escapeString(currentToken); |
815 | 0 | currentToken.insert(0, prefix); |
816 | 0 | back()->setstr(currentToken); |
817 | 0 | location.adjust(currentToken); |
818 | 0 | if (currentToken.find_first_of("\r\n") == std::string::npos) |
819 | 0 | location.col += 2 + 2 * delim.size(); |
820 | 0 | else |
821 | 0 | location.col += 1 + delim.size(); |
822 | |
|
823 | 0 | continue; |
824 | 0 | } |
825 | | |
826 | 2.72k | currentToken = readUntil(stream,location,ch,ch,outputList); |
827 | 2.72k | if (currentToken.size() < 2U) |
828 | | // Error is reported by readUntil() |
829 | 0 | return; |
830 | | |
831 | 2.72k | std::string s = currentToken; |
832 | 2.72k | std::string::size_type pos; |
833 | 2.72k | int newlines = 0; |
834 | 2.72k | while ((pos = s.find_first_of("\r\n")) != std::string::npos) { |
835 | 0 | s.erase(pos,1); |
836 | 0 | newlines++; |
837 | 0 | } |
838 | | |
839 | 2.72k | if (prefix.empty()) |
840 | 2.72k | push_back(new Token(s, location)); // push string without newlines |
841 | 0 | else |
842 | 0 | back()->setstr(prefix + s); |
843 | | |
844 | 2.72k | if (newlines > 0 && isLastLinePreprocessor() && lastLine().compare(0,9,"# define ") == 0) { |
845 | 0 | multiline += newlines; |
846 | 0 | location.adjust(s); |
847 | 2.72k | } else { |
848 | 2.72k | location.adjust(currentToken); |
849 | 2.72k | } |
850 | 2.72k | continue; |
851 | 2.72k | } |
852 | | |
853 | 44.3k | else { |
854 | 44.3k | currentToken += ch; |
855 | 44.3k | } |
856 | | |
857 | 96.5k | if (*currentToken.begin() == '<' && isLastLinePreprocessor() && lastLine() == "# include") { |
858 | 0 | currentToken = readUntil(stream, location, '<', '>', outputList); |
859 | 0 | if (currentToken.size() < 2U) |
860 | 0 | return; |
861 | 0 | } |
862 | | |
863 | 96.5k | push_back(new Token(currentToken, location)); |
864 | | |
865 | 96.5k | if (multiline) |
866 | 0 | location.col += currentToken.size(); |
867 | 96.5k | else |
868 | 96.5k | location.adjust(currentToken); |
869 | 96.5k | } |
870 | | |
871 | 9.53k | combineOperators(); |
872 | 9.53k | } |
873 | | |
874 | | void simplecpp::TokenList::constFold() |
875 | 0 | { |
876 | 0 | while (cfront()) { |
877 | | // goto last '(' |
878 | 0 | Token *tok = back(); |
879 | 0 | while (tok && tok->op != '(') |
880 | 0 | tok = tok->previous; |
881 | | |
882 | | // no '(', goto first token |
883 | 0 | if (!tok) |
884 | 0 | tok = front(); |
885 | | |
886 | | // Constant fold expression |
887 | 0 | constFoldUnaryNotPosNeg(tok); |
888 | 0 | constFoldMulDivRem(tok); |
889 | 0 | constFoldAddSub(tok); |
890 | 0 | constFoldShift(tok); |
891 | 0 | constFoldComparison(tok); |
892 | 0 | constFoldBitwise(tok); |
893 | 0 | constFoldLogicalOp(tok); |
894 | 0 | constFoldQuestionOp(&tok); |
895 | | |
896 | | // If there is no '(' we are done with the constant folding |
897 | 0 | if (tok->op != '(') |
898 | 0 | break; |
899 | | |
900 | 0 | if (!tok->next || !tok->next->next || tok->next->next->op != ')') |
901 | 0 | break; |
902 | | |
903 | 0 | tok = tok->next; |
904 | 0 | deleteToken(tok->previous); |
905 | 0 | deleteToken(tok->next); |
906 | 0 | } |
907 | 0 | } |
908 | | |
909 | | static bool isFloatSuffix(const simplecpp::Token *tok) |
910 | 0 | { |
911 | 0 | if (!tok || tok->str().size() != 1U) |
912 | 0 | return false; |
913 | 0 | const char c = std::tolower(tok->str()[0]); |
914 | 0 | return c == 'f' || c == 'l'; |
915 | 0 | } |
916 | | |
917 | | void simplecpp::TokenList::combineOperators() |
918 | 9.53k | { |
919 | 9.53k | std::stack<bool> executableScope; |
920 | 9.53k | executableScope.push(false); |
921 | 106k | for (Token *tok = front(); tok; tok = tok->next) { |
922 | 96.8k | if (tok->op == '{') { |
923 | 3.58k | if (executableScope.top()) { |
924 | 0 | executableScope.push(true); |
925 | 0 | continue; |
926 | 0 | } |
927 | 3.58k | const Token *prev = tok->previous; |
928 | 8.60k | while (prev && prev->isOneOf(";{}()")) |
929 | 5.02k | prev = prev->previous; |
930 | 3.58k | executableScope.push(prev && prev->op == ')'); |
931 | 3.58k | continue; |
932 | 3.58k | } |
933 | 93.2k | if (tok->op == '}') { |
934 | 3.58k | if (executableScope.size() > 1) |
935 | 3.58k | executableScope.pop(); |
936 | 3.58k | continue; |
937 | 3.58k | } |
938 | | |
939 | 89.6k | if (tok->op == '.') { |
940 | | // ellipsis ... |
941 | 0 | if (tok->next && tok->next->op == '.' && tok->next->location.col == (tok->location.col + 1) && |
942 | 0 | tok->next->next && tok->next->next->op == '.' && tok->next->next->location.col == (tok->location.col + 2)) { |
943 | 0 | tok->setstr("..."); |
944 | 0 | deleteToken(tok->next); |
945 | 0 | deleteToken(tok->next); |
946 | 0 | continue; |
947 | 0 | } |
948 | | // float literals.. |
949 | 0 | if (tok->previous && tok->previous->number && sameline(tok->previous, tok)) { |
950 | 0 | tok->setstr(tok->previous->str() + '.'); |
951 | 0 | deleteToken(tok->previous); |
952 | 0 | if (isFloatSuffix(tok->next) || (tok->next && tok->next->startsWithOneOf("AaBbCcDdEeFfPp"))) { |
953 | 0 | tok->setstr(tok->str() + tok->next->str()); |
954 | 0 | deleteToken(tok->next); |
955 | 0 | } |
956 | 0 | } |
957 | 0 | if (tok->next && tok->next->number) { |
958 | 0 | tok->setstr(tok->str() + tok->next->str()); |
959 | 0 | deleteToken(tok->next); |
960 | 0 | } |
961 | 0 | } |
962 | | // match: [0-9.]+E [+-] [0-9]+ |
963 | 89.6k | const char lastChar = tok->str()[tok->str().size() - 1]; |
964 | 89.6k | if (tok->number && !isOct(tok->str()) && |
965 | 89.6k | ((!isHex(tok->str()) && (lastChar == 'E' || lastChar == 'e')) || |
966 | 10.9k | (isHex(tok->str()) && (lastChar == 'P' || lastChar == 'p'))) && |
967 | 89.6k | tok->next && tok->next->isOneOf("+-") && tok->next->next && tok->next->next->number) { |
968 | 0 | tok->setstr(tok->str() + tok->next->op + tok->next->next->str()); |
969 | 0 | deleteToken(tok->next); |
970 | 0 | deleteToken(tok->next); |
971 | 0 | } |
972 | | |
973 | 89.6k | if (tok->op == '\0' || !tok->next || tok->next->op == '\0') |
974 | 75.4k | continue; |
975 | 14.2k | if (!sameline(tok,tok->next)) |
976 | 5.23k | continue; |
977 | 8.98k | if (tok->location.col + 1U != tok->next->location.col) |
978 | 1.36k | continue; |
979 | | |
980 | 7.62k | if (tok->next->op == '=' && tok->isOneOf("=!<>+-*/%&|^")) { |
981 | 976 | if (tok->op == '&' && !executableScope.top()) { |
982 | | // don't combine &= if it is a anonymous reference parameter with default value: |
983 | | // void f(x&=2) |
984 | 0 | int indentlevel = 0; |
985 | 0 | const Token *start = tok; |
986 | 0 | while (indentlevel >= 0 && start) { |
987 | 0 | if (start->op == ')') |
988 | 0 | ++indentlevel; |
989 | 0 | else if (start->op == '(') |
990 | 0 | --indentlevel; |
991 | 0 | else if (start->isOneOf(";{}")) |
992 | 0 | break; |
993 | 0 | start = start->previous; |
994 | 0 | } |
995 | 0 | if (indentlevel == -1 && start) { |
996 | 0 | const Token * const ftok = start; |
997 | 0 | bool isFuncDecl = ftok->name; |
998 | 0 | while (isFuncDecl) { |
999 | 0 | if (!start->name && start->str() != "::" && start->op != '*' && start->op != '&') |
1000 | 0 | isFuncDecl = false; |
1001 | 0 | if (!start->previous) |
1002 | 0 | break; |
1003 | 0 | if (start->previous->isOneOf(";{}:")) |
1004 | 0 | break; |
1005 | 0 | start = start->previous; |
1006 | 0 | } |
1007 | 0 | isFuncDecl &= start != ftok && start->name; |
1008 | 0 | if (isFuncDecl) { |
1009 | | // TODO: we could loop through the parameters here and check if they are correct. |
1010 | 0 | continue; |
1011 | 0 | } |
1012 | 0 | } |
1013 | 0 | } |
1014 | 976 | tok->setstr(tok->str() + "="); |
1015 | 976 | deleteToken(tok->next); |
1016 | 6.64k | } else if ((tok->op == '|' || tok->op == '&') && tok->op == tok->next->op) { |
1017 | 0 | tok->setstr(tok->str() + tok->next->str()); |
1018 | 0 | deleteToken(tok->next); |
1019 | 6.64k | } else if (tok->op == ':' && tok->next->op == ':') { |
1020 | 0 | tok->setstr(tok->str() + tok->next->str()); |
1021 | 0 | deleteToken(tok->next); |
1022 | 6.64k | } else if (tok->op == '-' && tok->next->op == '>') { |
1023 | 0 | tok->setstr(tok->str() + tok->next->str()); |
1024 | 0 | deleteToken(tok->next); |
1025 | 6.64k | } else if ((tok->op == '<' || tok->op == '>') && tok->op == tok->next->op) { |
1026 | 0 | tok->setstr(tok->str() + tok->next->str()); |
1027 | 0 | deleteToken(tok->next); |
1028 | 0 | if (tok->next && tok->next->op == '=' && tok->next->next && tok->next->next->op != '=') { |
1029 | 0 | tok->setstr(tok->str() + tok->next->str()); |
1030 | 0 | deleteToken(tok->next); |
1031 | 0 | } |
1032 | 6.64k | } else if ((tok->op == '+' || tok->op == '-') && tok->op == tok->next->op) { |
1033 | 1.45k | if (tok->location.col + 1U != tok->next->location.col) |
1034 | 0 | continue; |
1035 | 1.45k | if (tok->previous && tok->previous->number) |
1036 | 4 | continue; |
1037 | 1.44k | if (tok->next->next && tok->next->next->number) |
1038 | 0 | continue; |
1039 | 1.44k | tok->setstr(tok->str() + tok->next->str()); |
1040 | 1.44k | deleteToken(tok->next); |
1041 | 1.44k | } |
1042 | 7.62k | } |
1043 | 9.53k | } |
1044 | | |
1045 | | static const std::string COMPL("compl"); |
1046 | | static const std::string NOT("not"); |
1047 | | void simplecpp::TokenList::constFoldUnaryNotPosNeg(simplecpp::Token *tok) |
1048 | 0 | { |
1049 | 0 | for (; tok && tok->op != ')'; tok = tok->next) { |
1050 | | // "not" might be ! |
1051 | 0 | if (isAlternativeUnaryOp(tok, NOT)) |
1052 | 0 | tok->op = '!'; |
1053 | | // "compl" might be ~ |
1054 | 0 | else if (isAlternativeUnaryOp(tok, COMPL)) |
1055 | 0 | tok->op = '~'; |
1056 | |
|
1057 | 0 | if (tok->op == '!' && tok->next && tok->next->number) { |
1058 | 0 | tok->setstr(tok->next->str() == "0" ? "1" : "0"); |
1059 | 0 | deleteToken(tok->next); |
1060 | 0 | } else if (tok->op == '~' && tok->next && tok->next->number) { |
1061 | 0 | tok->setstr(toString(~stringToLL(tok->next->str()))); |
1062 | 0 | deleteToken(tok->next); |
1063 | 0 | } else { |
1064 | 0 | if (tok->previous && (tok->previous->number || tok->previous->name)) |
1065 | 0 | continue; |
1066 | 0 | if (!tok->next || !tok->next->number) |
1067 | 0 | continue; |
1068 | 0 | switch (tok->op) { |
1069 | 0 | case '+': |
1070 | 0 | tok->setstr(tok->next->str()); |
1071 | 0 | deleteToken(tok->next); |
1072 | 0 | break; |
1073 | 0 | case '-': |
1074 | 0 | tok->setstr(tok->op + tok->next->str()); |
1075 | 0 | deleteToken(tok->next); |
1076 | 0 | break; |
1077 | 0 | } |
1078 | 0 | } |
1079 | 0 | } |
1080 | 0 | } |
1081 | | |
1082 | | void simplecpp::TokenList::constFoldMulDivRem(Token *tok) |
1083 | 0 | { |
1084 | 0 | for (; tok && tok->op != ')'; tok = tok->next) { |
1085 | 0 | if (!tok->previous || !tok->previous->number) |
1086 | 0 | continue; |
1087 | 0 | if (!tok->next || !tok->next->number) |
1088 | 0 | continue; |
1089 | | |
1090 | 0 | long long result; |
1091 | 0 | if (tok->op == '*') |
1092 | 0 | result = (stringToLL(tok->previous->str()) * stringToLL(tok->next->str())); |
1093 | 0 | else if (tok->op == '/' || tok->op == '%') { |
1094 | 0 | const long long rhs = stringToLL(tok->next->str()); |
1095 | 0 | if (rhs == 0) |
1096 | 0 | throw std::overflow_error("division/modulo by zero"); |
1097 | 0 | const long long lhs = stringToLL(tok->previous->str()); |
1098 | 0 | if (rhs == -1 && lhs == std::numeric_limits<long long>::min()) |
1099 | 0 | throw std::overflow_error("division overflow"); |
1100 | 0 | if (tok->op == '/') |
1101 | 0 | result = (lhs / rhs); |
1102 | 0 | else |
1103 | 0 | result = (lhs % rhs); |
1104 | 0 | } else |
1105 | 0 | continue; |
1106 | | |
1107 | 0 | tok = tok->previous; |
1108 | 0 | tok->setstr(toString(result)); |
1109 | 0 | deleteToken(tok->next); |
1110 | 0 | deleteToken(tok->next); |
1111 | 0 | } |
1112 | 0 | } |
1113 | | |
1114 | | void simplecpp::TokenList::constFoldAddSub(Token *tok) |
1115 | 0 | { |
1116 | 0 | for (; tok && tok->op != ')'; tok = tok->next) { |
1117 | 0 | if (!tok->previous || !tok->previous->number) |
1118 | 0 | continue; |
1119 | 0 | if (!tok->next || !tok->next->number) |
1120 | 0 | continue; |
1121 | | |
1122 | 0 | long long result; |
1123 | 0 | if (tok->op == '+') |
1124 | 0 | result = stringToLL(tok->previous->str()) + stringToLL(tok->next->str()); |
1125 | 0 | else if (tok->op == '-') |
1126 | 0 | result = stringToLL(tok->previous->str()) - stringToLL(tok->next->str()); |
1127 | 0 | else |
1128 | 0 | continue; |
1129 | | |
1130 | 0 | tok = tok->previous; |
1131 | 0 | tok->setstr(toString(result)); |
1132 | 0 | deleteToken(tok->next); |
1133 | 0 | deleteToken(tok->next); |
1134 | 0 | } |
1135 | 0 | } |
1136 | | |
1137 | | void simplecpp::TokenList::constFoldShift(Token *tok) |
1138 | 0 | { |
1139 | 0 | for (; tok && tok->op != ')'; tok = tok->next) { |
1140 | 0 | if (!tok->previous || !tok->previous->number) |
1141 | 0 | continue; |
1142 | 0 | if (!tok->next || !tok->next->number) |
1143 | 0 | continue; |
1144 | | |
1145 | 0 | long long result; |
1146 | 0 | if (tok->str() == "<<") |
1147 | 0 | result = stringToLL(tok->previous->str()) << stringToLL(tok->next->str()); |
1148 | 0 | else if (tok->str() == ">>") |
1149 | 0 | result = stringToLL(tok->previous->str()) >> stringToLL(tok->next->str()); |
1150 | 0 | else |
1151 | 0 | continue; |
1152 | | |
1153 | 0 | tok = tok->previous; |
1154 | 0 | tok->setstr(toString(result)); |
1155 | 0 | deleteToken(tok->next); |
1156 | 0 | deleteToken(tok->next); |
1157 | 0 | } |
1158 | 0 | } |
1159 | | |
1160 | | static const std::string NOTEQ("not_eq"); |
1161 | | void simplecpp::TokenList::constFoldComparison(Token *tok) |
1162 | 0 | { |
1163 | 0 | for (; tok && tok->op != ')'; tok = tok->next) { |
1164 | 0 | if (isAlternativeBinaryOp(tok,NOTEQ)) |
1165 | 0 | tok->setstr("!="); |
1166 | |
|
1167 | 0 | if (!tok->startsWithOneOf("<>=!")) |
1168 | 0 | continue; |
1169 | 0 | if (!tok->previous || !tok->previous->number) |
1170 | 0 | continue; |
1171 | 0 | if (!tok->next || !tok->next->number) |
1172 | 0 | continue; |
1173 | | |
1174 | 0 | int result; |
1175 | 0 | if (tok->str() == "==") |
1176 | 0 | result = (stringToLL(tok->previous->str()) == stringToLL(tok->next->str())); |
1177 | 0 | else if (tok->str() == "!=") |
1178 | 0 | result = (stringToLL(tok->previous->str()) != stringToLL(tok->next->str())); |
1179 | 0 | else if (tok->str() == ">") |
1180 | 0 | result = (stringToLL(tok->previous->str()) > stringToLL(tok->next->str())); |
1181 | 0 | else if (tok->str() == ">=") |
1182 | 0 | result = (stringToLL(tok->previous->str()) >= stringToLL(tok->next->str())); |
1183 | 0 | else if (tok->str() == "<") |
1184 | 0 | result = (stringToLL(tok->previous->str()) < stringToLL(tok->next->str())); |
1185 | 0 | else if (tok->str() == "<=") |
1186 | 0 | result = (stringToLL(tok->previous->str()) <= stringToLL(tok->next->str())); |
1187 | 0 | else |
1188 | 0 | continue; |
1189 | | |
1190 | 0 | tok = tok->previous; |
1191 | 0 | tok->setstr(toString(result)); |
1192 | 0 | deleteToken(tok->next); |
1193 | 0 | deleteToken(tok->next); |
1194 | 0 | } |
1195 | 0 | } |
1196 | | |
1197 | | static const std::string BITAND("bitand"); |
1198 | | static const std::string BITOR("bitor"); |
1199 | | static const std::string XOR("xor"); |
1200 | | void simplecpp::TokenList::constFoldBitwise(Token *tok) |
1201 | 0 | { |
1202 | 0 | Token * const tok1 = tok; |
1203 | 0 | for (const char *op = "&^|"; *op; op++) { |
1204 | 0 | const std::string* alternativeOp; |
1205 | 0 | if (*op == '&') |
1206 | 0 | alternativeOp = &BITAND; |
1207 | 0 | else if (*op == '|') |
1208 | 0 | alternativeOp = &BITOR; |
1209 | 0 | else |
1210 | 0 | alternativeOp = &XOR; |
1211 | 0 | for (tok = tok1; tok && tok->op != ')'; tok = tok->next) { |
1212 | 0 | if (tok->op != *op && !isAlternativeBinaryOp(tok, *alternativeOp)) |
1213 | 0 | continue; |
1214 | 0 | if (!tok->previous || !tok->previous->number) |
1215 | 0 | continue; |
1216 | 0 | if (!tok->next || !tok->next->number) |
1217 | 0 | continue; |
1218 | 0 | long long result; |
1219 | 0 | if (*op == '&') |
1220 | 0 | result = (stringToLL(tok->previous->str()) & stringToLL(tok->next->str())); |
1221 | 0 | else if (*op == '^') |
1222 | 0 | result = (stringToLL(tok->previous->str()) ^ stringToLL(tok->next->str())); |
1223 | 0 | else /*if (*op == '|')*/ |
1224 | 0 | result = (stringToLL(tok->previous->str()) | stringToLL(tok->next->str())); |
1225 | 0 | tok = tok->previous; |
1226 | 0 | tok->setstr(toString(result)); |
1227 | 0 | deleteToken(tok->next); |
1228 | 0 | deleteToken(tok->next); |
1229 | 0 | } |
1230 | 0 | } |
1231 | 0 | } |
1232 | | |
1233 | | static const std::string AND("and"); |
1234 | | static const std::string OR("or"); |
1235 | | void simplecpp::TokenList::constFoldLogicalOp(Token *tok) |
1236 | 0 | { |
1237 | 0 | for (; tok && tok->op != ')'; tok = tok->next) { |
1238 | 0 | if (tok->name) { |
1239 | 0 | if (isAlternativeBinaryOp(tok,AND)) |
1240 | 0 | tok->setstr("&&"); |
1241 | 0 | else if (isAlternativeBinaryOp(tok,OR)) |
1242 | 0 | tok->setstr("||"); |
1243 | 0 | } |
1244 | 0 | if (tok->str() != "&&" && tok->str() != "||") |
1245 | 0 | continue; |
1246 | 0 | if (!tok->previous || !tok->previous->number) |
1247 | 0 | continue; |
1248 | 0 | if (!tok->next || !tok->next->number) |
1249 | 0 | continue; |
1250 | | |
1251 | 0 | int result; |
1252 | 0 | if (tok->str() == "||") |
1253 | 0 | result = (stringToLL(tok->previous->str()) || stringToLL(tok->next->str())); |
1254 | 0 | else /*if (tok->str() == "&&")*/ |
1255 | 0 | result = (stringToLL(tok->previous->str()) && stringToLL(tok->next->str())); |
1256 | |
|
1257 | 0 | tok = tok->previous; |
1258 | 0 | tok->setstr(toString(result)); |
1259 | 0 | deleteToken(tok->next); |
1260 | 0 | deleteToken(tok->next); |
1261 | 0 | } |
1262 | 0 | } |
1263 | | |
1264 | | void simplecpp::TokenList::constFoldQuestionOp(Token **tok1) |
1265 | 0 | { |
1266 | 0 | bool gotoTok1 = false; |
1267 | 0 | for (Token *tok = *tok1; tok && tok->op != ')'; tok = gotoTok1 ? *tok1 : tok->next) { |
1268 | 0 | gotoTok1 = false; |
1269 | 0 | if (tok->str() != "?") |
1270 | 0 | continue; |
1271 | 0 | if (!tok->previous || !tok->next || !tok->next->next) |
1272 | 0 | throw std::runtime_error("invalid expression"); |
1273 | 0 | if (!tok->previous->number) |
1274 | 0 | continue; |
1275 | 0 | if (tok->next->next->op != ':') |
1276 | 0 | continue; |
1277 | 0 | Token * const condTok = tok->previous; |
1278 | 0 | Token * const trueTok = tok->next; |
1279 | 0 | Token * const falseTok = trueTok->next->next; |
1280 | 0 | if (!falseTok) |
1281 | 0 | throw std::runtime_error("invalid expression"); |
1282 | 0 | if (condTok == *tok1) |
1283 | 0 | *tok1 = (condTok->str() != "0" ? trueTok : falseTok); |
1284 | 0 | deleteToken(condTok->next); // ? |
1285 | 0 | deleteToken(trueTok->next); // : |
1286 | 0 | deleteToken(condTok->str() == "0" ? trueTok : falseTok); |
1287 | 0 | deleteToken(condTok); |
1288 | 0 | gotoTok1 = true; |
1289 | 0 | } |
1290 | 0 | } |
1291 | | |
1292 | | void simplecpp::TokenList::removeComments() |
1293 | 2.72k | { |
1294 | 2.72k | Token *tok = frontToken; |
1295 | 163k | while (tok) { |
1296 | 160k | Token * const tok1 = tok; |
1297 | 160k | tok = tok->next; |
1298 | 160k | if (tok1->comment) |
1299 | 0 | deleteToken(tok1); |
1300 | 160k | } |
1301 | 2.72k | } |
1302 | | |
1303 | | std::string simplecpp::TokenList::readUntil(Stream &stream, const Location &location, const char start, const char end, OutputList *outputList) |
1304 | 2.72k | { |
1305 | 2.72k | std::string ret; |
1306 | 2.72k | ret += start; |
1307 | | |
1308 | 2.72k | bool backslash = false; |
1309 | 2.72k | char ch = 0; |
1310 | 31.3k | while (ch != end && ch != '\r' && ch != '\n' && stream.good()) { |
1311 | 28.6k | ch = stream.readChar(); |
1312 | 28.6k | if (backslash && ch == '\n') { |
1313 | 0 | ch = 0; |
1314 | 0 | backslash = false; |
1315 | 0 | continue; |
1316 | 0 | } |
1317 | 28.6k | backslash = false; |
1318 | 28.6k | ret += ch; |
1319 | 28.6k | if (ch == '\\') { |
1320 | 0 | bool update_ch = false; |
1321 | 0 | char next = 0; |
1322 | 0 | do { |
1323 | 0 | next = stream.readChar(); |
1324 | 0 | if (next == '\r' || next == '\n') { |
1325 | 0 | ret.erase(ret.size()-1U); |
1326 | 0 | backslash = (next == '\r'); |
1327 | 0 | update_ch = false; |
1328 | 0 | } else if (next == '\\') |
1329 | 0 | update_ch = !update_ch; |
1330 | 0 | ret += next; |
1331 | 0 | } while (next == '\\'); |
1332 | 0 | if (update_ch) |
1333 | 0 | ch = next; |
1334 | 0 | } |
1335 | 28.6k | } |
1336 | | |
1337 | 2.72k | if (!stream.good() || ch != end) { |
1338 | 0 | clear(); |
1339 | 0 | if (outputList) { |
1340 | 0 | Output err(files); |
1341 | 0 | err.type = Output::SYNTAX_ERROR; |
1342 | 0 | err.location = location; |
1343 | 0 | err.msg = std::string("No pair for character (") + start + "). Can't process file. File is either invalid or unicode, which is currently not supported."; |
1344 | 0 | outputList->push_back(err); |
1345 | 0 | } |
1346 | 0 | return ""; |
1347 | 0 | } |
1348 | | |
1349 | 2.72k | return ret; |
1350 | 2.72k | } |
1351 | | |
1352 | | std::string simplecpp::TokenList::lastLine(int maxsize) const |
1353 | 0 | { |
1354 | 0 | std::string ret; |
1355 | 0 | int count = 0; |
1356 | 0 | for (const Token *tok = cback(); ; tok = tok->previous) { |
1357 | 0 | if (!sameline(tok, cback())) { |
1358 | 0 | break; |
1359 | 0 | } |
1360 | 0 | if (tok->comment) |
1361 | 0 | continue; |
1362 | 0 | if (++count > maxsize) |
1363 | 0 | return ""; |
1364 | 0 | if (!ret.empty()) |
1365 | 0 | ret += ' '; |
1366 | | // add tokens in reverse for performance reasons |
1367 | 0 | if (tok->str()[0] == '\"') |
1368 | 0 | ret += "%rts%"; // %str% |
1369 | 0 | else if (tok->number) |
1370 | 0 | ret += "%mun%"; // %num% |
1371 | 0 | else { |
1372 | 0 | ret += tok->str(); |
1373 | 0 | std::reverse(ret.end() - tok->str().length(), ret.end()); |
1374 | 0 | } |
1375 | 0 | } |
1376 | 0 | std::reverse(ret.begin(), ret.end()); |
1377 | 0 | return ret; |
1378 | 0 | } |
1379 | | |
1380 | | bool simplecpp::TokenList::isLastLinePreprocessor(int maxsize) const |
1381 | 21.8k | { |
1382 | 21.8k | const Token* prevTok = nullptr; |
1383 | 21.8k | int count = 0; |
1384 | 109k | for (const Token *tok = cback(); ; tok = tok->previous) { |
1385 | 109k | if (!sameline(tok, cback())) |
1386 | 21.8k | break; |
1387 | 87.6k | if (tok->comment) |
1388 | 0 | continue; |
1389 | 87.6k | if (++count > maxsize) |
1390 | 0 | return false; |
1391 | 87.6k | prevTok = tok; |
1392 | 87.6k | } |
1393 | 21.8k | return prevTok && prevTok->str()[0] == '#'; |
1394 | 21.8k | } |
1395 | | |
1396 | | unsigned int simplecpp::TokenList::fileIndex(const std::string &filename) |
1397 | 9.53k | { |
1398 | 17.7k | for (unsigned int i = 0; i < files.size(); ++i) { |
1399 | 14.9k | if (files[i] == filename) |
1400 | 6.81k | return i; |
1401 | 14.9k | } |
1402 | 2.72k | files.push_back(filename); |
1403 | 2.72k | return files.size() - 1U; |
1404 | 9.53k | } |
1405 | | |
1406 | | |
1407 | | namespace simplecpp { |
1408 | | class Macro; |
1409 | | #if __cplusplus >= 201103L |
1410 | | using MacroMap = std::unordered_map<TokenString,Macro>; |
1411 | | #else |
1412 | | typedef std::map<TokenString,Macro> MacroMap; |
1413 | | #endif |
1414 | | |
1415 | | class Macro { |
1416 | | public: |
1417 | 0 | explicit Macro(std::vector<std::string> &f) : nameTokDef(nullptr), valueToken(nullptr), endToken(nullptr), files(f), tokenListDefine(f), variadic(false), valueDefinedInCode_(false) {} |
1418 | | |
1419 | 0 | Macro(const Token *tok, std::vector<std::string> &f) : nameTokDef(nullptr), files(f), tokenListDefine(f), valueDefinedInCode_(true) { |
1420 | 0 | if (sameline(tok->previousSkipComments(), tok)) |
1421 | 0 | throw std::runtime_error("bad macro syntax"); |
1422 | 0 | if (tok->op != '#') |
1423 | 0 | throw std::runtime_error("bad macro syntax"); |
1424 | 0 | const Token * const hashtok = tok; |
1425 | 0 | tok = tok->next; |
1426 | 0 | if (!tok || tok->str() != DEFINE) |
1427 | 0 | throw std::runtime_error("bad macro syntax"); |
1428 | 0 | tok = tok->next; |
1429 | 0 | if (!tok || !tok->name || !sameline(hashtok,tok)) |
1430 | 0 | throw std::runtime_error("bad macro syntax"); |
1431 | 0 | if (!parseDefine(tok)) |
1432 | 0 | throw std::runtime_error("bad macro syntax"); |
1433 | 0 | } |
1434 | | |
1435 | 8.17k | Macro(const std::string &name, const std::string &value, std::vector<std::string> &f) : nameTokDef(nullptr), files(f), tokenListDefine(f), valueDefinedInCode_(false) { |
1436 | 8.17k | const std::string def(name + ' ' + value); |
1437 | 8.17k | std::istringstream istr(def); |
1438 | 8.17k | StdIStream stream(istr); |
1439 | 8.17k | tokenListDefine.readfile(stream); |
1440 | 8.17k | if (!parseDefine(tokenListDefine.cfront())) |
1441 | 0 | throw std::runtime_error("bad macro syntax. macroname=" + name + " value=" + value); |
1442 | 8.17k | } |
1443 | | |
1444 | 16.3k | Macro(const Macro &other) : nameTokDef(nullptr), files(other.files), tokenListDefine(other.files), valueDefinedInCode_(other.valueDefinedInCode_) { |
1445 | 16.3k | *this = other; |
1446 | 16.3k | } |
1447 | | |
1448 | 16.3k | Macro &operator=(const Macro &other) { |
1449 | 16.3k | if (this != &other) { |
1450 | 16.3k | files = other.files; |
1451 | 16.3k | valueDefinedInCode_ = other.valueDefinedInCode_; |
1452 | 16.3k | if (other.tokenListDefine.empty()) |
1453 | 0 | parseDefine(other.nameTokDef); |
1454 | 16.3k | else { |
1455 | 16.3k | tokenListDefine = other.tokenListDefine; |
1456 | 16.3k | parseDefine(tokenListDefine.cfront()); |
1457 | 16.3k | } |
1458 | 16.3k | usageList = other.usageList; |
1459 | 16.3k | } |
1460 | 16.3k | return *this; |
1461 | 16.3k | } |
1462 | | |
1463 | 0 | bool valueDefinedInCode() const { |
1464 | 0 | return valueDefinedInCode_; |
1465 | 0 | } |
1466 | | |
1467 | | /** |
1468 | | * Expand macro. This will recursively expand inner macros. |
1469 | | * @param output destination tokenlist |
1470 | | * @param rawtok macro token |
1471 | | * @param macros list of macros |
1472 | | * @param inputFiles the input files |
1473 | | * @return token after macro |
1474 | | * @throw Can throw wrongNumberOfParameters or invalidHashHash |
1475 | | */ |
1476 | | const Token * expand(TokenList * const output, |
1477 | | const Token * rawtok, |
1478 | | const MacroMap ¯os, |
1479 | 0 | std::vector<std::string> &inputFiles) const { |
1480 | 0 | std::set<TokenString> expandedmacros; |
1481 | |
|
1482 | 0 | TokenList output2(inputFiles); |
1483 | |
|
1484 | 0 | if (functionLike() && rawtok->next && rawtok->next->op == '(') { |
1485 | | // Copy macro call to a new tokenlist with no linebreaks |
1486 | 0 | const Token * const rawtok1 = rawtok; |
1487 | 0 | TokenList rawtokens2(inputFiles); |
1488 | 0 | rawtokens2.push_back(new Token(rawtok->str(), rawtok1->location)); |
1489 | 0 | rawtok = rawtok->next; |
1490 | 0 | rawtokens2.push_back(new Token(rawtok->str(), rawtok1->location)); |
1491 | 0 | rawtok = rawtok->next; |
1492 | 0 | int par = 1; |
1493 | 0 | while (rawtok && par > 0) { |
1494 | 0 | if (rawtok->op == '(') |
1495 | 0 | ++par; |
1496 | 0 | else if (rawtok->op == ')') |
1497 | 0 | --par; |
1498 | 0 | else if (rawtok->op == '#' && !sameline(rawtok->previous, rawtok)) |
1499 | 0 | throw Error(rawtok->location, "it is invalid to use a preprocessor directive as macro parameter"); |
1500 | 0 | rawtokens2.push_back(new Token(rawtok->str(), rawtok1->location)); |
1501 | 0 | rawtok = rawtok->next; |
1502 | 0 | } |
1503 | 0 | bool first = true; |
1504 | 0 | if (valueToken && valueToken->str() == rawtok1->str()) |
1505 | 0 | first = false; |
1506 | 0 | if (expand(&output2, rawtok1->location, rawtokens2.cfront(), macros, expandedmacros, first)) |
1507 | 0 | rawtok = rawtok1->next; |
1508 | 0 | } else { |
1509 | 0 | rawtok = expand(&output2, rawtok->location, rawtok, macros, expandedmacros); |
1510 | 0 | } |
1511 | 0 | while (output2.cback() && rawtok) { |
1512 | 0 | unsigned int par = 0; |
1513 | 0 | Token* macro2tok = output2.back(); |
1514 | 0 | while (macro2tok) { |
1515 | 0 | if (macro2tok->op == '(') { |
1516 | 0 | if (par==0) |
1517 | 0 | break; |
1518 | 0 | --par; |
1519 | 0 | } else if (macro2tok->op == ')') |
1520 | 0 | ++par; |
1521 | 0 | macro2tok = macro2tok->previous; |
1522 | 0 | } |
1523 | 0 | if (macro2tok) { // macro2tok->op == '(' |
1524 | 0 | macro2tok = macro2tok->previous; |
1525 | 0 | expandedmacros.insert(name()); |
1526 | 0 | } else if (rawtok->op == '(') |
1527 | 0 | macro2tok = output2.back(); |
1528 | 0 | if (!macro2tok || !macro2tok->name) |
1529 | 0 | break; |
1530 | 0 | if (output2.cfront() != output2.cback() && macro2tok->str() == this->name()) |
1531 | 0 | break; |
1532 | 0 | const MacroMap::const_iterator macro = macros.find(macro2tok->str()); |
1533 | 0 | if (macro == macros.end() || !macro->second.functionLike()) |
1534 | 0 | break; |
1535 | 0 | TokenList rawtokens2(inputFiles); |
1536 | 0 | const Location loc(macro2tok->location); |
1537 | 0 | while (macro2tok) { |
1538 | 0 | Token * const next = macro2tok->next; |
1539 | 0 | rawtokens2.push_back(new Token(macro2tok->str(), loc)); |
1540 | 0 | output2.deleteToken(macro2tok); |
1541 | 0 | macro2tok = next; |
1542 | 0 | } |
1543 | 0 | par = (rawtokens2.cfront() != rawtokens2.cback()) ? 1U : 0U; |
1544 | 0 | const Token *rawtok2 = rawtok; |
1545 | 0 | for (; rawtok2; rawtok2 = rawtok2->next) { |
1546 | 0 | rawtokens2.push_back(new Token(rawtok2->str(), loc)); |
1547 | 0 | if (rawtok2->op == '(') |
1548 | 0 | ++par; |
1549 | 0 | else if (rawtok2->op == ')') { |
1550 | 0 | if (par <= 1U) |
1551 | 0 | break; |
1552 | 0 | --par; |
1553 | 0 | } |
1554 | 0 | } |
1555 | 0 | if (!rawtok2 || par != 1U) |
1556 | 0 | break; |
1557 | 0 | if (macro->second.expand(&output2, rawtok->location, rawtokens2.cfront(), macros, expandedmacros) != nullptr) |
1558 | 0 | break; |
1559 | 0 | rawtok = rawtok2->next; |
1560 | 0 | } |
1561 | 0 | output->takeTokens(output2); |
1562 | 0 | return rawtok; |
1563 | 0 | } |
1564 | | |
1565 | | /** macro name */ |
1566 | 8.17k | const TokenString &name() const { |
1567 | 8.17k | return nameTokDef->str(); |
1568 | 8.17k | } |
1569 | | |
1570 | | /** location for macro definition */ |
1571 | 0 | const Location &defineLocation() const { |
1572 | 0 | return nameTokDef->location; |
1573 | 0 | } |
1574 | | |
1575 | | /** how has this macro been used so far */ |
1576 | 8.17k | const std::list<Location> &usage() const { |
1577 | 8.17k | return usageList; |
1578 | 8.17k | } |
1579 | | |
1580 | | /** is this a function like macro */ |
1581 | 24.5k | bool functionLike() const { |
1582 | 24.5k | return nameTokDef->next && |
1583 | 24.5k | nameTokDef->next->op == '(' && |
1584 | 24.5k | sameline(nameTokDef, nameTokDef->next) && |
1585 | 24.5k | nameTokDef->next->location.col == nameTokDef->location.col + nameTokDef->str().size(); |
1586 | 24.5k | } |
1587 | | |
1588 | | /** base class for errors */ |
1589 | | struct Error { |
1590 | 0 | Error(const Location &loc, const std::string &s) : location(loc), what(s) {} |
1591 | | const Location location; |
1592 | | const std::string what; |
1593 | | }; |
1594 | | |
1595 | | /** Struct that is thrown when macro is expanded with wrong number of parameters */ |
1596 | | struct wrongNumberOfParameters : public Error { |
1597 | 0 | wrongNumberOfParameters(const Location &loc, const std::string ¯oName) : Error(loc, "Wrong number of parameters for macro \'" + macroName + "\'.") {} |
1598 | | }; |
1599 | | |
1600 | | /** Struct that is thrown when there is invalid ## usage */ |
1601 | | struct invalidHashHash : public Error { |
1602 | 0 | static inline std::string format(const std::string ¯oName, const std::string &message) { |
1603 | 0 | return "Invalid ## usage when expanding \'" + macroName + "\': " + message; |
1604 | 0 | } |
1605 | | |
1606 | | invalidHashHash(const Location &loc, const std::string ¯oName, const std::string &message) |
1607 | 0 | : Error(loc, format(macroName, message)) { } |
1608 | | |
1609 | 0 | static inline invalidHashHash unexpectedToken(const Location &loc, const std::string ¯oName, const Token *tokenA) { |
1610 | 0 | return invalidHashHash(loc, macroName, "Unexpected token '"+ tokenA->str()+"'"); |
1611 | 0 | } |
1612 | | |
1613 | 0 | static inline invalidHashHash cannotCombine(const Location &loc, const std::string ¯oName, const Token *tokenA, const Token *tokenB) { |
1614 | 0 | return invalidHashHash(loc, macroName, "Combining '"+ tokenA->str()+ "' and '"+ tokenB->str() + "' yields an invalid token."); |
1615 | 0 | } |
1616 | | |
1617 | 0 | static inline invalidHashHash unexpectedNewline(const Location &loc, const std::string ¯oName) { |
1618 | 0 | return invalidHashHash(loc, macroName, "Unexpected newline"); |
1619 | 0 | } |
1620 | | |
1621 | 0 | static inline invalidHashHash universalCharacterUB(const Location &loc, const std::string ¯oName, const Token* tokenA, const std::string& strAB) { |
1622 | 0 | return invalidHashHash(loc, macroName, "Combining '\\"+ tokenA->str()+ "' and '"+ strAB.substr(tokenA->str().size()) + "' yields universal character '\\" + strAB + "'. This is undefined behavior according to C standard chapter 5.1.1.2, paragraph 4."); |
1623 | 0 | } |
1624 | | }; |
1625 | | private: |
1626 | | /** Create new token where Token::macro is set for replaced tokens */ |
1627 | 0 | Token *newMacroToken(const TokenString &str, const Location &loc, bool replaced, const Token *expandedFromToken=nullptr) const { |
1628 | 0 | Token *tok = new Token(str,loc); |
1629 | 0 | if (replaced) |
1630 | 0 | tok->macro = nameTokDef->str(); |
1631 | 0 | if (expandedFromToken) |
1632 | 0 | tok->setExpandedFrom(expandedFromToken, this); |
1633 | 0 | return tok; |
1634 | 0 | } |
1635 | | |
1636 | 24.5k | bool parseDefine(const Token *nametoken) { |
1637 | 24.5k | nameTokDef = nametoken; |
1638 | 24.5k | variadic = false; |
1639 | 24.5k | if (!nameTokDef) { |
1640 | 0 | valueToken = endToken = nullptr; |
1641 | 0 | args.clear(); |
1642 | 0 | return false; |
1643 | 0 | } |
1644 | | |
1645 | | // function like macro.. |
1646 | 24.5k | if (functionLike()) { |
1647 | 0 | args.clear(); |
1648 | 0 | const Token *argtok = nameTokDef->next->next; |
1649 | 0 | while (sameline(nametoken, argtok) && argtok->op != ')') { |
1650 | 0 | if (argtok->str() == "..." && |
1651 | 0 | argtok->next && argtok->next->op == ')') { |
1652 | 0 | variadic = true; |
1653 | 0 | if (!argtok->previous->name) |
1654 | 0 | args.push_back("__VA_ARGS__"); |
1655 | 0 | argtok = argtok->next; // goto ')' |
1656 | 0 | break; |
1657 | 0 | } |
1658 | 0 | if (argtok->op != ',') |
1659 | 0 | args.push_back(argtok->str()); |
1660 | 0 | argtok = argtok->next; |
1661 | 0 | } |
1662 | 0 | if (!sameline(nametoken, argtok)) { |
1663 | 0 | endToken = argtok ? argtok->previous : argtok; |
1664 | 0 | valueToken = nullptr; |
1665 | 0 | return false; |
1666 | 0 | } |
1667 | 0 | valueToken = argtok ? argtok->next : nullptr; |
1668 | 24.5k | } else { |
1669 | 24.5k | args.clear(); |
1670 | 24.5k | valueToken = nameTokDef->next; |
1671 | 24.5k | } |
1672 | | |
1673 | 24.5k | if (!sameline(valueToken, nameTokDef)) |
1674 | 0 | valueToken = nullptr; |
1675 | 24.5k | endToken = valueToken; |
1676 | 49.0k | while (sameline(endToken, nameTokDef)) |
1677 | 24.5k | endToken = endToken->next; |
1678 | 24.5k | return true; |
1679 | 24.5k | } |
1680 | | |
1681 | 0 | unsigned int getArgNum(const TokenString &str) const { |
1682 | 0 | unsigned int par = 0; |
1683 | 0 | while (par < args.size()) { |
1684 | 0 | if (str == args[par]) |
1685 | 0 | return par; |
1686 | 0 | par++; |
1687 | 0 | } |
1688 | 0 | return ~0U; |
1689 | 0 | } |
1690 | | |
1691 | 0 | std::vector<const Token *> getMacroParameters(const Token *nameTokInst, bool calledInDefine) const { |
1692 | 0 | if (!nameTokInst->next || nameTokInst->next->op != '(' || !functionLike()) |
1693 | 0 | return std::vector<const Token *>(); |
1694 | | |
1695 | 0 | std::vector<const Token *> parametertokens; |
1696 | 0 | parametertokens.push_back(nameTokInst->next); |
1697 | 0 | unsigned int par = 0U; |
1698 | 0 | for (const Token *tok = nameTokInst->next->next; calledInDefine ? sameline(tok, nameTokInst) : (tok != nullptr); tok = tok->next) { |
1699 | 0 | if (tok->op == '(') |
1700 | 0 | ++par; |
1701 | 0 | else if (tok->op == ')') { |
1702 | 0 | if (par == 0U) { |
1703 | 0 | parametertokens.push_back(tok); |
1704 | 0 | break; |
1705 | 0 | } |
1706 | 0 | --par; |
1707 | 0 | } else if (par == 0U && tok->op == ',' && (!variadic || parametertokens.size() < args.size())) |
1708 | 0 | parametertokens.push_back(tok); |
1709 | 0 | } |
1710 | 0 | return parametertokens; |
1711 | 0 | } |
1712 | | |
1713 | | const Token *appendTokens(TokenList *tokens, |
1714 | | const Location &rawloc, |
1715 | | const Token * const lpar, |
1716 | | const MacroMap ¯os, |
1717 | | const std::set<TokenString> &expandedmacros, |
1718 | 0 | const std::vector<const Token*> ¶metertokens) const { |
1719 | 0 | if (!lpar || lpar->op != '(') |
1720 | 0 | return nullptr; |
1721 | 0 | unsigned int par = 0; |
1722 | 0 | const Token *tok = lpar; |
1723 | 0 | while (sameline(lpar, tok)) { |
1724 | 0 | if (tok->op == '#' && sameline(tok,tok->next) && tok->next->op == '#' && sameline(tok,tok->next->next)) { |
1725 | | // A##B => AB |
1726 | 0 | tok = expandHashHash(tokens, rawloc, tok, macros, expandedmacros, parametertokens); |
1727 | 0 | } else if (tok->op == '#' && sameline(tok, tok->next) && tok->next->op != '#') { |
1728 | 0 | tok = expandHash(tokens, rawloc, tok, macros, expandedmacros, parametertokens); |
1729 | 0 | } else { |
1730 | 0 | if (!expandArg(tokens, tok, rawloc, macros, expandedmacros, parametertokens)) { |
1731 | 0 | bool expanded = false; |
1732 | 0 | const MacroMap::const_iterator it = macros.find(tok->str()); |
1733 | 0 | if (it != macros.end() && expandedmacros.find(tok->str()) == expandedmacros.end()) { |
1734 | 0 | const Macro &m = it->second; |
1735 | 0 | if (!m.functionLike()) { |
1736 | 0 | m.expand(tokens, rawloc, tok, macros, expandedmacros); |
1737 | 0 | expanded = true; |
1738 | 0 | } |
1739 | 0 | } |
1740 | 0 | if (!expanded) { |
1741 | 0 | tokens->push_back(new Token(*tok)); |
1742 | 0 | if (tok->macro.empty() && (par > 0 || tok->str() != "(")) |
1743 | 0 | tokens->back()->macro = name(); |
1744 | 0 | } |
1745 | 0 | } |
1746 | |
|
1747 | 0 | if (tok->op == '(') |
1748 | 0 | ++par; |
1749 | 0 | else if (tok->op == ')') { |
1750 | 0 | --par; |
1751 | 0 | if (par == 0U) |
1752 | 0 | break; |
1753 | 0 | } |
1754 | 0 | tok = tok->next; |
1755 | 0 | } |
1756 | 0 | } |
1757 | 0 | for (Token *tok2 = tokens->front(); tok2; tok2 = tok2->next) |
1758 | 0 | tok2->location = lpar->location; |
1759 | 0 | return sameline(lpar,tok) ? tok : nullptr; |
1760 | 0 | } |
1761 | | |
1762 | 0 | const Token * expand(TokenList * const output, const Location &loc, const Token * const nameTokInst, const MacroMap ¯os, std::set<TokenString> expandedmacros, bool first=false) const { |
1763 | |
|
1764 | 0 | if (!first) |
1765 | 0 | expandedmacros.insert(nameTokInst->str()); |
1766 | |
|
1767 | 0 | usageList.push_back(loc); |
1768 | |
|
1769 | 0 | if (nameTokInst->str() == "__FILE__") { |
1770 | 0 | output->push_back(new Token('\"'+loc.file()+'\"', loc)); |
1771 | 0 | return nameTokInst->next; |
1772 | 0 | } |
1773 | 0 | if (nameTokInst->str() == "__LINE__") { |
1774 | 0 | output->push_back(new Token(toString(loc.line), loc)); |
1775 | 0 | return nameTokInst->next; |
1776 | 0 | } |
1777 | 0 | if (nameTokInst->str() == "__COUNTER__") { |
1778 | 0 | output->push_back(new Token(toString(usageList.size()-1U), loc)); |
1779 | 0 | return nameTokInst->next; |
1780 | 0 | } |
1781 | | |
1782 | 0 | const bool calledInDefine = (loc.fileIndex != nameTokInst->location.fileIndex || |
1783 | 0 | loc.line < nameTokInst->location.line); |
1784 | |
|
1785 | 0 | std::vector<const Token*> parametertokens1(getMacroParameters(nameTokInst, calledInDefine)); |
1786 | |
|
1787 | 0 | if (functionLike()) { |
1788 | | // No arguments => not macro expansion |
1789 | 0 | if (nameTokInst->next && nameTokInst->next->op != '(') { |
1790 | 0 | output->push_back(new Token(nameTokInst->str(), loc)); |
1791 | 0 | return nameTokInst->next; |
1792 | 0 | } |
1793 | | |
1794 | | // Parse macro-call |
1795 | 0 | if (variadic) { |
1796 | 0 | if (parametertokens1.size() < args.size()) { |
1797 | 0 | throw wrongNumberOfParameters(nameTokInst->location, name()); |
1798 | 0 | } |
1799 | 0 | } else { |
1800 | 0 | if (parametertokens1.size() != args.size() + (args.empty() ? 2U : 1U)) |
1801 | 0 | throw wrongNumberOfParameters(nameTokInst->location, name()); |
1802 | 0 | } |
1803 | 0 | } |
1804 | | |
1805 | | // If macro call uses __COUNTER__ then expand that first |
1806 | 0 | TokenList tokensparams(files); |
1807 | 0 | std::vector<const Token *> parametertokens2; |
1808 | 0 | if (!parametertokens1.empty()) { |
1809 | 0 | bool counter = false; |
1810 | 0 | for (const Token *tok = parametertokens1[0]; tok != parametertokens1.back(); tok = tok->next) { |
1811 | 0 | if (tok->str() == "__COUNTER__") { |
1812 | 0 | counter = true; |
1813 | 0 | break; |
1814 | 0 | } |
1815 | 0 | } |
1816 | |
|
1817 | 0 | const MacroMap::const_iterator m = macros.find("__COUNTER__"); |
1818 | |
|
1819 | 0 | if (!counter || m == macros.end()) |
1820 | 0 | parametertokens2.swap(parametertokens1); |
1821 | 0 | else { |
1822 | 0 | const Macro &counterMacro = m->second; |
1823 | 0 | unsigned int par = 0; |
1824 | 0 | for (const Token *tok = parametertokens1[0]; tok && par < parametertokens1.size(); tok = tok->next) { |
1825 | 0 | if (tok->str() == "__COUNTER__") { |
1826 | 0 | tokensparams.push_back(new Token(toString(counterMacro.usageList.size()), tok->location)); |
1827 | 0 | counterMacro.usageList.push_back(tok->location); |
1828 | 0 | } else { |
1829 | 0 | tokensparams.push_back(new Token(*tok)); |
1830 | 0 | if (tok == parametertokens1[par]) { |
1831 | 0 | parametertokens2.push_back(tokensparams.cback()); |
1832 | 0 | par++; |
1833 | 0 | } |
1834 | 0 | } |
1835 | 0 | } |
1836 | 0 | } |
1837 | 0 | } |
1838 | |
|
1839 | 0 | Token * const output_end_1 = output->back(); |
1840 | | |
1841 | | // expand |
1842 | 0 | for (const Token *tok = valueToken; tok != endToken;) { |
1843 | 0 | if (tok->op != '#') { |
1844 | | // A##B => AB |
1845 | 0 | if (sameline(tok, tok->next) && tok->next && tok->next->op == '#' && tok->next->next && tok->next->next->op == '#') { |
1846 | 0 | if (!sameline(tok, tok->next->next->next)) |
1847 | 0 | throw invalidHashHash::unexpectedNewline(tok->location, name()); |
1848 | 0 | TokenList new_output(files); |
1849 | 0 | if (!expandArg(&new_output, tok, parametertokens2)) |
1850 | 0 | output->push_back(newMacroToken(tok->str(), loc, isReplaced(expandedmacros), tok)); |
1851 | 0 | else if (new_output.empty()) // placemarker token |
1852 | 0 | output->push_back(newMacroToken("", loc, isReplaced(expandedmacros))); |
1853 | 0 | else |
1854 | 0 | for (const Token *tok2 = new_output.cfront(); tok2; tok2 = tok2->next) |
1855 | 0 | output->push_back(newMacroToken(tok2->str(), loc, isReplaced(expandedmacros), tok2)); |
1856 | 0 | tok = tok->next; |
1857 | 0 | } else { |
1858 | 0 | tok = expandToken(output, loc, tok, macros, expandedmacros, parametertokens2); |
1859 | 0 | } |
1860 | 0 | continue; |
1861 | 0 | } |
1862 | | |
1863 | 0 | int numberOfHash = 1; |
1864 | 0 | const Token *hashToken = tok->next; |
1865 | 0 | while (sameline(tok,hashToken) && hashToken->op == '#') { |
1866 | 0 | hashToken = hashToken->next; |
1867 | 0 | ++numberOfHash; |
1868 | 0 | } |
1869 | 0 | if (numberOfHash == 4 && tok->next->location.col + 1 == tok->next->next->location.col) { |
1870 | | // # ## # => ## |
1871 | 0 | output->push_back(newMacroToken("##", loc, isReplaced(expandedmacros))); |
1872 | 0 | tok = hashToken; |
1873 | 0 | continue; |
1874 | 0 | } |
1875 | | |
1876 | 0 | if (numberOfHash >= 2 && tok->location.col + 1 < tok->next->location.col) { |
1877 | 0 | output->push_back(new Token(*tok)); |
1878 | 0 | tok = tok->next; |
1879 | 0 | continue; |
1880 | 0 | } |
1881 | | |
1882 | 0 | tok = tok->next; |
1883 | 0 | if (tok == endToken) { |
1884 | 0 | output->push_back(new Token(*tok->previous)); |
1885 | 0 | break; |
1886 | 0 | } |
1887 | 0 | if (tok->op == '#') { |
1888 | | // A##B => AB |
1889 | 0 | tok = expandHashHash(output, loc, tok->previous, macros, expandedmacros, parametertokens2); |
1890 | 0 | } else { |
1891 | | // #123 => "123" |
1892 | 0 | tok = expandHash(output, loc, tok->previous, macros, expandedmacros, parametertokens2); |
1893 | 0 | } |
1894 | 0 | } |
1895 | | |
1896 | 0 | if (!functionLike()) { |
1897 | 0 | for (Token *tok = output_end_1 ? output_end_1->next : output->front(); tok; tok = tok->next) { |
1898 | 0 | tok->macro = nameTokInst->str(); |
1899 | 0 | } |
1900 | 0 | } |
1901 | |
|
1902 | 0 | if (!parametertokens1.empty()) |
1903 | 0 | parametertokens1.swap(parametertokens2); |
1904 | |
|
1905 | 0 | return functionLike() ? parametertokens2.back()->next : nameTokInst->next; |
1906 | 0 | } |
1907 | | |
1908 | 0 | const Token *recursiveExpandToken(TokenList *output, TokenList &temp, const Location &loc, const Token *tok, const MacroMap ¯os, const std::set<TokenString> &expandedmacros, const std::vector<const Token*> ¶metertokens) const { |
1909 | 0 | if (!(temp.cback() && temp.cback()->name && tok->next && tok->next->op == '(')) { |
1910 | 0 | output->takeTokens(temp); |
1911 | 0 | return tok->next; |
1912 | 0 | } |
1913 | | |
1914 | 0 | if (!sameline(tok, tok->next)) { |
1915 | 0 | output->takeTokens(temp); |
1916 | 0 | return tok->next; |
1917 | 0 | } |
1918 | | |
1919 | 0 | const MacroMap::const_iterator it = macros.find(temp.cback()->str()); |
1920 | 0 | if (it == macros.end() || expandedmacros.find(temp.cback()->str()) != expandedmacros.end()) { |
1921 | 0 | output->takeTokens(temp); |
1922 | 0 | return tok->next; |
1923 | 0 | } |
1924 | | |
1925 | 0 | const Macro &calledMacro = it->second; |
1926 | 0 | if (!calledMacro.functionLike()) { |
1927 | 0 | output->takeTokens(temp); |
1928 | 0 | return tok->next; |
1929 | 0 | } |
1930 | | |
1931 | 0 | TokenList temp2(files); |
1932 | 0 | temp2.push_back(new Token(temp.cback()->str(), tok->location)); |
1933 | |
|
1934 | 0 | const Token * const tok2 = appendTokens(&temp2, loc, tok->next, macros, expandedmacros, parametertokens); |
1935 | 0 | if (!tok2) |
1936 | 0 | return tok->next; |
1937 | 0 | output->takeTokens(temp); |
1938 | 0 | output->deleteToken(output->back()); |
1939 | 0 | calledMacro.expand(output, loc, temp2.cfront(), macros, expandedmacros); |
1940 | 0 | return tok2->next; |
1941 | 0 | } |
1942 | | |
1943 | 0 | const Token *expandToken(TokenList *output, const Location &loc, const Token *tok, const MacroMap ¯os, const std::set<TokenString> &expandedmacros, const std::vector<const Token*> ¶metertokens) const { |
1944 | | // Not name.. |
1945 | 0 | if (!tok->name) { |
1946 | 0 | output->push_back(newMacroToken(tok->str(), loc, true, tok)); |
1947 | 0 | return tok->next; |
1948 | 0 | } |
1949 | | |
1950 | | // Macro parameter.. |
1951 | 0 | { |
1952 | 0 | TokenList temp(files); |
1953 | 0 | if (expandArg(&temp, tok, loc, macros, expandedmacros, parametertokens)) { |
1954 | 0 | if (tok->str() == "__VA_ARGS__" && temp.empty() && output->cback() && output->cback()->str() == "," && |
1955 | 0 | tok->nextSkipComments() && tok->nextSkipComments()->str() == ")") |
1956 | 0 | output->deleteToken(output->back()); |
1957 | 0 | return recursiveExpandToken(output, temp, loc, tok, macros, expandedmacros, parametertokens); |
1958 | 0 | } |
1959 | 0 | } |
1960 | | |
1961 | | // Macro.. |
1962 | 0 | const MacroMap::const_iterator it = macros.find(tok->str()); |
1963 | 0 | if (it != macros.end() && expandedmacros.find(tok->str()) == expandedmacros.end()) { |
1964 | 0 | std::set<std::string> expandedmacros2(expandedmacros); |
1965 | 0 | expandedmacros2.insert(tok->str()); |
1966 | |
|
1967 | 0 | const Macro &calledMacro = it->second; |
1968 | 0 | if (!calledMacro.functionLike()) { |
1969 | 0 | TokenList temp(files); |
1970 | 0 | calledMacro.expand(&temp, loc, tok, macros, expandedmacros); |
1971 | 0 | return recursiveExpandToken(output, temp, loc, tok, macros, expandedmacros2, parametertokens); |
1972 | 0 | } |
1973 | 0 | if (!sameline(tok, tok->next) || tok->next->op != '(') { |
1974 | 0 | output->push_back(newMacroToken(tok->str(), loc, true, tok)); |
1975 | 0 | return tok->next; |
1976 | 0 | } |
1977 | 0 | TokenList tokens(files); |
1978 | 0 | tokens.push_back(new Token(*tok)); |
1979 | 0 | const Token * const tok2 = appendTokens(&tokens, loc, tok->next, macros, expandedmacros, parametertokens); |
1980 | 0 | if (!tok2) { |
1981 | 0 | output->push_back(newMacroToken(tok->str(), loc, true, tok)); |
1982 | 0 | return tok->next; |
1983 | 0 | } |
1984 | 0 | TokenList temp(files); |
1985 | 0 | calledMacro.expand(&temp, loc, tokens.cfront(), macros, expandedmacros); |
1986 | 0 | return recursiveExpandToken(output, temp, loc, tok2, macros, expandedmacros2, parametertokens); |
1987 | 0 | } |
1988 | | |
1989 | 0 | if (tok->str() == DEFINED) { |
1990 | 0 | const Token * const tok2 = tok->next; |
1991 | 0 | const Token * const tok3 = tok2 ? tok2->next : nullptr; |
1992 | 0 | const Token * const tok4 = tok3 ? tok3->next : nullptr; |
1993 | 0 | const Token *defToken = nullptr; |
1994 | 0 | const Token *lastToken = nullptr; |
1995 | 0 | if (sameline(tok, tok4) && tok2->op == '(' && tok3->name && tok4->op == ')') { |
1996 | 0 | defToken = tok3; |
1997 | 0 | lastToken = tok4; |
1998 | 0 | } else if (sameline(tok,tok2) && tok2->name) { |
1999 | 0 | defToken = lastToken = tok2; |
2000 | 0 | } |
2001 | 0 | if (defToken) { |
2002 | 0 | std::string macroName = defToken->str(); |
2003 | 0 | if (defToken->next && defToken->next->op == '#' && defToken->next->next && defToken->next->next->op == '#' && defToken->next->next->next && defToken->next->next->next->name && sameline(defToken,defToken->next->next->next)) { |
2004 | 0 | TokenList temp(files); |
2005 | 0 | if (expandArg(&temp, defToken, parametertokens)) |
2006 | 0 | macroName = temp.cback()->str(); |
2007 | 0 | if (expandArg(&temp, defToken->next->next->next, parametertokens)) |
2008 | 0 | macroName += temp.cback()->str(); |
2009 | 0 | else |
2010 | 0 | macroName += defToken->next->next->next->str(); |
2011 | 0 | lastToken = defToken->next->next->next; |
2012 | 0 | } |
2013 | 0 | const bool def = (macros.find(macroName) != macros.end()); |
2014 | 0 | output->push_back(newMacroToken(def ? "1" : "0", loc, true)); |
2015 | 0 | return lastToken->next; |
2016 | 0 | } |
2017 | 0 | } |
2018 | | |
2019 | 0 | output->push_back(newMacroToken(tok->str(), loc, true, tok)); |
2020 | 0 | return tok->next; |
2021 | 0 | } |
2022 | | |
2023 | 0 | bool expandArg(TokenList *output, const Token *tok, const std::vector<const Token*> ¶metertokens) const { |
2024 | 0 | if (!tok->name) |
2025 | 0 | return false; |
2026 | | |
2027 | 0 | const unsigned int argnr = getArgNum(tok->str()); |
2028 | 0 | if (argnr >= args.size()) |
2029 | 0 | return false; |
2030 | | |
2031 | | // empty variadic parameter |
2032 | 0 | if (variadic && argnr + 1U >= parametertokens.size()) |
2033 | 0 | return true; |
2034 | | |
2035 | 0 | for (const Token *partok = parametertokens[argnr]->next; partok != parametertokens[argnr + 1U]; partok = partok->next) |
2036 | 0 | output->push_back(new Token(*partok)); |
2037 | |
|
2038 | 0 | return true; |
2039 | 0 | } |
2040 | | |
2041 | 0 | bool expandArg(TokenList *output, const Token *tok, const Location &loc, const MacroMap ¯os, const std::set<TokenString> &expandedmacros, const std::vector<const Token*> ¶metertokens) const { |
2042 | 0 | if (!tok->name) |
2043 | 0 | return false; |
2044 | 0 | const unsigned int argnr = getArgNum(tok->str()); |
2045 | 0 | if (argnr >= args.size()) |
2046 | 0 | return false; |
2047 | 0 | if (variadic && argnr + 1U >= parametertokens.size()) // empty variadic parameter |
2048 | 0 | return true; |
2049 | 0 | for (const Token *partok = parametertokens[argnr]->next; partok != parametertokens[argnr + 1U];) { |
2050 | 0 | const MacroMap::const_iterator it = macros.find(partok->str()); |
2051 | 0 | if (it != macros.end() && !partok->isExpandedFrom(&it->second) && (partok->str() == name() || expandedmacros.find(partok->str()) == expandedmacros.end())) |
2052 | 0 | partok = it->second.expand(output, loc, partok, macros, expandedmacros); |
2053 | 0 | else { |
2054 | 0 | output->push_back(newMacroToken(partok->str(), loc, isReplaced(expandedmacros), partok)); |
2055 | 0 | output->back()->macro = partok->macro; |
2056 | 0 | partok = partok->next; |
2057 | 0 | } |
2058 | 0 | } |
2059 | 0 | return true; |
2060 | 0 | } |
2061 | | |
2062 | | /** |
2063 | | * Expand #X => "X" |
2064 | | * @param output destination tokenlist |
2065 | | * @param loc location for expanded token |
2066 | | * @param tok The # token |
2067 | | * @param macros all macros |
2068 | | * @param expandedmacros set with expanded macros, with this macro |
2069 | | * @param parametertokens parameters given when expanding this macro |
2070 | | * @return token after the X |
2071 | | */ |
2072 | 0 | const Token *expandHash(TokenList *output, const Location &loc, const Token *tok, const MacroMap ¯os, const std::set<TokenString> &expandedmacros, const std::vector<const Token*> ¶metertokens) const { |
2073 | 0 | TokenList tokenListHash(files); |
2074 | 0 | tok = expandToken(&tokenListHash, loc, tok->next, macros, expandedmacros, parametertokens); |
2075 | 0 | std::ostringstream ostr; |
2076 | 0 | ostr << '\"'; |
2077 | 0 | for (const Token *hashtok = tokenListHash.cfront(); hashtok; hashtok = hashtok->next) |
2078 | 0 | ostr << hashtok->str(); |
2079 | 0 | ostr << '\"'; |
2080 | 0 | output->push_back(newMacroToken(escapeString(ostr.str()), loc, isReplaced(expandedmacros))); |
2081 | 0 | return tok; |
2082 | 0 | } |
2083 | | |
2084 | | /** |
2085 | | * Expand A##B => AB |
2086 | | * The A should already be expanded. Call this when you reach the first # token |
2087 | | * @param output destination tokenlist |
2088 | | * @param loc location for expanded token |
2089 | | * @param tok first # token |
2090 | | * @param macros all macros |
2091 | | * @param expandedmacros set with expanded macros, with this macro |
2092 | | * @param parametertokens parameters given when expanding this macro |
2093 | | * @return token after B |
2094 | | */ |
2095 | 0 | const Token *expandHashHash(TokenList *output, const Location &loc, const Token *tok, const MacroMap ¯os, const std::set<TokenString> &expandedmacros, const std::vector<const Token*> ¶metertokens) const { |
2096 | 0 | Token *A = output->back(); |
2097 | 0 | if (!A) |
2098 | 0 | throw invalidHashHash(tok->location, name(), "Missing first argument"); |
2099 | 0 | if (!sameline(tok, tok->next) || !sameline(tok, tok->next->next)) |
2100 | 0 | throw invalidHashHash::unexpectedNewline(tok->location, name()); |
2101 | | |
2102 | 0 | const bool canBeConcatenatedWithEqual = A->isOneOf("+-*/%&|^") || A->str() == "<<" || A->str() == ">>"; |
2103 | 0 | const bool canBeConcatenatedStringOrChar = isStringLiteral_(A->str()) || isCharLiteral_(A->str()); |
2104 | 0 | if (!A->name && !A->number && A->op != ',' && !A->str().empty() && !canBeConcatenatedWithEqual && !canBeConcatenatedStringOrChar) |
2105 | 0 | throw invalidHashHash::unexpectedToken(tok->location, name(), A); |
2106 | | |
2107 | 0 | Token * const B = tok->next->next; |
2108 | 0 | if (!B->name && !B->number && B->op && !B->isOneOf("#=")) |
2109 | 0 | throw invalidHashHash::unexpectedToken(tok->location, name(), B); |
2110 | | |
2111 | 0 | if ((canBeConcatenatedWithEqual && B->op != '=') || |
2112 | 0 | (!canBeConcatenatedWithEqual && B->op == '=')) |
2113 | 0 | throw invalidHashHash::cannotCombine(tok->location, name(), A, B); |
2114 | | |
2115 | | // Superficial check; more in-depth would in theory be possible _after_ expandArg |
2116 | 0 | if (canBeConcatenatedStringOrChar && (B->number || !B->name)) |
2117 | 0 | throw invalidHashHash::cannotCombine(tok->location, name(), A, B); |
2118 | | |
2119 | 0 | TokenList tokensB(files); |
2120 | 0 | const Token *nextTok = B->next; |
2121 | |
|
2122 | 0 | if (canBeConcatenatedStringOrChar) { |
2123 | | // It seems clearer to handle this case separately even though the code is similar-ish, but we don't want to merge here. |
2124 | | // TODO The question is whether the ## or varargs may still apply, and how to provoke? |
2125 | 0 | if (expandArg(&tokensB, B, parametertokens)) { |
2126 | 0 | for (Token *b = tokensB.front(); b; b = b->next) |
2127 | 0 | b->location = loc; |
2128 | 0 | } else { |
2129 | 0 | tokensB.push_back(new Token(*B)); |
2130 | 0 | tokensB.back()->location = loc; |
2131 | 0 | } |
2132 | 0 | output->takeTokens(tokensB); |
2133 | 0 | } else { |
2134 | 0 | std::string strAB; |
2135 | |
|
2136 | 0 | const bool varargs = variadic && !args.empty() && B->str() == args[args.size()-1U]; |
2137 | |
|
2138 | 0 | if (expandArg(&tokensB, B, parametertokens)) { |
2139 | 0 | if (tokensB.empty()) |
2140 | 0 | strAB = A->str(); |
2141 | 0 | else if (varargs && A->op == ',') { |
2142 | 0 | strAB = ","; |
2143 | 0 | } else { |
2144 | 0 | strAB = A->str() + tokensB.cfront()->str(); |
2145 | 0 | tokensB.deleteToken(tokensB.front()); |
2146 | 0 | } |
2147 | 0 | } else { |
2148 | 0 | strAB = A->str() + B->str(); |
2149 | 0 | } |
2150 | | |
2151 | | // producing universal character is undefined behavior |
2152 | 0 | if (A->previous && A->previous->str() == "\\") { |
2153 | 0 | if (strAB[0] == 'u' && strAB.size() == 5) |
2154 | 0 | throw invalidHashHash::universalCharacterUB(tok->location, name(), A, strAB); |
2155 | 0 | if (strAB[0] == 'U' && strAB.size() == 9) |
2156 | 0 | throw invalidHashHash::universalCharacterUB(tok->location, name(), A, strAB); |
2157 | 0 | } |
2158 | | |
2159 | 0 | if (varargs && tokensB.empty() && tok->previous->str() == ",") |
2160 | 0 | output->deleteToken(A); |
2161 | 0 | else if (strAB != "," && macros.find(strAB) == macros.end()) { |
2162 | 0 | A->setstr(strAB); |
2163 | 0 | for (Token *b = tokensB.front(); b; b = b->next) |
2164 | 0 | b->location = loc; |
2165 | 0 | output->takeTokens(tokensB); |
2166 | 0 | } else if (sameline(B, nextTok) && sameline(B, nextTok->next) && nextTok->op == '#' && nextTok->next->op == '#') { |
2167 | 0 | TokenList output2(files); |
2168 | 0 | output2.push_back(new Token(strAB, tok->location)); |
2169 | 0 | nextTok = expandHashHash(&output2, loc, nextTok, macros, expandedmacros, parametertokens); |
2170 | 0 | output->deleteToken(A); |
2171 | 0 | output->takeTokens(output2); |
2172 | 0 | } else { |
2173 | 0 | output->deleteToken(A); |
2174 | 0 | TokenList tokens(files); |
2175 | 0 | tokens.push_back(new Token(strAB, tok->location)); |
2176 | | // for function like macros, push the (...) |
2177 | 0 | if (tokensB.empty() && sameline(B,B->next) && B->next->op=='(') { |
2178 | 0 | const MacroMap::const_iterator it = macros.find(strAB); |
2179 | 0 | if (it != macros.end() && expandedmacros.find(strAB) == expandedmacros.end() && it->second.functionLike()) { |
2180 | 0 | const Token * const tok2 = appendTokens(&tokens, loc, B->next, macros, expandedmacros, parametertokens); |
2181 | 0 | if (tok2) |
2182 | 0 | nextTok = tok2->next; |
2183 | 0 | } |
2184 | 0 | } |
2185 | 0 | expandToken(output, loc, tokens.cfront(), macros, expandedmacros, parametertokens); |
2186 | 0 | for (Token *b = tokensB.front(); b; b = b->next) |
2187 | 0 | b->location = loc; |
2188 | 0 | output->takeTokens(tokensB); |
2189 | 0 | } |
2190 | 0 | } |
2191 | | |
2192 | 0 | return nextTok; |
2193 | 0 | } |
2194 | | |
2195 | 0 | static bool isReplaced(const std::set<std::string> &expandedmacros) { |
2196 | | // return true if size > 1 |
2197 | 0 | std::set<std::string>::const_iterator it = expandedmacros.begin(); |
2198 | 0 | if (it == expandedmacros.end()) |
2199 | 0 | return false; |
2200 | 0 | ++it; |
2201 | 0 | return (it != expandedmacros.end()); |
2202 | 0 | } |
2203 | | |
2204 | | /** name token in definition */ |
2205 | | const Token *nameTokDef; |
2206 | | |
2207 | | /** arguments for macro */ |
2208 | | std::vector<TokenString> args; |
2209 | | |
2210 | | /** first token in replacement string */ |
2211 | | const Token *valueToken; |
2212 | | |
2213 | | /** token after replacement string */ |
2214 | | const Token *endToken; |
2215 | | |
2216 | | /** files */ |
2217 | | std::vector<std::string> &files; |
2218 | | |
2219 | | /** this is used for -D where the definition is not seen anywhere in code */ |
2220 | | TokenList tokenListDefine; |
2221 | | |
2222 | | /** usage of this macro */ |
2223 | | mutable std::list<Location> usageList; |
2224 | | |
2225 | | /** is macro variadic? */ |
2226 | | bool variadic; |
2227 | | |
2228 | | /** was the value of this macro actually defined in the code? */ |
2229 | | bool valueDefinedInCode_; |
2230 | | }; |
2231 | | } |
2232 | | |
2233 | | namespace simplecpp { |
2234 | | |
2235 | | #ifdef __CYGWIN__ |
2236 | | bool startsWith(const std::string &str, const std::string &s) |
2237 | | { |
2238 | | return (str.size() >= s.size() && str.compare(0, s.size(), s) == 0); |
2239 | | } |
2240 | | |
2241 | | std::string convertCygwinToWindowsPath(const std::string &cygwinPath) |
2242 | | { |
2243 | | std::string windowsPath; |
2244 | | |
2245 | | std::string::size_type pos = 0; |
2246 | | if (cygwinPath.size() >= 11 && startsWith(cygwinPath, "/cygdrive/")) { |
2247 | | const unsigned char driveLetter = cygwinPath[10]; |
2248 | | if (std::isalpha(driveLetter)) { |
2249 | | if (cygwinPath.size() == 11) { |
2250 | | windowsPath = toupper(driveLetter); |
2251 | | windowsPath += ":\\"; // volume root directory |
2252 | | pos = 11; |
2253 | | } else if (cygwinPath[11] == '/') { |
2254 | | windowsPath = toupper(driveLetter); |
2255 | | windowsPath += ":"; |
2256 | | pos = 11; |
2257 | | } |
2258 | | } |
2259 | | } |
2260 | | |
2261 | | for (; pos < cygwinPath.size(); ++pos) { |
2262 | | unsigned char c = cygwinPath[pos]; |
2263 | | if (c == '/') |
2264 | | c = '\\'; |
2265 | | windowsPath += c; |
2266 | | } |
2267 | | |
2268 | | return windowsPath; |
2269 | | } |
2270 | | #endif |
2271 | | } |
2272 | | |
2273 | | #ifdef SIMPLECPP_WINDOWS |
2274 | | |
2275 | | #if __cplusplus >= 201103L |
2276 | | using MyMutex = std::mutex; |
2277 | | template<class T> |
2278 | | using MyLock = std::lock_guard<T>; |
2279 | | #else |
2280 | | class MyMutex { |
2281 | | public: |
2282 | | MyMutex() { |
2283 | | InitializeCriticalSection(&m_criticalSection); |
2284 | | } |
2285 | | |
2286 | | ~MyMutex() { |
2287 | | DeleteCriticalSection(&m_criticalSection); |
2288 | | } |
2289 | | |
2290 | | CRITICAL_SECTION* lock() { |
2291 | | return &m_criticalSection; |
2292 | | } |
2293 | | private: |
2294 | | CRITICAL_SECTION m_criticalSection; |
2295 | | }; |
2296 | | |
2297 | | template<typename T> |
2298 | | class MyLock { |
2299 | | public: |
2300 | | explicit MyLock(T& m) |
2301 | | : m_mutex(m) { |
2302 | | EnterCriticalSection(m_mutex.lock()); |
2303 | | } |
2304 | | |
2305 | | ~MyLock() { |
2306 | | LeaveCriticalSection(m_mutex.lock()); |
2307 | | } |
2308 | | |
2309 | | private: |
2310 | | MyLock& operator=(const MyLock&); |
2311 | | MyLock(const MyLock&); |
2312 | | |
2313 | | T& m_mutex; |
2314 | | }; |
2315 | | #endif |
2316 | | |
2317 | | class RealFileNameMap { |
2318 | | public: |
2319 | | RealFileNameMap() {} |
2320 | | |
2321 | | bool getCacheEntry(const std::string& path, std::string& returnPath) { |
2322 | | MyLock<MyMutex> lock(m_mutex); |
2323 | | |
2324 | | const std::map<std::string, std::string>::iterator it = m_fileMap.find(path); |
2325 | | if (it != m_fileMap.end()) { |
2326 | | returnPath = it->second; |
2327 | | return true; |
2328 | | } |
2329 | | return false; |
2330 | | } |
2331 | | |
2332 | | void addToCache(const std::string& path, const std::string& actualPath) { |
2333 | | MyLock<MyMutex> lock(m_mutex); |
2334 | | m_fileMap[path] = actualPath; |
2335 | | } |
2336 | | |
2337 | | private: |
2338 | | std::map<std::string, std::string> m_fileMap; |
2339 | | MyMutex m_mutex; |
2340 | | }; |
2341 | | |
2342 | | static RealFileNameMap realFileNameMap; |
2343 | | |
2344 | | static bool realFileName(const std::string &f, std::string &result) |
2345 | | { |
2346 | | // are there alpha characters in last subpath? |
2347 | | bool alpha = false; |
2348 | | for (std::string::size_type pos = 1; pos <= f.size(); ++pos) { |
2349 | | const unsigned char c = f[f.size() - pos]; |
2350 | | if (c == '/' || c == '\\') |
2351 | | break; |
2352 | | if (std::isalpha(c)) { |
2353 | | alpha = true; |
2354 | | break; |
2355 | | } |
2356 | | } |
2357 | | |
2358 | | // do not convert this path if there are no alpha characters (either pointless or cause wrong results for . and ..) |
2359 | | if (!alpha) |
2360 | | return false; |
2361 | | |
2362 | | // Lookup filename or foldername on file system |
2363 | | if (!realFileNameMap.getCacheEntry(f, result)) { |
2364 | | |
2365 | | WIN32_FIND_DATAA FindFileData; |
2366 | | |
2367 | | #ifdef __CYGWIN__ |
2368 | | const std::string fConverted = simplecpp::convertCygwinToWindowsPath(f); |
2369 | | const HANDLE hFind = FindFirstFileExA(fConverted.c_str(), FindExInfoBasic, &FindFileData, FindExSearchNameMatch, NULL, 0); |
2370 | | #else |
2371 | | HANDLE hFind = FindFirstFileExA(f.c_str(), FindExInfoBasic, &FindFileData, FindExSearchNameMatch, NULL, 0); |
2372 | | #endif |
2373 | | |
2374 | | if (INVALID_HANDLE_VALUE == hFind) |
2375 | | return false; |
2376 | | result = FindFileData.cFileName; |
2377 | | realFileNameMap.addToCache(f, result); |
2378 | | FindClose(hFind); |
2379 | | } |
2380 | | return true; |
2381 | | } |
2382 | | |
2383 | | static RealFileNameMap realFilePathMap; |
2384 | | |
2385 | | /** Change case in given path to match filesystem */ |
2386 | | static std::string realFilename(const std::string &f) |
2387 | | { |
2388 | | std::string ret; |
2389 | | ret.reserve(f.size()); // this will be the final size |
2390 | | if (realFilePathMap.getCacheEntry(f, ret)) |
2391 | | return ret; |
2392 | | |
2393 | | // Current subpath |
2394 | | std::string subpath; |
2395 | | |
2396 | | for (std::string::size_type pos = 0; pos < f.size(); ++pos) { |
2397 | | const unsigned char c = f[pos]; |
2398 | | |
2399 | | // Separator.. add subpath and separator |
2400 | | if (c == '/' || c == '\\') { |
2401 | | // if subpath is empty just add separator |
2402 | | if (subpath.empty()) { |
2403 | | ret += c; |
2404 | | continue; |
2405 | | } |
2406 | | |
2407 | | const bool isDriveSpecification = |
2408 | | (pos == 2 && subpath.size() == 2 && std::isalpha(subpath[0]) && subpath[1] == ':'); |
2409 | | |
2410 | | // Append real filename (proper case) |
2411 | | std::string f2; |
2412 | | if (!isDriveSpecification && realFileName(f.substr(0, pos), f2)) |
2413 | | ret += f2; |
2414 | | else |
2415 | | ret += subpath; |
2416 | | |
2417 | | subpath.clear(); |
2418 | | |
2419 | | // Append separator |
2420 | | ret += c; |
2421 | | } else { |
2422 | | subpath += c; |
2423 | | } |
2424 | | } |
2425 | | |
2426 | | if (!subpath.empty()) { |
2427 | | std::string f2; |
2428 | | if (realFileName(f,f2)) |
2429 | | ret += f2; |
2430 | | else |
2431 | | ret += subpath; |
2432 | | } |
2433 | | |
2434 | | realFilePathMap.addToCache(f, ret); |
2435 | | return ret; |
2436 | | } |
2437 | | |
2438 | | static bool isAbsolutePath(const std::string &path) |
2439 | | { |
2440 | | if (path.length() >= 3 && path[0] > 0 && std::isalpha(path[0]) && path[1] == ':' && (path[2] == '\\' || path[2] == '/')) |
2441 | | return true; |
2442 | | return path.length() > 1U && (path[0] == '/' || path[0] == '\\'); |
2443 | | } |
2444 | | #else |
2445 | 8.37k | #define realFilename(f) f |
2446 | | |
2447 | | static bool isAbsolutePath(const std::string &path) |
2448 | 0 | { |
2449 | 0 | return path.length() > 1U && path[0] == '/'; |
2450 | 0 | } |
2451 | | #endif |
2452 | | |
2453 | | namespace simplecpp { |
2454 | | /** |
2455 | | * perform path simplifications for . and .. |
2456 | | */ |
2457 | | std::string simplifyPath(std::string path) |
2458 | 4.18k | { |
2459 | 4.18k | if (path.empty()) |
2460 | 0 | return path; |
2461 | | |
2462 | 4.18k | std::string::size_type pos; |
2463 | | |
2464 | | // replace backslash separators |
2465 | 4.18k | std::replace(path.begin(), path.end(), '\\', '/'); |
2466 | | |
2467 | 4.18k | const bool unc(path.compare(0,2,"//") == 0); |
2468 | | |
2469 | | // replace "//" with "/" |
2470 | 4.18k | pos = 0; |
2471 | 4.18k | while ((pos = path.find("//",pos)) != std::string::npos) { |
2472 | 0 | path.erase(pos,1); |
2473 | 0 | } |
2474 | | |
2475 | | // remove "./" |
2476 | 4.18k | pos = 0; |
2477 | 4.18k | while ((pos = path.find("./",pos)) != std::string::npos) { |
2478 | 0 | if (pos == 0 || path[pos - 1U] == '/') |
2479 | 0 | path.erase(pos,2); |
2480 | 0 | else |
2481 | 0 | pos += 2; |
2482 | 0 | } |
2483 | | |
2484 | | // remove trailing dot if path ends with "/." |
2485 | 4.18k | if (endsWith(path,"/.")) |
2486 | 0 | path.erase(path.size()-1); |
2487 | | |
2488 | | // simplify ".." |
2489 | 4.18k | pos = 1; // don't simplify ".." if path starts with that |
2490 | 4.18k | while ((pos = path.find("/..", pos)) != std::string::npos) { |
2491 | | // not end of path, then string must be "/../" |
2492 | 0 | if (pos + 3 < path.size() && path[pos + 3] != '/') { |
2493 | 0 | ++pos; |
2494 | 0 | continue; |
2495 | 0 | } |
2496 | | // get previous subpath |
2497 | 0 | std::string::size_type pos1 = path.rfind('/', pos - 1U); |
2498 | 0 | if (pos1 == std::string::npos) { |
2499 | 0 | pos1 = 0; |
2500 | 0 | } else { |
2501 | 0 | pos1 += 1U; |
2502 | 0 | } |
2503 | 0 | const std::string previousSubPath = path.substr(pos1, pos - pos1); |
2504 | 0 | if (previousSubPath == "..") { |
2505 | | // don't simplify |
2506 | 0 | ++pos; |
2507 | 0 | } else { |
2508 | | // remove previous subpath and ".." |
2509 | 0 | path.erase(pos1, pos - pos1 + 4); |
2510 | 0 | if (path.empty()) |
2511 | 0 | path = "."; |
2512 | | // update pos |
2513 | 0 | pos = (pos1 == 0) ? 1 : (pos1 - 1); |
2514 | 0 | } |
2515 | 0 | } |
2516 | | |
2517 | | // Remove trailing '/'? |
2518 | | //if (path.size() > 1 && endsWith(path, "/")) |
2519 | | // path.erase(path.size()-1); |
2520 | | |
2521 | 4.18k | if (unc) |
2522 | 0 | path = '/' + path; |
2523 | | |
2524 | | // cppcheck-suppress duplicateExpressionTernary - platform-dependent implementation |
2525 | 4.18k | return strpbrk(path.c_str(), "*?") == nullptr ? realFilename(path) : path; |
2526 | 4.18k | } |
2527 | | } |
2528 | | |
2529 | | /** Evaluate sizeof(type) */ |
2530 | | static void simplifySizeof(simplecpp::TokenList &expr, const std::map<std::string, std::size_t> &sizeOfType) |
2531 | 0 | { |
2532 | 0 | for (simplecpp::Token *tok = expr.front(); tok; tok = tok->next) { |
2533 | 0 | if (tok->str() != "sizeof") |
2534 | 0 | continue; |
2535 | 0 | simplecpp::Token *tok1 = tok->next; |
2536 | 0 | if (!tok1) { |
2537 | 0 | throw std::runtime_error("missing sizeof argument"); |
2538 | 0 | } |
2539 | 0 | simplecpp::Token *tok2 = tok1->next; |
2540 | 0 | if (!tok2) { |
2541 | 0 | throw std::runtime_error("missing sizeof argument"); |
2542 | 0 | } |
2543 | 0 | if (tok1->op == '(') { |
2544 | 0 | tok1 = tok1->next; |
2545 | 0 | while (tok2->op != ')') { |
2546 | 0 | tok2 = tok2->next; |
2547 | 0 | if (!tok2) { |
2548 | 0 | throw std::runtime_error("invalid sizeof expression"); |
2549 | 0 | } |
2550 | 0 | } |
2551 | 0 | } |
2552 | | |
2553 | 0 | std::string type; |
2554 | 0 | for (simplecpp::Token *typeToken = tok1; typeToken != tok2; typeToken = typeToken->next) { |
2555 | 0 | if ((typeToken->str() == "unsigned" || typeToken->str() == "signed") && typeToken->next->name) |
2556 | 0 | continue; |
2557 | 0 | if (typeToken->str() == "*" && type.find('*') != std::string::npos) |
2558 | 0 | continue; |
2559 | 0 | if (!type.empty()) |
2560 | 0 | type += ' '; |
2561 | 0 | type += typeToken->str(); |
2562 | 0 | } |
2563 | |
|
2564 | 0 | const std::map<std::string, std::size_t>::const_iterator it = sizeOfType.find(type); |
2565 | 0 | if (it != sizeOfType.end()) |
2566 | 0 | tok->setstr(toString(it->second)); |
2567 | 0 | else |
2568 | 0 | continue; |
2569 | | |
2570 | 0 | tok2 = tok2->next; |
2571 | 0 | while (tok->next != tok2) |
2572 | 0 | expr.deleteToken(tok->next); |
2573 | 0 | } |
2574 | 0 | } |
2575 | | |
2576 | | /** Evaluate __has_include(file) */ |
2577 | | static std::string openHeader(std::ifstream &f, const simplecpp::DUI &dui, const std::string &sourcefile, const std::string &header, bool systemheader); |
2578 | | static void simplifyHasInclude(simplecpp::TokenList &expr, const simplecpp::DUI &dui) |
2579 | 0 | { |
2580 | 0 | for (simplecpp::Token *tok = expr.front(); tok; tok = tok->next) { |
2581 | 0 | if (tok->str() != "__has_include") |
2582 | 0 | continue; |
2583 | 0 | simplecpp::Token *tok1 = tok->next; |
2584 | 0 | if (!tok1) { |
2585 | 0 | throw std::runtime_error("missing __has_include argument"); |
2586 | 0 | } |
2587 | 0 | simplecpp::Token *tok2 = tok1->next; |
2588 | 0 | if (!tok2) { |
2589 | 0 | throw std::runtime_error("missing __has_include argument"); |
2590 | 0 | } |
2591 | 0 | if (tok1->op == '(') { |
2592 | 0 | tok1 = tok1->next; |
2593 | 0 | while (tok2->op != ')') { |
2594 | 0 | tok2 = tok2->next; |
2595 | 0 | if (!tok2) { |
2596 | 0 | throw std::runtime_error("invalid __has_include expression"); |
2597 | 0 | } |
2598 | 0 | } |
2599 | 0 | } |
2600 | | |
2601 | 0 | const std::string &sourcefile = tok->location.file(); |
2602 | 0 | const bool systemheader = (tok1 && tok1->op == '<'); |
2603 | 0 | std::string header; |
2604 | 0 | if (systemheader) { |
2605 | 0 | simplecpp::Token *tok3 = tok1->next; |
2606 | 0 | if (!tok3) { |
2607 | 0 | throw std::runtime_error("missing __has_include closing angular bracket"); |
2608 | 0 | } |
2609 | 0 | while (tok3->op != '>') { |
2610 | 0 | tok3 = tok3->next; |
2611 | 0 | if (!tok3) { |
2612 | 0 | throw std::runtime_error("invalid __has_include expression"); |
2613 | 0 | } |
2614 | 0 | } |
2615 | | |
2616 | 0 | for (simplecpp::Token *headerToken = tok1->next; headerToken != tok3; headerToken = headerToken->next) |
2617 | 0 | header += headerToken->str(); |
2618 | | // cppcheck-suppress selfAssignment - platform-dependent implementation |
2619 | 0 | header = realFilename(header); |
2620 | 0 | } |
2621 | 0 | else { |
2622 | 0 | header = realFilename(tok1->str().substr(1U, tok1->str().size() - 2U)); |
2623 | 0 | } |
2624 | 0 | std::ifstream f; |
2625 | 0 | const std::string header2 = openHeader(f,dui,sourcefile,header,systemheader); |
2626 | 0 | tok->setstr(header2.empty() ? "0" : "1"); |
2627 | |
|
2628 | 0 | tok2 = tok2->next; |
2629 | 0 | while (tok->next != tok2) |
2630 | 0 | expr.deleteToken(tok->next); |
2631 | 0 | } |
2632 | 0 | } |
2633 | | |
2634 | | static const char * const altopData[] = {"and","or","bitand","bitor","compl","not","not_eq","xor"}; |
2635 | | static const std::set<std::string> altop(&altopData[0], &altopData[8]); |
2636 | | static void simplifyName(simplecpp::TokenList &expr) |
2637 | 0 | { |
2638 | 0 | for (simplecpp::Token *tok = expr.front(); tok; tok = tok->next) { |
2639 | 0 | if (tok->name) { |
2640 | 0 | if (altop.find(tok->str()) != altop.end()) { |
2641 | 0 | bool alt; |
2642 | 0 | if (tok->str() == "not" || tok->str() == "compl") { |
2643 | 0 | alt = isAlternativeUnaryOp(tok,tok->str()); |
2644 | 0 | } else { |
2645 | 0 | alt = isAlternativeBinaryOp(tok,tok->str()); |
2646 | 0 | } |
2647 | 0 | if (alt) |
2648 | 0 | continue; |
2649 | 0 | } |
2650 | 0 | tok->setstr("0"); |
2651 | 0 | } |
2652 | 0 | } |
2653 | 0 | } |
2654 | | |
2655 | | /* |
2656 | | * Reads at least minlen and at most maxlen digits (inc. prefix) in base base |
2657 | | * from s starting at position pos and converts them to a |
2658 | | * unsigned long long value, updating pos to point to the first |
2659 | | * unused element of s. |
2660 | | * Returns ULLONG_MAX if the result is not representable and |
2661 | | * throws if the above requirements were not possible to satisfy. |
2662 | | */ |
2663 | | static unsigned long long stringToULLbounded( |
2664 | | const std::string& s, |
2665 | | std::size_t& pos, |
2666 | | int base = 0, |
2667 | | std::ptrdiff_t minlen = 1, |
2668 | | std::size_t maxlen = std::string::npos |
2669 | | ) |
2670 | 0 | { |
2671 | 0 | const std::string sub = s.substr(pos, maxlen); |
2672 | 0 | const char * const start = sub.c_str(); |
2673 | 0 | char* end; |
2674 | 0 | const unsigned long long value = std::strtoull(start, &end, base); |
2675 | 0 | pos += end - start; |
2676 | 0 | if (end - start < minlen) |
2677 | 0 | throw std::runtime_error("expected digit"); |
2678 | 0 | return value; |
2679 | 0 | } |
2680 | | |
2681 | | /* Converts character literal (including prefix, but not ud-suffix) |
2682 | | * to long long value. |
2683 | | * |
2684 | | * Assumes ASCII-compatible single-byte encoded str for narrow literals |
2685 | | * and UTF-8 otherwise. |
2686 | | * |
2687 | | * For target assumes |
2688 | | * - execution character set encoding matching str |
2689 | | * - UTF-32 execution wide-character set encoding |
2690 | | * - requirements for __STDC_UTF_16__, __STDC_UTF_32__ and __STDC_ISO_10646__ satisfied |
2691 | | * - char16_t is 16bit wide |
2692 | | * - char32_t is 32bit wide |
2693 | | * - wchar_t is 32bit wide and unsigned |
2694 | | * - matching char signedness to host |
2695 | | * - matching sizeof(int) to host |
2696 | | * |
2697 | | * For host assumes |
2698 | | * - ASCII-compatible execution character set |
2699 | | * |
2700 | | * For host and target assumes |
2701 | | * - CHAR_BIT == 8 |
2702 | | * - two's complement |
2703 | | * |
2704 | | * Implements multi-character narrow literals according to GCC's behavior, |
2705 | | * except multi code unit universal character names are not supported. |
2706 | | * Multi-character wide literals are not supported. |
2707 | | * Limited support of universal character names for non-UTF-8 execution character set encodings. |
2708 | | */ |
2709 | | long long simplecpp::characterLiteralToLL(const std::string& str) |
2710 | 0 | { |
2711 | | // default is wide/utf32 |
2712 | 0 | bool narrow = false; |
2713 | 0 | bool utf8 = false; |
2714 | 0 | bool utf16 = false; |
2715 | |
|
2716 | 0 | std::size_t pos; |
2717 | |
|
2718 | 0 | if (!str.empty() && str[0] == '\'') { |
2719 | 0 | narrow = true; |
2720 | 0 | pos = 1; |
2721 | 0 | } else if (str.size() >= 2 && str[0] == 'u' && str[1] == '\'') { |
2722 | 0 | utf16 = true; |
2723 | 0 | pos = 2; |
2724 | 0 | } else if (str.size() >= 3 && str[0] == 'u' && str[1] == '8' && str[2] == '\'') { |
2725 | 0 | utf8 = true; |
2726 | 0 | pos = 3; |
2727 | 0 | } else if (str.size() >= 2 && (str[0] == 'L' || str[0] == 'U') && str[1] == '\'') { |
2728 | 0 | pos = 2; |
2729 | 0 | } else |
2730 | 0 | throw std::runtime_error("expected a character literal"); |
2731 | | |
2732 | 0 | unsigned long long multivalue = 0; |
2733 | |
|
2734 | 0 | std::size_t nbytes = 0; |
2735 | |
|
2736 | 0 | while (pos + 1 < str.size()) { |
2737 | 0 | if (str[pos] == '\'' || str[pos] == '\n') |
2738 | 0 | throw std::runtime_error("raw single quotes and newlines not allowed in character literals"); |
2739 | | |
2740 | 0 | if (nbytes >= 1 && !narrow) |
2741 | 0 | throw std::runtime_error("multiple characters only supported in narrow character literals"); |
2742 | | |
2743 | 0 | unsigned long long value; |
2744 | |
|
2745 | 0 | if (str[pos] == '\\') { |
2746 | 0 | pos++; |
2747 | 0 | const char escape = str[pos++]; |
2748 | |
|
2749 | 0 | if (pos >= str.size()) |
2750 | 0 | throw std::runtime_error("unexpected end of character literal"); |
2751 | | |
2752 | 0 | switch (escape) { |
2753 | | // obscure GCC extensions |
2754 | 0 | case '%': |
2755 | 0 | case '(': |
2756 | 0 | case '[': |
2757 | 0 | case '{': |
2758 | | // standard escape sequences |
2759 | 0 | case '\'': |
2760 | 0 | case '"': |
2761 | 0 | case '?': |
2762 | 0 | case '\\': |
2763 | 0 | value = static_cast<unsigned char>(escape); |
2764 | 0 | break; |
2765 | | |
2766 | 0 | case 'a': |
2767 | 0 | value = static_cast<unsigned char>('\a'); |
2768 | 0 | break; |
2769 | 0 | case 'b': |
2770 | 0 | value = static_cast<unsigned char>('\b'); |
2771 | 0 | break; |
2772 | 0 | case 'f': |
2773 | 0 | value = static_cast<unsigned char>('\f'); |
2774 | 0 | break; |
2775 | 0 | case 'n': |
2776 | 0 | value = static_cast<unsigned char>('\n'); |
2777 | 0 | break; |
2778 | 0 | case 'r': |
2779 | 0 | value = static_cast<unsigned char>('\r'); |
2780 | 0 | break; |
2781 | 0 | case 't': |
2782 | 0 | value = static_cast<unsigned char>('\t'); |
2783 | 0 | break; |
2784 | 0 | case 'v': |
2785 | 0 | value = static_cast<unsigned char>('\v'); |
2786 | 0 | break; |
2787 | | |
2788 | | // GCC extension for ESC character |
2789 | 0 | case 'e': |
2790 | 0 | case 'E': |
2791 | 0 | value = static_cast<unsigned char>('\x1b'); |
2792 | 0 | break; |
2793 | | |
2794 | 0 | case '0': |
2795 | 0 | case '1': |
2796 | 0 | case '2': |
2797 | 0 | case '3': |
2798 | 0 | case '4': |
2799 | 0 | case '5': |
2800 | 0 | case '6': |
2801 | 0 | case '7': |
2802 | | // octal escape sequences consist of 1 to 3 digits |
2803 | 0 | value = stringToULLbounded(str, --pos, 8, 1, 3); |
2804 | 0 | break; |
2805 | | |
2806 | 0 | case 'x': |
2807 | | // hexadecimal escape sequences consist of at least 1 digit |
2808 | 0 | value = stringToULLbounded(str, pos, 16); |
2809 | 0 | break; |
2810 | | |
2811 | 0 | case 'u': |
2812 | 0 | case 'U': { |
2813 | | // universal character names have exactly 4 or 8 digits |
2814 | 0 | const std::size_t ndigits = (escape == 'u' ? 4 : 8); |
2815 | 0 | value = stringToULLbounded(str, pos, 16, ndigits, ndigits); |
2816 | | |
2817 | | // UTF-8 encodes code points above 0x7f in multiple code units |
2818 | | // code points above 0x10ffff are not allowed |
2819 | 0 | if (((narrow || utf8) && value > 0x7f) || (utf16 && value > 0xffff) || value > 0x10ffff) |
2820 | 0 | throw std::runtime_error("code point too large"); |
2821 | | |
2822 | 0 | if (value >= 0xd800 && value <= 0xdfff) |
2823 | 0 | throw std::runtime_error("surrogate code points not allowed in universal character names"); |
2824 | | |
2825 | 0 | break; |
2826 | 0 | } |
2827 | | |
2828 | 0 | default: |
2829 | 0 | throw std::runtime_error("invalid escape sequence"); |
2830 | 0 | } |
2831 | 0 | } else { |
2832 | 0 | value = static_cast<unsigned char>(str[pos++]); |
2833 | |
|
2834 | 0 | if (!narrow && value >= 0x80) { |
2835 | | // Assuming this is a UTF-8 encoded code point. |
2836 | | // This decoder may not completely validate the input. |
2837 | | // Noncharacters are neither rejected nor replaced. |
2838 | |
|
2839 | 0 | int additional_bytes; |
2840 | 0 | if (value >= 0xf5) // higher values would result in code points above 0x10ffff |
2841 | 0 | throw std::runtime_error("assumed UTF-8 encoded source, but sequence is invalid"); |
2842 | 0 | if (value >= 0xf0) |
2843 | 0 | additional_bytes = 3; |
2844 | 0 | else if (value >= 0xe0) |
2845 | 0 | additional_bytes = 2; |
2846 | 0 | else if (value >= 0xc2) // 0xc0 and 0xc1 are always overlong 2-bytes encodings |
2847 | 0 | additional_bytes = 1; |
2848 | 0 | else |
2849 | 0 | throw std::runtime_error("assumed UTF-8 encoded source, but sequence is invalid"); |
2850 | | |
2851 | 0 | value &= (1 << (6 - additional_bytes)) - 1; |
2852 | |
|
2853 | 0 | while (additional_bytes--) { |
2854 | 0 | if (pos + 1 >= str.size()) |
2855 | 0 | throw std::runtime_error("assumed UTF-8 encoded source, but character literal ends unexpectedly"); |
2856 | | |
2857 | 0 | const unsigned char c = str[pos++]; |
2858 | |
|
2859 | 0 | if (((c >> 6) != 2) // ensure c has form 0xb10xxxxxx |
2860 | 0 | || (!value && additional_bytes == 1 && c < 0xa0) // overlong 3-bytes encoding |
2861 | 0 | || (!value && additional_bytes == 2 && c < 0x90)) // overlong 4-bytes encoding |
2862 | 0 | throw std::runtime_error("assumed UTF-8 encoded source, but sequence is invalid"); |
2863 | | |
2864 | 0 | value = (value << 6) | (c & ((1 << 7) - 1)); |
2865 | 0 | } |
2866 | | |
2867 | 0 | if (value >= 0xd800 && value <= 0xdfff) |
2868 | 0 | throw std::runtime_error("assumed UTF-8 encoded source, but sequence is invalid"); |
2869 | | |
2870 | 0 | if ((utf8 && value > 0x7f) || (utf16 && value > 0xffff) || value > 0x10ffff) |
2871 | 0 | throw std::runtime_error("code point too large"); |
2872 | 0 | } |
2873 | 0 | } |
2874 | | |
2875 | 0 | if (((narrow || utf8) && value > std::numeric_limits<unsigned char>::max()) || (utf16 && value >> 16) || value >> 32) |
2876 | 0 | throw std::runtime_error("numeric escape sequence too large"); |
2877 | | |
2878 | 0 | multivalue <<= CHAR_BIT; |
2879 | 0 | multivalue |= value; |
2880 | 0 | nbytes++; |
2881 | 0 | } |
2882 | | |
2883 | 0 | if (pos + 1 != str.size() || str[pos] != '\'') |
2884 | 0 | throw std::runtime_error("missing closing quote in character literal"); |
2885 | | |
2886 | 0 | if (!nbytes) |
2887 | 0 | throw std::runtime_error("empty character literal"); |
2888 | | |
2889 | | // ordinary narrow character literal's value is determined by (possibly signed) char |
2890 | 0 | if (narrow && nbytes == 1) |
2891 | 0 | return static_cast<char>(multivalue); |
2892 | | |
2893 | | // while multi-character literal's value is determined by (signed) int |
2894 | 0 | if (narrow) |
2895 | 0 | return static_cast<int>(multivalue); |
2896 | | |
2897 | | // All other cases are unsigned. Since long long is at least 64bit wide, |
2898 | | // while the literals at most 32bit wide, the conversion preserves all values. |
2899 | 0 | return multivalue; |
2900 | 0 | } |
2901 | | |
2902 | | static void simplifyNumbers(simplecpp::TokenList &expr) |
2903 | 0 | { |
2904 | 0 | for (simplecpp::Token *tok = expr.front(); tok; tok = tok->next) { |
2905 | 0 | if (tok->str().size() == 1U) |
2906 | 0 | continue; |
2907 | 0 | if (tok->str().compare(0,2,"0x") == 0) |
2908 | 0 | tok->setstr(toString(stringToULL(tok->str()))); |
2909 | 0 | else if (!tok->number && tok->str().find('\'') != std::string::npos) |
2910 | 0 | tok->setstr(toString(simplecpp::characterLiteralToLL(tok->str()))); |
2911 | 0 | } |
2912 | 0 | } |
2913 | | |
2914 | | static void simplifyComments(simplecpp::TokenList &expr) |
2915 | 0 | { |
2916 | 0 | for (simplecpp::Token *tok = expr.front(); tok;) { |
2917 | 0 | simplecpp::Token * const d = tok; |
2918 | 0 | tok = tok->next; |
2919 | 0 | if (d->comment) |
2920 | 0 | expr.deleteToken(d); |
2921 | 0 | } |
2922 | 0 | } |
2923 | | |
2924 | | static long long evaluate(simplecpp::TokenList &expr, const simplecpp::DUI &dui, const std::map<std::string, std::size_t> &sizeOfType) |
2925 | 0 | { |
2926 | 0 | simplifyComments(expr); |
2927 | 0 | simplifySizeof(expr, sizeOfType); |
2928 | 0 | simplifyHasInclude(expr, dui); |
2929 | 0 | simplifyName(expr); |
2930 | 0 | simplifyNumbers(expr); |
2931 | 0 | expr.constFold(); |
2932 | | // TODO: handle invalid expressions |
2933 | 0 | return expr.cfront() && expr.cfront() == expr.cback() && expr.cfront()->number ? stringToLL(expr.cfront()->str()) : 0LL; |
2934 | 0 | } |
2935 | | |
2936 | | static const simplecpp::Token *gotoNextLine(const simplecpp::Token *tok) |
2937 | 0 | { |
2938 | 0 | const unsigned int line = tok->location.line; |
2939 | 0 | const unsigned int file = tok->location.fileIndex; |
2940 | 0 | while (tok && tok->location.line == line && tok->location.fileIndex == file) |
2941 | 0 | tok = tok->next; |
2942 | 0 | return tok; |
2943 | 0 | } |
2944 | | |
2945 | | #ifdef SIMPLECPP_WINDOWS |
2946 | | |
2947 | | class NonExistingFilesCache { |
2948 | | public: |
2949 | | NonExistingFilesCache() {} |
2950 | | |
2951 | | bool contains(const std::string& path) { |
2952 | | MyLock<MyMutex> lock(m_mutex); |
2953 | | return (m_pathSet.find(path) != m_pathSet.end()); |
2954 | | } |
2955 | | |
2956 | | void add(const std::string& path) { |
2957 | | MyLock<MyMutex> lock(m_mutex); |
2958 | | m_pathSet.insert(path); |
2959 | | } |
2960 | | |
2961 | | void clear() { |
2962 | | MyLock<MyMutex> lock(m_mutex); |
2963 | | m_pathSet.clear(); |
2964 | | } |
2965 | | |
2966 | | private: |
2967 | | std::set<std::string> m_pathSet; |
2968 | | MyMutex m_mutex; |
2969 | | }; |
2970 | | |
2971 | | static NonExistingFilesCache nonExistingFilesCache; |
2972 | | |
2973 | | #endif |
2974 | | |
2975 | | static std::string openHeader(std::ifstream &f, const std::string &path) |
2976 | 0 | { |
2977 | 0 | std::string simplePath = simplecpp::simplifyPath(path); |
2978 | | #ifdef SIMPLECPP_WINDOWS |
2979 | | if (nonExistingFilesCache.contains(simplePath)) |
2980 | | return ""; // file is known not to exist, skip expensive file open call |
2981 | | #endif |
2982 | 0 | f.open(simplePath.c_str()); |
2983 | 0 | if (f.is_open()) |
2984 | 0 | return simplePath; |
2985 | | #ifdef SIMPLECPP_WINDOWS |
2986 | | nonExistingFilesCache.add(simplePath); |
2987 | | #endif |
2988 | 0 | return ""; |
2989 | 0 | } |
2990 | | |
2991 | | static std::string getRelativeFileName(const std::string &sourcefile, const std::string &header) |
2992 | 0 | { |
2993 | 0 | if (sourcefile.find_first_of("\\/") != std::string::npos) |
2994 | 0 | return simplecpp::simplifyPath(sourcefile.substr(0, sourcefile.find_last_of("\\/") + 1U) + header); |
2995 | 0 | return simplecpp::simplifyPath(header); |
2996 | 0 | } |
2997 | | |
2998 | | static std::string openHeaderRelative(std::ifstream &f, const std::string &sourcefile, const std::string &header) |
2999 | 0 | { |
3000 | 0 | return openHeader(f, getRelativeFileName(sourcefile, header)); |
3001 | 0 | } |
3002 | | |
3003 | | static std::string getIncludePathFileName(const std::string &includePath, const std::string &header) |
3004 | 0 | { |
3005 | 0 | std::string path = includePath; |
3006 | 0 | if (!path.empty() && path[path.size()-1U]!='/' && path[path.size()-1U]!='\\') |
3007 | 0 | path += '/'; |
3008 | 0 | return path + header; |
3009 | 0 | } |
3010 | | |
3011 | | static std::string openHeaderIncludePath(std::ifstream &f, const simplecpp::DUI &dui, const std::string &header) |
3012 | 0 | { |
3013 | 0 | for (std::list<std::string>::const_iterator it = dui.includePaths.begin(); it != dui.includePaths.end(); ++it) { |
3014 | 0 | std::string simplePath = openHeader(f, getIncludePathFileName(*it, header)); |
3015 | 0 | if (!simplePath.empty()) |
3016 | 0 | return simplePath; |
3017 | 0 | } |
3018 | 0 | return ""; |
3019 | 0 | } |
3020 | | |
3021 | | static std::string openHeader(std::ifstream &f, const simplecpp::DUI &dui, const std::string &sourcefile, const std::string &header, bool systemheader) |
3022 | 0 | { |
3023 | 0 | if (isAbsolutePath(header)) |
3024 | 0 | return openHeader(f, header); |
3025 | | |
3026 | 0 | std::string ret; |
3027 | |
|
3028 | 0 | if (systemheader) { |
3029 | 0 | ret = openHeaderIncludePath(f, dui, header); |
3030 | 0 | return ret; |
3031 | 0 | } |
3032 | | |
3033 | 0 | ret = openHeaderRelative(f, sourcefile, header); |
3034 | 0 | if (ret.empty()) |
3035 | 0 | return openHeaderIncludePath(f, dui, header); |
3036 | 0 | return ret; |
3037 | 0 | } |
3038 | | |
3039 | | static std::string getFileName(const std::map<std::string, simplecpp::TokenList *> &filedata, const std::string &sourcefile, const std::string &header, const simplecpp::DUI &dui, bool systemheader) |
3040 | 0 | { |
3041 | 0 | if (filedata.empty()) { |
3042 | 0 | return ""; |
3043 | 0 | } |
3044 | 0 | if (isAbsolutePath(header)) { |
3045 | 0 | return (filedata.find(header) != filedata.end()) ? simplecpp::simplifyPath(header) : ""; |
3046 | 0 | } |
3047 | | |
3048 | 0 | const std::string relativeFilename = getRelativeFileName(sourcefile, header); |
3049 | 0 | if (!systemheader && filedata.find(relativeFilename) != filedata.end()) |
3050 | 0 | return relativeFilename; |
3051 | | |
3052 | 0 | for (std::list<std::string>::const_iterator it = dui.includePaths.begin(); it != dui.includePaths.end(); ++it) { |
3053 | 0 | std::string s = simplecpp::simplifyPath(getIncludePathFileName(*it, header)); |
3054 | 0 | if (filedata.find(s) != filedata.end()) |
3055 | 0 | return s; |
3056 | 0 | } |
3057 | | |
3058 | 0 | if (systemheader && filedata.find(header) != filedata.end()) |
3059 | 0 | return header; |
3060 | | |
3061 | 0 | return ""; |
3062 | 0 | } |
3063 | | |
3064 | | static bool hasFile(const std::map<std::string, simplecpp::TokenList *> &filedata, const std::string &sourcefile, const std::string &header, const simplecpp::DUI &dui, bool systemheader) |
3065 | 0 | { |
3066 | 0 | return !getFileName(filedata, sourcefile, header, dui, systemheader).empty(); |
3067 | 0 | } |
3068 | | |
3069 | | std::map<std::string, simplecpp::TokenList*> simplecpp::load(const simplecpp::TokenList &rawtokens, std::vector<std::string> &filenames, const simplecpp::DUI &dui, simplecpp::OutputList *outputList) |
3070 | 1.36k | { |
3071 | | #ifdef SIMPLECPP_WINDOWS |
3072 | | if (dui.clearIncludeCache) |
3073 | | nonExistingFilesCache.clear(); |
3074 | | #endif |
3075 | | |
3076 | 1.36k | std::map<std::string, simplecpp::TokenList*> ret; |
3077 | | |
3078 | 1.36k | std::list<const Token *> filelist; |
3079 | | |
3080 | | // -include files |
3081 | 1.36k | for (std::list<std::string>::const_iterator it = dui.includes.begin(); it != dui.includes.end(); ++it) { |
3082 | 0 | const std::string &filename = realFilename(*it); |
3083 | |
|
3084 | 0 | if (ret.find(filename) != ret.end()) |
3085 | 0 | continue; |
3086 | | |
3087 | 0 | std::ifstream fin(filename.c_str()); |
3088 | 0 | if (!fin.is_open()) { |
3089 | 0 | if (outputList) { |
3090 | 0 | simplecpp::Output err(filenames); |
3091 | 0 | err.type = simplecpp::Output::EXPLICIT_INCLUDE_NOT_FOUND; |
3092 | 0 | err.location = Location(filenames); |
3093 | 0 | err.msg = "Can not open include file '" + filename + "' that is explicitly included."; |
3094 | 0 | outputList->push_back(err); |
3095 | 0 | } |
3096 | 0 | continue; |
3097 | 0 | } |
3098 | 0 | fin.close(); |
3099 | |
|
3100 | 0 | TokenList *tokenlist = new TokenList(filename, filenames, outputList); |
3101 | 0 | if (!tokenlist->front()) { |
3102 | 0 | delete tokenlist; |
3103 | 0 | continue; |
3104 | 0 | } |
3105 | | |
3106 | 0 | ret[filename] = tokenlist; |
3107 | 0 | filelist.push_back(tokenlist->front()); |
3108 | 0 | } |
3109 | | |
3110 | 81.8k | for (const Token *rawtok = rawtokens.cfront(); rawtok || !filelist.empty(); rawtok = rawtok ? rawtok->next : nullptr) { |
3111 | 80.4k | if (rawtok == nullptr) { |
3112 | 0 | rawtok = filelist.back(); |
3113 | 0 | filelist.pop_back(); |
3114 | 0 | } |
3115 | | |
3116 | 80.4k | if (rawtok->op != '#' || sameline(rawtok->previousSkipComments(), rawtok)) |
3117 | 80.4k | continue; |
3118 | | |
3119 | 0 | rawtok = rawtok->nextSkipComments(); |
3120 | 0 | if (!rawtok || rawtok->str() != INCLUDE) |
3121 | 0 | continue; |
3122 | | |
3123 | 0 | const std::string &sourcefile = rawtok->location.file(); |
3124 | |
|
3125 | 0 | const Token * const htok = rawtok->nextSkipComments(); |
3126 | 0 | if (!sameline(rawtok, htok)) |
3127 | 0 | continue; |
3128 | | |
3129 | 0 | const bool systemheader = (htok->str()[0] == '<'); |
3130 | 0 | const std::string header(realFilename(htok->str().substr(1U, htok->str().size() - 2U))); |
3131 | 0 | if (hasFile(ret, sourcefile, header, dui, systemheader)) |
3132 | 0 | continue; |
3133 | | |
3134 | 0 | std::ifstream f; |
3135 | 0 | const std::string header2 = openHeader(f,dui,sourcefile,header,systemheader); |
3136 | 0 | if (!f.is_open()) |
3137 | 0 | continue; |
3138 | 0 | f.close(); |
3139 | |
|
3140 | 0 | TokenList *tokens = new TokenList(header2, filenames, outputList); |
3141 | 0 | ret[header2] = tokens; |
3142 | 0 | if (tokens->front()) |
3143 | 0 | filelist.push_back(tokens->front()); |
3144 | 0 | } |
3145 | | |
3146 | 1.36k | return ret; |
3147 | 1.36k | } |
3148 | | |
3149 | | static bool preprocessToken(simplecpp::TokenList &output, const simplecpp::Token **tok1, simplecpp::MacroMap ¯os, std::vector<std::string> &files, simplecpp::OutputList *outputList) |
3150 | 80.4k | { |
3151 | 80.4k | const simplecpp::Token * const tok = *tok1; |
3152 | 80.4k | const simplecpp::MacroMap::const_iterator it = macros.find(tok->str()); |
3153 | 80.4k | if (it != macros.end()) { |
3154 | 0 | simplecpp::TokenList value(files); |
3155 | 0 | try { |
3156 | 0 | *tok1 = it->second.expand(&value, tok, macros, files); |
3157 | 0 | } catch (simplecpp::Macro::Error &err) { |
3158 | 0 | if (outputList) { |
3159 | 0 | simplecpp::Output out(files); |
3160 | 0 | out.type = simplecpp::Output::SYNTAX_ERROR; |
3161 | 0 | out.location = err.location; |
3162 | 0 | out.msg = "failed to expand \'" + tok->str() + "\', " + err.what; |
3163 | 0 | outputList->push_back(out); |
3164 | 0 | } |
3165 | 0 | return false; |
3166 | 0 | } |
3167 | 0 | output.takeTokens(value); |
3168 | 80.4k | } else { |
3169 | 80.4k | if (!tok->comment) |
3170 | 80.4k | output.push_back(new simplecpp::Token(*tok)); |
3171 | 80.4k | *tok1 = tok->next; |
3172 | 80.4k | } |
3173 | 80.4k | return true; |
3174 | 80.4k | } |
3175 | | |
3176 | | static void getLocaltime(struct tm <ime) |
3177 | 1.36k | { |
3178 | 1.36k | time_t t; |
3179 | 1.36k | time(&t); |
3180 | 1.36k | #ifndef _WIN32 |
3181 | | // NOLINTNEXTLINE(misc-include-cleaner) - false positive |
3182 | 1.36k | localtime_r(&t, <ime); |
3183 | | #else |
3184 | | localtime_s(<ime, &t); |
3185 | | #endif |
3186 | 1.36k | } |
3187 | | |
3188 | | static std::string getDateDefine(const struct tm *timep) |
3189 | 1.36k | { |
3190 | 1.36k | char buf[] = "??? ?? ????"; |
3191 | 1.36k | strftime(buf, sizeof(buf), "%b %d %Y", timep); |
3192 | 1.36k | return std::string("\"").append(buf).append("\""); |
3193 | 1.36k | } |
3194 | | |
3195 | | static std::string getTimeDefine(const struct tm *timep) |
3196 | 1.36k | { |
3197 | 1.36k | char buf[] = "??:??:??"; |
3198 | 1.36k | strftime(buf, sizeof(buf), "%T", timep); |
3199 | 1.36k | return std::string("\"").append(buf).append("\""); |
3200 | 1.36k | } |
3201 | | |
3202 | | void simplecpp::preprocess(simplecpp::TokenList &output, const simplecpp::TokenList &rawtokens, std::vector<std::string> &files, std::map<std::string, simplecpp::TokenList *> &filedata, const simplecpp::DUI &dui, simplecpp::OutputList *outputList, std::list<simplecpp::MacroUsage> *macroUsage, std::list<simplecpp::IfCond> *ifCond) |
3203 | 1.36k | { |
3204 | | #ifdef SIMPLECPP_WINDOWS |
3205 | | if (dui.clearIncludeCache) |
3206 | | nonExistingFilesCache.clear(); |
3207 | | #endif |
3208 | | |
3209 | 1.36k | std::map<std::string, std::size_t> sizeOfType(rawtokens.sizeOfType); |
3210 | 1.36k | sizeOfType.insert(std::make_pair("char", sizeof(char))); |
3211 | 1.36k | sizeOfType.insert(std::make_pair("short", sizeof(short))); |
3212 | 1.36k | sizeOfType.insert(std::make_pair("short int", sizeOfType["short"])); |
3213 | 1.36k | sizeOfType.insert(std::make_pair("int", sizeof(int))); |
3214 | 1.36k | sizeOfType.insert(std::make_pair("long", sizeof(long))); |
3215 | 1.36k | sizeOfType.insert(std::make_pair("long int", sizeOfType["long"])); |
3216 | 1.36k | sizeOfType.insert(std::make_pair("long long", sizeof(long long))); |
3217 | 1.36k | sizeOfType.insert(std::make_pair("float", sizeof(float))); |
3218 | 1.36k | sizeOfType.insert(std::make_pair("double", sizeof(double))); |
3219 | 1.36k | sizeOfType.insert(std::make_pair("long double", sizeof(long double))); |
3220 | 1.36k | sizeOfType.insert(std::make_pair("char *", sizeof(char *))); |
3221 | 1.36k | sizeOfType.insert(std::make_pair("short *", sizeof(short *))); |
3222 | 1.36k | sizeOfType.insert(std::make_pair("short int *", sizeOfType["short *"])); |
3223 | 1.36k | sizeOfType.insert(std::make_pair("int *", sizeof(int *))); |
3224 | 1.36k | sizeOfType.insert(std::make_pair("long *", sizeof(long *))); |
3225 | 1.36k | sizeOfType.insert(std::make_pair("long int *", sizeOfType["long *"])); |
3226 | 1.36k | sizeOfType.insert(std::make_pair("long long *", sizeof(long long *))); |
3227 | 1.36k | sizeOfType.insert(std::make_pair("float *", sizeof(float *))); |
3228 | 1.36k | sizeOfType.insert(std::make_pair("double *", sizeof(double *))); |
3229 | 1.36k | sizeOfType.insert(std::make_pair("long double *", sizeof(long double *))); |
3230 | | |
3231 | 1.36k | const bool hasInclude = (dui.std.size() == 5 && dui.std.compare(0,3,"c++") == 0 && dui.std >= "c++17"); |
3232 | 1.36k | MacroMap macros; |
3233 | 1.36k | for (std::list<std::string>::const_iterator it = dui.defines.begin(); it != dui.defines.end(); ++it) { |
3234 | 0 | const std::string ¯ostr = *it; |
3235 | 0 | const std::string::size_type eq = macrostr.find('='); |
3236 | 0 | const std::string::size_type par = macrostr.find('('); |
3237 | 0 | const std::string macroname = macrostr.substr(0, std::min(eq,par)); |
3238 | 0 | if (dui.undefined.find(macroname) != dui.undefined.end()) |
3239 | 0 | continue; |
3240 | 0 | const std::string lhs(macrostr.substr(0,eq)); |
3241 | 0 | const std::string rhs(eq==std::string::npos ? std::string("1") : macrostr.substr(eq+1)); |
3242 | 0 | const Macro macro(lhs, rhs, files); |
3243 | 0 | macros.insert(std::pair<TokenString,Macro>(macro.name(), macro)); |
3244 | 0 | } |
3245 | | |
3246 | 1.36k | macros.insert(std::make_pair("__FILE__", Macro("__FILE__", "__FILE__", files))); |
3247 | 1.36k | macros.insert(std::make_pair("__LINE__", Macro("__LINE__", "__LINE__", files))); |
3248 | 1.36k | macros.insert(std::make_pair("__COUNTER__", Macro("__COUNTER__", "__COUNTER__", files))); |
3249 | 1.36k | struct tm ltime = {}; |
3250 | 1.36k | getLocaltime(ltime); |
3251 | 1.36k | macros.insert(std::make_pair("__DATE__", Macro("__DATE__", getDateDefine(<ime), files))); |
3252 | 1.36k | macros.insert(std::make_pair("__TIME__", Macro("__TIME__", getTimeDefine(<ime), files))); |
3253 | | |
3254 | 1.36k | if (!dui.std.empty()) { |
3255 | 1.36k | std::string std_def = simplecpp::getCStdString(dui.std); |
3256 | 1.36k | if (!std_def.empty()) { |
3257 | 0 | macros.insert(std::make_pair("__STDC_VERSION__", Macro("__STDC_VERSION__", std_def, files))); |
3258 | 1.36k | } else { |
3259 | 1.36k | std_def = simplecpp::getCppStdString(dui.std); |
3260 | 1.36k | if (!std_def.empty()) |
3261 | 1.36k | macros.insert(std::make_pair("__cplusplus", Macro("__cplusplus", std_def, files))); |
3262 | 1.36k | } |
3263 | 1.36k | } |
3264 | | |
3265 | | // True => code in current #if block should be kept |
3266 | | // ElseIsTrue => code in current #if block should be dropped. the code in the #else should be kept. |
3267 | | // AlwaysFalse => drop all code in #if and #else |
3268 | 1.36k | enum IfState { True, ElseIsTrue, AlwaysFalse }; |
3269 | 1.36k | std::stack<int> ifstates; |
3270 | 1.36k | ifstates.push(True); |
3271 | | |
3272 | 1.36k | std::stack<const Token *> includetokenstack; |
3273 | | |
3274 | 1.36k | std::set<std::string> pragmaOnce; |
3275 | | |
3276 | 1.36k | includetokenstack.push(rawtokens.cfront()); |
3277 | 1.36k | for (std::list<std::string>::const_iterator it = dui.includes.begin(); it != dui.includes.end(); ++it) { |
3278 | 0 | const std::map<std::string, TokenList*>::const_iterator f = filedata.find(*it); |
3279 | 0 | if (f != filedata.end()) |
3280 | 0 | includetokenstack.push(f->second->cfront()); |
3281 | 0 | } |
3282 | | |
3283 | 1.36k | std::map<std::string, std::list<Location> > maybeUsedMacros; |
3284 | | |
3285 | 83.2k | for (const Token *rawtok = nullptr; rawtok || !includetokenstack.empty();) { |
3286 | 81.8k | if (rawtok == nullptr) { |
3287 | 1.36k | rawtok = includetokenstack.top(); |
3288 | 1.36k | includetokenstack.pop(); |
3289 | 1.36k | continue; |
3290 | 1.36k | } |
3291 | | |
3292 | 80.4k | if (rawtok->op == '#' && !sameline(rawtok->previousSkipComments(), rawtok)) { |
3293 | 0 | if (!sameline(rawtok, rawtok->next)) { |
3294 | 0 | rawtok = rawtok->next; |
3295 | 0 | continue; |
3296 | 0 | } |
3297 | 0 | rawtok = rawtok->next; |
3298 | 0 | if (!rawtok->name) { |
3299 | 0 | rawtok = gotoNextLine(rawtok); |
3300 | 0 | continue; |
3301 | 0 | } |
3302 | | |
3303 | 0 | if (ifstates.size() <= 1U && (rawtok->str() == ELIF || rawtok->str() == ELSE || rawtok->str() == ENDIF)) { |
3304 | 0 | if (outputList) { |
3305 | 0 | simplecpp::Output err(files); |
3306 | 0 | err.type = Output::SYNTAX_ERROR; |
3307 | 0 | err.location = rawtok->location; |
3308 | 0 | err.msg = "#" + rawtok->str() + " without #if"; |
3309 | 0 | outputList->push_back(err); |
3310 | 0 | } |
3311 | 0 | output.clear(); |
3312 | 0 | return; |
3313 | 0 | } |
3314 | | |
3315 | 0 | if (ifstates.top() == True && (rawtok->str() == ERROR || rawtok->str() == WARNING)) { |
3316 | 0 | if (outputList) { |
3317 | 0 | simplecpp::Output err(rawtok->location.files); |
3318 | 0 | err.type = rawtok->str() == ERROR ? Output::ERROR : Output::WARNING; |
3319 | 0 | err.location = rawtok->location; |
3320 | 0 | for (const Token *tok = rawtok->next; tok && sameline(rawtok,tok); tok = tok->next) { |
3321 | 0 | if (!err.msg.empty() && isNameChar(tok->str()[0])) |
3322 | 0 | err.msg += ' '; |
3323 | 0 | err.msg += tok->str(); |
3324 | 0 | } |
3325 | 0 | err.msg = '#' + rawtok->str() + ' ' + err.msg; |
3326 | 0 | outputList->push_back(err); |
3327 | 0 | } |
3328 | 0 | if (rawtok->str() == ERROR) { |
3329 | 0 | output.clear(); |
3330 | 0 | return; |
3331 | 0 | } |
3332 | 0 | } |
3333 | | |
3334 | 0 | if (rawtok->str() == DEFINE) { |
3335 | 0 | if (ifstates.top() != True) |
3336 | 0 | continue; |
3337 | 0 | try { |
3338 | 0 | const Macro ¯o = Macro(rawtok->previous, files); |
3339 | 0 | if (dui.undefined.find(macro.name()) == dui.undefined.end()) { |
3340 | 0 | const MacroMap::iterator it = macros.find(macro.name()); |
3341 | 0 | if (it == macros.end()) |
3342 | 0 | macros.insert(std::pair<TokenString, Macro>(macro.name(), macro)); |
3343 | 0 | else |
3344 | 0 | it->second = macro; |
3345 | 0 | } |
3346 | 0 | } catch (const std::runtime_error &) { |
3347 | 0 | if (outputList) { |
3348 | 0 | simplecpp::Output err(files); |
3349 | 0 | err.type = Output::SYNTAX_ERROR; |
3350 | 0 | err.location = rawtok->location; |
3351 | 0 | err.msg = "Failed to parse #define"; |
3352 | 0 | outputList->push_back(err); |
3353 | 0 | } |
3354 | 0 | output.clear(); |
3355 | 0 | return; |
3356 | 0 | } |
3357 | 0 | } else if (ifstates.top() == True && rawtok->str() == INCLUDE) { |
3358 | 0 | TokenList inc1(files); |
3359 | 0 | for (const Token *inctok = rawtok->next; sameline(rawtok,inctok); inctok = inctok->next) { |
3360 | 0 | if (!inctok->comment) |
3361 | 0 | inc1.push_back(new Token(*inctok)); |
3362 | 0 | } |
3363 | 0 | TokenList inc2(files); |
3364 | 0 | if (!inc1.empty() && inc1.cfront()->name) { |
3365 | 0 | const Token *inctok = inc1.cfront(); |
3366 | 0 | if (!preprocessToken(inc2, &inctok, macros, files, outputList)) { |
3367 | 0 | output.clear(); |
3368 | 0 | return; |
3369 | 0 | } |
3370 | 0 | } else { |
3371 | 0 | inc2.takeTokens(inc1); |
3372 | 0 | } |
3373 | | |
3374 | 0 | if (!inc2.empty() && inc2.cfront()->op == '<' && inc2.cback()->op == '>') { |
3375 | 0 | TokenString hdr; |
3376 | | // TODO: Sometimes spaces must be added in the string |
3377 | | // Somehow preprocessToken etc must be told that the location should be source location not destination location |
3378 | 0 | for (const Token *tok = inc2.cfront(); tok; tok = tok->next) { |
3379 | 0 | hdr += tok->str(); |
3380 | 0 | } |
3381 | 0 | inc2.clear(); |
3382 | 0 | inc2.push_back(new Token(hdr, inc1.cfront()->location)); |
3383 | 0 | inc2.front()->op = '<'; |
3384 | 0 | } |
3385 | |
|
3386 | 0 | if (inc2.empty() || inc2.cfront()->str().size() <= 2U) { |
3387 | 0 | if (outputList) { |
3388 | 0 | simplecpp::Output err(files); |
3389 | 0 | err.type = Output::SYNTAX_ERROR; |
3390 | 0 | err.location = rawtok->location; |
3391 | 0 | err.msg = "No header in #include"; |
3392 | 0 | outputList->push_back(err); |
3393 | 0 | } |
3394 | 0 | output.clear(); |
3395 | 0 | return; |
3396 | 0 | } |
3397 | | |
3398 | 0 | const Token * const inctok = inc2.cfront(); |
3399 | |
|
3400 | 0 | const bool systemheader = (inctok->str()[0] == '<'); |
3401 | 0 | const std::string header(realFilename(inctok->str().substr(1U, inctok->str().size() - 2U))); |
3402 | 0 | std::string header2 = getFileName(filedata, rawtok->location.file(), header, dui, systemheader); |
3403 | 0 | if (header2.empty()) { |
3404 | | // try to load file.. |
3405 | 0 | std::ifstream f; |
3406 | 0 | header2 = openHeader(f, dui, rawtok->location.file(), header, systemheader); |
3407 | 0 | if (f.is_open()) { |
3408 | 0 | TokenList * const tokens = new TokenList(f, files, header2, outputList); |
3409 | 0 | filedata[header2] = tokens; |
3410 | 0 | } |
3411 | 0 | } |
3412 | 0 | if (header2.empty()) { |
3413 | 0 | if (outputList) { |
3414 | 0 | simplecpp::Output out(files); |
3415 | 0 | out.type = Output::MISSING_HEADER; |
3416 | 0 | out.location = rawtok->location; |
3417 | 0 | out.msg = "Header not found: " + inctok->str(); |
3418 | 0 | outputList->push_back(out); |
3419 | 0 | } |
3420 | 0 | } else if (includetokenstack.size() >= 400) { |
3421 | 0 | if (outputList) { |
3422 | 0 | simplecpp::Output out(files); |
3423 | 0 | out.type = Output::INCLUDE_NESTED_TOO_DEEPLY; |
3424 | 0 | out.location = rawtok->location; |
3425 | 0 | out.msg = "#include nested too deeply"; |
3426 | 0 | outputList->push_back(out); |
3427 | 0 | } |
3428 | 0 | } else if (pragmaOnce.find(header2) == pragmaOnce.end()) { |
3429 | 0 | includetokenstack.push(gotoNextLine(rawtok)); |
3430 | 0 | const TokenList * const includetokens = filedata.find(header2)->second; |
3431 | 0 | rawtok = includetokens ? includetokens->cfront() : nullptr; |
3432 | 0 | continue; |
3433 | 0 | } |
3434 | 0 | } else if (rawtok->str() == IF || rawtok->str() == IFDEF || rawtok->str() == IFNDEF || rawtok->str() == ELIF) { |
3435 | 0 | if (!sameline(rawtok,rawtok->next)) { |
3436 | 0 | if (outputList) { |
3437 | 0 | simplecpp::Output out(files); |
3438 | 0 | out.type = Output::SYNTAX_ERROR; |
3439 | 0 | out.location = rawtok->location; |
3440 | 0 | out.msg = "Syntax error in #" + rawtok->str(); |
3441 | 0 | outputList->push_back(out); |
3442 | 0 | } |
3443 | 0 | output.clear(); |
3444 | 0 | return; |
3445 | 0 | } |
3446 | | |
3447 | 0 | bool conditionIsTrue; |
3448 | 0 | if (ifstates.top() == AlwaysFalse || (ifstates.top() == ElseIsTrue && rawtok->str() != ELIF)) |
3449 | 0 | conditionIsTrue = false; |
3450 | 0 | else if (rawtok->str() == IFDEF) { |
3451 | 0 | conditionIsTrue = (macros.find(rawtok->next->str()) != macros.end() || (hasInclude && rawtok->next->str() == HAS_INCLUDE)); |
3452 | 0 | maybeUsedMacros[rawtok->next->str()].push_back(rawtok->next->location); |
3453 | 0 | } else if (rawtok->str() == IFNDEF) { |
3454 | 0 | conditionIsTrue = (macros.find(rawtok->next->str()) == macros.end() && !(hasInclude && rawtok->next->str() == HAS_INCLUDE)); |
3455 | 0 | maybeUsedMacros[rawtok->next->str()].push_back(rawtok->next->location); |
3456 | 0 | } else { /*if (rawtok->str() == IF || rawtok->str() == ELIF)*/ |
3457 | 0 | TokenList expr(files); |
3458 | 0 | for (const Token *tok = rawtok->next; tok && tok->location.sameline(rawtok->location); tok = tok->next) { |
3459 | 0 | if (!tok->name) { |
3460 | 0 | expr.push_back(new Token(*tok)); |
3461 | 0 | continue; |
3462 | 0 | } |
3463 | | |
3464 | 0 | if (tok->str() == DEFINED) { |
3465 | 0 | tok = tok->next; |
3466 | 0 | const bool par = (tok && tok->op == '('); |
3467 | 0 | if (par) |
3468 | 0 | tok = tok->next; |
3469 | 0 | maybeUsedMacros[rawtok->next->str()].push_back(rawtok->next->location); |
3470 | 0 | if (tok) { |
3471 | 0 | if (macros.find(tok->str()) != macros.end()) |
3472 | 0 | expr.push_back(new Token("1", tok->location)); |
3473 | 0 | else if (hasInclude && tok->str() == HAS_INCLUDE) |
3474 | 0 | expr.push_back(new Token("1", tok->location)); |
3475 | 0 | else |
3476 | 0 | expr.push_back(new Token("0", tok->location)); |
3477 | 0 | } |
3478 | 0 | if (par) |
3479 | 0 | tok = tok ? tok->next : nullptr; |
3480 | 0 | if (!tok || !sameline(rawtok,tok) || (par && tok->op != ')')) { |
3481 | 0 | if (outputList) { |
3482 | 0 | Output out(rawtok->location.files); |
3483 | 0 | out.type = Output::SYNTAX_ERROR; |
3484 | 0 | out.location = rawtok->location; |
3485 | 0 | out.msg = "failed to evaluate " + std::string(rawtok->str() == IF ? "#if" : "#elif") + " condition"; |
3486 | 0 | outputList->push_back(out); |
3487 | 0 | } |
3488 | 0 | output.clear(); |
3489 | 0 | return; |
3490 | 0 | } |
3491 | 0 | continue; |
3492 | 0 | } |
3493 | | |
3494 | 0 | if (hasInclude && tok->str() == HAS_INCLUDE) { |
3495 | 0 | tok = tok->next; |
3496 | 0 | const bool par = (tok && tok->op == '('); |
3497 | 0 | if (par) |
3498 | 0 | tok = tok->next; |
3499 | 0 | bool closingAngularBracket = false; |
3500 | 0 | if (tok) { |
3501 | 0 | const std::string &sourcefile = rawtok->location.file(); |
3502 | 0 | const bool systemheader = (tok && tok->op == '<'); |
3503 | 0 | std::string header; |
3504 | |
|
3505 | 0 | if (systemheader) { |
3506 | 0 | while ((tok = tok->next) && tok->op != '>') |
3507 | 0 | header += tok->str(); |
3508 | | // cppcheck-suppress selfAssignment - platform-dependent implementation |
3509 | 0 | header = realFilename(header); |
3510 | 0 | if (tok && tok->op == '>') |
3511 | 0 | closingAngularBracket = true; |
3512 | 0 | } |
3513 | 0 | else { |
3514 | 0 | header = realFilename(tok->str().substr(1U, tok->str().size() - 2U)); |
3515 | 0 | closingAngularBracket = true; |
3516 | 0 | } |
3517 | 0 | std::ifstream f; |
3518 | 0 | const std::string header2 = openHeader(f,dui,sourcefile,header,systemheader); |
3519 | 0 | expr.push_back(new Token(header2.empty() ? "0" : "1", tok->location)); |
3520 | 0 | } |
3521 | 0 | if (par) |
3522 | 0 | tok = tok ? tok->next : nullptr; |
3523 | 0 | if (!tok || !sameline(rawtok,tok) || (par && tok->op != ')') || (!closingAngularBracket)) { |
3524 | 0 | if (outputList) { |
3525 | 0 | Output out(rawtok->location.files); |
3526 | 0 | out.type = Output::SYNTAX_ERROR; |
3527 | 0 | out.location = rawtok->location; |
3528 | 0 | out.msg = "failed to evaluate " + std::string(rawtok->str() == IF ? "#if" : "#elif") + " condition"; |
3529 | 0 | outputList->push_back(out); |
3530 | 0 | } |
3531 | 0 | output.clear(); |
3532 | 0 | return; |
3533 | 0 | } |
3534 | 0 | continue; |
3535 | 0 | } |
3536 | | |
3537 | 0 | maybeUsedMacros[rawtok->next->str()].push_back(rawtok->next->location); |
3538 | |
|
3539 | 0 | const Token *tmp = tok; |
3540 | 0 | if (!preprocessToken(expr, &tmp, macros, files, outputList)) { |
3541 | 0 | output.clear(); |
3542 | 0 | return; |
3543 | 0 | } |
3544 | 0 | if (!tmp) |
3545 | 0 | break; |
3546 | 0 | tok = tmp->previous; |
3547 | 0 | } |
3548 | 0 | try { |
3549 | 0 | if (ifCond) { |
3550 | 0 | std::string E; |
3551 | 0 | for (const simplecpp::Token *tok = expr.cfront(); tok; tok = tok->next) |
3552 | 0 | E += (E.empty() ? "" : " ") + tok->str(); |
3553 | 0 | const long long result = evaluate(expr, dui, sizeOfType); |
3554 | 0 | conditionIsTrue = (result != 0); |
3555 | 0 | ifCond->push_back(IfCond(rawtok->location, E, result)); |
3556 | 0 | } else { |
3557 | 0 | const long long result = evaluate(expr, dui, sizeOfType); |
3558 | 0 | conditionIsTrue = (result != 0); |
3559 | 0 | } |
3560 | 0 | } catch (const std::exception &e) { |
3561 | 0 | if (outputList) { |
3562 | 0 | Output out(rawtok->location.files); |
3563 | 0 | out.type = Output::SYNTAX_ERROR; |
3564 | 0 | out.location = rawtok->location; |
3565 | 0 | out.msg = "failed to evaluate " + std::string(rawtok->str() == IF ? "#if" : "#elif") + " condition"; |
3566 | 0 | if (e.what() && *e.what()) |
3567 | 0 | out.msg += std::string(", ") + e.what(); |
3568 | 0 | outputList->push_back(out); |
3569 | 0 | } |
3570 | 0 | output.clear(); |
3571 | 0 | return; |
3572 | 0 | } |
3573 | 0 | } |
3574 | | |
3575 | 0 | if (rawtok->str() != ELIF) { |
3576 | | // push a new ifstate.. |
3577 | 0 | if (ifstates.top() != True) |
3578 | 0 | ifstates.push(AlwaysFalse); |
3579 | 0 | else |
3580 | 0 | ifstates.push(conditionIsTrue ? True : ElseIsTrue); |
3581 | 0 | } else if (ifstates.top() == True) { |
3582 | 0 | ifstates.top() = AlwaysFalse; |
3583 | 0 | } else if (ifstates.top() == ElseIsTrue && conditionIsTrue) { |
3584 | 0 | ifstates.top() = True; |
3585 | 0 | } |
3586 | 0 | } else if (rawtok->str() == ELSE) { |
3587 | 0 | ifstates.top() = (ifstates.top() == ElseIsTrue) ? True : AlwaysFalse; |
3588 | 0 | } else if (rawtok->str() == ENDIF) { |
3589 | 0 | ifstates.pop(); |
3590 | 0 | } else if (rawtok->str() == UNDEF) { |
3591 | 0 | if (ifstates.top() == True) { |
3592 | 0 | const Token *tok = rawtok->next; |
3593 | 0 | while (sameline(rawtok,tok) && tok->comment) |
3594 | 0 | tok = tok->next; |
3595 | 0 | if (sameline(rawtok, tok)) |
3596 | 0 | macros.erase(tok->str()); |
3597 | 0 | } |
3598 | 0 | } else if (ifstates.top() == True && rawtok->str() == PRAGMA && rawtok->next && rawtok->next->str() == ONCE && sameline(rawtok,rawtok->next)) { |
3599 | 0 | pragmaOnce.insert(rawtok->location.file()); |
3600 | 0 | } |
3601 | 0 | rawtok = gotoNextLine(rawtok); |
3602 | 0 | continue; |
3603 | 0 | } |
3604 | | |
3605 | 80.4k | if (ifstates.top() != True) { |
3606 | | // drop code |
3607 | 0 | rawtok = gotoNextLine(rawtok); |
3608 | 0 | continue; |
3609 | 0 | } |
3610 | | |
3611 | 80.4k | bool hash=false, hashhash=false; |
3612 | 80.4k | if (rawtok->op == '#' && sameline(rawtok,rawtok->next)) { |
3613 | 0 | if (rawtok->next->op != '#') { |
3614 | 0 | hash = true; |
3615 | 0 | rawtok = rawtok->next; // skip '#' |
3616 | 0 | } else if (sameline(rawtok,rawtok->next->next)) { |
3617 | 0 | hashhash = true; |
3618 | 0 | rawtok = rawtok->next->next; // skip '#' '#' |
3619 | 0 | } |
3620 | 0 | } |
3621 | | |
3622 | 80.4k | const Location loc(rawtok->location); |
3623 | 80.4k | TokenList tokens(files); |
3624 | | |
3625 | 80.4k | if (!preprocessToken(tokens, &rawtok, macros, files, outputList)) { |
3626 | 0 | output.clear(); |
3627 | 0 | return; |
3628 | 0 | } |
3629 | | |
3630 | 80.4k | if (hash || hashhash) { |
3631 | 0 | std::string s; |
3632 | 0 | for (const Token *hashtok = tokens.cfront(); hashtok; hashtok = hashtok->next) |
3633 | 0 | s += hashtok->str(); |
3634 | 0 | if (hash) |
3635 | 0 | output.push_back(new Token('\"' + s + '\"', loc)); |
3636 | 0 | else if (output.back()) |
3637 | 0 | output.back()->setstr(output.cback()->str() + s); |
3638 | 0 | else |
3639 | 0 | output.push_back(new Token(s, loc)); |
3640 | 80.4k | } else { |
3641 | 80.4k | output.takeTokens(tokens); |
3642 | 80.4k | } |
3643 | 80.4k | } |
3644 | | |
3645 | 1.36k | if (macroUsage) { |
3646 | 9.53k | for (simplecpp::MacroMap::const_iterator macroIt = macros.begin(); macroIt != macros.end(); ++macroIt) { |
3647 | 8.17k | const Macro ¯o = macroIt->second; |
3648 | 8.17k | std::list<Location> usage = macro.usage(); |
3649 | 8.17k | const std::list<Location>& temp = maybeUsedMacros[macro.name()]; |
3650 | 8.17k | usage.insert(usage.end(), temp.begin(), temp.end()); |
3651 | 8.17k | for (std::list<Location>::const_iterator usageIt = usage.begin(); usageIt != usage.end(); ++usageIt) { |
3652 | 0 | MacroUsage mu(usageIt->files, macro.valueDefinedInCode()); |
3653 | 0 | mu.macroName = macro.name(); |
3654 | 0 | mu.macroLocation = macro.defineLocation(); |
3655 | 0 | mu.useLocation = *usageIt; |
3656 | 0 | macroUsage->push_back(mu); |
3657 | 0 | } |
3658 | 8.17k | } |
3659 | 1.36k | } |
3660 | 1.36k | } |
3661 | | |
3662 | | void simplecpp::cleanup(std::map<std::string, TokenList*> &filedata) |
3663 | 0 | { |
3664 | 0 | for (std::map<std::string, TokenList*>::iterator it = filedata.begin(); it != filedata.end(); ++it) |
3665 | 0 | delete it->second; |
3666 | 0 | filedata.clear(); |
3667 | 0 | } |
3668 | | |
3669 | | std::string simplecpp::getCStdString(const std::string &std) |
3670 | 1.36k | { |
3671 | 1.36k | if (std == "c90" || std == "c89" || std == "iso9899:1990" || std == "iso9899:199409" || std == "gnu90" || std == "gnu89") { |
3672 | | // __STDC_VERSION__ is not set for C90 although the macro was added in the 1994 amendments |
3673 | 0 | return ""; |
3674 | 0 | } |
3675 | 1.36k | if (std == "c99" || std == "c9x" || std == "iso9899:1999" || std == "iso9899:199x" || std == "gnu99"|| std == "gnu9x") |
3676 | 0 | return "199901L"; |
3677 | 1.36k | if (std == "c11" || std == "c1x" || std == "iso9899:2011" || std == "gnu11" || std == "gnu1x") |
3678 | 0 | return "201112L"; |
3679 | 1.36k | if (std == "c17" || std == "c18" || std == "iso9899:2017" || std == "iso9899:2018" || std == "gnu17"|| std == "gnu18") |
3680 | 0 | return "201710L"; |
3681 | 1.36k | if (std == "c23" || std == "gnu23" || std == "c2x" || std == "gnu2x") { |
3682 | | // supported by GCC 9+ and Clang 9+ |
3683 | | // Clang 9, 10, 11, 12, 13 return "201710L" |
3684 | | // Clang 14, 15, 16, 17 return "202000L" |
3685 | | // Clang 9, 10, 11, 12, 13, 14, 15, 16, 17 do not support "c23" and "gnu23" |
3686 | 0 | return "202311L"; |
3687 | 0 | } |
3688 | 1.36k | return ""; |
3689 | 1.36k | } |
3690 | | |
3691 | | std::string simplecpp::getCppStdString(const std::string &std) |
3692 | 1.36k | { |
3693 | 1.36k | if (std == "c++98" || std == "c++03" || std == "gnu++98" || std == "gnu++03") |
3694 | 0 | return "199711L"; |
3695 | 1.36k | if (std == "c++11" || std == "gnu++11" || std == "c++0x" || std == "gnu++0x") |
3696 | 0 | return "201103L"; |
3697 | 1.36k | if (std == "c++14" || std == "c++1y" || std == "gnu++14" || std == "gnu++1y") |
3698 | 0 | return "201402L"; |
3699 | 1.36k | if (std == "c++17" || std == "c++1z" || std == "gnu++17" || std == "gnu++1z") |
3700 | 0 | return "201703L"; |
3701 | 1.36k | if (std == "c++20" || std == "c++2a" || std == "gnu++20" || std == "gnu++2a") { |
3702 | | // GCC 10 returns "201703L" - correct in 11+ |
3703 | 0 | return "202002L"; |
3704 | 0 | } |
3705 | 1.36k | if (std == "c++23" || std == "c++2b" || std == "gnu++23" || std == "gnu++2b") { |
3706 | | // supported by GCC 11+ and Clang 12+ |
3707 | | // GCC 11, 12, 13 return "202100L" |
3708 | | // Clang 12, 13, 14, 15, 16 do not support "c++23" and "gnu++23" and return "202101L" |
3709 | | // Clang 17, 18 return "202302L" |
3710 | 1.36k | return "202302L"; |
3711 | 1.36k | } |
3712 | 0 | if (std == "c++26" || std == "c++2c" || std == "gnu++26" || std == "gnu++2c") { |
3713 | | // supported by Clang 17+ |
3714 | 0 | return "202400L"; |
3715 | 0 | } |
3716 | 0 | return ""; |
3717 | 0 | } |
3718 | | |
3719 | | #if (__cplusplus < 201103L) && !defined(__APPLE__) |
3720 | | #undef nullptr |
3721 | | #endif |