diff options
Diffstat (limited to 'src/libs/cplusplus/pp-engine.cpp')
-rw-r--r-- | src/libs/cplusplus/pp-engine.cpp | 1123 |
1 files changed, 1123 insertions, 0 deletions
diff --git a/src/libs/cplusplus/pp-engine.cpp b/src/libs/cplusplus/pp-engine.cpp new file mode 100644 index 0000000000..f2e1d4908e --- /dev/null +++ b/src/libs/cplusplus/pp-engine.cpp @@ -0,0 +1,1123 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 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 GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception +** version 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +/* + 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 <Lexer.h> +#include <Token.h> +#include <QtDebug> +#include <algorithm> + +using namespace CPlusPlus; + +namespace { + +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(Environment *env) + : 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)->length); + return text; + } + + bool process_expression() + { return process_constant_expression(); } + + bool process_primary() + { + if ((*_lex)->is(T_INT_LITERAL)) { + _value.set_long(tokenSpell().toLong()); + ++(*_lex); + return true; + } else if (isTokenDefined()) { + ++(*_lex); + if ((*_lex)->is(T_IDENTIFIER)) { + _value.set_long(env->resolve(tokenSpell()) != 0); + ++(*_lex); + return true; + } else if ((*_lex)->is(T_LPAREN)) { + ++(*_lex); + if ((*_lex)->is(T_IDENTIFIER)) { + _value.set_long(env->resolve(tokenSpell()) != 0); + ++(*_lex); + if ((*_lex)->is(T_RPAREN)) { + ++(*_lex); + return true; + } + } + return false; + } + return true; + } else if ((*_lex)->is(T_IDENTIFIER)) { + _value.set_long(0); + ++(*_lex); + return true; + } else if ((*_lex)->is(T_MINUS)) { + ++(*_lex); + process_primary(); + _value.set_long(- _value.l); + return true; + } else if ((*_lex)->is(T_PLUS)) { + ++(*_lex); + process_primary(); + return true; + } else if ((*_lex)->is(T_EXCLAIM)) { + ++(*_lex); + process_primary(); + _value.set_long(_value.is_zero()); + return true; + } else if ((*_lex)->is(T_LPAREN)) { + ++(*_lex); + process_expression(); + if ((*_lex)->is(T_RPAREN)) + ++(*_lex); + return true; + } + + return false; + } + + bool process_multiplicative() + { + process_primary(); + + while ((*_lex)->is(T_STAR) || (*_lex)->is(T_SLASH) || (*_lex)->is(T_PERCENT)) { + const Token op = *(*_lex); + ++(*_lex); + + const Value left = _value; + process_primary(); + + if (op.is(T_STAR)) { + _value = left * _value; + } else if (op.is(T_SLASH)) { + if (_value.is_zero()) + _value.set_long(0); + else + _value = left / _value; + } else if (op.is(T_PERCENT)) { + if (_value.is_zero()) + _value.set_long(0); + else + _value = left % _value; + } + } + + return true; + } + + bool process_additive() + { + process_multiplicative(); + + while ((*_lex)->is(T_PLUS) || (*_lex)->is(T_MINUS)) { + const Token op = *(*_lex); + ++(*_lex); + + const Value left = _value; + process_multiplicative(); + + if (op.is(T_PLUS)) + _value = left + _value; + else if (op.is(T_MINUS)) + _value = left - _value; + } + + return true; + } + + bool process_shift() + { + process_additive(); + + while ((*_lex)->is(T_MINUS_MINUS) || (*_lex)->is(T_GREATER_GREATER)) { + const Token op = *(*_lex); + ++(*_lex); + + const Value left = _value; + process_additive(); + + if (op.is(T_MINUS_MINUS)) + _value = left << _value; + else if (op.is(T_GREATER_GREATER)) + _value = left >> _value; + } + + return true; + } + + bool process_relational() + { + process_shift(); + + while ((*_lex)->is(T_LESS) || (*_lex)->is(T_LESS_EQUAL) || + (*_lex)->is(T_GREATER) || (*_lex)->is(T_GREATER_EQUAL)) { + const Token op = *(*_lex); + ++(*_lex); + + const Value left = _value; + process_shift(); + + if (op.is(T_LESS)) + _value = left < _value; + else if (op.is(T_LESS_EQUAL)) + _value = left <= _value; + else if (op.is(T_GREATER)) + _value = left > _value; + else if (op.is(T_GREATER_EQUAL)) + _value = left >= _value; + } + + return true; + } + + bool process_equality() + { + process_relational(); + + while ((*_lex)->is(T_EXCLAIM_EQUAL) || (*_lex)->is(T_EQUAL_EQUAL)) { + const Token op = *(*_lex); + ++(*_lex); + + const Value left = _value; + process_relational(); + + if (op.is(T_EXCLAIM_EQUAL)) + _value = left != _value; + else if (op.is(T_EQUAL_EQUAL)) + _value = left == _value; + } + + return true; + } + + bool process_and() + { + process_equality(); + + while ((*_lex)->is(T_AMPER)) { + const Token op = *(*_lex); + ++(*_lex); + + const Value left = _value; + process_equality(); + + _value = left & _value; + } + + return true; + } + + bool process_xor() + { + process_and(); + + while ((*_lex)->is(T_CARET)) { + const Token op = *(*_lex); + ++(*_lex); + + const Value left = _value; + process_and(); + + _value = left ^ _value; + } + + return true; + } + + bool process_or() + { + process_xor(); + + while ((*_lex)->is(T_CARET)) { + const Token op = *(*_lex); + ++(*_lex); + + const Value left = _value; + process_xor(); + + _value = left | _value; + } + + return true; + } + + bool process_logical_and() + { + process_or(); + + while ((*_lex)->is(T_AMPER_AMPER)) { + const Token op = *(*_lex); + ++(*_lex); + + const Value left = _value; + process_or(); + + _value = left && _value; + } + + return true; + } + + bool process_logical_or() + { + process_logical_and(); + + while ((*_lex)->is(T_PIPE_PIPE)) { + const Token op = *(*_lex); + ++(*_lex); + + const Value left = _value; + process_logical_and(); + + _value = left || _value; + } + + return true; + } + + bool process_constant_expression() + { + process_logical_or(); + const Value cond = _value; + if ((*_lex)->is(T_QUESTION)) { + ++(*_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; + } + + return true; + } + +private: + Environment *env; + QByteArray source; + RangeLexer *_lex; + Value _value; +}; + +} // end of anonymous namespace + + +pp::pp (Client *client, Environment &env) + : client(client), + env(env), + expand(env) +{ + resetIfLevel (); +} + +void pp::pushState(const State &s) +{ + _savedStates.append(state()); + _source = s.source; + _tokens = s.tokens; + _dot = s.dot; +} + +pp::State pp::state() const +{ + State state; + state.source = _source; + state.tokens = _tokens; + state.dot = _dot; + return state; +} + +void pp::popState() +{ + const State &state = _savedStates.last(); + _source = state.source; + _tokens = state.tokens; + _dot = state.dot; + _savedStates.removeLast(); +} + +void pp::operator () (const QByteArray &filename, + const QByteArray &source, + QByteArray *result) +{ + const QByteArray previousFile = env.current_file; + env.current_file = filename; + + operator () (source, result); + + env.current_file = previousFile; +} + +pp::State pp::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 pp::operator()(const QByteArray &source, QByteArray *result) +{ + pushState(createStateFromSource(source)); + + const unsigned previousCurrentLine = env.currentLine; + env.currentLine = 0; + + while (true) { + if (env.currentLine != _dot->lineno) { + if (env.currentLine > _dot->lineno) { + result->append("\n# "); + result->append(QByteArray::number(_dot->lineno)); + result->append(' '); + result->append('"'); + result->append(env.current_file); + result->append('"'); + result->append('\n'); + } else { + for (unsigned i = env.currentLine; i < _dot->lineno; ++i) + result->append('\n'); + } + env.currentLine = _dot->lineno; + } + + if (_dot->is(T_EOF_SYMBOL)) { + break; + } else if (_dot->is(T_POUND) && (! _dot->joined && _dot->newline)) { + TokenIterator start = _dot; + do { + ++_dot; + } while (_dot->isNot(T_EOF_SYMBOL) && (_dot->joined || ! _dot->newline)); + + //qDebug() << QByteArray(first + beginPP.offset, + //tokens.last().end() - beginPP.offset); + + const bool skippingBlocks = _skipping[iflevel]; + + processDirective(start, _dot); + + if (client && skippingBlocks != _skipping[iflevel]) { + unsigned offset = start->offset; + if (_skipping[iflevel]) { + if (_dot->newline) + ++offset; + client->startSkippingBlocks(offset); + } else { + if (offset) + --offset; + client->stopSkippingBlocks(offset); + } + } + } else if (skipping()) { + do { + ++_dot; + } while (_dot->isNot(T_EOF_SYMBOL) && (_dot->joined || ! _dot->newline)); + } else { + if (_dot->joined) + result->append("\\\n"); + else if (_dot->whitespace) + result->append(' '); + + if (_dot->isNot(T_IDENTIFIER)) { + result->append(tokenSpell(*_dot)); + ++_dot; + } else { + const TokenIterator identifierToken = _dot; + ++_dot; // skip T_IDENTIFIER + + const QByteArray spell = tokenSpell(*identifierToken); + if (env.isBuiltinMacro(spell)) { + const Macro trivial; + + if (client) + client->startExpandingMacro(identifierToken->offset, + trivial, spell); + + expand(spell.constBegin(), spell.constEnd(), result); + + if (client) + client->stopExpandingMacro(_dot->offset, trivial); + + continue; + } + + Macro *m = env.resolve(spell); + if (! m) { + result->append(spell); + } else { + if (! m->function_like) { + if (_dot->isNot(T_LPAREN)) { + if (client) + client->startExpandingMacro(identifierToken->offset, + *m, spell); + + m->hidden = true; + + expand(m->definition.constBegin(), + m->definition.constEnd(), + result); + + if (client) + client->stopExpandingMacro(_dot->offset, *m); + + m->hidden = false; + continue; + } else { + QByteArray tmp; + m->hidden = true; + + if (client) + client->startExpandingMacro(identifierToken->offset, + *m, spell); + + expand(m->definition.constBegin(), + m->definition.constEnd(), + &tmp); + + if (client) + client->stopExpandingMacro(_dot->offset, *m); + + m->hidden = false; + + m = 0; // reset the active the macro + + pushState(createStateFromSource(tmp)); + if (_dot->is(T_IDENTIFIER)) { + const QByteArray id = tokenSpell(*_dot); + Macro *macro = env.resolve(id); + if (macro && macro->function_like) + m = macro; + } + popState(); + + if (! m) { + result->append(tmp); + continue; + } + } + } + + // collect the actual arguments + if (_dot->isNot(T_LPAREN)) { + // ### warnng expected T_LPAREN + result->append(m->name); + continue; + } + + int count = 0; + while (_dot->isNot(T_EOF_SYMBOL)) { + if (_dot->is(T_LPAREN)) + ++count; + else if (_dot->is(T_RPAREN)) { + if (! --count) + break; + } + ++_dot; + } + if (_dot->isNot(T_RPAREN)) { + // ### warning expected T_RPAREN + } else { + 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); + } + + expand(beginOfText, endOfText, result); + + if (client) + client->stopExpandingMacro(_dot->offset, *m); + } + } + } + } + } + + popState(); + env.currentLine = previousCurrentLine; +} + +const char *pp::startOfToken(const Token &token) const +{ return _source.constBegin() + token.begin(); } + +const char *pp::endOfToken(const Token &token) const +{ return _source.constBegin() + token.end(); } + +QByteArray pp::tokenSpell(const Token &token) const +{ + const QByteArray text = QByteArray::fromRawData(_source.constBegin() + token.offset, + token.length); + return text; +} + +QByteArray pp::tokenText(const Token &token) const +{ + const QByteArray text(_source.constBegin() + token.offset, + token.length); + return text; +} + +void pp::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: + 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> pp::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 pp::processInclude(bool skipCurentPath, + TokenIterator firstToken, TokenIterator lastToken, + bool acceptMacros) +{ + RangeLexer tk(firstToken, lastToken); + ++tk; // skip T_POUND + ++tk; // skip `include|nclude_next' + + if (acceptMacros && tk->is(T_IDENTIFIER)) { +#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); + const QByteArray path = QByteArray::fromRawData(beginOfPath, + endOfPath - beginOfPath); + + QString fn = QString::fromUtf8(path.constData(), path.length()); + + if (client) + client->sourceNeeded(fn, Client::IncludeGlobal); + } 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] == '>'))) { + const QByteArray path = QByteArray::fromRawData(beginOfPath + 1, + spell.length() - 2); + QString fn = QString::fromUtf8(path.constData(), path.length()); + + if (client) + client->sourceNeeded(fn, Client::IncludeLocal); + } + } +} + +void pp::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.name = tokenText(*tk); + ++tk; // skip T_IDENTIFIER + + if (tk->is(T_LPAREN) && ! tk->whitespace) { + // a function-like macro definition + macro.function_like = true; + + ++tk; // skip T_LPAREN + if (tk->is(T_IDENTIFIER)) { + macro.formals.append(tokenText(*tk)); + ++tk; // skip T_IDENTIFIER + while (tk->is(T_COMMA)) { + ++tk;// skip T_COMMA + if (tk->isNot(T_IDENTIFIER)) + break; + macro.formals.append(tokenText(*tk)); + ++tk; // skip T_IDENTIFIER + } + } + + if (tk->is(T_DOT_DOT_DOT)) { + macro.variadics = true; + ++tk; // skip T_DOT_DOT_DOT + } + + if (tk->isNot(T_RPAREN)) { + // ### warning expected `)' + return; + } + + ++tk; // skip T_RPAREN + } + + QByteArray macroId = macro.name; + const bool isQtWord = isQtReservedWord(macroId); + + if (macro.function_like) { + macroId += '('; + for (int i = 0; i < macro.formals.size(); ++i) { + if (i != 0) + macroId += ", "; + + const QByteArray formal = macro.formals.at(i); + macroId += formal; + } + macroId += ')'; + } + + if (isQtWord) + macro.definition = macroId; + else { + // ### make me fast! + const char *startOfDefinition = startOfToken(*tk); + const char *endOfDefinition = startOfToken(*lastToken); + macro.definition.append(startOfDefinition, + endOfDefinition - startOfDefinition); + macro.definition.replace("\\\n", " "); + macro.definition.replace('\n', ' '); + macro.definition = macro.definition.trimmed(); + } + + env.bind(macro); + + QByteArray macroText; + macroText.reserve(64); + macroText += "#define "; + + macroText += macroId; + macroText += ' '; + macroText += macro.definition; + macroText += '\n'; + + client->macroAdded(macroId, macroText); +} + +void pp::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); + QByteArray condition; + condition.reserve(256); + expandCondition(first, last, &condition); + + QVector<Token> tokens = tokenize(condition); + + const Value result = evalExpression(tokens.constBegin(), + tokens.constEnd() - 1, + condition); + + _true_test[iflevel] = ! result.is_zero (); + _skipping[iflevel] = result.is_zero (); + } +} + +void pp::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] = _true_test[iflevel]; + } +} + +void pp::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 (!_true_test[iflevel] && !_skipping[iflevel - 1]) { + const Value result = evalExpression(tk.dot(), lastToken, _source); + _true_test[iflevel] = ! result.is_zero (); + _skipping[iflevel] = result.is_zero (); + } else { + _skipping[iflevel] = true; + } +} + +void pp::processEndif(TokenIterator, TokenIterator) +{ + if (iflevel == 0 && !skipping()) { + // std::cerr << "*** WARNING #endif without #if" << std::endl; + } else { + _skipping[iflevel] = false; + _true_test[iflevel] = false; + + --iflevel; + } +} + +void pp::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 = env.resolve(macroName) != 0 || env.isBuiltinMacro(macroName); + + if (checkUndefined) + value = ! value; + + _true_test[iflevel] = value; + _skipping [iflevel] = ! value; + } + } +} + +void pp::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); + env.remove(macroName); + + QByteArray macroText; + macroText += "#undef "; + macroText += macroName; + macroText += '\n'; + client->macroAdded(macroName, macroText); + } +} + +void pp::resetIfLevel () +{ + iflevel = 0; + _skipping[iflevel] = false; + _true_test[iflevel] = false; +} + +pp::PP_DIRECTIVE_TYPE pp::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] == '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 pp::testIfLevel() +{ + const bool result = !_skipping[iflevel++]; + _skipping[iflevel] = _skipping[iflevel - 1]; + _true_test[iflevel] = false; + return result; +} + +int pp::skipping() const +{ return _skipping[iflevel]; } + +Value pp::evalExpression(TokenIterator firstToken, TokenIterator lastToken, + const QByteArray &source) const +{ + ExpressionEvaluator eval(&env); + const Value result = eval(firstToken, lastToken, source); + return result; +} + +bool pp::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 == 7 && macroId.at(0) == 'Q' && macroId == "Q_SLOTS") + 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 == 5 && macroId.at(0) == 's' && macroId == "slots") + return true; + return false; +} |