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