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