| /**************************************************************************** |
| ** |
| ** 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 "preprocessor.h" |
| #include "utils.h" |
| #include <QStringList> |
| #include <QFile> |
| #include <QDir> |
| #include <QFileInfo> |
| |
| QT_BEGIN_NAMESPACE |
| |
| #include "ppkeywords.cpp" |
| #include "keywords.cpp" |
| |
| // transform \r\n into \n |
| // \r into \n (os9 style) |
| // backslash-newlines into newlines |
| static QByteArray cleaned(const QByteArray &input) |
| { |
| QByteArray result; |
| result.reserve(input.size()); |
| const char *data = input; |
| char *output = result.data(); |
| |
| int newlines = 0; |
| while (*data) { |
| while (*data && is_space(*data)) |
| ++data; |
| bool takeLine = (*data == '#'); |
| if (*data == '%' && *(data+1) == ':') { |
| takeLine = true; |
| ++data; |
| } |
| if (takeLine) { |
| *output = '#'; |
| ++output; |
| do ++data; while (*data && is_space(*data)); |
| } |
| while (*data) { |
| // handle \\\n, \\\r\n and \\\r |
| if (*data == '\\') { |
| if (*(data + 1) == '\r') { |
| ++data; |
| } |
| if (*data && (*(data + 1) == '\n' || (*data) == '\r')) { |
| ++newlines; |
| data += 1; |
| if (*data != '\r') |
| data += 1; |
| continue; |
| } |
| } else if (*data == '\r' && *(data + 1) == '\n') { // reduce \r\n to \n |
| ++data; |
| } |
| |
| char ch = *data; |
| if (ch == '\r') // os9: replace \r with \n |
| ch = '\n'; |
| *output = ch; |
| ++output; |
| |
| if (*data == '\n') { |
| // output additional newlines to keep the correct line-numbering |
| // for the lines following the backslash-newline sequence(s) |
| while (newlines) { |
| *output = '\n'; |
| ++output; |
| --newlines; |
| } |
| ++data; |
| break; |
| } |
| ++data; |
| } |
| } |
| result.resize(output - result.constData()); |
| return result; |
| } |
| |
| bool Preprocessor::preprocessOnly = false; |
| void Preprocessor::skipUntilEndif() |
| { |
| while(index < symbols.size() - 1 && symbols.at(index).token != PP_ENDIF){ |
| switch (symbols.at(index).token) { |
| case PP_IF: |
| case PP_IFDEF: |
| case PP_IFNDEF: |
| ++index; |
| skipUntilEndif(); |
| break; |
| default: |
| ; |
| } |
| ++index; |
| } |
| } |
| |
| bool Preprocessor::skipBranch() |
| { |
| while (index < symbols.size() - 1 |
| && (symbols.at(index).token != PP_ENDIF |
| && symbols.at(index).token != PP_ELIF |
| && symbols.at(index).token != PP_ELSE) |
| ){ |
| switch (symbols.at(index).token) { |
| case PP_IF: |
| case PP_IFDEF: |
| case PP_IFNDEF: |
| ++index; |
| skipUntilEndif(); |
| break; |
| default: |
| ; |
| } |
| ++index; |
| } |
| return (index < symbols.size() - 1); |
| } |
| |
| |
| enum TokenizeMode { TokenizeCpp, TokenizePreprocessor, PreparePreprocessorStatement, TokenizePreprocessorStatement, TokenizeInclude }; |
| static Symbols tokenize(const QByteArray &input, int lineNum = 1, TokenizeMode mode = TokenizeCpp) |
| { |
| Symbols symbols; |
| const char *begin = input; |
| const char *data = begin; |
| while (*data) { |
| if (mode == TokenizeCpp) { |
| int column = 0; |
| |
| const char *lexem = data; |
| int state = 0; |
| Token token = NOTOKEN; |
| for (;;) { |
| if (static_cast<signed char>(*data) < 0) { |
| ++data; |
| continue; |
| } |
| int nextindex = keywords[state].next; |
| int next = 0; |
| if (*data == keywords[state].defchar) |
| next = keywords[state].defnext; |
| else if (!state || nextindex) |
| next = keyword_trans[nextindex][(int)*data]; |
| if (!next) |
| break; |
| state = next; |
| token = keywords[state].token; |
| ++data; |
| } |
| |
| // suboptimal, is_ident_char should use a table |
| if (keywords[state].ident && is_ident_char(*data)) |
| token = keywords[state].ident; |
| |
| if (token == NOTOKEN) { |
| // an error really |
| ++data; |
| continue; |
| } |
| |
| ++column; |
| |
| if (token > SPECIAL_TREATMENT_MARK) { |
| switch (token) { |
| case QUOTE: |
| data = skipQuote(data); |
| token = STRING_LITERAL; |
| // concatenate multi-line strings for easier |
| // STRING_LITERAAL handling in moc |
| if (!Preprocessor::preprocessOnly |
| && !symbols.isEmpty() |
| && symbols.last().token == STRING_LITERAL) { |
| |
| QByteArray newString = symbols.last().unquotedLexem(); |
| newString += input.mid(lexem - begin + 1, data - lexem - 2); |
| newString.prepend('\"'); |
| newString.append('\"'); |
| symbols.last() = Symbol(symbols.last().lineNum, |
| STRING_LITERAL, |
| newString); |
| continue; |
| } |
| break; |
| case SINGLEQUOTE: |
| while (*data && (*data != '\'' |
| || (*(data-1)=='\\' |
| && *(data-2)!='\\'))) |
| ++data; |
| if (*data) |
| ++data; |
| token = CHARACTER_LITERAL; |
| break; |
| case LANGLE_SCOPE: |
| // split <:: into two tokens, < and :: |
| token = LANGLE; |
| data -= 2; |
| break; |
| case DIGIT: |
| while (is_digit_char(*data)) |
| ++data; |
| if (!*data || *data != '.') { |
| token = INTEGER_LITERAL; |
| if (data - lexem == 1 && |
| (*data == 'x' || *data == 'X') |
| && *lexem == '0') { |
| ++data; |
| while (is_hex_char(*data)) |
| ++data; |
| } |
| break; |
| } |
| token = FLOATING_LITERAL; |
| ++data; |
| // fall through |
| case FLOATING_LITERAL: |
| while (is_digit_char(*data)) |
| ++data; |
| if (*data == '+' || *data == '-') |
| ++data; |
| if (*data == 'e' || *data == 'E') { |
| ++data; |
| while (is_digit_char(*data)) |
| ++data; |
| } |
| if (*data == 'f' || *data == 'F' |
| || *data == 'l' || *data == 'L') |
| ++data; |
| break; |
| case HASH: |
| if (column == 1) { |
| mode = PreparePreprocessorStatement; |
| while (*data && (*data == ' ' || *data == '\t')) |
| ++data; |
| if (is_ident_char(*data)) |
| mode = TokenizePreprocessorStatement; |
| continue; |
| } |
| break; |
| case NEWLINE: |
| ++lineNum; |
| continue; |
| case BACKSLASH: |
| { |
| const char *rewind = data; |
| while (*data && (*data == ' ' || *data == '\t')) |
| ++data; |
| if (*data && *data == '\n') { |
| ++data; |
| continue; |
| } |
| data = rewind; |
| } break; |
| case CHARACTER: |
| while (is_ident_char(*data)) |
| ++data; |
| token = IDENTIFIER; |
| break; |
| case C_COMMENT: |
| if (*data) { |
| if (*data == '\n') |
| ++lineNum; |
| ++data; |
| if (*data) { |
| if (*data == '\n') |
| ++lineNum; |
| ++data; |
| } |
| } |
| while (*data && (*(data-1) != '/' || *(data-2) != '*')) { |
| if (*data == '\n') |
| ++lineNum; |
| ++data; |
| } |
| token = WHITESPACE; // one comment, one whitespace |
| // fall through; |
| case WHITESPACE: |
| if (column == 1) |
| column = 0; |
| while (*data && (*data == ' ' || *data == '\t')) |
| ++data; |
| if (Preprocessor::preprocessOnly) // tokenize whitespace |
| break; |
| continue; |
| case CPP_COMMENT: |
| while (*data && *data != '\n') |
| ++data; |
| continue; // ignore safely, the newline is a separator |
| default: |
| continue; //ignore |
| } |
| } |
| #ifdef USE_LEXEM_STORE |
| if (!Preprocessor::preprocessOnly |
| && token != IDENTIFIER |
| && token != STRING_LITERAL |
| && token != FLOATING_LITERAL |
| && token != INTEGER_LITERAL) |
| symbols += Symbol(lineNum, token); |
| else |
| #endif |
| symbols += Symbol(lineNum, token, input, lexem-begin, data-lexem); |
| |
| } else { // Preprocessor |
| |
| const char *lexem = data; |
| int state = 0; |
| Token token = NOTOKEN; |
| if (mode == TokenizePreprocessorStatement) { |
| state = pp_keyword_trans[0][(int)'#']; |
| mode = TokenizePreprocessor; |
| } |
| for (;;) { |
| if (static_cast<signed char>(*data) < 0) { |
| ++data; |
| continue; |
| } |
| |
| int nextindex = pp_keywords[state].next; |
| int next = 0; |
| if (*data == pp_keywords[state].defchar) |
| next = pp_keywords[state].defnext; |
| else if (!state || nextindex) |
| next = pp_keyword_trans[nextindex][(int)*data]; |
| if (!next) |
| break; |
| state = next; |
| token = pp_keywords[state].token; |
| ++data; |
| } |
| // suboptimal, is_ident_char should use a table |
| if (pp_keywords[state].ident && is_ident_char(*data)) |
| token = pp_keywords[state].ident; |
| |
| switch (token) { |
| case NOTOKEN: |
| ++data; |
| break; |
| case PP_IFDEF: |
| symbols += Symbol(lineNum, PP_IF); |
| symbols += Symbol(lineNum, PP_DEFINED); |
| continue; |
| case PP_IFNDEF: |
| symbols += Symbol(lineNum, PP_IF); |
| symbols += Symbol(lineNum, PP_NOT); |
| symbols += Symbol(lineNum, PP_DEFINED); |
| continue; |
| case PP_INCLUDE: |
| mode = TokenizeInclude; |
| break; |
| case PP_QUOTE: |
| data = skipQuote(data); |
| token = PP_STRING_LITERAL; |
| break; |
| case PP_SINGLEQUOTE: |
| while (*data && (*data != '\'' |
| || (*(data-1)=='\\' |
| && *(data-2)!='\\'))) |
| ++data; |
| if (*data) |
| ++data; |
| token = PP_CHARACTER_LITERAL; |
| break; |
| case PP_DIGIT: |
| while (is_digit_char(*data)) |
| ++data; |
| if (!*data || *data != '.') { |
| token = PP_INTEGER_LITERAL; |
| if (data - lexem == 1 && |
| (*data == 'x' || *data == 'X') |
| && *lexem == '0') { |
| ++data; |
| while (is_hex_char(*data)) |
| ++data; |
| } |
| break; |
| } |
| token = PP_FLOATING_LITERAL; |
| ++data; |
| // fall through |
| case PP_FLOATING_LITERAL: |
| while (is_digit_char(*data)) |
| ++data; |
| if (*data == '+' || *data == '-') |
| ++data; |
| if (*data == 'e' || *data == 'E') { |
| ++data; |
| while (is_digit_char(*data)) |
| ++data; |
| } |
| if (*data == 'f' || *data == 'F' |
| || *data == 'l' || *data == 'L') |
| ++data; |
| break; |
| case PP_CHARACTER: |
| if (mode == PreparePreprocessorStatement) { |
| // rewind entire token to begin |
| data = lexem; |
| mode = TokenizePreprocessorStatement; |
| continue; |
| } |
| while (is_ident_char(*data)) |
| ++data; |
| token = PP_IDENTIFIER; |
| break; |
| case PP_C_COMMENT: |
| if (*data) { |
| if (*data == '\n') |
| ++lineNum; |
| ++data; |
| if (*data) { |
| if (*data == '\n') |
| ++lineNum; |
| ++data; |
| } |
| } |
| while (*data && (*(data-1) != '/' || *(data-2) != '*')) { |
| if (*data == '\n') |
| ++lineNum; |
| ++data; |
| } |
| token = PP_WHITESPACE; // one comment, one whitespace |
| // fall through; |
| case PP_WHITESPACE: |
| while (*data && (*data == ' ' || *data == '\t')) |
| ++data; |
| continue; // the preprocessor needs no whitespace |
| case PP_CPP_COMMENT: |
| while (*data && *data != '\n') |
| ++data; |
| continue; // ignore safely, the newline is a separator |
| case PP_NEWLINE: |
| ++lineNum; |
| mode = TokenizeCpp; |
| break; |
| case PP_BACKSLASH: |
| { |
| const char *rewind = data; |
| while (*data && (*data == ' ' || *data == '\t')) |
| ++data; |
| if (*data && *data == '\n') { |
| ++data; |
| continue; |
| } |
| data = rewind; |
| } break; |
| case PP_LANGLE: |
| if (mode != TokenizeInclude) |
| break; |
| token = PP_STRING_LITERAL; |
| while (*data && *data != '\n' && *(data-1) != '>') |
| ++data; |
| break; |
| default: |
| break; |
| } |
| if (mode == PreparePreprocessorStatement) |
| continue; |
| #ifdef USE_LEXEM_STORE |
| if (token != PP_IDENTIFIER |
| && token != PP_STRING_LITERAL |
| && token != PP_FLOATING_LITERAL |
| && token != PP_INTEGER_LITERAL) |
| symbols += Symbol(lineNum, token); |
| else |
| #endif |
| symbols += Symbol(lineNum, token, input, lexem-begin, data-lexem); |
| } |
| } |
| symbols += Symbol(); // eof symbol |
| return symbols; |
| } |
| |
| void Preprocessor::substituteMacro(const MacroName ¯o, Symbols &substituted, MacroSafeSet safeset) |
| { |
| Symbols saveSymbols = symbols; |
| int saveIndex = index; |
| |
| symbols = macros.value(macro).symbols; |
| index = 0; |
| |
| safeset += macro; |
| substituteUntilNewline(substituted, safeset); |
| |
| symbols = saveSymbols; |
| index = saveIndex; |
| } |
| |
| |
| |
| void Preprocessor::substituteUntilNewline(Symbols &substituted, MacroSafeSet safeset) |
| { |
| while (hasNext()) { |
| Token token = next(); |
| if (token == PP_IDENTIFIER) { |
| MacroName macro = symbol(); |
| if (macros.contains(macro) && !safeset.contains(macro)) { |
| substituteMacro(macro, substituted, safeset); |
| continue; |
| } |
| } else if (token == PP_DEFINED) { |
| test(PP_LPAREN); |
| next(PP_IDENTIFIER); |
| Symbol definedOrNotDefined = symbol(); |
| definedOrNotDefined.token = macros.contains(definedOrNotDefined)? PP_MOC_TRUE : PP_MOC_FALSE; |
| substituted += definedOrNotDefined; |
| test(PP_RPAREN); |
| continue; |
| } else if (token == PP_NEWLINE) { |
| substituted += symbol(); |
| break; |
| } |
| substituted += symbol(); |
| } |
| } |
| |
| |
| class PP_Expression : public Parser |
| { |
| public: |
| int value() { index = 0; return unary_expression_lookup() ? conditional_expression() : 0; } |
| |
| int conditional_expression(); |
| int logical_OR_expression(); |
| int logical_AND_expression(); |
| int inclusive_OR_expression(); |
| int exclusive_OR_expression(); |
| int AND_expression(); |
| int equality_expression(); |
| int relational_expression(); |
| int shift_expression(); |
| int additive_expression(); |
| int multiplicative_expression(); |
| int unary_expression(); |
| bool unary_expression_lookup(); |
| int primary_expression(); |
| bool primary_expression_lookup(); |
| }; |
| |
| int PP_Expression::conditional_expression() |
| { |
| int value = logical_OR_expression(); |
| if (test(PP_QUESTION)) { |
| int alt1 = conditional_expression(); |
| int alt2 = test(PP_COLON) ? conditional_expression() : 0; |
| return value ? alt1 : alt2; |
| } |
| return value; |
| } |
| |
| int PP_Expression::logical_OR_expression() |
| { |
| int value = logical_AND_expression(); |
| if (test(PP_OROR)) |
| return logical_OR_expression() || value; |
| return value; |
| } |
| |
| int PP_Expression::logical_AND_expression() |
| { |
| int value = inclusive_OR_expression(); |
| if (test(PP_ANDAND)) |
| return logical_AND_expression() && value; |
| return value; |
| } |
| |
| int PP_Expression::inclusive_OR_expression() |
| { |
| int value = exclusive_OR_expression(); |
| if (test(PP_OR)) |
| return value | inclusive_OR_expression(); |
| return value; |
| } |
| |
| int PP_Expression::exclusive_OR_expression() |
| { |
| int value = AND_expression(); |
| if (test(PP_HAT)) |
| return value ^ exclusive_OR_expression(); |
| return value; |
| } |
| |
| int PP_Expression::AND_expression() |
| { |
| int value = equality_expression(); |
| if (test(PP_AND)) |
| return value & AND_expression(); |
| return value; |
| } |
| |
| int PP_Expression::equality_expression() |
| { |
| int value = relational_expression(); |
| switch (next()) { |
| case PP_EQEQ: |
| return value == equality_expression(); |
| case PP_NE: |
| return value != equality_expression(); |
| default: |
| prev(); |
| return value; |
| } |
| } |
| |
| int PP_Expression::relational_expression() |
| { |
| int value = shift_expression(); |
| switch (next()) { |
| case PP_LANGLE: |
| return value < relational_expression(); |
| case PP_RANGLE: |
| return value > relational_expression(); |
| case PP_LE: |
| return value <= relational_expression(); |
| case PP_GE: |
| return value >= relational_expression(); |
| default: |
| prev(); |
| return value; |
| } |
| } |
| |
| int PP_Expression::shift_expression() |
| { |
| int value = additive_expression(); |
| switch (next()) { |
| case PP_LTLT: |
| return value << shift_expression(); |
| case PP_GTGT: |
| return value >> shift_expression(); |
| default: |
| prev(); |
| return value; |
| } |
| } |
| |
| int PP_Expression::additive_expression() |
| { |
| int value = multiplicative_expression(); |
| switch (next()) { |
| case PP_PLUS: |
| return value + additive_expression(); |
| case PP_MINUS: |
| return value - additive_expression(); |
| default: |
| prev(); |
| return value; |
| } |
| } |
| |
| int PP_Expression::multiplicative_expression() |
| { |
| int value = unary_expression(); |
| switch (next()) { |
| case PP_STAR: |
| return value * multiplicative_expression(); |
| case PP_PERCENT: |
| { |
| int remainder = multiplicative_expression(); |
| return remainder ? value % remainder : 0; |
| } |
| case PP_SLASH: |
| { |
| int div = multiplicative_expression(); |
| return div ? value / div : 0; |
| } |
| default: |
| prev(); |
| return value; |
| }; |
| } |
| |
| int PP_Expression::unary_expression() |
| { |
| switch (next()) { |
| case PP_PLUS: |
| return unary_expression(); |
| case PP_MINUS: |
| return -unary_expression(); |
| case PP_NOT: |
| return !unary_expression(); |
| case PP_TILDE: |
| return ~unary_expression(); |
| case PP_MOC_TRUE: |
| return 1; |
| case PP_MOC_FALSE: |
| return 0; |
| default: |
| prev(); |
| return primary_expression(); |
| } |
| } |
| |
| bool PP_Expression::unary_expression_lookup() |
| { |
| Token t = lookup(); |
| return (primary_expression_lookup() |
| || t == PP_PLUS |
| || t == PP_MINUS |
| || t == PP_NOT |
| || t == PP_TILDE |
| || t == PP_DEFINED); |
| } |
| |
| int PP_Expression::primary_expression() |
| { |
| int value; |
| if (test(PP_LPAREN)) { |
| value = conditional_expression(); |
| test(PP_RPAREN); |
| } else { |
| next(); |
| value = lexem().toInt(0, 0); |
| } |
| return value; |
| } |
| |
| bool PP_Expression::primary_expression_lookup() |
| { |
| Token t = lookup(); |
| return (t == PP_IDENTIFIER |
| || t == PP_INTEGER_LITERAL |
| || t == PP_FLOATING_LITERAL |
| || t == PP_MOC_TRUE |
| || t == PP_MOC_FALSE |
| || t == PP_LPAREN); |
| } |
| |
| int Preprocessor::evaluateCondition() |
| { |
| PP_Expression expression; |
| expression.currentFilenames = currentFilenames; |
| |
| substituteUntilNewline(expression.symbols); |
| |
| return expression.value(); |
| } |
| |
| void Preprocessor::preprocess(const QByteArray &filename, Symbols &preprocessed) |
| { |
| currentFilenames.push(filename); |
| preprocessed.reserve(preprocessed.size() + symbols.size()); |
| while (hasNext()) { |
| Token token = next(); |
| |
| switch (token) { |
| case PP_INCLUDE: |
| { |
| int lineNum = symbol().lineNum; |
| QByteArray include; |
| bool local = false; |
| if (test(PP_STRING_LITERAL)) { |
| local = lexem().startsWith('\"'); |
| include = unquotedLexem(); |
| } else |
| continue; |
| until(PP_NEWLINE); |
| |
| // #### stringery |
| QFileInfo fi; |
| if (local) |
| fi.setFile(QFileInfo(QString::fromLocal8Bit(filename)).dir(), QString::fromLocal8Bit(include)); |
| for (int j = 0; j < Preprocessor::includes.size() && !fi.exists(); ++j) { |
| const IncludePath &p = Preprocessor::includes.at(j); |
| if (p.isFrameworkPath) { |
| const int slashPos = include.indexOf('/'); |
| if (slashPos == -1) |
| continue; |
| QByteArray frameworkCandidate = include.left(slashPos); |
| frameworkCandidate.append(".framework/Headers/"); |
| fi.setFile(QString::fromLocal8Bit(p.path + '/' + frameworkCandidate), QString::fromLocal8Bit(include.mid(slashPos + 1))); |
| } else { |
| fi.setFile(QString::fromLocal8Bit(p.path), QString::fromLocal8Bit(include)); |
| } |
| // try again, maybe there's a file later in the include paths with the same name |
| // (186067) |
| if (fi.isDir()) { |
| fi = QFileInfo(); |
| continue; |
| } |
| } |
| |
| if (!fi.exists() || fi.isDir()) |
| continue; |
| include = fi.canonicalFilePath().toLocal8Bit(); |
| |
| if (Preprocessor::preprocessedIncludes.contains(include)) |
| continue; |
| Preprocessor::preprocessedIncludes.insert(include); |
| |
| QFile file(QString::fromLocal8Bit(include)); |
| if (!file.open(QFile::ReadOnly)) |
| continue; |
| |
| QByteArray input = file.readAll(); |
| file.close(); |
| if (input.isEmpty()) |
| continue; |
| |
| Symbols saveSymbols = symbols; |
| int saveIndex = index; |
| |
| // phase 1: get rid of backslash-newlines |
| input = cleaned(input); |
| |
| // phase 2: tokenize for the preprocessor |
| symbols = tokenize(input); |
| input.clear(); |
| |
| index = 0; |
| |
| // phase 3: preprocess conditions and substitute macros |
| preprocessed += Symbol(0, MOC_INCLUDE_BEGIN, include); |
| preprocess(include, preprocessed); |
| preprocessed += Symbol(lineNum, MOC_INCLUDE_END, include); |
| |
| symbols = saveSymbols; |
| index = saveIndex; |
| continue; |
| } |
| case PP_DEFINE: |
| { |
| next(IDENTIFIER); |
| QByteArray name = lexem(); |
| int start = index; |
| until(PP_NEWLINE); |
| Macro macro; |
| macro.symbols.reserve(index - start - 1); |
| for (int i = start; i < index - 1; ++i) |
| macro.symbols += symbols.at(i); |
| macros.insert(name, macro); |
| continue; |
| } |
| case PP_UNDEF: { |
| next(IDENTIFIER); |
| QByteArray name = lexem(); |
| until(PP_NEWLINE); |
| macros.remove(name); |
| continue; |
| } |
| case PP_IDENTIFIER: |
| { |
| // if (macros.contains(symbol())) |
| // ; |
| } |
| // we _could_ easily substitute macros by the following |
| // four lines, but we choose not to. |
| /* |
| if (macros.contains(sym.lexem())) { |
| preprocessed += substitute(macros, symbols, i); |
| continue; |
| } |
| */ |
| break; |
| case PP_HASH: |
| until(PP_NEWLINE); |
| continue; // skip unknown preprocessor statement |
| case PP_IFDEF: |
| case PP_IFNDEF: |
| case PP_IF: |
| while (!evaluateCondition()) { |
| if (!skipBranch()) |
| break; |
| if (test(PP_ELIF)) { |
| } else { |
| until(PP_NEWLINE); |
| break; |
| } |
| } |
| continue; |
| case PP_ELIF: |
| case PP_ELSE: |
| skipUntilEndif(); |
| // fall through |
| case PP_ENDIF: |
| until(PP_NEWLINE); |
| continue; |
| case SIGNALS: |
| case SLOTS: { |
| Symbol sym = symbol(); |
| if (macros.contains("QT_NO_KEYWORDS")) |
| sym.token = IDENTIFIER; |
| else |
| sym.token = (token == SIGNALS ? Q_SIGNALS_TOKEN : Q_SLOTS_TOKEN); |
| preprocessed += sym; |
| } continue; |
| default: |
| break; |
| } |
| preprocessed += symbol(); |
| } |
| |
| currentFilenames.pop(); |
| } |
| |
| Symbols Preprocessor::preprocessed(const QByteArray &filename, FILE *file) |
| { |
| QFile qfile; |
| qfile.open(file, QFile::ReadOnly); |
| QByteArray input = qfile.readAll(); |
| if (input.isEmpty()) |
| return symbols; |
| |
| // phase 1: get rid of backslash-newlines |
| input = cleaned(input); |
| |
| // phase 2: tokenize for the preprocessor |
| symbols = tokenize(input); |
| |
| #if 0 |
| for (int j = 0; j < symbols.size(); ++j) |
| fprintf(stderr, "line %d: %s(%s)\n", |
| symbols[j].lineNum, |
| symbols[j].lexem().constData(), |
| tokenTypeName(symbols[j].token)); |
| #endif |
| |
| // phase 3: preprocess conditions and substitute macros |
| Symbols result; |
| preprocess(filename, result); |
| |
| #if 0 |
| for (int j = 0; j < result.size(); ++j) |
| fprintf(stderr, "line %d: %s(%s)\n", |
| result[j].lineNum, |
| result[j].lexem().constData(), |
| tokenTypeName(result[j].token)); |
| #endif |
| |
| return result; |
| } |
| |
| void Preprocessor::until(Token t) |
| { |
| while(hasNext() && next() != t) |
| ; |
| } |
| |
| QT_END_NAMESPACE |