--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/icheck/parser/src/libs/cplusplus/pp-engine.cpp Wed Aug 25 15:49:42 2010 +0300
@@ -0,0 +1,1424 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt Mobility Components.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+/*
+ Copyright 2005 Roberto Raggi <roberto@kdevelop.org>
+
+ Permission to use, copy, modify, distribute, and sell this software and its
+ documentation for any purpose is hereby granted without fee, provided that
+ the above copyright notice appear in all copies and that both that
+ copyright notice and this permission notice appear in supporting
+ documentation.
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ KDEVELOP TEAM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include "pp.h"
+#include "pp-cctype.h"
+
+#include <Lexer.h>
+#include <Token.h>
+#include <Literals.h>
+#include <cctype>
+
+#include <QtDebug>
+#include <algorithm>
+
+namespace CPlusPlus {
+
+struct Value
+{
+ enum Kind {
+ Kind_Long,
+ Kind_ULong
+ };
+
+ Kind kind;
+
+ union {
+ long l;
+ unsigned long ul;
+ };
+
+
+ Value()
+ : kind(Kind_Long), l(0)
+ { }
+
+ inline bool is_ulong () const
+ { return kind == Kind_ULong; }
+
+ inline void set_ulong (unsigned long v)
+ {
+ ul = v;
+ kind = Kind_ULong;
+ }
+
+ inline void set_long (long v)
+ {
+ l = v;
+ kind = Kind_Long;
+ }
+
+ inline bool is_zero () const
+ { return l == 0; }
+
+#define PP_DEFINE_BIN_OP(name, op) \
+ inline Value operator op(const Value &other) const \
+ { \
+ Value v = *this; \
+ if (v.is_ulong () || other.is_ulong ()) \
+ v.set_ulong (v.ul op other.ul); \
+ else \
+ v.set_long (v.l op other.l); \
+ return v; \
+ }
+
+ PP_DEFINE_BIN_OP(op_add, +)
+ PP_DEFINE_BIN_OP(op_sub, -)
+ PP_DEFINE_BIN_OP(op_mult, *)
+ PP_DEFINE_BIN_OP(op_div, /)
+ PP_DEFINE_BIN_OP(op_mod, %)
+ PP_DEFINE_BIN_OP(op_lhs, <<)
+ PP_DEFINE_BIN_OP(op_rhs, >>)
+ PP_DEFINE_BIN_OP(op_lt, <)
+ PP_DEFINE_BIN_OP(op_gt, >)
+ PP_DEFINE_BIN_OP(op_le, <=)
+ PP_DEFINE_BIN_OP(op_ge, >=)
+ PP_DEFINE_BIN_OP(op_eq, ==)
+ PP_DEFINE_BIN_OP(op_ne, !=)
+ PP_DEFINE_BIN_OP(op_bit_and, &)
+ PP_DEFINE_BIN_OP(op_bit_or, |)
+ PP_DEFINE_BIN_OP(op_bit_xor, ^)
+ PP_DEFINE_BIN_OP(op_and, &&)
+ PP_DEFINE_BIN_OP(op_or, ||)
+
+#undef PP_DEFINE_BIN_OP
+};
+
+} // end of namespace CPlusPlus
+
+
+using namespace CPlusPlus;
+
+
+namespace {
+
+bool isMacroDefined(QByteArray name, unsigned offset, Environment *env, Client *client)
+{
+ Macro *m = env->resolve(name);
+ if (client) {
+ if (m)
+ client->passedMacroDefinitionCheck(offset, *m);
+ else
+ client->failedMacroDefinitionCheck(offset, name);
+ }
+ return m != 0;
+}
+
+class RangeLexer
+{
+ const Token *first;
+ const Token *last;
+ Token trivial;
+
+public:
+ inline RangeLexer(const Token *first, const Token *last)
+ : first(first), last(last)
+ {
+ // WARN: `last' must be a valid iterator.
+ trivial.offset = last->offset;
+ }
+
+ inline operator bool() const
+ { return first != last; }
+
+ inline bool isValid() const
+ { return first != last; }
+
+ inline int size() const
+ { return std::distance(first, last); }
+
+ inline const Token *dot() const
+ { return first; }
+
+ inline const Token &operator*() const
+ {
+ if (first != last)
+ return *first;
+
+ return trivial;
+ }
+
+ inline const Token *operator->() const
+ {
+ if (first != last)
+ return first;
+
+ return &trivial;
+ }
+
+ inline RangeLexer &operator++()
+ {
+ ++first;
+ return *this;
+ }
+};
+
+class ExpressionEvaluator
+{
+ ExpressionEvaluator(const ExpressionEvaluator &other);
+ void operator = (const ExpressionEvaluator &other);
+
+public:
+ ExpressionEvaluator(Client *client, Environment *env)
+ : client(client), env(env), _lex(0)
+ { }
+
+ Value operator()(const Token *firstToken, const Token *lastToken,
+ const QByteArray &source)
+ {
+ this->source = source;
+ const Value previousValue = switchValue(Value());
+ RangeLexer tmp(firstToken, lastToken);
+ RangeLexer *previousLex = _lex;
+ _lex = &tmp;
+ process_expression();
+ _lex = previousLex;
+ return switchValue(previousValue);
+ }
+
+protected:
+ Value switchValue(const Value &value)
+ {
+ Value previousValue = _value;
+ _value = value;
+ return previousValue;
+ }
+
+ bool isTokenDefined() const
+ {
+ if ((*_lex)->isNot(T_IDENTIFIER))
+ return false;
+ const QByteArray spell = tokenSpell();
+ if (spell.size() != 7)
+ return false;
+ return spell == "defined";
+ }
+
+ QByteArray tokenSpell() const
+ {
+ const QByteArray text = QByteArray::fromRawData(source.constData() + (*_lex)->offset,
+ (*_lex)->f.length);
+ return text;
+ }
+
+ inline void process_expression()
+ { process_constant_expression(); }
+
+ void process_primary()
+ {
+ if ((*_lex)->is(T_NUMERIC_LITERAL)) {
+ int base = 10;
+ const QByteArray spell = tokenSpell();
+ if (spell.at(0) == '0') {
+ if (spell.size() > 1 && (spell.at(1) == 'x' || spell.at(1) == 'X'))
+ base = 16;
+ else
+ base = 8;
+ }
+ _value.set_long(tokenSpell().toLong(0, base));
+ ++(*_lex);
+ } else if (isTokenDefined()) {
+ ++(*_lex);
+ if ((*_lex)->is(T_IDENTIFIER)) {
+ _value.set_long(isMacroDefined(tokenSpell(), (*_lex)->offset, env, client));
+ ++(*_lex);
+ } else if ((*_lex)->is(T_LPAREN)) {
+ ++(*_lex);
+ if ((*_lex)->is(T_IDENTIFIER)) {
+ _value.set_long(isMacroDefined(tokenSpell(), (*_lex)->offset, env, client));
+ ++(*_lex);
+ if ((*_lex)->is(T_RPAREN)) {
+ ++(*_lex);
+ }
+ }
+ }
+ } else if ((*_lex)->is(T_IDENTIFIER)) {
+ _value.set_long(0);
+ ++(*_lex);
+ } else if ((*_lex)->is(T_MINUS)) {
+ ++(*_lex);
+ process_primary();
+ _value.set_long(- _value.l);
+ } else if ((*_lex)->is(T_PLUS)) {
+ ++(*_lex);
+ process_primary();
+ } else if ((*_lex)->is(T_EXCLAIM)) {
+ ++(*_lex);
+ process_primary();
+ _value.set_long(_value.is_zero());
+ } else if ((*_lex)->is(T_LPAREN)) {
+ ++(*_lex);
+ process_expression();
+ if ((*_lex)->is(T_RPAREN))
+ ++(*_lex);
+ }
+ }
+
+ Value process_expression_with_operator_precedence(const Value &lhs, int minPrecedence)
+ {
+ Value result = lhs;
+
+ while (precedence((*_lex)->kind()) >= minPrecedence) {
+ const int oper = (*_lex)->kind();
+ const int operPrecedence = precedence(oper);
+ ++(*_lex);
+ process_primary();
+ Value rhs = _value;
+
+ for (int LA_token_kind = (*_lex)->kind(), LA_precedence = precedence(LA_token_kind);
+ LA_precedence > operPrecedence && isBinaryOperator(LA_token_kind);
+ LA_token_kind = (*_lex)->kind(), LA_precedence = precedence(LA_token_kind)) {
+ rhs = process_expression_with_operator_precedence(rhs, LA_precedence);
+ }
+
+ result = evaluate_expression(oper, result, rhs);
+ }
+
+ return result;
+ }
+
+ void process_constant_expression()
+ {
+ process_primary();
+ _value = process_expression_with_operator_precedence(_value, precedence(T_PIPE_PIPE));
+
+ if ((*_lex)->is(T_QUESTION)) {
+ const Value cond = _value;
+ ++(*_lex);
+ process_constant_expression();
+ Value left = _value, right;
+ if ((*_lex)->is(T_COLON)) {
+ ++(*_lex);
+ process_constant_expression();
+ right = _value;
+ }
+ _value = ! cond.is_zero() ? left : right;
+ }
+ }
+
+private:
+ inline int precedence(int tokenKind) const
+ {
+ switch (tokenKind) {
+ case T_PIPE_PIPE: return 0;
+ case T_AMPER_AMPER: return 1;
+ case T_PIPE: return 2;
+ case T_CARET: return 3;
+ case T_AMPER: return 4;
+ case T_EQUAL_EQUAL:
+ case T_EXCLAIM_EQUAL: return 5;
+ case T_GREATER:
+ case T_LESS:
+ case T_LESS_EQUAL:
+ case T_GREATER_EQUAL: return 6;
+ case T_LESS_LESS:
+ case T_GREATER_GREATER: return 7;
+ case T_PLUS:
+ case T_MINUS: return 8;
+ case T_STAR:
+ case T_SLASH:
+ case T_PERCENT: return 9;
+
+ default:
+ return -1;
+ }
+ }
+
+ static inline bool isBinaryOperator(int tokenKind)
+ {
+ switch (tokenKind) {
+ case T_PIPE_PIPE:
+ case T_AMPER_AMPER:
+ case T_PIPE:
+ case T_CARET:
+ case T_AMPER:
+ case T_EQUAL_EQUAL:
+ case T_EXCLAIM_EQUAL:
+ case T_GREATER:
+ case T_LESS:
+ case T_LESS_EQUAL:
+ case T_GREATER_EQUAL:
+ case T_LESS_LESS:
+ case T_GREATER_GREATER:
+ case T_PLUS:
+ case T_MINUS:
+ case T_STAR:
+ case T_SLASH:
+ case T_PERCENT:
+ return true;
+
+ default:
+ return false;
+ }
+ }
+
+ static inline Value evaluate_expression(int tokenKind, const Value &lhs, const Value &rhs)
+ {
+ switch (tokenKind) {
+ case T_PIPE_PIPE: return lhs || rhs;
+ case T_AMPER_AMPER: return lhs && rhs;
+ case T_PIPE: return lhs | rhs;
+ case T_CARET: return lhs ^ rhs;
+ case T_AMPER: return lhs & rhs;
+ case T_EQUAL_EQUAL: return lhs == rhs;
+ case T_EXCLAIM_EQUAL: return lhs != rhs;
+ case T_GREATER: return lhs > rhs;
+ case T_LESS: return lhs < rhs;
+ case T_LESS_EQUAL: return lhs <= rhs;
+ case T_GREATER_EQUAL: return lhs >= rhs;
+ case T_LESS_LESS: return lhs << rhs;
+ case T_GREATER_GREATER: return lhs >> rhs;
+ case T_PLUS: return lhs + rhs;
+ case T_MINUS: return lhs - rhs;
+ case T_STAR: return lhs * rhs;
+ case T_SLASH: return rhs.is_zero() ? Value() : lhs / rhs;
+ case T_PERCENT: return rhs.is_zero() ? Value() : lhs % rhs;
+
+ default:
+ return Value();
+ }
+ }
+
+private:
+ Client *client;
+ Environment *env;
+ QByteArray source;
+ RangeLexer *_lex;
+ Value _value;
+};
+
+} // end of anonymous namespace
+
+
+Preprocessor::Preprocessor(Client *client, Environment *env)
+ : client(client),
+ env(env),
+ _expand(env),
+ _skipping(MAX_LEVEL),
+ _trueTest(MAX_LEVEL),
+ _dot(_tokens.end()),
+ _result(0),
+ _markGeneratedTokens(false),
+ _expandMacros(true)
+{
+ resetIfLevel ();
+}
+
+void Preprocessor::pushState(const State &s)
+{
+ _savedStates.append(state());
+ _source = s.source;
+ _tokens = s.tokens;
+ _dot = s.dot;
+}
+
+Preprocessor::State Preprocessor::state() const
+{
+ State state;
+ state.source = _source;
+ state.tokens = _tokens;
+ state.dot = _dot;
+ return state;
+}
+
+void Preprocessor::popState()
+{
+ const State &state = _savedStates.last();
+ _source = state.source;
+ _tokens = state.tokens;
+ _dot = state.dot;
+ _savedStates.removeLast();
+}
+
+QByteArray Preprocessor::operator()(const QString &fileName, const QString &source)
+{
+ const QString previousOriginalSource = _originalSource;
+ _originalSource = source;
+ const QByteArray bytes = source.toLatin1();
+ const QByteArray preprocessedCode = operator()(fileName, bytes);
+ _originalSource = previousOriginalSource;
+ return preprocessedCode;
+}
+
+QByteArray Preprocessor::operator()(const QString &fileName,
+ const QByteArray &source)
+{
+ QByteArray preprocessed;
+ preprocess(fileName, source, &preprocessed);
+ return preprocessed;
+}
+
+QByteArray Preprocessor::expand(const QByteArray &source)
+{
+ QByteArray result;
+ result.reserve(256);
+ expand(source, &result);
+ return result;
+}
+
+void Preprocessor::expand(const QByteArray &source, QByteArray *result)
+{
+ if (result)
+ _expand(source, result);
+}
+
+void Preprocessor::expand(const char *first, const char *last, QByteArray *result)
+{
+ const QByteArray source = QByteArray::fromRawData(first, last - first);
+ return expand(source, result);
+}
+
+void Preprocessor::out(const QByteArray &text)
+{
+ if (_result)
+ _result->append(text);
+}
+
+void Preprocessor::out(char ch)
+{
+ if (_result)
+ _result->append(ch);
+}
+
+void Preprocessor::out(const char *s)
+{
+ if (_result)
+ _result->append(s);
+}
+
+bool Preprocessor::expandMacros() const
+{
+ return _expandMacros;
+}
+
+void Preprocessor::setExpandMacros(bool expandMacros)
+{
+ _expandMacros = expandMacros;
+}
+
+Preprocessor::State Preprocessor::createStateFromSource(const QByteArray &source) const
+{
+ State state;
+ state.source = source;
+ Lexer lex(state.source.constBegin(), state.source.constEnd());
+ lex.setScanKeywords(false);
+ Token tok;
+ do {
+ lex(&tok);
+ state.tokens.append(tok);
+ } while (tok.isNot(T_EOF_SYMBOL));
+ state.dot = state.tokens.constBegin();
+ return state;
+}
+
+void Preprocessor::processNewline(bool force)
+{
+ if (_dot != _tokens.constBegin()) {
+ TokenIterator prevTok = _dot - 1;
+
+ if (prevTok->isLiteral()) {
+ const char *ptr = _source.constBegin() + prevTok->begin();
+ const char *end = ptr + prevTok->length();
+
+ for (; ptr != end; ++ptr) {
+ if (*ptr == '\n')
+ ++env->currentLine;
+ }
+ }
+ }
+
+ if (! force && env->currentLine == _dot->lineno)
+ return;
+
+ if (force || env->currentLine > _dot->lineno) {
+ out("\n# ");
+ out(QByteArray::number(_dot->lineno));
+ out(' ');
+ out('"');
+ out(env->currentFile.toUtf8());
+ out('"');
+ out('\n');
+ } else {
+ for (unsigned i = env->currentLine; i < _dot->lineno; ++i)
+ out('\n');
+ }
+
+ env->currentLine = _dot->lineno;
+}
+
+void Preprocessor::processSkippingBlocks(bool skippingBlocks,
+ TokenIterator start, TokenIterator /*end*/)
+{
+ if (! client)
+ return;
+
+ if (skippingBlocks != _skipping[iflevel]) {
+ unsigned offset = start->offset;
+
+ if (_skipping[iflevel]) {
+ if (_dot->f.newline)
+ ++offset;
+
+ client->startSkippingBlocks(offset);
+
+ } else {
+ if (offset)
+ --offset;
+
+ client->stopSkippingBlocks(offset);
+ }
+ }
+}
+
+bool Preprocessor::markGeneratedTokens(bool markGeneratedTokens,
+ TokenIterator dot)
+{
+ bool previous = _markGeneratedTokens;
+ _markGeneratedTokens = markGeneratedTokens;
+
+ if (previous != _markGeneratedTokens) {
+ if (! dot)
+ dot = _dot;
+
+ if (_markGeneratedTokens)
+ out("\n#gen true");
+ else
+ out("\n#gen false");
+
+ processNewline(/*force = */ true);
+
+ const char *begin = _source.constBegin();
+ const char *end = begin;
+
+ if (markGeneratedTokens)
+ end += dot->begin();
+ else
+ end += (dot - 1)->end();
+
+ const char *it = end - 1;
+ for (; it != begin - 1; --it) {
+ if (*it == '\n')
+ break;
+ }
+ ++it;
+
+ for (; it != end; ++it) {
+ if (! pp_isspace(*it))
+ out(' ');
+
+ else
+ out(*it);
+ }
+
+ if (! markGeneratedTokens && dot->f.newline)
+ processNewline(/*force = */ true);
+ }
+
+ return previous;
+}
+
+void Preprocessor::preprocess(const QString &fileName, const QByteArray &source,
+ QByteArray *result)
+{
+ const int previousIfLevel = iflevel;
+
+ QByteArray *previousResult = _result;
+ _result = result;
+
+ pushState(createStateFromSource(source));
+
+ const QString previousFileName = env->currentFile;
+ env->currentFile = fileName;
+
+ const unsigned previousCurrentLine = env->currentLine;
+ env->currentLine = 0;
+
+ while (true) {
+
+ if (_dot->f.joined)
+ out("\\");
+
+ processNewline();
+
+ if (_dot->is(T_EOF_SYMBOL)) {
+ break;
+
+ } else if (_dot->is(T_POUND) && (! _dot->f.joined && _dot->f.newline)) {
+ // handle the preprocessor directive
+
+ TokenIterator start = _dot;
+ do {
+ ++_dot;
+ } while (_dot->isNot(T_EOF_SYMBOL) && (_dot->f.joined || ! _dot->f.newline));
+
+ const bool skippingBlocks = _skipping[iflevel];
+
+ processDirective(start, _dot);
+ processSkippingBlocks(skippingBlocks, start, _dot);
+
+ } else if (skipping()) {
+ // skip the current line
+
+ do {
+ ++_dot;
+ } while (_dot->isNot(T_EOF_SYMBOL) && (_dot->f.joined || ! _dot->f.newline));
+
+ } else {
+
+ if (_dot->f.whitespace) {
+ unsigned endOfPreviousToken = 0;
+
+ if (_dot != _tokens.constBegin())
+ endOfPreviousToken = (_dot - 1)->end();
+
+ const unsigned beginOfToken = _dot->begin();
+
+ const char *start = _source.constBegin() + endOfPreviousToken;
+ const char *end = _source.constBegin() + beginOfToken;
+
+ const char *it = end - 1;
+ for (; it != start - 1; --it) {
+ if (*it == '\n')
+ break;
+ }
+ ++it;
+
+ for (; it != end; ++it) {
+ if (pp_isspace(*it))
+ out(*it);
+
+ else
+ out(' ');
+ }
+ }
+
+ if (_dot->isNot(T_IDENTIFIER)) {
+ out(tokenSpell(*_dot));
+ ++_dot;
+
+ } else {
+ const TokenIterator identifierToken = _dot;
+ ++_dot; // skip T_IDENTIFIER
+
+ const QByteArray spell = tokenSpell(*identifierToken);
+ if (! _expandMacros) {
+ if (! env->isBuiltinMacro(spell)) {
+ Macro *m = env->resolve(spell);
+ if (m && ! m->isFunctionLike()) {
+ QByteArray expandedDefinition;
+ expandObjectLikeMacro(identifierToken, spell, m, &expandedDefinition);
+ if (expandedDefinition.trimmed().isEmpty()) {
+ out(QByteArray(spell.length(), ' '));
+ continue;
+ }
+ }
+ }
+ out(spell);
+ continue;
+ }
+
+ else if (env->isBuiltinMacro(spell))
+ expandBuiltinMacro(identifierToken, spell);
+
+ else {
+#ifdef ICHECK_BUILD
+ if(spell != "Q_PROPERTY" && spell != "Q_INVOKABLE" && spell != "Q_ENUMS"
+ && spell != "Q_FLAGS" && spell != "Q_DECLARE_FLAGS"){
+#endif
+ if (Macro *m = env->resolve(spell)) {
+ if (! m->isFunctionLike()) {
+ if (0 == (m = processObjectLikeMacro(identifierToken, spell, m)))
+ continue;
+
+ // the macro expansion generated something that looks like
+ // a function-like macro.
+ }
+
+ // `m' is function-like macro.
+ if (_dot->is(T_LPAREN)) {
+ QVector<MacroArgumentReference> actuals;
+ collectActualArguments(&actuals);
+
+ if (_dot->is(T_RPAREN)) {
+ expandFunctionLikeMacro(identifierToken, m, actuals);
+ continue;
+ }
+ }
+ }
+#ifdef ICHECK_BUILD
+ }
+#endif
+ // it's not a function or object-like macro.
+ out(spell);
+ }
+ }
+ }
+ }
+
+ popState();
+
+ env->currentFile = previousFileName;
+ env->currentLine = previousCurrentLine;
+ _result = previousResult;
+
+ iflevel = previousIfLevel;
+}
+
+void Preprocessor::collectActualArguments(QVector<MacroArgumentReference> *actuals)
+{
+ if (_dot->isNot(T_LPAREN))
+ return;
+
+ ++_dot;
+
+ if (_dot->is(T_RPAREN))
+ return;
+
+ actuals->append(collectOneActualArgument());
+
+ while (_dot->is(T_COMMA)) {
+ ++_dot;
+
+ actuals->append(collectOneActualArgument());
+ }
+}
+
+MacroArgumentReference Preprocessor::collectOneActualArgument()
+{
+ const unsigned position = _dot->begin();
+
+ while (_dot->isNot(T_EOF_SYMBOL)) {
+ if (_dot->is(T_COMMA) || _dot->is(T_RPAREN))
+ break;
+
+ if (_dot->isNot(T_LPAREN))
+ ++_dot;
+
+ else {
+ int count = 0;
+
+ for (; _dot->isNot(T_EOF_SYMBOL); ++_dot) {
+ if (_dot->is(T_LPAREN))
+ ++count;
+
+ else if (_dot->is(T_RPAREN)) {
+ if (! --count) {
+ ++_dot;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ const unsigned end = _dot->begin();
+
+ return MacroArgumentReference(position, end - position);
+}
+
+Macro *Preprocessor::processObjectLikeMacro(TokenIterator identifierToken,
+ const QByteArray &spell,
+ Macro *m)
+{
+ QByteArray tmp;
+ expandObjectLikeMacro(identifierToken, spell, m, &tmp);
+
+ if (_dot->is(T_LPAREN)) {
+ // check if the expension generated a function-like macro.
+
+ m = 0; // reset the active the macro
+
+ pushState(createStateFromSource(tmp));
+
+ if (_dot->is(T_IDENTIFIER)) {
+ const QByteArray id = tokenSpell(*_dot);
+
+ if (Macro *macro = env->resolve(id)) {
+ if (macro->isFunctionLike())
+ m = macro;
+ }
+ }
+
+ popState();
+
+ if (m != 0)
+ return m;
+ }
+
+ const bool was = markGeneratedTokens(true, identifierToken);
+ out(tmp);
+ (void) markGeneratedTokens(was);
+ return 0;
+}
+
+void Preprocessor::expandBuiltinMacro(TokenIterator identifierToken,
+ const QByteArray &spell)
+{
+ const bool was = markGeneratedTokens(true, identifierToken);
+ expand(spell, _result);
+ (void) markGeneratedTokens(was);
+}
+
+void Preprocessor::expandObjectLikeMacro(TokenIterator identifierToken,
+ const QByteArray &spell,
+ Macro *m,
+ QByteArray *result)
+{
+ if (client)
+ client->startExpandingMacro(identifierToken->offset,
+ *m, spell, false);
+
+ m->setHidden(true);
+ expand(m->definition(), result);
+ m->setHidden(false);
+
+ if (client)
+ client->stopExpandingMacro(_dot->offset, *m);
+}
+
+void Preprocessor::expandFunctionLikeMacro(TokenIterator identifierToken,
+ Macro *m,
+ const QVector<MacroArgumentReference> &actuals)
+{
+ const char *beginOfText = startOfToken(*identifierToken);
+ const char *endOfText = endOfToken(*_dot);
+ ++_dot; // skip T_RPAREN
+
+ if (client) {
+ const QByteArray text =
+ QByteArray::fromRawData(beginOfText,
+ endOfText - beginOfText);
+
+ client->startExpandingMacro(identifierToken->offset,
+ *m, text, false, actuals);
+ }
+
+ const bool was = markGeneratedTokens(true, identifierToken);
+ expand(beginOfText, endOfText, _result);
+ (void) markGeneratedTokens(was);
+
+ if (client)
+ client->stopExpandingMacro(_dot->offset, *m);
+}
+
+const char *Preprocessor::startOfToken(const Token &token) const
+{ return _source.constBegin() + token.begin(); }
+
+const char *Preprocessor::endOfToken(const Token &token) const
+{ return _source.constBegin() + token.end(); }
+
+QByteArray Preprocessor::tokenSpell(const Token &token) const
+{
+ const QByteArray text = QByteArray::fromRawData(_source.constBegin() + token.offset,
+ token.f.length);
+ return text;
+}
+
+QByteArray Preprocessor::tokenText(const Token &token) const
+{
+ const QByteArray text(_source.constBegin() + token.offset,
+ token.f.length);
+ return text;
+}
+
+void Preprocessor::processDirective(TokenIterator firstToken, TokenIterator lastToken)
+{
+ RangeLexer tk(firstToken, lastToken);
+ ++tk; // skip T_POUND
+
+ if (tk->is(T_IDENTIFIER)) {
+ const QByteArray directive = tokenSpell(*tk);
+ switch (PP_DIRECTIVE_TYPE d = classifyDirective(directive)) {
+ case PP_DEFINE:
+ if (! skipping())
+ processDefine(firstToken, lastToken);
+ break;
+
+ case PP_INCLUDE:
+ case PP_INCLUDE_NEXT:
+ case PP_IMPORT:
+ if (! skipping())
+ processInclude(d == PP_INCLUDE_NEXT, firstToken, lastToken);
+ break;
+
+ case PP_UNDEF:
+ if (! skipping())
+ processUndef(firstToken, lastToken);
+ break;
+
+ case PP_ELIF:
+ processElif(firstToken, lastToken);
+ break;
+
+ case PP_ELSE:
+ processElse(firstToken, lastToken);
+ break;
+
+ case PP_ENDIF:
+ processEndif(firstToken, lastToken);
+ break;
+
+ case PP_IF:
+ processIf(firstToken, lastToken);
+ break;
+
+ case PP_IFDEF:
+ case PP_IFNDEF:
+ processIfdef(d == PP_IFNDEF, firstToken, lastToken);
+ break;
+
+ default:
+ break;
+ } // switch
+ }
+}
+
+QVector<Token> Preprocessor::tokenize(const QByteArray &text) const
+{
+ QVector<Token> tokens;
+ Lexer lex(text.constBegin(), text.constEnd());
+ lex.setScanKeywords(false);
+ Token tk;
+ do {
+ lex(&tk);
+ tokens.append(tk);
+ } while (tk.isNot(T_EOF_SYMBOL));
+ return tokens;
+}
+
+void Preprocessor::processInclude(bool, TokenIterator firstToken,
+ TokenIterator lastToken, bool acceptMacros)
+{
+ if (! client)
+ return; // nothing to do.
+
+ RangeLexer tk(firstToken, lastToken);
+ ++tk; // skip T_POUND
+ ++tk; // skip `include|nclude_next'
+
+ if (acceptMacros && tk->is(T_IDENTIFIER)) {
+ // ### TODO: implement me
+#if 0
+ QByteArray name;
+ name.reserve(256);
+ MacroExpander expandInclude(env);
+ expandInclude(startOfToken(tokens.at(2)),
+ startOfToken(tokens.last()),
+ &name);
+ const QByteArray previousSource = switchSource(name);
+ //processInclude(skipCurentPath, tokenize(name), /*accept macros=*/ false);
+ (void) switchSource(previousSource);
+#endif
+
+ } else if (tk->is(T_LESS)) {
+
+ TokenIterator start = tk.dot();
+
+ for (; tk->isNot(T_EOF_SYMBOL); ++tk) {
+ if (tk->is(T_GREATER))
+ break;
+ }
+
+ const char *beginOfPath = endOfToken(*start);
+ const char *endOfPath = startOfToken(*tk);
+
+ QString fn = string(beginOfPath, endOfPath - beginOfPath);
+ client->sourceNeeded(fn, Client::IncludeGlobal, firstToken->lineno);
+
+ } else if (tk->is(T_ANGLE_STRING_LITERAL) || tk->is(T_STRING_LITERAL)) {
+
+ const QByteArray spell = tokenSpell(*tk);
+ const char *beginOfPath = spell.constBegin();
+ const char *endOfPath = spell.constEnd();
+ const char quote = *beginOfPath;
+
+ if (beginOfPath + 1 != endOfPath && ((quote == '"' && endOfPath[-1] == '"') ||
+ (quote == '<' && endOfPath[-1] == '>'))) {
+
+ QString fn = string(beginOfPath + 1, spell.length() - 2);
+ client->sourceNeeded(fn, Client::IncludeLocal, firstToken->lineno);
+ }
+ }
+}
+
+void Preprocessor::processDefine(TokenIterator firstToken, TokenIterator lastToken)
+{
+ RangeLexer tk(firstToken, lastToken);
+
+ if (tk.size() < 3)
+ return; // nothing to do
+
+ ++tk; // skip T_POUND
+ ++tk; // skip T_DEFINE
+
+ if (tk->isNot(T_IDENTIFIER)) {
+ // ### warning expected an `identifier'
+ return;
+ }
+
+ Macro macro;
+ macro.setFileName(env->currentFile);
+ macro.setLine(env->currentLine);
+ macro.setName(tokenText(*tk));
+ macro.setOffset(firstToken->offset);
+ macro.setLength(endOfToken(lastToken[- 1]) - startOfToken(*firstToken));
+ ++tk; // skip T_IDENTIFIER
+
+ if (tk->is(T_LPAREN) && ! tk->f.whitespace) {
+ // a function-like macro definition
+ macro.setFunctionLike(true);
+
+ ++tk; // skip T_LPAREN
+ if (tk->is(T_IDENTIFIER)) {
+ macro.addFormal(tokenText(*tk));
+ ++tk; // skip T_IDENTIFIER
+ while (tk->is(T_COMMA)) {
+ ++tk;// skip T_COMMA
+ if (tk->isNot(T_IDENTIFIER))
+ break;
+ macro.addFormal(tokenText(*tk));
+ ++tk; // skip T_IDENTIFIER
+ }
+ }
+
+ if (tk->is(T_DOT_DOT_DOT)) {
+ macro.setVariadic(true);
+ ++tk; // skip T_DOT_DOT_DOT
+ }
+
+ if (tk->isNot(T_RPAREN)) {
+ // ### warning expected `)'
+ return;
+ }
+
+ ++tk; // skip T_RPAREN
+ }
+
+ if (isQtReservedWord(macro.name())) {
+ QByteArray macroId = macro.name();
+
+ if (macro.isFunctionLike()) {
+ macroId += '(';
+ bool fst = true;
+ foreach (const QByteArray formal, macro.formals()) {
+ if (! fst)
+ macroId += ", ";
+ fst = false;
+ macroId += formal;
+ }
+ macroId += ')';
+ }
+
+ macro.setDefinition(macroId);
+ } else {
+ // ### make me fast!
+ const char *startOfDefinition = startOfToken(*tk);
+ const char *endOfDefinition = endOfToken(lastToken[- 1]);
+ QByteArray definition(startOfDefinition,
+ endOfDefinition - startOfDefinition);
+ definition.replace("\\\n", " ");
+ definition.replace('\n', ' ');
+ macro.setDefinition(definition.trimmed());
+ }
+
+ env->bind(macro);
+
+ if (client)
+ client->macroAdded(macro);
+}
+
+void Preprocessor::processIf(TokenIterator firstToken, TokenIterator lastToken)
+{
+ RangeLexer tk(firstToken, lastToken);
+
+ ++tk; // skip T_POUND
+ ++tk; // skipt `if'
+
+ if (testIfLevel()) {
+ const char *first = startOfToken(*tk);
+ const char *last = startOfToken(*lastToken);
+
+ MacroExpander expandCondition (env, 0, client, tk.dot()->offset);
+ QByteArray condition;
+ condition.reserve(256);
+ expandCondition(first, last, &condition);
+
+ QVector<Token> tokens = tokenize(condition);
+
+ const Value result = evalExpression(tokens.constBegin(),
+ tokens.constEnd() - 1,
+ condition);
+
+ _trueTest[iflevel] = ! result.is_zero ();
+ _skipping[iflevel] = result.is_zero ();
+ }
+}
+
+void Preprocessor::processElse(TokenIterator firstToken, TokenIterator lastToken)
+{
+ RangeLexer tk(firstToken, lastToken);
+
+ if (iflevel == 0 && !skipping ()) {
+ // std::cerr << "*** WARNING #else without #if" << std::endl;
+ } else if (iflevel > 0 && _skipping[iflevel - 1]) {
+ _skipping[iflevel] = true;
+ } else {
+ _skipping[iflevel] = _trueTest[iflevel];
+ }
+}
+
+void Preprocessor::processElif(TokenIterator firstToken, TokenIterator lastToken)
+{
+ RangeLexer tk(firstToken, lastToken);
+ ++tk; // skip T_POUND
+ ++tk; // skipt `elif'
+
+ if (! (iflevel > 0)) {
+ // std::cerr << "*** WARNING: " << __FILE__ << __LINE__ << std::endl;
+ } else if (iflevel == 0 && !skipping()) {
+ // std::cerr << "*** WARNING #else without #if" << std::endl;
+ } else if (!_trueTest[iflevel] && !_skipping[iflevel - 1]) {
+
+ const char *first = startOfToken(*tk);
+ const char *last = startOfToken(*lastToken);
+
+ MacroExpander expandCondition (env, 0, client, tk.dot()->offset);
+ QByteArray condition;
+ condition.reserve(256);
+ expandCondition(first, last, &condition);
+
+ QVector<Token> tokens = tokenize(condition);
+
+ const Value result = evalExpression(tokens.constBegin(),
+ tokens.constEnd() - 1,
+ condition);
+
+ _trueTest[iflevel] = ! result.is_zero ();
+ _skipping[iflevel] = result.is_zero ();
+ } else {
+ _skipping[iflevel] = true;
+ }
+}
+
+void Preprocessor::processEndif(TokenIterator, TokenIterator)
+{
+ if (iflevel == 0 && !skipping()) {
+ // std::cerr << "*** WARNING #endif without #if" << std::endl;
+ } else {
+ _skipping[iflevel] = false;
+ _trueTest[iflevel] = false;
+
+ --iflevel;
+ }
+}
+
+void Preprocessor::processIfdef(bool checkUndefined,
+ TokenIterator firstToken,
+ TokenIterator lastToken)
+{
+ RangeLexer tk(firstToken, lastToken);
+
+ ++tk; // skip T_POUND
+ ++tk; // skip `ifdef'
+ if (testIfLevel()) {
+ if (tk->is(T_IDENTIFIER)) {
+ const QByteArray macroName = tokenSpell(*tk);
+ bool value = isMacroDefined(macroName, tk->offset, env, client) || env->isBuiltinMacro(macroName);
+
+ if (checkUndefined)
+ value = ! value;
+
+ _trueTest[iflevel] = value;
+ _skipping [iflevel] = ! value;
+ }
+ }
+}
+
+void Preprocessor::processUndef(TokenIterator firstToken, TokenIterator lastToken)
+{
+ RangeLexer tk(firstToken, lastToken);
+
+ ++tk; // skip T_POUND
+ ++tk; // skip `undef'
+
+ if (tk->is(T_IDENTIFIER)) {
+ const QByteArray macroName = tokenText(*tk);
+ const Macro *macro = env->remove(macroName);
+
+ if (client && macro)
+ client->macroAdded(*macro);
+ }
+}
+
+void Preprocessor::resetIfLevel ()
+{
+ iflevel = 0;
+ _skipping[iflevel] = false;
+ _trueTest[iflevel] = false;
+}
+
+Preprocessor::PP_DIRECTIVE_TYPE Preprocessor::classifyDirective(const QByteArray &directive) const
+{
+ switch (directive.size())
+ {
+ case 2:
+ if (directive[0] == 'i' && directive[1] == 'f')
+ return PP_IF;
+ break;
+
+ case 4:
+ if (directive[0] == 'e' && directive == "elif")
+ return PP_ELIF;
+ else if (directive[0] == 'e' && directive == "else")
+ return PP_ELSE;
+ break;
+
+ case 5:
+ if (directive[0] == 'i' && directive == "ifdef")
+ return PP_IFDEF;
+ else if (directive[0] == 'u' && directive == "undef")
+ return PP_UNDEF;
+ else if (directive[0] == 'e' && directive == "endif")
+ return PP_ENDIF;
+ break;
+
+ case 6:
+ if (directive[0] == 'i' && directive == "ifndef")
+ return PP_IFNDEF;
+ else if (directive[0] == 'i' && directive == "import")
+ return PP_IMPORT;
+ else if (directive[0] == 'd' && directive == "define")
+ return PP_DEFINE;
+ break;
+
+ case 7:
+ if (directive[0] == 'i' && directive == "include")
+ return PP_INCLUDE;
+ break;
+
+ case 12:
+ if (directive[0] == 'i' && directive == "include_next")
+ return PP_INCLUDE_NEXT;
+ break;
+
+ default:
+ break;
+ }
+
+ return PP_UNKNOWN_DIRECTIVE;
+}
+
+bool Preprocessor::testIfLevel()
+{
+ const bool result = !_skipping[iflevel++];
+ _skipping[iflevel] = _skipping[iflevel - 1];
+ _trueTest[iflevel] = false;
+ return result;
+}
+
+int Preprocessor::skipping() const
+{ return _skipping[iflevel]; }
+
+Value Preprocessor::evalExpression(TokenIterator firstToken, TokenIterator lastToken,
+ const QByteArray &source) const
+{
+ ExpressionEvaluator eval(client, env);
+ const Value result = eval(firstToken, lastToken, source);
+ return result;
+}
+
+bool Preprocessor::isQtReservedWord(const QByteArray ¯oId) const
+{
+ const int size = macroId.size();
+ if (size == 9 && macroId.at(0) == 'Q' && macroId == "Q_SIGNALS")
+ return true;
+ else if (size == 9 && macroId.at(0) == 'Q' && macroId == "Q_FOREACH")
+ return true;
+ else if (size == 7 && macroId.at(0) == 'Q' && macroId == "Q_SLOTS")
+ return true;
+ else if (size == 8 && macroId.at(0) == 'Q' && macroId == "Q_SIGNAL")
+ return true;
+ else if (size == 6 && macroId.at(0) == 'Q' && macroId == "Q_SLOT")
+ return true;
+ else if (size == 3 && macroId.at(0) == 'Q' && macroId == "Q_D")
+ return true;
+ else if (size == 3 && macroId.at(0) == 'Q' && macroId == "Q_Q")
+ return true;
+ else if (size == 6 && macroId.at(0) == 'S' && macroId == "SIGNAL")
+ return true;
+ else if (size == 4 && macroId.at(0) == 'S' && macroId == "SLOT")
+ return true;
+ else if (size == 7 && macroId.at(0) == 's' && macroId == "signals")
+ return true;
+ else if (size == 7 && macroId.at(0) == 'f' && macroId == "foreach")
+ return true;
+ else if (size == 5 && macroId.at(0) == 's' && macroId == "slots")
+ return true;
+ return false;
+}
+
+QString Preprocessor::string(const char *first, int length) const
+{
+ if (_originalSource.isEmpty())
+ return QString::fromUtf8(first, length);
+
+ const int position = first - _source.constData();
+ return _originalSource.mid(position, length);
+}