[C++] Rewrite of the preprocessor.

This rewrite fixes a couple of issues with the pre-processor. It now
supports:
- macros in macro bodies
- stringification of parameters [cpp.stringize]
- the concatenation operator [cpp.concat]
- #include MACRO_HERE
- defined() inside macro bodies used in pp-conditions.

Change-Id: Ifdb78041fb6afadf44f939a4bd66ce2832b8601f
Reviewed-by: Roberto Raggi <roberto.raggi@nokia.com>
This commit is contained in:
Erik Verbruggen
2012-03-26 15:18:01 +02:00
parent 159058d9eb
commit 60db573660
50 changed files with 1843 additions and 1620 deletions

View File

@@ -198,6 +198,8 @@ protected:
};
#define DO_NOT_DUMP_ALL_PARSER_ERRORS
class DocumentDiagnosticClient : public DiagnosticClient
{
enum { MAX_MESSAGE_COUNT = 10 };
@@ -217,8 +219,10 @@ public:
if (level == Error) {
++errorCount;
#ifdef DO_NOT_DUMP_ALL_PARSER_ERRORS
if (errorCount >= MAX_MESSAGE_COUNT)
return; // ignore the error
#endif // DO_NOT_DUMP_ALL_PARSER_ERRORS
}
const QString fileName = QString::fromUtf8(fileId->chars(), fileId->size());
@@ -229,6 +233,16 @@ public:
QString message;
message.vsprintf(format, ap);
#ifndef DO_NOT_DUMP_ALL_PARSER_ERRORS
{
const char *levelStr = "Unknown level";
if (level == Document::DiagnosticMessage::Warning) levelStr = "Warning";
if (level == Document::DiagnosticMessage::Error) levelStr = "Error";
if (level == Document::DiagnosticMessage::Fatal) levelStr = "Fatal";
qDebug("%s:%u:%u: %s: %s", fileId->chars(), line, column, levelStr, message.toUtf8().constData());
}
#endif // DO_NOT_DUMP_ALL_PARSER_ERRORS
Document::DiagnosticMessage m(convertLevel(level), doc->fileName(),
line, column, message);
messages->append(m);
@@ -341,10 +355,9 @@ void Document::appendMacro(const Macro &macro)
void Document::addMacroUse(const Macro &macro, unsigned offset, unsigned length,
unsigned beginLine,
const QVector<MacroArgumentReference> &actuals, bool inCondition)
const QVector<MacroArgumentReference> &actuals)
{
MacroUse use(macro, offset, offset + length, beginLine);
use.setInCondition(inCondition);
foreach (const MacroArgumentReference &actual, actuals) {
const Block arg(actual.position(), actual.position() + actual.length());

View File

@@ -77,8 +77,7 @@ public:
void appendMacro(const Macro &macro);
void addMacroUse(const Macro &macro, unsigned offset, unsigned length,
unsigned beginLine, const QVector<MacroArgumentReference> &range,
bool inCondition);
unsigned beginLine, const QVector<MacroArgumentReference> &range);
void addUndefinedMacroUse(const QByteArray &name, unsigned offset);
Control *control() const;
@@ -247,7 +246,6 @@ public:
class MacroUse: public Block {
Macro _macro;
QVector<Block> _arguments;
bool _inCondition;
unsigned _beginLine;
public:
@@ -255,7 +253,6 @@ public:
unsigned begin, unsigned end, unsigned beginLine)
: Block(begin, end),
_macro(macro),
_inCondition(false),
_beginLine(beginLine)
{ }
@@ -268,9 +265,6 @@ public:
QVector<Block> arguments() const
{ return _arguments; }
bool isInCondition() const
{ return _inCondition; }
unsigned beginLine() const
{ return _beginLine; }
@@ -281,9 +275,6 @@ public:
void addArgument(const Block &block)
{ _arguments.append(block); }
void setInCondition(bool set)
{ _inCondition = set; }
friend class Document;
};

View File

@@ -69,7 +69,6 @@ public:
virtual void startExpandingMacro(unsigned,
const Macro &,
const QByteArray &,
bool,
const QVector<MacroArgumentReference> &) {}
virtual void stopExpandingMacro(unsigned, const Macro &) {}

View File

@@ -92,19 +92,11 @@ QString Macro::decoratedName() const
QString Macro::toString() const
{
QString text = decoratedName();
text.append(QString::fromUtf8(_definition.constData(), _definition.size()));
text.append(QString::fromUtf8(_definitionText.constData(), _definitionText.size()));
return text;
}
QString Macro::toStringWithLineBreaks() const
{
if (_lineBreaks.isEmpty())
return toString();
QString text = decoratedName();
QString definitionWithBreaks = QString::fromUtf8(_definition.constData(), _definition.size());
foreach (unsigned pos, _lineBreaks)
definitionWithBreaks[pos] = '\n';
text.append(definitionWithBreaks);
return text;
return toString();
}

View File

@@ -52,6 +52,8 @@
#ifndef CPLUSPLUS_PP_MACRO_H
#define CPLUSPLUS_PP_MACRO_H
#include "PPToken.h"
#include <CPlusPlusForwardDeclarations.h>
#include <QByteArray>
@@ -60,8 +62,12 @@
namespace CPlusPlus {
class Environment;
class CPLUSPLUS_EXPORT Macro
{
typedef Internal::PPToken PPToken;
public:
Macro();
@@ -71,13 +77,16 @@ public:
void setName(const QByteArray &name)
{ _name = name; }
QByteArray definition() const
{ return _definition; }
const QByteArray definitionText() const
{ return _definitionText; }
void setDefinition(const QByteArray &definition)
{ _definition = definition; }
const QVector<PPToken> &definitionTokens() const
{ return _definitionTokens; }
QVector<QByteArray> formals() const
void setDefinition(const QByteArray &definitionText, const QVector<PPToken> &definitionTokens)
{ _definitionText = definitionText; _definitionTokens = definitionTokens; }
const QVector<QByteArray> &formals() const
{ return _formals; }
void addFormal(const QByteArray &formal)
@@ -125,16 +134,11 @@ public:
void setVariadic(bool isVariadic)
{ f._variadic = isVariadic; }
void setLineBreaks(const QList<unsigned> &breaks)
{ _lineBreaks = breaks; }
const QList<unsigned> &lineBreaks() const
{ return _lineBreaks; }
QString toString() const;
QString toStringWithLineBreaks() const;
// ### private
private:
friend class Environment;
Macro *_next;
unsigned _hashcode;
@@ -149,10 +153,10 @@ private:
};
QByteArray _name;
QByteArray _definition;
QByteArray _definitionText;
QVector<PPToken> _definitionTokens;
QVector<QByteArray> _formals;
QString _fileName;
QList<unsigned> _lineBreaks;
unsigned _line;
unsigned _offset;
unsigned _length;

View File

@@ -0,0 +1,45 @@
#include "PPToken.h"
#include <cstring>
using namespace CPlusPlus::Internal;
ByteArrayRef::ByteArrayRef()
: m_ref(0)
, m_offset(0)
, m_length(0)
{}
bool ByteArrayRef::startsWith(const char *s) const
{
int l = std::strlen(s);
if (l > m_length)
return false;
return !qstrncmp(start(), s, l);
}
int ByteArrayRef::count(char ch) const
{
if (!m_ref)
return 0;
int num = 0;
const char *b = start();
const char *i = b + m_length;
while (i != b)
if (*--i == ch)
++num;
return num;
}
PPToken::PPToken()
{}
void PPToken::squeeze()
{
if (isValid()) {
m_src = m_src.mid(offset, length());
m_src.squeeze();
offset = 0;
}
}

View File

@@ -0,0 +1,106 @@
#ifndef CPLUSPLUS_INTERNAL_PPTOKEN_H
#define CPLUSPLUS_INTERNAL_PPTOKEN_H
#include <CPlusPlus.h>
#include <Token.h>
#include <QByteArray>
namespace CPlusPlus {
namespace Internal {
class CPLUSPLUS_EXPORT ByteArrayRef
{
public:
ByteArrayRef();
ByteArrayRef(const QByteArray *ref)
: m_ref(ref)
, m_offset(0)
, m_length(ref->length())
{}
ByteArrayRef(const QByteArray *ref, int offset, int length)
: m_ref(ref)
, m_offset(offset)
, m_length(length)
{
Q_ASSERT(ref);
Q_ASSERT(offset >= 0);
Q_ASSERT(length >= 0);
Q_ASSERT(offset + length <= ref->size());
}
inline const char *start() const
{ return m_ref ? m_ref->constData() + m_offset : 0; }
inline int length() const
{ return m_length; }
inline int size() const
{ return length(); }
inline char at(int pos) const
{ return m_ref && pos >= 0 && pos < m_length ? m_ref->at(m_offset + pos) : '\0'; }
inline char operator[](int pos) const
{ return at(pos); }
QByteArray toByteArray() const
{ return m_ref ? QByteArray(m_ref->constData() + m_offset, m_length) : QByteArray(); }
bool operator==(const QByteArray &other) const
{ return m_ref ? (m_length == other.length() && !qstrncmp(m_ref->constData() + m_offset, other.constData(), m_length)) : false; }
bool operator!=(const QByteArray &other) const
{ return !this->operator==(other); }
bool startsWith(const char *ch) const;
int count(char c) const;
private:
const QByteArray *m_ref;
int m_offset;
int m_length;
};
inline bool operator==(const QByteArray &other, const ByteArrayRef &ref)
{ return ref == other; }
inline bool operator!=(const QByteArray &other, const ByteArrayRef &ref)
{ return ref != other; }
class CPLUSPLUS_EXPORT PPToken: public Token
{
public:
PPToken();
PPToken(const QByteArray &src)
: m_src(src)
{}
void setSource(const QByteArray &src)
{ m_src = src; }
const QByteArray &source() const
{ return m_src; }
const char *start() const
{ return m_src.constData() + offset; }
ByteArrayRef asByteArrayRef() const
{ return ByteArrayRef(&m_src, offset, length()); }
bool isValid() const
{ return !m_src.isEmpty(); }
void squeeze();
private:
QByteArray m_src;
};
} // namespace Internal
} // namespace CPlusPlus
#endif // CPLUSPLUS_INTERNAL_PPTOKEN_H

View File

@@ -75,7 +75,7 @@ public:
public:
Client();
virtual ~Client();
virtual ~Client() = 0;
virtual void macroAdded(const Macro &macro) = 0;
@@ -85,13 +85,13 @@ public:
virtual void startExpandingMacro(unsigned offset,
const Macro &macro,
const QByteArray &originalText,
bool inCondition = false,
const QVector<MacroArgumentReference> &actuals
= QVector<MacroArgumentReference>()) = 0;
virtual void stopExpandingMacro(unsigned offset,
const Macro &macro) = 0;
/// Start skipping from the given offset.
virtual void startSkippingBlocks(unsigned offset) = 0;
virtual void stopSkippingBlocks(unsigned offset) = 0;

View File

@@ -150,7 +150,7 @@ void Environment::reset()
_hash_count = 401;
}
bool Environment::isBuiltinMacro(const QByteArray &s)
bool Environment::isBuiltinMacro(const Internal::ByteArrayRef &s)
{
if (s.length() != 8)
return false;
@@ -236,6 +236,22 @@ Macro *Environment::resolve(const QByteArray &name) const
return it;
}
Macro *Environment::resolve(const Internal::ByteArrayRef &name) const
{
if (! _macros)
return 0;
Macro *it = _hash[hashCode(name) % _hash_count];
for (; it; it = it->_next) {
if (it->name() != name)
continue;
else if (it->isHidden())
return 0;
else break;
}
return it;
}
unsigned Environment::hashCode(const QByteArray &s)
{
unsigned hash_value = 0;
@@ -246,6 +262,16 @@ unsigned Environment::hashCode(const QByteArray &s)
return hash_value;
}
unsigned Environment::hashCode(const Internal::ByteArrayRef &s)
{
unsigned hash_value = 0;
for (int i = 0; i < s.length(); ++i)
hash_value = (hash_value << 5) - hash_value + s.at(i);
return hash_value;
}
void Environment::rehash()
{
if (_hash) {

View File

@@ -53,6 +53,7 @@
#define CPLUSPLUS_PP_ENVIRONMENT_H
#include "CPlusPlusForwardDeclarations.h"
#include "PPToken.h"
#include <QList>
#include <QByteArray>
@@ -78,6 +79,7 @@ public:
Macro *remove(const QByteArray &name);
Macro *resolve(const QByteArray &name) const;
Macro *resolve(const Internal::ByteArrayRef &name) const;
iterator firstMacro() const;
iterator lastMacro() const;
@@ -85,10 +87,11 @@ public:
void reset();
void addMacros(const QList<Macro> &macros);
static bool isBuiltinMacro(const QByteArray &name);
static bool isBuiltinMacro(const Internal::ByteArrayRef &name);
private:
static unsigned hashCode(const QByteArray &s);
static unsigned hashCode(const Internal::ByteArrayRef &s);
void rehash();
public:

View File

@@ -51,9 +51,9 @@ HEADERS += \
$$PWD/pp.h \
$$PWD/pp-cctype.h \
$$PWD/pp-engine.h \
$$PWD/pp-macro-expander.h \
$$PWD/pp-scanner.h \
$$PWD/findcdbbreakpoint.h
$$PWD/findcdbbreakpoint.h \
$$PWD/PPToken.h
SOURCES += \
$$PWD/SimpleLexer.cpp \
@@ -78,8 +78,8 @@ SOURCES += \
$$PWD/FastPreprocessor.cpp \
$$PWD/Macro.cpp \
$$PWD/pp-engine.cpp \
$$PWD/pp-macro-expander.cpp \
$$PWD/pp-scanner.cpp \
$$PWD/findcdbbreakpoint.cpp
$$PWD/findcdbbreakpoint.cpp \
$$PWD/PPToken.cpp
RESOURCES += $$PWD/cplusplus.qrc

View File

@@ -1,9 +1,9 @@
TEMPLATE = lib
TARGET = CPlusPlus
DEFINES += NDEBUG
unix:QMAKE_CXXFLAGS_DEBUG += -O2
#DEFINES += NDEBUG
#unix:QMAKE_CXXFLAGS_DEBUG += -O2
QMAKE_CXXFLAGS_DEBUG += -ggdb
include(../../qtcreatorlibrary.pri)
include(cplusplus-lib.pri)
include(../languageutils/languageutils.pri)

View File

@@ -149,8 +149,8 @@ DynamicLibrary {
"pp-cctype.h",
"pp-engine.cpp",
"pp-engine.h",
"pp-macro-expander.cpp",
"pp-macro-expander.h",
"PPToken.cpp",
"PPToken.h",
"pp-scanner.cpp",
"pp-scanner.h",
"pp.h",

File diff suppressed because it is too large Load Diff

View File

@@ -52,29 +52,39 @@
#ifndef CPLUSPLUS_PP_ENGINE_H
#define CPLUSPLUS_PP_ENGINE_H
#include "PPToken.h"
#include "PreprocessorClient.h"
#include "pp-macro-expander.h"
#include <Lexer.h>
#include <Token.h>
#include <QVector>
#include <QBitArray>
#include <QByteArray>
namespace CPlusPlus {
struct Value;
class Environment;
namespace Internal {
class PPToken;
struct TokenBuffer;
struct Value;
}
class CPLUSPLUS_EXPORT Preprocessor
{
typedef Internal::PPToken PPToken;
typedef Internal::Value Value;
public:
Preprocessor(Client *client, Environment *env);
QByteArray operator()(const QString &filename, const QString &source);
QByteArray operator()(const QString &filename, const QByteArray &source);
QByteArray operator()(const QString &filename, const QByteArray &source, bool noLines = false, bool markGeneratedTokens = true);
void preprocess(const QString &filename,
const QByteArray &source,
QByteArray *result);
QByteArray *result, bool noLines, bool markGeneratedTokens, bool inCondition);
bool expandMacros() const;
void setExpandMacros(bool expandMacros);
@@ -85,126 +95,101 @@ public:
private:
enum { MAX_LEVEL = 512 };
enum PP_DIRECTIVE_TYPE
{
PP_UNKNOWN_DIRECTIVE,
PP_DEFINE,
PP_IMPORT,
PP_INCLUDE,
PP_INCLUDE_NEXT,
PP_ELIF,
PP_ELSE,
PP_ENDIF,
PP_IF,
PP_IFDEF,
PP_IFNDEF,
PP_UNDEF
};
typedef const CPlusPlus::Token *TokenIterator;
struct State {
QByteArray source;
QVector<CPlusPlus::Token> tokens;
TokenIterator dot;
State();
QString m_currentFileName;
QByteArray m_source;
Lexer *m_lexer;
QBitArray m_skipping;
QBitArray m_trueTest;
int m_ifLevel;
Internal::TokenBuffer *m_tokenBuffer;
bool m_inPreprocessorDirective;
QByteArray *m_result;
bool m_markGeneratedTokens;
bool m_noLines;
bool m_inCondition;
bool m_inDefine;
};
bool markGeneratedTokens(bool markGeneratedTokens, TokenIterator dot = 0);
bool markGeneratedTokens(bool markGeneratedTokens, int position, int extraLines=0, bool newline=false);
void handleDefined(PPToken *tk);
void pushToken(PPToken *tk);
void lex(PPToken *tk);
void skipPreprocesorDirective(PPToken *tk);
bool handleIdentifier(PPToken *tk);
bool handleFunctionLikeMacro(PPToken *tk, const Macro *macro, QVector<PPToken> &body, bool addWhitespaceMarker);
QByteArray expand(const QByteArray &source);
void expand(const QByteArray &source, QByteArray *result);
void expand(const char *first, const char *last, QByteArray *result);
void expandBuiltinMacro(TokenIterator identifierToken,
const QByteArray &spell);
void expandObjectLikeMacro(TokenIterator identifierToken,
const QByteArray &spell,
Macro *m, QByteArray *result);
void expandFunctionLikeMacro(TokenIterator identifierToken, Macro *m,
const QVector<MacroArgumentReference> &actuals);
void resetIfLevel();
bool testIfLevel();
int skipping() const;
PP_DIRECTIVE_TYPE classifyDirective(const QByteArray &directive) const;
Value evalExpression(TokenIterator firstToken,
TokenIterator lastToken,
const QByteArray &source) const;
bool skipping() const
{ return m_state.m_skipping[m_state.m_ifLevel]; }
QVector<CPlusPlus::Token> tokenize(const QByteArray &text) const;
const char *startOfToken(const CPlusPlus::Token &token) const;
const char *endOfToken(const CPlusPlus::Token &token) const;
bool collectActualArguments(PPToken *tk, QVector<QVector<PPToken> > *actuals);
void scanActualArgument(PPToken *tk, QVector<PPToken> *tokens);
QByteArray tokenSpell(const CPlusPlus::Token &token) const;
QByteArray tokenText(const CPlusPlus::Token &token) const; // does a deep copy
void handlePreprocessorDirective(PPToken *tk);
void handleIncludeDirective(PPToken *tk);
void handleDefineDirective(PPToken *tk);
QByteArray expand(PPToken *tk, PPToken *lastConditionToken = 0);
const Internal::PPToken evalExpression(PPToken *tk, Value &result);
void handleIfDirective(PPToken *tk);
void handleElifDirective(PPToken *tk, const PPToken &poundToken);
void handleElseDirective(PPToken *tk, const PPToken &poundToken);
void handleEndIfDirective(PPToken *tk, const PPToken &poundToken);
void handleIfDefDirective(bool checkUndefined, PPToken *tk);
void handleUndefDirective(PPToken *tk);
void collectActualArguments(QVector<MacroArgumentReference> *actuals);
MacroArgumentReference collectOneActualArgument();
static bool isQtReservedWord(const Internal::ByteArrayRef &name);
void processNewline(bool force = false, int extraLines = 0);
void processSkippingBlocks(bool skippingBlocks,
TokenIterator dot, TokenIterator lastToken);
Macro *processObjectLikeMacro(TokenIterator identifierToken,
const QByteArray &spell,
Macro *m);
void processDirective(TokenIterator dot, TokenIterator lastToken);
void processInclude(bool skipCurrentPath,
TokenIterator dot, TokenIterator lastToken,
bool acceptMacros = true);
void processDefine(TokenIterator dot, TokenIterator lastToken);
void processIf(TokenIterator dot, TokenIterator lastToken);
void processElse(TokenIterator dot, TokenIterator lastToken);
void processElif(TokenIterator dot, TokenIterator lastToken);
void processEndif(TokenIterator dot, TokenIterator lastToken);
void processIfdef(bool checkUndefined,
TokenIterator dot, TokenIterator lastToken);
void processUndef(TokenIterator dot, TokenIterator lastToken);
bool isQtReservedWord(const QByteArray &name) const;
State state() const;
void pushState(const State &state);
void pushState(const State &newState);
void popState();
State createStateFromSource(const QByteArray &source) const;
State createStateFromSource(const QString &fileName, const QByteArray &source, QByteArray *result, bool noLines, bool markGeneratedTokens, bool inCondition) const;
void out(const QByteArray &text);
void out(char ch);
void out(const char *s);
inline bool atStartOfOutputLine() const
{ return (m_state.m_result && !m_state.m_result->isEmpty()) ? m_state.m_result->end()[-1] == '\n' : true; }
inline void startNewOutputLine() const
{
if (m_state.m_result && !m_state.m_result->isEmpty() && m_state.m_result->end()[-1] != '\n')
out('\n');
}
inline void out(const QByteArray &text) const
{ if (m_state.m_result) m_state.m_result->append(text); }
inline void out(char ch) const
{ if (m_state.m_result) m_state.m_result->append(ch); }
inline void out(const char *s) const
{ if (m_state.m_result) m_state.m_result->append(s); }
inline void out(const Internal::ByteArrayRef &ref) const
{ if (m_state.m_result) m_state.m_result->append(ref.start(), ref.length()); }
QString string(const char *first, int len) const;
bool maybeAfterComment() const;
bool maybeMultilineToken(TokenIterator tok);
void skipToNextLine();
PPToken generateToken(enum Kind kind, const Internal::ByteArrayRef &content, unsigned lineno, bool addQuotes);
PPToken generateConcatenated(const PPToken &leftTk, const PPToken &rightTk);
void startSkippingBlocks(const PPToken &tk) const;
private:
Client *client;
Environment *env;
MacroExpander _expand;
Client *m_client;
Environment *m_env;
QByteArray m_scratchBuffer;
QBitArray _skipping; // ### move in state
QBitArray _trueTest; // ### move in state
int iflevel; // ### move in state
QList<State> m_savedStates;
QList<State> _savedStates;
QString m_originalSource;
bool m_expandMacros;
bool m_keepComments;
QByteArray _source;
QVector<CPlusPlus::Token> _tokens;
TokenIterator _dot;
QByteArray *_result;
bool _markGeneratedTokens;
QString _originalSource;
bool _expandMacros;
bool _keepComments;
State m_state;
};
} // namespace CPlusPlus

View File

@@ -1,449 +0,0 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
**
** 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.
**
** 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.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**************************************************************************/
#include "pp-macro-expander.h"
#include "pp.h"
#include "pp-cctype.h"
#include <QDateTime>
namespace CPlusPlus {
struct pp_frame
{
Macro *expanding_macro;
const QVector<QByteArray> actuals;
pp_frame(Macro *expanding_macro, const QVector<QByteArray> &actuals)
: expanding_macro (expanding_macro),
actuals (actuals)
{ }
};
} // namespace CPlusPlus
using namespace CPlusPlus;
inline static bool comment_p (const char *__first, const char *__last)
{
if (__first == __last)
return false;
if (*__first != '/')
return false;
if (++__first == __last)
return false;
return (*__first == '/' || *__first == '*');
}
MacroExpander::MacroExpander(Environment *env, pp_frame *frame, Client *client, unsigned start_offset)
: env(env),
frame(frame),
client(client),
start_offset(start_offset),
lines(0)
{ }
const QByteArray *MacroExpander::resolve_formal(const QByteArray &__name)
{
if (! (frame && frame->expanding_macro))
return 0;
const QVector<QByteArray> formals = frame->expanding_macro->formals();
for (int index = 0; index < formals.size(); ++index) {
const QByteArray formal = formals.at(index);
if (formal == __name && index < frame->actuals.size())
return &frame->actuals.at(index);
}
return 0;
}
const char *MacroExpander::operator()(const char *first, const char *last,
QByteArray *result)
{
return expand(first, last, result);
}
const char *MacroExpander::expand(const char *__first, const char *__last,
QByteArray *__result)
{
__first = skip_blanks (__first, __last);
lines = skip_blanks.lines;
while (__first != __last)
{
if (*__first == '\n')
{
__result->append("\n# ");
__result->append(QByteArray::number(env->currentLine));
__result->append(' ');
__result->append('"');
__result->append(env->currentFile.toUtf8());
__result->append('"');
__result->append('\n');
++lines;
__first = skip_blanks (++__first, __last);
lines += skip_blanks.lines;
if (__first != __last && *__first == '#')
break;
}
else if (*__first == '#')
{
__first = skip_blanks (++__first, __last);
lines += skip_blanks.lines;
const char *end_id = skip_identifier (__first, __last);
const QByteArray fast_name(__first, end_id - __first);
__first = end_id;
if (const QByteArray *actual = resolve_formal (fast_name))
{
__result->append('\"');
const char *actual_begin = actual->constData ();
const char *actual_end = actual_begin + actual->size ();
for (const char *it = skip_whitespaces (actual_begin, actual_end);
it != actual_end; ++it)
{
if (*it == '"' || *it == '\\')
{
__result->append('\\');
__result->append(*it);
}
else if (*it == '\n')
{
__result->append('"');
__result->append('\n');
__result->append('"');
}
else
__result->append(*it);
}
__result->append('\"');
}
else {
// ### warning message?
}
}
else if (*__first == '\"')
{
const char *next_pos = skip_string_literal (__first, __last);
lines += skip_string_literal.lines;
__result->append(__first, next_pos - __first);
__first = next_pos;
}
else if (*__first == '\'')
{
const char *next_pos = skip_char_literal (__first, __last);
lines += skip_char_literal.lines;
__result->append(__first, next_pos - __first);
__first = next_pos;
}
else if (*__first == '\\')
{
++__first;
if (__first != __last && *__first == '\n')
{
++lines;
++__first;
} else {
__result->append('\\');
}
}
else if (comment_p (__first, __last))
{
__first = skip_comment_or_divop (__first, __last);
int n = skip_comment_or_divop.lines;
lines += n;
while (n-- > 0)
__result->append('\n');
}
else if (pp_isspace (*__first))
{
for (; __first != __last; ++__first)
{
if (*__first == '\n' || !pp_isspace (*__first))
break;
}
__result->append(' ');
}
else if (pp_isdigit (*__first))
{
const char *next_pos = skip_number (__first, __last);
lines += skip_number.lines;
__result->append(__first, next_pos - __first);
__first = next_pos;
}
else if (pp_isalpha (*__first) || *__first == '_')
{
const char *name_begin = __first;
const char *name_end = skip_identifier (__first, __last);
__first = name_end; // advance
// search for the paste token
const char *next = skip_blanks (__first, __last);
bool paste = false;
bool need_comma = false;
if (next != __last && *next == ',') {
const char *x = skip_blanks(__first + 1, __last);
if (x != __last && *x == '#' && (x + 1) != __last && x[1] == '#') {
need_comma = true;
paste = true;
__first = skip_blanks(x + 2, __last);
}
}
if (next != __last && *next == '#')
{
paste = true;
++next;
if (next != __last && *next == '#')
__first = skip_blanks(++next, __last);
}
const QByteArray fast_name(name_begin, name_end - name_begin);
const QByteArray *actual = resolve_formal (fast_name);
if (actual)
{
const char *begin = actual->constData ();
const char *end = begin + actual->size ();
if (paste) {
for (--end; end != begin - 1; --end) {
if (! pp_isspace(*end))
break;
}
++end;
}
__result->append(begin, end - begin);
if (need_comma)
__result->append(',');
continue;
}
Macro *macro = env->resolve (fast_name);
if (! macro || macro->isHidden() || env->hideNext)
{
if (fast_name.size () == 7 && fast_name [0] == 'd' && fast_name == "defined")
env->hideNext = true;
else
env->hideNext = false;
if (fast_name.size () == 8 && fast_name [0] == '_' && fast_name [1] == '_')
{
if (fast_name == "__LINE__")
{
__result->append(QByteArray::number(env->currentLine + lines));
continue;
}
else if (fast_name == "__FILE__")
{
__result->append('"');
__result->append(env->currentFile.toUtf8());
__result->append('"');
continue;
}
else if (fast_name == "__DATE__")
{
__result->append('"');
__result->append(QDate::currentDate().toString().toUtf8());
__result->append('"');
continue;
}
else if (fast_name == "__TIME__")
{
__result->append('"');
__result->append(QTime::currentTime().toString().toUtf8());
__result->append('"');
continue;
}
}
__result->append(name_begin, name_end - name_begin);
continue;
}
if (! macro->isFunctionLike())
{
Macro *m = 0;
if (! macro->definition().isEmpty())
{
macro->setHidden(true);
QByteArray __tmp;
__tmp.reserve (256);
MacroExpander expand_macro (env);
expand_macro(macro->definition(), &__tmp);
if (! __tmp.isEmpty ())
{
const char *__tmp_begin = __tmp.constBegin();
const char *__tmp_end = __tmp.constEnd();
const char *__begin_id = skip_whitespaces (__tmp_begin, __tmp_end);
const char *__end_id = skip_identifier (__begin_id, __tmp_end);
if (__end_id == __tmp_end)
{
const QByteArray __id (__begin_id, __end_id - __begin_id);
m = env->resolve (__id);
}
if (! m)
*__result += __tmp;
}
macro->setHidden(false);
}
if (! m)
continue;
macro = m;
}
// function like macro
const char *arg_it = skip_whitespaces (__first, __last);
if (arg_it == __last || *arg_it != '(')
{
__result->append(name_begin, name_end - name_begin);
lines += skip_whitespaces.lines;
__first = arg_it;
continue;
}
QVector<QByteArray> actuals;
actuals.reserve(macro->formals().size());
++arg_it; // skip '('
MacroExpander expand_actual (env, frame);
const char *arg_end = skip_argument(arg_it, __last);
if (arg_it != arg_end || (arg_end != __last && *arg_end == ','))
{
const QByteArray actual(arg_it, arg_end - arg_it);
QByteArray expanded;
expand_actual(actual.constBegin (), actual.constEnd (), &expanded);
pushActuals(actuals, macro, expanded);
arg_it = arg_end;
}
while (arg_it != __last && *arg_end == ',')
{
++arg_it; // skip ','
arg_end = skip_argument(arg_it, __last);
const QByteArray actual(arg_it, arg_end - arg_it);
QByteArray expanded;
expand_actual(actual.constBegin (), actual.constEnd (), &expanded);
pushActuals(actuals, macro, expanded);
arg_it = arg_end;
}
if (! (arg_it != __last && *arg_it == ')'))
return __last;
++arg_it; // skip ')'
__first = arg_it;
pp_frame frame (macro, actuals);
MacroExpander expand_macro (env, &frame);
macro->setHidden(true);
expand_macro (macro->definition(), __result);
macro->setHidden(false);
}
else
__result->append(*__first++);
}
return __first;
}
const char *MacroExpander::skip_argument_variadics (QVector<QByteArray> const &__actuals,
Macro *__macro,
const char *__first, const char *__last)
{
const char *arg_end = skip_argument (__first, __last);
while (__macro->isVariadic() && __first != arg_end && arg_end != __last && *arg_end == ','
&& (__actuals.size () + 1) == __macro->formals().size ())
{
arg_end = skip_argument (++arg_end, __last);
}
return arg_end;
}
void MacroExpander::pushActuals(QVector<QByteArray> & actuals, Macro *__macro, const QByteArray& expanded)
{
if (__macro->isVariadic() && actuals.count() == __macro->formals().count()) {
//already enough params --> append to the last one
QByteArray& b = actuals.last();
b.append(",");
b.append(expanded.trimmed());
}
else {
const char * __first = expanded.constData();
const char * __last = __first + expanded.length();
const char * arg_it = __first;
const char *arg_end = skip_argument_variadics(actuals, __macro, arg_it, __last);
actuals.push_back(QByteArray(arg_it, arg_end - arg_it).trimmed());
arg_it = arg_end;
while (arg_it != __last) {
++arg_it; // skip ','
const char *arg_end = skip_argument_variadics(actuals, __macro, arg_it, __last);
actuals.push_back(QByteArray(arg_it, arg_end - arg_it).trimmed());
arg_it = arg_end;
}
}
}

View File

@@ -1,110 +0,0 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
**
** 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.
**
** 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.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**************************************************************************/
/*
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.
*/
#ifndef CPLUSPLUS_PP_MACRO_EXPANDER_H
#define CPLUSPLUS_PP_MACRO_EXPANDER_H
#include "Macro.h"
#include "pp-scanner.h"
#include <QVector>
#include <QByteArray>
namespace CPlusPlus {
class Environment;
class Client;
struct pp_frame;
class MacroExpander
{
Environment *env;
pp_frame *frame;
Client *client;
unsigned start_offset;
pp_skip_number skip_number;
pp_skip_identifier skip_identifier;
pp_skip_string_literal skip_string_literal;
pp_skip_char_literal skip_char_literal;
pp_skip_argument skip_argument;
pp_skip_comment_or_divop skip_comment_or_divop;
pp_skip_blanks skip_blanks;
pp_skip_whitespaces skip_whitespaces;
const QByteArray *resolve_formal(const QByteArray &name);
public:
explicit MacroExpander(Environment *env, pp_frame *frame = 0, Client *client = 0, unsigned start_offset = 0);
const char *operator()(const char *first, const char *last,
QByteArray *result);
const char *operator()(const QByteArray &source,
QByteArray *result)
{ return operator()(source.constBegin(), source.constEnd(), result); }
const char *expand(const char *first, const char *last,
QByteArray *result);
const char *skip_argument_variadics(const QVector<QByteArray> &actuals,
Macro *macro,
const char *first, const char *last);
void pushActuals(QVector<QByteArray> & actuals, Macro *__macro, const QByteArray& expanded);
public: // attributes
int lines;
};
} // namespace CPlusPlus
#endif // CPLUSPLUS_PP_MACRO_EXPANDER_H

View File

@@ -56,7 +56,6 @@
#include "PreprocessorClient.h"
#include "PreprocessorEnvironment.h"
#include "pp-scanner.h"
#include "pp-macro-expander.h"
#include "pp-engine.h"
#endif // CPLUSPLUS_PREPROCESSOR_H