|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). |
|
4 ** All rights reserved. |
|
5 ** Contact: Nokia Corporation (qt-info@nokia.com) |
|
6 ** |
|
7 ** This file is part of the tools applications of the Qt Toolkit. |
|
8 ** |
|
9 ** $QT_BEGIN_LICENSE:LGPL$ |
|
10 ** No Commercial Usage |
|
11 ** This file contains pre-release code and may not be distributed. |
|
12 ** You may use this file in accordance with the terms and conditions |
|
13 ** contained in the Technology Preview License Agreement accompanying |
|
14 ** this package. |
|
15 ** |
|
16 ** GNU Lesser General Public License Usage |
|
17 ** Alternatively, this file may be used under the terms of the GNU Lesser |
|
18 ** General Public License version 2.1 as published by the Free Software |
|
19 ** Foundation and appearing in the file LICENSE.LGPL included in the |
|
20 ** packaging of this file. Please review the following information to |
|
21 ** ensure the GNU Lesser General Public License version 2.1 requirements |
|
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
|
23 ** |
|
24 ** In addition, as a special exception, Nokia gives you certain additional |
|
25 ** rights. These rights are described in the Nokia Qt LGPL Exception |
|
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
|
27 ** |
|
28 ** If you have questions regarding the use of this file, please contact |
|
29 ** Nokia at qt-info@nokia.com. |
|
30 ** |
|
31 ** |
|
32 ** |
|
33 ** |
|
34 ** |
|
35 ** |
|
36 ** |
|
37 ** |
|
38 ** $QT_END_LICENSE$ |
|
39 ** |
|
40 ****************************************************************************/ |
|
41 |
|
42 #include "config.h" |
|
43 #include "tokenizer.h" |
|
44 |
|
45 #include <qdebug.h> |
|
46 #include <qfile.h> |
|
47 #include <qhash.h> |
|
48 #include <qregexp.h> |
|
49 #include <qstring.h> |
|
50 |
|
51 #include <ctype.h> |
|
52 #include <string.h> |
|
53 |
|
54 QT_BEGIN_NAMESPACE |
|
55 |
|
56 #define LANGUAGE_CPP "Cpp" |
|
57 |
|
58 /* qmake ignore Q_OBJECT */ |
|
59 |
|
60 /* |
|
61 Keep in sync with tokenizer.h. |
|
62 */ |
|
63 static const char *kwords[] = { |
|
64 "char", "class", "const", "double", "enum", "explicit", |
|
65 "friend", "inline", "int", "long", "namespace", "operator", |
|
66 "private", "protected", "public", "short", "signals", "signed", |
|
67 "slots", "static", "struct", "template", "typedef", "typename", |
|
68 "union", "unsigned", "using", "virtual", "void", "volatile", |
|
69 "__int64", "Q_OBJECT", "Q_OVERRIDE", "Q_PROPERTY", |
|
70 "Q_DECLARE_SEQUENTIAL_ITERATOR", |
|
71 "Q_DECLARE_MUTABLE_SEQUENTIAL_ITERATOR", |
|
72 "Q_DECLARE_ASSOCIATIVE_ITERATOR", |
|
73 "Q_DECLARE_MUTABLE_ASSOCIATIVE_ITERATOR", |
|
74 "Q_DECLARE_FLAGS", |
|
75 "Q_SIGNALS", |
|
76 "Q_SLOTS", |
|
77 "QT_COMPAT", |
|
78 "QT_COMPAT_CONSTRUCTOR", |
|
79 "QT_DEPRECATED", |
|
80 "QT_MOC_COMPAT", |
|
81 "QT_MODULE", |
|
82 "QT3_SUPPORT", |
|
83 "QT3_SUPPORT_CONSTRUCTOR", |
|
84 "QT3_MOC_SUPPORT", |
|
85 "QDOC_PROPERTY" |
|
86 }; |
|
87 |
|
88 static const int KwordHashTableSize = 4096; |
|
89 static int kwordHashTable[KwordHashTableSize]; |
|
90 |
|
91 static QHash<QByteArray, bool> *ignoredTokensAndDirectives = 0; |
|
92 |
|
93 static QRegExp *comment = 0; |
|
94 static QRegExp *versionX = 0; |
|
95 static QRegExp *definedX = 0; |
|
96 |
|
97 static QRegExp *defines = 0; |
|
98 static QRegExp *falsehoods = 0; |
|
99 |
|
100 /* |
|
101 This function is a perfect hash function for the 37 keywords of C99 |
|
102 (with a hash table size of 512). It should perform well on our |
|
103 Qt-enhanced C++ subset. |
|
104 */ |
|
105 static int hashKword(const char *s, int len) |
|
106 { |
|
107 return (((uchar) s[0]) + (((uchar) s[2]) << 5) + |
|
108 (((uchar) s[len - 1]) << 3)) % KwordHashTableSize; |
|
109 } |
|
110 |
|
111 static void insertKwordIntoHash(const char *s, int number) |
|
112 { |
|
113 int k = hashKword(s, strlen(s)); |
|
114 while (kwordHashTable[k]) { |
|
115 if (++k == KwordHashTableSize) |
|
116 k = 0; |
|
117 } |
|
118 kwordHashTable[k] = number; |
|
119 } |
|
120 |
|
121 Tokenizer::Tokenizer(const Location& loc, FILE *in) |
|
122 { |
|
123 init(); |
|
124 QFile file; |
|
125 file.open(in, QIODevice::ReadOnly); |
|
126 yyIn = file.readAll(); |
|
127 file.close(); |
|
128 yyPos = 0; |
|
129 start(loc); |
|
130 } |
|
131 |
|
132 Tokenizer::Tokenizer(const Location& loc, const QByteArray &in) |
|
133 : yyIn(in) |
|
134 { |
|
135 init(); |
|
136 yyPos = 0; |
|
137 start(loc); |
|
138 } |
|
139 |
|
140 Tokenizer::~Tokenizer() |
|
141 { |
|
142 delete[] yyLexBuf1; |
|
143 delete[] yyLexBuf2; |
|
144 } |
|
145 |
|
146 int Tokenizer::getToken() |
|
147 { |
|
148 char *t = yyPrevLex; |
|
149 yyPrevLex = yyLex; |
|
150 yyLex = t; |
|
151 |
|
152 while (yyCh != EOF) { |
|
153 yyTokLoc = yyCurLoc; |
|
154 yyLexLen = 0; |
|
155 |
|
156 if (isspace(yyCh)) { |
|
157 do { |
|
158 yyCh = getChar(); |
|
159 } while (isspace(yyCh)); |
|
160 } |
|
161 else if (isalpha(yyCh) || yyCh == '_') { |
|
162 do { |
|
163 yyCh = getChar(); |
|
164 } while (isalnum(yyCh) || yyCh == '_'); |
|
165 |
|
166 int k = hashKword(yyLex, yyLexLen); |
|
167 for (;;) { |
|
168 int i = kwordHashTable[k]; |
|
169 if (i == 0) { |
|
170 return Tok_Ident; |
|
171 } |
|
172 else if (i == -1) { |
|
173 if (!parsingMacro && ignoredTokensAndDirectives->contains(yyLex)) { |
|
174 if (ignoredTokensAndDirectives->value(yyLex)) { // it's a directive |
|
175 int parenDepth = 0; |
|
176 while (yyCh != EOF && (yyCh != ')' || parenDepth > 1)) { |
|
177 if (yyCh == '(') |
|
178 ++parenDepth; |
|
179 else if (yyCh == ')') |
|
180 --parenDepth; |
|
181 yyCh = getChar(); |
|
182 } |
|
183 if (yyCh == ')') |
|
184 yyCh = getChar(); |
|
185 } |
|
186 break; |
|
187 } |
|
188 } |
|
189 else if (strcmp(yyLex, kwords[i - 1]) == 0) { |
|
190 int ret = (int) Tok_FirstKeyword + i - 1; |
|
191 if (ret != Tok_explicit && ret != Tok_inline && ret != Tok_typename) |
|
192 return ret; |
|
193 break; |
|
194 } |
|
195 |
|
196 if (++k == KwordHashTableSize) |
|
197 k = 0; |
|
198 } |
|
199 } |
|
200 else if (isdigit(yyCh)) { |
|
201 do { |
|
202 yyCh = getChar(); |
|
203 } while (isalnum(yyCh) || yyCh == '.' || yyCh == '+' || |
|
204 yyCh == '-'); |
|
205 return Tok_Number; |
|
206 } |
|
207 else { |
|
208 switch (yyCh) { |
|
209 case '!': |
|
210 case '%': |
|
211 yyCh = getChar(); |
|
212 if (yyCh == '=') |
|
213 yyCh = getChar(); |
|
214 return Tok_SomeOperator; |
|
215 case '"': |
|
216 yyCh = getChar(); |
|
217 |
|
218 while (yyCh != EOF && yyCh != '"') { |
|
219 if (yyCh == '\\') |
|
220 yyCh = getChar(); |
|
221 yyCh = getChar(); |
|
222 } |
|
223 yyCh = getChar(); |
|
224 |
|
225 if (yyCh == EOF) |
|
226 yyTokLoc.warning(tr("Unterminated C++ string literal"), |
|
227 tr("Maybe you forgot '/*!' at the beginning of the file?")); |
|
228 else |
|
229 return Tok_String; |
|
230 break; |
|
231 case '#': |
|
232 return getTokenAfterPreprocessor(); |
|
233 case '&': |
|
234 yyCh = getChar(); |
|
235 if (yyCh == '&' || yyCh == '=') { |
|
236 yyCh = getChar(); |
|
237 return Tok_SomeOperator; |
|
238 } |
|
239 else { |
|
240 return Tok_Ampersand; |
|
241 } |
|
242 case '\'': |
|
243 yyCh = getChar(); |
|
244 if (yyCh == '\\') |
|
245 yyCh = getChar(); |
|
246 do { |
|
247 yyCh = getChar(); |
|
248 } while (yyCh != EOF && yyCh != '\''); |
|
249 |
|
250 if (yyCh == EOF) { |
|
251 yyTokLoc.warning(tr("Unterminated C++ character" |
|
252 " literal")); |
|
253 } |
|
254 else { |
|
255 yyCh = getChar(); |
|
256 return Tok_Number; |
|
257 } |
|
258 break; |
|
259 case '(': |
|
260 yyCh = getChar(); |
|
261 if (yyNumPreprocessorSkipping == 0) |
|
262 yyParenDepth++; |
|
263 if (isspace(yyCh)) { |
|
264 do { |
|
265 yyCh = getChar(); |
|
266 } while (isspace(yyCh)); |
|
267 yyLexLen = 1; |
|
268 yyLex[1] = '\0'; |
|
269 } |
|
270 if (yyCh == '*') { |
|
271 yyCh = getChar(); |
|
272 return Tok_LeftParenAster; |
|
273 } |
|
274 return Tok_LeftParen; |
|
275 case ')': |
|
276 yyCh = getChar(); |
|
277 if (yyNumPreprocessorSkipping == 0) |
|
278 yyParenDepth--; |
|
279 return Tok_RightParen; |
|
280 case '*': |
|
281 yyCh = getChar(); |
|
282 if (yyCh == '=') { |
|
283 yyCh = getChar(); |
|
284 return Tok_SomeOperator; |
|
285 } else { |
|
286 return Tok_Aster; |
|
287 } |
|
288 case '^': |
|
289 yyCh = getChar(); |
|
290 if (yyCh == '=') { |
|
291 yyCh = getChar(); |
|
292 return Tok_SomeOperator; |
|
293 } else { |
|
294 return Tok_Caret; |
|
295 } |
|
296 case '+': |
|
297 yyCh = getChar(); |
|
298 if (yyCh == '+' || yyCh == '=') |
|
299 yyCh = getChar(); |
|
300 return Tok_SomeOperator; |
|
301 case ',': |
|
302 yyCh = getChar(); |
|
303 return Tok_Comma; |
|
304 case '-': |
|
305 yyCh = getChar(); |
|
306 if (yyCh == '-' || yyCh == '=') { |
|
307 yyCh = getChar(); |
|
308 } else if (yyCh == '>') { |
|
309 yyCh = getChar(); |
|
310 if (yyCh == '*') |
|
311 yyCh = getChar(); |
|
312 } |
|
313 return Tok_SomeOperator; |
|
314 case '.': |
|
315 yyCh = getChar(); |
|
316 if (yyCh == '*') { |
|
317 yyCh = getChar(); |
|
318 } else if (yyCh == '.') { |
|
319 do { |
|
320 yyCh = getChar(); |
|
321 } while (yyCh == '.'); |
|
322 return Tok_Ellipsis; |
|
323 } else if (isdigit(yyCh)) { |
|
324 do { |
|
325 yyCh = getChar(); |
|
326 } while (isalnum(yyCh) || yyCh == '.' || yyCh == '+' || |
|
327 yyCh == '-'); |
|
328 return Tok_Number; |
|
329 } |
|
330 return Tok_SomeOperator; |
|
331 case '/': |
|
332 yyCh = getChar(); |
|
333 if (yyCh == '/') { |
|
334 do { |
|
335 yyCh = getChar(); |
|
336 } while (yyCh != EOF && yyCh != '\n'); |
|
337 } else if (yyCh == '*') { |
|
338 bool metDoc = false; // empty doc is no doc |
|
339 bool metSlashAsterBang = false; |
|
340 bool metAster = false; |
|
341 bool metAsterSlash = false; |
|
342 |
|
343 yyCh = getChar(); |
|
344 if (yyCh == '!') |
|
345 metSlashAsterBang = true; |
|
346 |
|
347 while (!metAsterSlash) { |
|
348 if (yyCh == EOF) { |
|
349 yyTokLoc.warning(tr("Unterminated C++ comment")); |
|
350 break; |
|
351 } else { |
|
352 if (yyCh == '*') { |
|
353 metAster = true; |
|
354 } else if (metAster && yyCh == '/') { |
|
355 metAsterSlash = true; |
|
356 } else { |
|
357 metAster = false; |
|
358 if (isgraph(yyCh)) |
|
359 metDoc = true; |
|
360 } |
|
361 } |
|
362 yyCh = getChar(); |
|
363 } |
|
364 if (metSlashAsterBang && metDoc) |
|
365 return Tok_Doc; |
|
366 else if (yyParenDepth > 0) |
|
367 return Tok_Comment; |
|
368 } else { |
|
369 if (yyCh == '=') |
|
370 yyCh = getChar(); |
|
371 return Tok_SomeOperator; |
|
372 } |
|
373 break; |
|
374 case ':': |
|
375 yyCh = getChar(); |
|
376 if (yyCh == ':') { |
|
377 yyCh = getChar(); |
|
378 return Tok_Gulbrandsen; |
|
379 } else { |
|
380 return Tok_Colon; |
|
381 } |
|
382 case ';': |
|
383 yyCh = getChar(); |
|
384 return Tok_Semicolon; |
|
385 case '<': |
|
386 yyCh = getChar(); |
|
387 if (yyCh == '<') { |
|
388 yyCh = getChar(); |
|
389 if (yyCh == '=') |
|
390 yyCh = getChar(); |
|
391 return Tok_SomeOperator; |
|
392 } else if (yyCh == '=') { |
|
393 yyCh = getChar(); |
|
394 return Tok_SomeOperator; |
|
395 } else { |
|
396 return Tok_LeftAngle; |
|
397 } |
|
398 case '=': |
|
399 yyCh = getChar(); |
|
400 if (yyCh == '=') { |
|
401 yyCh = getChar(); |
|
402 return Tok_SomeOperator; |
|
403 } else { |
|
404 return Tok_Equal; |
|
405 } |
|
406 case '>': |
|
407 yyCh = getChar(); |
|
408 if (yyCh == '>') { |
|
409 yyCh = getChar(); |
|
410 if (yyCh == '=') |
|
411 yyCh = getChar(); |
|
412 return Tok_SomeOperator; |
|
413 } else if (yyCh == '=') { |
|
414 yyCh = getChar(); |
|
415 return Tok_SomeOperator; |
|
416 } else { |
|
417 return Tok_RightAngle; |
|
418 } |
|
419 case '?': |
|
420 yyCh = getChar(); |
|
421 return Tok_SomeOperator; |
|
422 case '[': |
|
423 yyCh = getChar(); |
|
424 if (yyNumPreprocessorSkipping == 0) |
|
425 yyBracketDepth++; |
|
426 return Tok_LeftBracket; |
|
427 case '\\': |
|
428 yyCh = getChar(); |
|
429 yyCh = getChar(); // skip one character |
|
430 break; |
|
431 case ']': |
|
432 yyCh = getChar(); |
|
433 if (yyNumPreprocessorSkipping == 0) |
|
434 yyBracketDepth--; |
|
435 return Tok_RightBracket; |
|
436 case '{': |
|
437 yyCh = getChar(); |
|
438 if (yyNumPreprocessorSkipping == 0) |
|
439 yyBraceDepth++; |
|
440 return Tok_LeftBrace; |
|
441 case '}': |
|
442 yyCh = getChar(); |
|
443 if (yyNumPreprocessorSkipping == 0) |
|
444 yyBraceDepth--; |
|
445 return Tok_RightBrace; |
|
446 case '|': |
|
447 yyCh = getChar(); |
|
448 if (yyCh == '|' || yyCh == '=') |
|
449 yyCh = getChar(); |
|
450 return Tok_SomeOperator; |
|
451 case '~': |
|
452 yyCh = getChar(); |
|
453 return Tok_Tilde; |
|
454 case '@': |
|
455 yyCh = getChar(); |
|
456 return Tok_At; |
|
457 default: |
|
458 // ### We should really prevent qdoc from looking at snippet files rather than |
|
459 // ### suppress warnings when reading them. |
|
460 if (yyNumPreprocessorSkipping == 0 && !yyTokLoc.fileName().endsWith(".qdoc")) { |
|
461 yyTokLoc.warning(tr("Hostile character 0x%1 in C++ source") |
|
462 .arg((uchar)yyCh, 1, 16)); |
|
463 } |
|
464 yyCh = getChar(); |
|
465 } |
|
466 } |
|
467 } |
|
468 |
|
469 if (yyPreprocessorSkipping.count() > 1) { |
|
470 yyTokLoc.warning(tr("Expected #endif before end of file")); |
|
471 // clear it out or we get an infinite loop! |
|
472 while (!yyPreprocessorSkipping.isEmpty()) { |
|
473 popSkipping(); |
|
474 } |
|
475 } |
|
476 |
|
477 strcpy(yyLex, "end-of-input"); |
|
478 yyLexLen = strlen(yyLex); |
|
479 return Tok_Eoi; |
|
480 } |
|
481 |
|
482 void Tokenizer::initialize(const Config &config) |
|
483 { |
|
484 QString versionSym = config.getString(CONFIG_VERSIONSYM); |
|
485 |
|
486 comment = new QRegExp("/(?:\\*.*\\*/|/.*\n|/[^\n]*$)"); |
|
487 comment->setMinimal(true); |
|
488 versionX = new QRegExp("$cannot possibly match^"); |
|
489 if (!versionSym.isEmpty()) |
|
490 versionX->setPattern("[ \t]*(?:" + QRegExp::escape(versionSym) |
|
491 + ")[ \t]+\"([^\"]*)\"[ \t]*"); |
|
492 definedX = new QRegExp("defined ?\\(?([A-Z_0-9a-z]+) ?\\)"); |
|
493 |
|
494 QStringList d = config.getStringList(CONFIG_DEFINES); |
|
495 d += "qdoc"; |
|
496 defines = new QRegExp(d.join("|")); |
|
497 falsehoods = new QRegExp(config.getStringList(CONFIG_FALSEHOODS).join("|")); |
|
498 |
|
499 memset(kwordHashTable, 0, sizeof(kwordHashTable)); |
|
500 for (int i = 0; i < Tok_LastKeyword - Tok_FirstKeyword + 1; i++) |
|
501 insertKwordIntoHash(kwords[i], i + 1); |
|
502 |
|
503 ignoredTokensAndDirectives = new QHash<QByteArray, bool>; |
|
504 |
|
505 QStringList tokens = config.getStringList(LANGUAGE_CPP + Config::dot + CONFIG_IGNORETOKENS); |
|
506 foreach (const QString &t, tokens) { |
|
507 const QByteArray tb = t.toAscii(); |
|
508 ignoredTokensAndDirectives->insert(tb, false); |
|
509 insertKwordIntoHash(tb.data(), -1); |
|
510 } |
|
511 |
|
512 QStringList directives = config.getStringList(LANGUAGE_CPP + Config::dot |
|
513 + CONFIG_IGNOREDIRECTIVES); |
|
514 foreach (const QString &d, directives) { |
|
515 const QByteArray db = d.toAscii(); |
|
516 ignoredTokensAndDirectives->insert(db, true); |
|
517 insertKwordIntoHash(db.data(), -1); |
|
518 } |
|
519 } |
|
520 |
|
521 void Tokenizer::terminate() |
|
522 { |
|
523 delete comment; |
|
524 comment = 0; |
|
525 delete versionX; |
|
526 versionX = 0; |
|
527 delete definedX; |
|
528 definedX = 0; |
|
529 delete defines; |
|
530 defines = 0; |
|
531 delete falsehoods; |
|
532 falsehoods = 0; |
|
533 delete ignoredTokensAndDirectives; |
|
534 ignoredTokensAndDirectives = 0; |
|
535 } |
|
536 |
|
537 void Tokenizer::init() |
|
538 { |
|
539 yyLexBuf1 = new char[(int) yyLexBufSize]; |
|
540 yyLexBuf2 = new char[(int) yyLexBufSize]; |
|
541 yyPrevLex = yyLexBuf1; |
|
542 yyPrevLex[0] = '\0'; |
|
543 yyLex = yyLexBuf2; |
|
544 yyLex[0] = '\0'; |
|
545 yyLexLen = 0; |
|
546 yyPreprocessorSkipping.push(false); |
|
547 yyNumPreprocessorSkipping = 0; |
|
548 yyBraceDepth = 0; |
|
549 yyParenDepth = 0; |
|
550 yyBracketDepth = 0; |
|
551 yyCh = '\0'; |
|
552 parsingMacro = false; |
|
553 } |
|
554 |
|
555 void Tokenizer::start(const Location& loc) |
|
556 { |
|
557 yyTokLoc = loc; |
|
558 yyCurLoc = loc; |
|
559 yyCurLoc.start(); |
|
560 strcpy(yyPrevLex, "beginning-of-input"); |
|
561 strcpy(yyLex, "beginning-of-input"); |
|
562 yyLexLen = strlen(yyLex); |
|
563 yyBraceDepth = 0; |
|
564 yyParenDepth = 0; |
|
565 yyBracketDepth = 0; |
|
566 yyCh = '\0'; |
|
567 yyCh = getChar(); |
|
568 } |
|
569 |
|
570 /* |
|
571 Returns the next token, if # was met. This function interprets the |
|
572 preprocessor directive, skips over any #ifdef'd out tokens, and returns the |
|
573 token after all of that. |
|
574 */ |
|
575 int Tokenizer::getTokenAfterPreprocessor() |
|
576 { |
|
577 yyCh = getChar(); |
|
578 while (isspace(yyCh) && yyCh != '\n') |
|
579 yyCh = getChar(); |
|
580 |
|
581 /* |
|
582 #directive condition |
|
583 */ |
|
584 QString directive; |
|
585 QString condition; |
|
586 |
|
587 while (isalpha(yyCh)) { |
|
588 directive += QChar(yyCh); |
|
589 yyCh = getChar(); |
|
590 } |
|
591 if (!directive.isEmpty()) { |
|
592 while (yyCh != EOF && yyCh != '\n') { |
|
593 if (yyCh == '\\') |
|
594 yyCh = getChar(); |
|
595 condition += yyCh; |
|
596 yyCh = getChar(); |
|
597 } |
|
598 condition.replace(*comment, ""); |
|
599 condition = condition.simplified(); |
|
600 |
|
601 /* |
|
602 The #if, #ifdef, #ifndef, #elif, #else, and #endif |
|
603 directives have an effect on the skipping stack. For |
|
604 instance, if the code processed so far is |
|
605 |
|
606 #if 1 |
|
607 #if 0 |
|
608 #if 1 |
|
609 // ... |
|
610 #else |
|
611 |
|
612 the skipping stack contains, from bottom to top, false true |
|
613 true (assuming 0 is false and 1 is true). If at least one |
|
614 entry of the stack is true, the tokens are skipped. |
|
615 |
|
616 This mechanism is simple yet hard to understand. |
|
617 */ |
|
618 if (directive[0] == QChar('i')) { |
|
619 if (directive == QString("if")) |
|
620 pushSkipping(!isTrue(condition)); |
|
621 else if (directive == QString("ifdef")) |
|
622 pushSkipping(!defines->exactMatch(condition)); |
|
623 else if (directive == QString("ifndef")) |
|
624 pushSkipping(defines->exactMatch(condition)); |
|
625 } else if (directive[0] == QChar('e')) { |
|
626 if (directive == QString("elif")) { |
|
627 bool old = popSkipping(); |
|
628 if (old) |
|
629 pushSkipping(!isTrue(condition)); |
|
630 else |
|
631 pushSkipping(true); |
|
632 } else if (directive == QString("else")) { |
|
633 pushSkipping(!popSkipping()); |
|
634 } else if (directive == QString("endif")) { |
|
635 popSkipping(); |
|
636 } |
|
637 } else if (directive == QString("define")) { |
|
638 if (versionX->exactMatch(condition)) |
|
639 yyVersion = versionX->cap(1); |
|
640 } |
|
641 } |
|
642 |
|
643 int tok; |
|
644 do { |
|
645 /* |
|
646 We set yyLex now, and after getToken() this will be |
|
647 yyPrevLex. This way, we skip over the preprocessor |
|
648 directive. |
|
649 */ |
|
650 qstrcpy(yyLex, yyPrevLex); |
|
651 |
|
652 /* |
|
653 If getToken() meets another #, it will call |
|
654 getTokenAfterPreprocessor() once again, which could in turn |
|
655 call getToken() again, etc. Unless there are 10,000 or so |
|
656 preprocessor directives in a row, this shouldn't overflow |
|
657 the stack. |
|
658 */ |
|
659 tok = getToken(); |
|
660 } while (yyNumPreprocessorSkipping > 0); |
|
661 return tok; |
|
662 } |
|
663 |
|
664 /* |
|
665 Pushes a new skipping value onto the stack. This corresponds to entering a |
|
666 new #if block. |
|
667 */ |
|
668 void Tokenizer::pushSkipping(bool skip) |
|
669 { |
|
670 yyPreprocessorSkipping.push(skip); |
|
671 if (skip) |
|
672 yyNumPreprocessorSkipping++; |
|
673 } |
|
674 |
|
675 /* |
|
676 Pops a skipping value from the stack. This corresponds to reaching a #endif. |
|
677 */ |
|
678 bool Tokenizer::popSkipping() |
|
679 { |
|
680 if (yyPreprocessorSkipping.isEmpty()) { |
|
681 yyTokLoc.warning(tr("Unexpected #elif, #else or #endif")); |
|
682 return true; |
|
683 } |
|
684 |
|
685 bool skip = yyPreprocessorSkipping.pop(); |
|
686 if (skip) |
|
687 yyNumPreprocessorSkipping--; |
|
688 return skip; |
|
689 } |
|
690 |
|
691 /* |
|
692 Returns true if the condition evaluates as true, otherwise false. The |
|
693 condition is represented by a string. Unsophisticated parsing techniques are |
|
694 used. The preprocessing method could be named StriNg-Oriented PreProcessing, |
|
695 as SNOBOL stands for StriNg-Oriented symBOlic Language. |
|
696 */ |
|
697 bool Tokenizer::isTrue(const QString &condition) |
|
698 { |
|
699 int firstOr = -1; |
|
700 int firstAnd = -1; |
|
701 int parenDepth = 0; |
|
702 |
|
703 /* |
|
704 Find the first logical operator at top level, but be careful |
|
705 about precedence. Examples: |
|
706 |
|
707 X || Y // the or |
|
708 X || Y || Z // the leftmost or |
|
709 X || Y && Z // the or |
|
710 X && Y || Z // the or |
|
711 (X || Y) && Z // the and |
|
712 */ |
|
713 for (int i = 0; i < (int) condition.length() - 1; i++) { |
|
714 QChar ch = condition[i]; |
|
715 if (ch == QChar('(')) { |
|
716 parenDepth++; |
|
717 } else if (ch == QChar(')')) { |
|
718 parenDepth--; |
|
719 } else if (parenDepth == 0) { |
|
720 if (condition[i + 1] == ch) { |
|
721 if (ch == QChar('|')) { |
|
722 firstOr = i; |
|
723 break; |
|
724 } else if (ch == QChar('&')) { |
|
725 if (firstAnd == -1) |
|
726 firstAnd = i; |
|
727 } |
|
728 } |
|
729 } |
|
730 } |
|
731 if (firstOr != -1) |
|
732 return isTrue(condition.left(firstOr)) || |
|
733 isTrue(condition.mid(firstOr + 2)); |
|
734 if (firstAnd != -1) |
|
735 return isTrue(condition.left(firstAnd)) && |
|
736 isTrue(condition.mid(firstAnd + 2)); |
|
737 |
|
738 QString t = condition.simplified(); |
|
739 if (t.isEmpty()) |
|
740 return true; |
|
741 |
|
742 if (t[0] == QChar('!')) |
|
743 return !isTrue(t.mid(1)); |
|
744 if (t[0] == QChar('(') && t.right(1)[0] == QChar(')')) |
|
745 return isTrue(t.mid(1, t.length() - 2)); |
|
746 |
|
747 if (definedX->exactMatch(t)) |
|
748 return defines->exactMatch(definedX->cap(1)); |
|
749 else |
|
750 return !falsehoods->exactMatch(t); |
|
751 } |
|
752 |
|
753 QT_END_NAMESPACE |