| /**************************************************************************** |
| ** |
| ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). |
| ** All rights reserved. |
| ** Contact: Nokia Corporation (qt-info@nokia.com) |
| ** |
| ** This file is part of the tools applications of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:LGPL$ |
| ** GNU Lesser General Public License Usage |
| ** This file may be used under the terms of the GNU Lesser General Public |
| ** License version 2.1 as published by the Free Software Foundation and |
| ** appearing in the file LICENSE.LGPL included in the packaging of this |
| ** file. Please review the following information to ensure the GNU Lesser |
| ** General Public License version 2.1 requirements will be met: |
| ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
| ** |
| ** In addition, as a special exception, Nokia gives you certain additional |
| ** rights. These rights are described in the Nokia Qt LGPL Exception |
| ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU General |
| ** Public License version 3.0 as published by the Free Software Foundation |
| ** and appearing in the file LICENSE.GPL included in the packaging of this |
| ** file. Please review the following information to ensure the GNU General |
| ** Public License version 3.0 requirements will be met: |
| ** http://www.gnu.org/copyleft/gpl.html. |
| ** |
| ** Other Usage |
| ** Alternatively, this file may be used in accordance with the terms and |
| ** conditions contained in a signed written agreement between you and Nokia. |
| ** |
| ** |
| ** |
| ** |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #include "config.h" |
| #include "tokenizer.h" |
| |
| #include <qfile.h> |
| #include <qhash.h> |
| #include <qregexp.h> |
| #include <qstring.h> |
| #include <qtextcodec.h> |
| |
| #include <ctype.h> |
| #include <string.h> |
| |
| QT_BEGIN_NAMESPACE |
| |
| #define LANGUAGE_CPP "Cpp" |
| |
| /* qmake ignore Q_OBJECT */ |
| |
| /* |
| Keep in sync with tokenizer.h. |
| */ |
| static const char *kwords[] = { |
| "char", "class", "const", "double", "enum", "explicit", |
| "friend", "inline", "int", "long", "namespace", "operator", |
| "private", "protected", "public", "short", "signals", "signed", |
| "slots", "static", "struct", "template", "typedef", "typename", |
| "union", "unsigned", "using", "virtual", "void", "volatile", |
| "__int64", |
| "Q_OBJECT", |
| "Q_OVERRIDE", |
| "Q_PROPERTY", |
| "Q_PRIVATE_PROPERTY", |
| "Q_DECLARE_SEQUENTIAL_ITERATOR", |
| "Q_DECLARE_MUTABLE_SEQUENTIAL_ITERATOR", |
| "Q_DECLARE_ASSOCIATIVE_ITERATOR", |
| "Q_DECLARE_MUTABLE_ASSOCIATIVE_ITERATOR", |
| "Q_DECLARE_FLAGS", |
| "Q_SIGNALS", |
| "Q_SLOTS", |
| "QT_COMPAT", |
| "QT_COMPAT_CONSTRUCTOR", |
| "QT_DEPRECATED", |
| "QT_MOC_COMPAT", |
| "QT_MODULE", |
| "QT3_SUPPORT", |
| "QT3_SUPPORT_CONSTRUCTOR", |
| "QT3_MOC_SUPPORT", |
| "QDOC_PROPERTY" |
| }; |
| |
| static const int KwordHashTableSize = 4096; |
| static int kwordHashTable[KwordHashTableSize]; |
| |
| static QHash<QByteArray, bool> *ignoredTokensAndDirectives = 0; |
| |
| static QRegExp *comment = 0; |
| static QRegExp *versionX = 0; |
| static QRegExp *definedX = 0; |
| |
| static QRegExp *defines = 0; |
| static QRegExp *falsehoods = 0; |
| |
| static QTextCodec *sourceCodec = 0; |
| |
| /* |
| This function is a perfect hash function for the 37 keywords of C99 |
| (with a hash table size of 512). It should perform well on our |
| Qt-enhanced C++ subset. |
| */ |
| static int hashKword(const char *s, int len) |
| { |
| return (((uchar) s[0]) + (((uchar) s[2]) << 5) + |
| (((uchar) s[len - 1]) << 3)) % KwordHashTableSize; |
| } |
| |
| static void insertKwordIntoHash(const char *s, int number) |
| { |
| int k = hashKword(s, strlen(s)); |
| while (kwordHashTable[k]) { |
| if (++k == KwordHashTableSize) |
| k = 0; |
| } |
| kwordHashTable[k] = number; |
| } |
| |
| Tokenizer::Tokenizer(const Location& loc, QFile &in) |
| { |
| init(); |
| yyIn = in.readAll(); |
| yyPos = 0; |
| start(loc); |
| } |
| |
| Tokenizer::Tokenizer(const Location& loc, const QByteArray &in) |
| : yyIn(in) |
| { |
| init(); |
| yyPos = 0; |
| start(loc); |
| } |
| |
| Tokenizer::~Tokenizer() |
| { |
| delete[] yyLexBuf1; |
| delete[] yyLexBuf2; |
| } |
| |
| int Tokenizer::getToken() |
| { |
| char *t = yyPrevLex; |
| yyPrevLex = yyLex; |
| yyLex = t; |
| |
| while (yyCh != EOF) { |
| yyTokLoc = yyCurLoc; |
| yyLexLen = 0; |
| |
| if (isspace(yyCh)) { |
| do { |
| yyCh = getChar(); |
| } while (isspace(yyCh)); |
| } |
| else if (isalpha(yyCh) || yyCh == '_') { |
| do { |
| yyCh = getChar(); |
| } while (isalnum(yyCh) || yyCh == '_'); |
| |
| int k = hashKword(yyLex, yyLexLen); |
| for (;;) { |
| int i = kwordHashTable[k]; |
| if (i == 0) { |
| return Tok_Ident; |
| } |
| else if (i == -1) { |
| if (!parsingMacro && ignoredTokensAndDirectives->contains(yyLex)) { |
| if (ignoredTokensAndDirectives->value(yyLex)) { // it's a directive |
| int parenDepth = 0; |
| while (yyCh != EOF && (yyCh != ')' || parenDepth > 1)) { |
| if (yyCh == '(') |
| ++parenDepth; |
| else if (yyCh == ')') |
| --parenDepth; |
| yyCh = getChar(); |
| } |
| if (yyCh == ')') |
| yyCh = getChar(); |
| } |
| break; |
| } |
| } |
| else if (strcmp(yyLex, kwords[i - 1]) == 0) { |
| int ret = (int) Tok_FirstKeyword + i - 1; |
| if (ret != Tok_explicit && ret != Tok_inline && ret != Tok_typename) |
| return ret; |
| break; |
| } |
| |
| if (++k == KwordHashTableSize) |
| k = 0; |
| } |
| } |
| else if (isdigit(yyCh)) { |
| do { |
| yyCh = getChar(); |
| } while (isalnum(yyCh) || yyCh == '.' || yyCh == '+' || |
| yyCh == '-'); |
| return Tok_Number; |
| } |
| else { |
| switch (yyCh) { |
| case '!': |
| case '%': |
| yyCh = getChar(); |
| if (yyCh == '=') |
| yyCh = getChar(); |
| return Tok_SomeOperator; |
| case '"': |
| yyCh = getChar(); |
| |
| while (yyCh != EOF && yyCh != '"') { |
| if (yyCh == '\\') |
| yyCh = getChar(); |
| yyCh = getChar(); |
| } |
| yyCh = getChar(); |
| |
| if (yyCh == EOF) |
| yyTokLoc.warning(tr("Unterminated C++ string literal"), |
| tr("Maybe you forgot '/*!' at the beginning of the file?")); |
| else |
| return Tok_String; |
| break; |
| case '#': |
| return getTokenAfterPreprocessor(); |
| case '&': |
| yyCh = getChar(); |
| if (yyCh == '&' || yyCh == '=') { |
| yyCh = getChar(); |
| return Tok_SomeOperator; |
| } |
| else { |
| return Tok_Ampersand; |
| } |
| case '\'': |
| yyCh = getChar(); |
| if (yyCh == '\\') |
| yyCh = getChar(); |
| do { |
| yyCh = getChar(); |
| } while (yyCh != EOF && yyCh != '\''); |
| |
| if (yyCh == EOF) { |
| yyTokLoc.warning(tr("Unterminated C++ character" |
| " literal")); |
| } |
| else { |
| yyCh = getChar(); |
| return Tok_Number; |
| } |
| break; |
| case '(': |
| yyCh = getChar(); |
| if (yyNumPreprocessorSkipping == 0) |
| yyParenDepth++; |
| if (isspace(yyCh)) { |
| do { |
| yyCh = getChar(); |
| } while (isspace(yyCh)); |
| yyLexLen = 1; |
| yyLex[1] = '\0'; |
| } |
| if (yyCh == '*') { |
| yyCh = getChar(); |
| return Tok_LeftParenAster; |
| } |
| return Tok_LeftParen; |
| case ')': |
| yyCh = getChar(); |
| if (yyNumPreprocessorSkipping == 0) |
| yyParenDepth--; |
| return Tok_RightParen; |
| case '*': |
| yyCh = getChar(); |
| if (yyCh == '=') { |
| yyCh = getChar(); |
| return Tok_SomeOperator; |
| } else { |
| return Tok_Aster; |
| } |
| case '^': |
| yyCh = getChar(); |
| if (yyCh == '=') { |
| yyCh = getChar(); |
| return Tok_SomeOperator; |
| } else { |
| return Tok_Caret; |
| } |
| case '+': |
| yyCh = getChar(); |
| if (yyCh == '+' || yyCh == '=') |
| yyCh = getChar(); |
| return Tok_SomeOperator; |
| case ',': |
| yyCh = getChar(); |
| return Tok_Comma; |
| case '-': |
| yyCh = getChar(); |
| if (yyCh == '-' || yyCh == '=') { |
| yyCh = getChar(); |
| } else if (yyCh == '>') { |
| yyCh = getChar(); |
| if (yyCh == '*') |
| yyCh = getChar(); |
| } |
| return Tok_SomeOperator; |
| case '.': |
| yyCh = getChar(); |
| if (yyCh == '*') { |
| yyCh = getChar(); |
| } else if (yyCh == '.') { |
| do { |
| yyCh = getChar(); |
| } while (yyCh == '.'); |
| return Tok_Ellipsis; |
| } else if (isdigit(yyCh)) { |
| do { |
| yyCh = getChar(); |
| } while (isalnum(yyCh) || yyCh == '.' || yyCh == '+' || |
| yyCh == '-'); |
| return Tok_Number; |
| } |
| return Tok_SomeOperator; |
| case '/': |
| yyCh = getChar(); |
| if (yyCh == '/') { |
| do { |
| yyCh = getChar(); |
| } while (yyCh != EOF && yyCh != '\n'); |
| } else if (yyCh == '*') { |
| bool metDoc = false; // empty doc is no doc |
| bool metSlashAsterBang = false; |
| bool metAster = false; |
| bool metAsterSlash = false; |
| |
| yyCh = getChar(); |
| if (yyCh == '!') |
| metSlashAsterBang = true; |
| |
| while (!metAsterSlash) { |
| if (yyCh == EOF) { |
| yyTokLoc.warning(tr("Unterminated C++ comment")); |
| break; |
| } else { |
| if (yyCh == '*') { |
| metAster = true; |
| } else if (metAster && yyCh == '/') { |
| metAsterSlash = true; |
| } else { |
| metAster = false; |
| if (isgraph(yyCh)) |
| metDoc = true; |
| } |
| } |
| yyCh = getChar(); |
| } |
| if (metSlashAsterBang && metDoc) |
| return Tok_Doc; |
| else if (yyParenDepth > 0) |
| return Tok_Comment; |
| } else { |
| if (yyCh == '=') |
| yyCh = getChar(); |
| return Tok_SomeOperator; |
| } |
| break; |
| case ':': |
| yyCh = getChar(); |
| if (yyCh == ':') { |
| yyCh = getChar(); |
| return Tok_Gulbrandsen; |
| } else { |
| return Tok_Colon; |
| } |
| case ';': |
| yyCh = getChar(); |
| return Tok_Semicolon; |
| case '<': |
| yyCh = getChar(); |
| if (yyCh == '<') { |
| yyCh = getChar(); |
| if (yyCh == '=') |
| yyCh = getChar(); |
| return Tok_SomeOperator; |
| } else if (yyCh == '=') { |
| yyCh = getChar(); |
| return Tok_SomeOperator; |
| } else { |
| return Tok_LeftAngle; |
| } |
| case '=': |
| yyCh = getChar(); |
| if (yyCh == '=') { |
| yyCh = getChar(); |
| return Tok_SomeOperator; |
| } else { |
| return Tok_Equal; |
| } |
| case '>': |
| yyCh = getChar(); |
| if (yyCh == '>') { |
| yyCh = getChar(); |
| if (yyCh == '=') |
| yyCh = getChar(); |
| return Tok_SomeOperator; |
| } else if (yyCh == '=') { |
| yyCh = getChar(); |
| return Tok_SomeOperator; |
| } else { |
| return Tok_RightAngle; |
| } |
| case '?': |
| yyCh = getChar(); |
| return Tok_SomeOperator; |
| case '[': |
| yyCh = getChar(); |
| if (yyNumPreprocessorSkipping == 0) |
| yyBracketDepth++; |
| return Tok_LeftBracket; |
| case '\\': |
| yyCh = getChar(); |
| yyCh = getChar(); // skip one character |
| break; |
| case ']': |
| yyCh = getChar(); |
| if (yyNumPreprocessorSkipping == 0) |
| yyBracketDepth--; |
| return Tok_RightBracket; |
| case '{': |
| yyCh = getChar(); |
| if (yyNumPreprocessorSkipping == 0) |
| yyBraceDepth++; |
| return Tok_LeftBrace; |
| case '}': |
| yyCh = getChar(); |
| if (yyNumPreprocessorSkipping == 0) |
| yyBraceDepth--; |
| return Tok_RightBrace; |
| case '|': |
| yyCh = getChar(); |
| if (yyCh == '|' || yyCh == '=') |
| yyCh = getChar(); |
| return Tok_SomeOperator; |
| case '~': |
| yyCh = getChar(); |
| return Tok_Tilde; |
| case '@': |
| yyCh = getChar(); |
| return Tok_At; |
| default: |
| // ### We should really prevent qdoc from looking at snippet files rather than |
| // ### suppress warnings when reading them. |
| if (yyNumPreprocessorSkipping == 0 && !yyTokLoc.fileName().endsWith(".qdoc")) { |
| yyTokLoc.warning(tr("Hostile character 0x%1 in C++ source") |
| .arg((uchar)yyCh, 1, 16)); |
| } |
| yyCh = getChar(); |
| } |
| } |
| } |
| |
| if (yyPreprocessorSkipping.count() > 1) { |
| yyTokLoc.warning(tr("Expected #endif before end of file")); |
| // clear it out or we get an infinite loop! |
| while (!yyPreprocessorSkipping.isEmpty()) { |
| popSkipping(); |
| } |
| } |
| |
| strcpy(yyLex, "end-of-input"); |
| yyLexLen = strlen(yyLex); |
| return Tok_Eoi; |
| } |
| |
| void Tokenizer::initialize(const Config &config) |
| { |
| QString versionSym = config.getString(CONFIG_VERSIONSYM); |
| |
| QString sourceEncoding = config.getString(CONFIG_SOURCEENCODING); |
| if (sourceEncoding.isEmpty()) |
| sourceEncoding = QLatin1String("ISO-8859-1"); |
| sourceCodec = QTextCodec::codecForName(sourceEncoding.toLocal8Bit()); |
| |
| comment = new QRegExp("/(?:\\*.*\\*/|/.*\n|/[^\n]*$)"); |
| comment->setMinimal(true); |
| versionX = new QRegExp("$cannot possibly match^"); |
| if (!versionSym.isEmpty()) |
| versionX->setPattern("[ \t]*(?:" + QRegExp::escape(versionSym) |
| + ")[ \t]+\"([^\"]*)\"[ \t]*"); |
| definedX = new QRegExp("defined ?\\(?([A-Z_0-9a-z]+) ?\\)"); |
| |
| QStringList d = config.getStringList(CONFIG_DEFINES); |
| d += "qdoc"; |
| defines = new QRegExp(d.join("|")); |
| falsehoods = new QRegExp(config.getStringList(CONFIG_FALSEHOODS).join("|")); |
| |
| memset(kwordHashTable, 0, sizeof(kwordHashTable)); |
| for (int i = 0; i < Tok_LastKeyword - Tok_FirstKeyword + 1; i++) |
| insertKwordIntoHash(kwords[i], i + 1); |
| |
| ignoredTokensAndDirectives = new QHash<QByteArray, bool>; |
| |
| QStringList tokens = config.getStringList(LANGUAGE_CPP + Config::dot + CONFIG_IGNORETOKENS); |
| foreach (const QString &t, tokens) { |
| const QByteArray tb = t.toAscii(); |
| ignoredTokensAndDirectives->insert(tb, false); |
| insertKwordIntoHash(tb.data(), -1); |
| } |
| |
| QStringList directives = config.getStringList(LANGUAGE_CPP + Config::dot |
| + CONFIG_IGNOREDIRECTIVES); |
| foreach (const QString &d, directives) { |
| const QByteArray db = d.toAscii(); |
| ignoredTokensAndDirectives->insert(db, true); |
| insertKwordIntoHash(db.data(), -1); |
| } |
| } |
| |
| void Tokenizer::terminate() |
| { |
| delete comment; |
| comment = 0; |
| delete versionX; |
| versionX = 0; |
| delete definedX; |
| definedX = 0; |
| delete defines; |
| defines = 0; |
| delete falsehoods; |
| falsehoods = 0; |
| delete ignoredTokensAndDirectives; |
| ignoredTokensAndDirectives = 0; |
| } |
| |
| void Tokenizer::init() |
| { |
| yyLexBuf1 = new char[(int) yyLexBufSize]; |
| yyLexBuf2 = new char[(int) yyLexBufSize]; |
| yyPrevLex = yyLexBuf1; |
| yyPrevLex[0] = '\0'; |
| yyLex = yyLexBuf2; |
| yyLex[0] = '\0'; |
| yyLexLen = 0; |
| yyPreprocessorSkipping.push(false); |
| yyNumPreprocessorSkipping = 0; |
| yyBraceDepth = 0; |
| yyParenDepth = 0; |
| yyBracketDepth = 0; |
| yyCh = '\0'; |
| parsingMacro = false; |
| } |
| |
| void Tokenizer::start(const Location& loc) |
| { |
| yyTokLoc = loc; |
| yyCurLoc = loc; |
| yyCurLoc.start(); |
| strcpy(yyPrevLex, "beginning-of-input"); |
| strcpy(yyLex, "beginning-of-input"); |
| yyLexLen = strlen(yyLex); |
| yyBraceDepth = 0; |
| yyParenDepth = 0; |
| yyBracketDepth = 0; |
| yyCh = '\0'; |
| yyCh = getChar(); |
| } |
| |
| /* |
| Returns the next token, if # was met. This function interprets the |
| preprocessor directive, skips over any #ifdef'd out tokens, and returns the |
| token after all of that. |
| */ |
| int Tokenizer::getTokenAfterPreprocessor() |
| { |
| yyCh = getChar(); |
| while (isspace(yyCh) && yyCh != '\n') |
| yyCh = getChar(); |
| |
| /* |
| #directive condition |
| */ |
| QString directive; |
| QString condition; |
| |
| while (isalpha(yyCh)) { |
| directive += QChar(yyCh); |
| yyCh = getChar(); |
| } |
| if (!directive.isEmpty()) { |
| while (yyCh != EOF && yyCh != '\n') { |
| if (yyCh == '\\') |
| yyCh = getChar(); |
| condition += yyCh; |
| yyCh = getChar(); |
| } |
| condition.replace(*comment, ""); |
| condition = condition.simplified(); |
| |
| /* |
| The #if, #ifdef, #ifndef, #elif, #else, and #endif |
| directives have an effect on the skipping stack. For |
| instance, if the code processed so far is |
| |
| #if 1 |
| #if 0 |
| #if 1 |
| // ... |
| #else |
| |
| the skipping stack contains, from bottom to top, false true |
| true (assuming 0 is false and 1 is true). If at least one |
| entry of the stack is true, the tokens are skipped. |
| |
| This mechanism is simple yet hard to understand. |
| */ |
| if (directive[0] == QChar('i')) { |
| if (directive == QString("if")) |
| pushSkipping(!isTrue(condition)); |
| else if (directive == QString("ifdef")) |
| pushSkipping(!defines->exactMatch(condition)); |
| else if (directive == QString("ifndef")) |
| pushSkipping(defines->exactMatch(condition)); |
| } else if (directive[0] == QChar('e')) { |
| if (directive == QString("elif")) { |
| bool old = popSkipping(); |
| if (old) |
| pushSkipping(!isTrue(condition)); |
| else |
| pushSkipping(true); |
| } else if (directive == QString("else")) { |
| pushSkipping(!popSkipping()); |
| } else if (directive == QString("endif")) { |
| popSkipping(); |
| } |
| } else if (directive == QString("define")) { |
| if (versionX->exactMatch(condition)) |
| yyVersion = versionX->cap(1); |
| } |
| } |
| |
| int tok; |
| do { |
| /* |
| We set yyLex now, and after getToken() this will be |
| yyPrevLex. This way, we skip over the preprocessor |
| directive. |
| */ |
| qstrcpy(yyLex, yyPrevLex); |
| |
| /* |
| If getToken() meets another #, it will call |
| getTokenAfterPreprocessor() once again, which could in turn |
| call getToken() again, etc. Unless there are 10,000 or so |
| preprocessor directives in a row, this shouldn't overflow |
| the stack. |
| */ |
| tok = getToken(); |
| } while (yyNumPreprocessorSkipping > 0); |
| return tok; |
| } |
| |
| /* |
| Pushes a new skipping value onto the stack. This corresponds to entering a |
| new #if block. |
| */ |
| void Tokenizer::pushSkipping(bool skip) |
| { |
| yyPreprocessorSkipping.push(skip); |
| if (skip) |
| yyNumPreprocessorSkipping++; |
| } |
| |
| /* |
| Pops a skipping value from the stack. This corresponds to reaching a #endif. |
| */ |
| bool Tokenizer::popSkipping() |
| { |
| if (yyPreprocessorSkipping.isEmpty()) { |
| yyTokLoc.warning(tr("Unexpected #elif, #else or #endif")); |
| return true; |
| } |
| |
| bool skip = yyPreprocessorSkipping.pop(); |
| if (skip) |
| yyNumPreprocessorSkipping--; |
| return skip; |
| } |
| |
| /* |
| Returns true if the condition evaluates as true, otherwise false. The |
| condition is represented by a string. Unsophisticated parsing techniques are |
| used. The preprocessing method could be named StriNg-Oriented PreProcessing, |
| as SNOBOL stands for StriNg-Oriented symBOlic Language. |
| */ |
| bool Tokenizer::isTrue(const QString &condition) |
| { |
| int firstOr = -1; |
| int firstAnd = -1; |
| int parenDepth = 0; |
| |
| /* |
| Find the first logical operator at top level, but be careful |
| about precedence. Examples: |
| |
| X || Y // the or |
| X || Y || Z // the leftmost or |
| X || Y && Z // the or |
| X && Y || Z // the or |
| (X || Y) && Z // the and |
| */ |
| for (int i = 0; i < (int) condition.length() - 1; i++) { |
| QChar ch = condition[i]; |
| if (ch == QChar('(')) { |
| parenDepth++; |
| } else if (ch == QChar(')')) { |
| parenDepth--; |
| } else if (parenDepth == 0) { |
| if (condition[i + 1] == ch) { |
| if (ch == QChar('|')) { |
| firstOr = i; |
| break; |
| } else if (ch == QChar('&')) { |
| if (firstAnd == -1) |
| firstAnd = i; |
| } |
| } |
| } |
| } |
| if (firstOr != -1) |
| return isTrue(condition.left(firstOr)) || |
| isTrue(condition.mid(firstOr + 2)); |
| if (firstAnd != -1) |
| return isTrue(condition.left(firstAnd)) && |
| isTrue(condition.mid(firstAnd + 2)); |
| |
| QString t = condition.simplified(); |
| if (t.isEmpty()) |
| return true; |
| |
| if (t[0] == QChar('!')) |
| return !isTrue(t.mid(1)); |
| if (t[0] == QChar('(') && t.right(1)[0] == QChar(')')) |
| return isTrue(t.mid(1, t.length() - 2)); |
| |
| if (definedX->exactMatch(t)) |
| return defines->exactMatch(definedX->cap(1)); |
| else |
| return !falsehoods->exactMatch(t); |
| } |
| |
| QString Tokenizer::lexeme() const |
| { |
| return sourceCodec->toUnicode(yyLex); |
| } |
| |
| QString Tokenizer::previousLexeme() const |
| { |
| return sourceCodec->toUnicode(yyPrevLex); |
| } |
| |
| QT_END_NAMESPACE |