forked from qt-creator/qt-creator
Change-Id: I6d607177b12ef4f55cfbaa2c0e339216627906ec Reviewed-by: Nikolai Kosjar <nikolai.kosjar@theqtcompany.com>
1760 lines
56 KiB
C++
1760 lines
56 KiB
C++
/****************************************************************************
|
|
**
|
|
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
|
|
** Contact: http://www.qt-project.org/legal
|
|
**
|
|
** This file is part of Qt Creator.
|
|
**
|
|
** Commercial License Usage
|
|
** Licensees holding valid commercial Qt licenses may use this file in
|
|
** accordance with the commercial license agreement provided with the
|
|
** Software or, alternatively, in accordance with the terms contained in
|
|
** a written agreement between you and Digia. For licensing terms and
|
|
** conditions see http://www.qt.io/licensing. For further information
|
|
** use the contact form at http://www.qt.io/contact-us.
|
|
**
|
|
** 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 or version 3 as published by the Free
|
|
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
|
|
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
|
|
** following information to ensure the GNU Lesser General Public License
|
|
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
|
|
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
|
**
|
|
** In addition, as a special exception, Digia gives you certain additional
|
|
** rights. These rights are described in the Digia Qt LGPL Exception
|
|
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
|
**
|
|
****************************************************************************/
|
|
|
|
#include <QDebug>
|
|
#include <QDir>
|
|
#include <QGuiApplication>
|
|
#include <QStringList>
|
|
#include <QTextBlock>
|
|
#include <QTextCursor>
|
|
#include <QTextDocument>
|
|
|
|
#include <cplusplus/Control.h>
|
|
#include <cplusplus/Parser.h>
|
|
#include <cplusplus/AST.h>
|
|
#include <cplusplus/ASTVisitor.h>
|
|
#include <cplusplus/Symbols.h>
|
|
#include <cplusplus/CoreTypes.h>
|
|
#include <cplusplus/Literals.h>
|
|
#include <cplusplus/CppDocument.h>
|
|
#include <cplusplus/Overview.h>
|
|
#include <cplusplus/Names.h>
|
|
#include <cplusplus/Scope.h>
|
|
#include <cplusplus/BackwardsScanner.h>
|
|
|
|
#include <utils/changeset.h>
|
|
|
|
#include <iostream>
|
|
#include <cstdlib>
|
|
|
|
using namespace CPlusPlus;
|
|
|
|
static const char copyrightHeader[] =
|
|
"// Copyright (c) 2008 Roberto Raggi <roberto.raggi@gmail.com>\n"
|
|
"//\n"
|
|
"// Permission is hereby granted, free of charge, to any person obtaining a copy\n"
|
|
"// of this software and associated documentation files (the \"Software\"), to deal\n"
|
|
"// in the Software without restriction, including without limitation the rights\n"
|
|
"// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n"
|
|
"// copies of the Software, and to permit persons to whom the Software is\n"
|
|
"// furnished to do so, subject to the following conditions:\n"
|
|
"//\n"
|
|
"// The above copyright notice and this permission notice shall be included in\n"
|
|
"// all copies or substantial portions of the Software.\n"
|
|
"//\n"
|
|
"// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n"
|
|
"// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n"
|
|
"// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n"
|
|
"// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n"
|
|
"// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n"
|
|
"// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n"
|
|
"// THE SOFTWARE.\n"
|
|
;
|
|
|
|
static const char generatedHeader[] =
|
|
"\n"
|
|
"//\n"
|
|
"// W A R N I N G\n"
|
|
"// -------------\n"
|
|
"//\n"
|
|
"// This file is automatically generated by \"cplusplus-update-frontend\".\n"
|
|
"// Changes will be lost.\n"
|
|
"//\n"
|
|
"\n"
|
|
;
|
|
|
|
static QIODevice::OpenMode openFlags = QIODevice::WriteOnly | QIODevice::Text;
|
|
|
|
static void closeAndPrintFilePath(QFile &file)
|
|
{
|
|
if (file.isOpen()) {
|
|
const QString filePath = QFileInfo(file).canonicalFilePath();
|
|
std::cout << QDir::toNativeSeparators(filePath).toLatin1().constData() << std::endl;
|
|
file.close();
|
|
}
|
|
}
|
|
|
|
class ASTNodes
|
|
{
|
|
public:
|
|
ASTNodes(): base(0) {}
|
|
|
|
ClassSpecifierAST *base; // points to "class AST"
|
|
QList<ClassSpecifierAST *> deriveds; // n where n extends AST
|
|
QList<QTextCursor> endOfPublicClassSpecifiers;
|
|
};
|
|
|
|
static Document::Ptr AST_h_document;
|
|
static ASTNodes astNodes;
|
|
|
|
static QTextCursor createCursor(TranslationUnit *unit, AST *ast, QTextDocument *document)
|
|
{
|
|
unsigned startLine, startColumn, endLine, endColumn;
|
|
unit->getTokenStartPosition(ast->firstToken(), &startLine, &startColumn);
|
|
unit->getTokenEndPosition(ast->lastToken() - 1, &endLine, &endColumn);
|
|
|
|
QTextCursor tc(document);
|
|
tc.setPosition(document->findBlockByNumber(startLine - 1).position());
|
|
tc.setPosition(document->findBlockByNumber(endLine - 1).position() + endColumn - 1,
|
|
QTextCursor::KeepAnchor);
|
|
|
|
int charsToSkip = 0;
|
|
forever {
|
|
QChar ch = document->characterAt(tc.position() + charsToSkip);
|
|
|
|
if (! ch.isSpace())
|
|
break;
|
|
|
|
++charsToSkip;
|
|
|
|
if (ch == QChar::ParagraphSeparator)
|
|
break;
|
|
}
|
|
|
|
tc.setPosition(tc.position() + charsToSkip, QTextCursor::KeepAnchor);
|
|
return tc;
|
|
}
|
|
|
|
class FindASTNodes: protected ASTVisitor
|
|
{
|
|
public:
|
|
FindASTNodes(Document::Ptr doc, QTextDocument *document)
|
|
: ASTVisitor(doc->translationUnit()), document(document)
|
|
{
|
|
}
|
|
|
|
ASTNodes operator()(AST *ast)
|
|
{
|
|
accept(ast);
|
|
return _nodes;
|
|
}
|
|
|
|
protected:
|
|
virtual bool visit(ClassSpecifierAST *ast)
|
|
{
|
|
Class *klass = ast->symbol;
|
|
Q_ASSERT(klass != 0);
|
|
|
|
const QString className = oo(klass->name());
|
|
|
|
if (className.endsWith(QLatin1String("AST"))) {
|
|
if (className == QLatin1String("AST"))
|
|
_nodes.base = ast;
|
|
else {
|
|
_nodes.deriveds.append(ast);
|
|
|
|
AccessDeclarationAST *accessDeclaration = 0;
|
|
for (DeclarationListAST *it = ast->member_specifier_list; it; it = it->next) {
|
|
if (AccessDeclarationAST *decl = it->value->asAccessDeclaration()) {
|
|
if (tokenKind(decl->access_specifier_token) == T_PUBLIC)
|
|
accessDeclaration = decl;
|
|
}
|
|
}
|
|
|
|
if (! accessDeclaration)
|
|
qDebug() << "no access declaration for class:" << className;
|
|
|
|
Q_ASSERT(accessDeclaration != 0);
|
|
|
|
QTextCursor tc = createCursor(translationUnit(), accessDeclaration, document);
|
|
tc.setPosition(tc.position());
|
|
|
|
_nodes.endOfPublicClassSpecifiers.append(tc);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private:
|
|
QTextDocument *document;
|
|
ASTNodes _nodes;
|
|
Overview oo;
|
|
};
|
|
|
|
class Accept0CG: protected ASTVisitor
|
|
{
|
|
QDir _cplusplusDir;
|
|
QTextStream *out;
|
|
|
|
public:
|
|
Accept0CG(const QDir &cplusplusDir, TranslationUnit *unit)
|
|
: ASTVisitor(unit), _cplusplusDir(cplusplusDir), out(0)
|
|
{ }
|
|
|
|
QList<QByteArray> classes() const { return classMap.keys(); }
|
|
|
|
void operator()(AST *ast)
|
|
{
|
|
QFileInfo fileInfo(_cplusplusDir, QLatin1String("ASTVisit.cpp"));
|
|
|
|
QFile file(fileInfo.absoluteFilePath());
|
|
if (! file.open(openFlags))
|
|
return;
|
|
|
|
QTextStream output(&file);
|
|
out = &output;
|
|
|
|
*out << copyrightHeader << generatedHeader <<
|
|
"\n"
|
|
"#include \"AST.h\"\n"
|
|
"#include \"ASTVisitor.h\"\n"
|
|
"\n"
|
|
"using namespace CPlusPlus;\n" << endl;
|
|
|
|
accept(ast);
|
|
|
|
closeAndPrintFilePath(file);
|
|
}
|
|
|
|
protected:
|
|
using ASTVisitor::visit;
|
|
|
|
QMap<QByteArray, ClassSpecifierAST *> classMap;
|
|
|
|
QByteArray id_cast(NameAST *name)
|
|
{
|
|
if (! name)
|
|
return QByteArray();
|
|
|
|
const Identifier *id = identifier(name->asSimpleName()->identifier_token);
|
|
|
|
return QByteArray::fromRawData(id->chars(), id->size());
|
|
}
|
|
|
|
void visitMembers(Class *klass)
|
|
{
|
|
// *out << " // visit " << className.constData() << endl;
|
|
for (unsigned i = 0; i < klass->memberCount(); ++i) {
|
|
Symbol *member = klass->memberAt(i);
|
|
if (! member->name())
|
|
continue;
|
|
|
|
const Identifier *id = member->name()->identifier();
|
|
|
|
if (! id)
|
|
continue;
|
|
|
|
const QByteArray memberName = QByteArray::fromRawData(id->chars(), id->size());
|
|
if (member->type().isUnsigned() && memberName.endsWith("_token")) {
|
|
// nothing to do. The member is a token.
|
|
|
|
} else if (PointerType *ptrTy = member->type()->asPointerType()) {
|
|
|
|
if (NamedType *namedTy = ptrTy->elementType()->asNamedType()) {
|
|
QByteArray typeName = namedTy->name()->identifier()->chars();
|
|
|
|
if (typeName.endsWith("AST") && memberName != "next")
|
|
*out << " accept(" << memberName.constData() << ", visitor);" << endl;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (unsigned i = 0; i < klass->baseClassCount(); ++i) {
|
|
const QByteArray baseClassName = klass->baseClassAt(i)->identifier()->chars();
|
|
|
|
if (ClassSpecifierAST *baseClassSpec = classMap.value(baseClassName, 0))
|
|
visitMembers(baseClassSpec->symbol);
|
|
}
|
|
}
|
|
|
|
bool checkMethod(Symbol *accept0Method) const
|
|
{
|
|
Declaration *decl = accept0Method->asDeclaration();
|
|
if (! decl)
|
|
return false;
|
|
|
|
Function *funTy = decl->type()->asFunctionType();
|
|
if (! funTy)
|
|
return false;
|
|
|
|
else if (funTy->isPureVirtual())
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
virtual bool visit(ClassSpecifierAST *ast)
|
|
{
|
|
Class *klass = ast->symbol;
|
|
const QByteArray className = id_cast(ast->name);
|
|
|
|
const Identifier *visit_id = control()->identifier("accept0");
|
|
Symbol *accept0Method = klass->find(visit_id);
|
|
for (; accept0Method; accept0Method = accept0Method->next()) {
|
|
if (accept0Method->identifier() != visit_id)
|
|
continue;
|
|
|
|
if (checkMethod(accept0Method))
|
|
break;
|
|
}
|
|
|
|
if (! accept0Method)
|
|
return true;
|
|
|
|
classMap.insert(className, ast);
|
|
|
|
*out
|
|
<< "void " << className.constData() << "::accept0(ASTVisitor *visitor)" << endl
|
|
<< "{" << endl
|
|
<< " if (visitor->visit(this)) {" << endl;
|
|
|
|
visitMembers(klass);
|
|
|
|
*out
|
|
<< " }" << endl
|
|
<< " visitor->endVisit(this);" << endl
|
|
<< "}" << endl
|
|
<< endl;
|
|
|
|
return true;
|
|
}
|
|
};
|
|
|
|
class Match0CG: protected ASTVisitor
|
|
{
|
|
QDir _cplusplusDir;
|
|
QTextStream *out;
|
|
|
|
public:
|
|
Match0CG(const QDir &cplusplusDir, TranslationUnit *unit)
|
|
: ASTVisitor(unit), _cplusplusDir(cplusplusDir), out(0)
|
|
{ }
|
|
|
|
void operator()(AST *ast)
|
|
{
|
|
QFileInfo fileInfo(_cplusplusDir, QLatin1String("ASTMatch0.cpp"));
|
|
|
|
QFile file(fileInfo.absoluteFilePath());
|
|
if (! file.open(openFlags))
|
|
return;
|
|
|
|
QTextStream output(&file);
|
|
out = &output;
|
|
*out << copyrightHeader << generatedHeader <<
|
|
"\n"
|
|
"#include \"AST.h\"\n"
|
|
"#include \"ASTMatcher.h\"\n"
|
|
"\n"
|
|
"using namespace CPlusPlus;\n" << endl;
|
|
|
|
accept(ast);
|
|
|
|
closeAndPrintFilePath(file);
|
|
}
|
|
|
|
protected:
|
|
using ASTVisitor::visit;
|
|
|
|
QMap<QByteArray, ClassSpecifierAST *> classMap;
|
|
|
|
QByteArray id_cast(NameAST *name)
|
|
{
|
|
if (! name)
|
|
return QByteArray();
|
|
|
|
const Identifier *id = identifier(name->asSimpleName()->identifier_token);
|
|
|
|
return QByteArray::fromRawData(id->chars(), id->size());
|
|
}
|
|
|
|
void visitMembers(Class *klass)
|
|
{
|
|
Overview oo;
|
|
const QString className = oo(klass->name());
|
|
|
|
*out << " if (" << className << " *_other = pattern->as" << className.left(className.length() - 3) << "())" << endl;
|
|
|
|
*out << " return matcher->match(this, _other);" << endl;
|
|
|
|
*out << endl;
|
|
}
|
|
|
|
bool checkMethod(Symbol *accept0Method) const
|
|
{
|
|
Declaration *decl = accept0Method->asDeclaration();
|
|
if (! decl)
|
|
return false;
|
|
|
|
Function *funTy = decl->type()->asFunctionType();
|
|
if (! funTy)
|
|
return false;
|
|
|
|
else if (funTy->isPureVirtual())
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
virtual bool visit(ClassSpecifierAST *ast)
|
|
{
|
|
Class *klass = ast->symbol;
|
|
const QByteArray className = id_cast(ast->name);
|
|
|
|
const Identifier *match0_id = control()->identifier("match0");
|
|
Symbol *accept0Method = klass->find(match0_id);
|
|
for (; accept0Method; accept0Method = accept0Method->next()) {
|
|
if (accept0Method->identifier() != match0_id)
|
|
continue;
|
|
|
|
if (checkMethod(accept0Method))
|
|
break;
|
|
}
|
|
|
|
if (! accept0Method)
|
|
return true;
|
|
|
|
classMap.insert(className, ast);
|
|
|
|
*out
|
|
<< "bool " << className.constData() << "::match0(AST *pattern, ASTMatcher *matcher)" << endl
|
|
<< "{" << endl;
|
|
|
|
visitMembers(klass);
|
|
|
|
*out
|
|
<< " return false;" << endl
|
|
<< "}" << endl
|
|
<< endl;
|
|
|
|
return true;
|
|
}
|
|
};
|
|
|
|
|
|
class MatcherCPPCG: protected ASTVisitor
|
|
{
|
|
QDir _cplusplusDir;
|
|
QTextStream *out;
|
|
|
|
public:
|
|
MatcherCPPCG(const QDir &cplusplusDir, TranslationUnit *unit)
|
|
: ASTVisitor(unit), _cplusplusDir(cplusplusDir), out(0)
|
|
{ }
|
|
|
|
void operator()(AST *ast)
|
|
{
|
|
QFileInfo fileInfo(_cplusplusDir, QLatin1String("ASTMatcher.cpp"));
|
|
|
|
QFile file(fileInfo.absoluteFilePath());
|
|
if (! file.open(openFlags))
|
|
return;
|
|
|
|
QTextStream output(&file);
|
|
out = &output;
|
|
|
|
*out << copyrightHeader << endl
|
|
<< generatedHeader
|
|
<< "#include \"AST.h\"" << endl
|
|
<< "#include \"ASTMatcher.h\"" << endl
|
|
<< endl
|
|
<< "using namespace CPlusPlus;" << endl
|
|
<< endl
|
|
<< "ASTMatcher::ASTMatcher()" << endl
|
|
<< "{ }" << endl
|
|
<< endl
|
|
<< "ASTMatcher::~ASTMatcher()" << endl
|
|
<< "{ }" << endl
|
|
<< endl;
|
|
|
|
accept(ast);
|
|
|
|
closeAndPrintFilePath(file);
|
|
}
|
|
|
|
protected:
|
|
using ASTVisitor::visit;
|
|
|
|
QMap<QByteArray, ClassSpecifierAST *> classMap;
|
|
|
|
QByteArray id_cast(NameAST *name)
|
|
{
|
|
if (! name)
|
|
return QByteArray();
|
|
|
|
const Identifier *id = identifier(name->asSimpleName()->identifier_token);
|
|
|
|
return QByteArray::fromRawData(id->chars(), id->size());
|
|
}
|
|
|
|
void visitMembers(Class *klass)
|
|
{
|
|
for (unsigned i = 0; i < klass->memberCount(); ++i) {
|
|
Symbol *member = klass->memberAt(i);
|
|
if (! member->name())
|
|
continue;
|
|
|
|
const Identifier *id = member->name()->identifier();
|
|
|
|
if (! id)
|
|
continue;
|
|
|
|
const QByteArray memberName = QByteArray::fromRawData(id->chars(), id->size());
|
|
if (member->type().isUnsigned() && memberName.endsWith("_token")) {
|
|
|
|
*out
|
|
<< " pattern->" << memberName << " = node->" << memberName << ";" << endl
|
|
<< endl;
|
|
|
|
} else if (PointerType *ptrTy = member->type()->asPointerType()) {
|
|
|
|
if (NamedType *namedTy = ptrTy->elementType()->asNamedType()) {
|
|
QByteArray typeName = namedTy->name()->identifier()->chars();
|
|
|
|
if (typeName.endsWith("AST")) {
|
|
*out
|
|
<< " if (! pattern->" << memberName << ")" << endl
|
|
<< " pattern->" << memberName << " = node->" << memberName << ";" << endl
|
|
<< " else if (! AST::match(node->" << memberName << ", pattern->" << memberName << ", this))" << endl
|
|
<< " return false;" << endl
|
|
<< endl;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for (unsigned i = 0; i < klass->baseClassCount(); ++i) {
|
|
const QByteArray baseClassName = klass->baseClassAt(i)->identifier()->chars();
|
|
|
|
if (ClassSpecifierAST *baseClassSpec = classMap.value(baseClassName, 0))
|
|
visitMembers(baseClassSpec->symbol);
|
|
}
|
|
}
|
|
|
|
bool checkMethod(Symbol *accept0Method) const
|
|
{
|
|
Declaration *decl = accept0Method->asDeclaration();
|
|
if (! decl)
|
|
return false;
|
|
|
|
Function *funTy = decl->type()->asFunctionType();
|
|
if (! funTy)
|
|
return false;
|
|
|
|
else if (funTy->isPureVirtual())
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
virtual bool visit(ClassSpecifierAST *ast)
|
|
{
|
|
Class *klass = ast->symbol;
|
|
const QByteArray className = id_cast(ast->name);
|
|
|
|
const Identifier *match0_id = control()->identifier("match0");
|
|
Symbol *match0Method = klass->find(match0_id);
|
|
for (; match0Method; match0Method = match0Method->next()) {
|
|
if (match0Method->identifier() != match0_id)
|
|
continue;
|
|
|
|
if (checkMethod(match0Method))
|
|
break;
|
|
}
|
|
|
|
if (! match0Method)
|
|
return true;
|
|
|
|
classMap.insert(className, ast);
|
|
|
|
*out
|
|
<< "bool ASTMatcher::match(" << className.constData() << " *node, " << className.constData() << " *pattern)" << endl
|
|
<< "{" << endl
|
|
<< " (void) node;" << endl
|
|
<< " (void) pattern;" << endl
|
|
<< endl;
|
|
|
|
visitMembers(klass);
|
|
|
|
*out
|
|
<< " return true;" << endl
|
|
<< "}" << endl
|
|
<< endl;
|
|
|
|
return true;
|
|
}
|
|
};
|
|
|
|
class CloneCPPCG: protected ASTVisitor
|
|
{
|
|
QDir _cplusplusDir;
|
|
QTextStream *out;
|
|
|
|
public:
|
|
CloneCPPCG(const QDir &cplusplusDir, TranslationUnit *unit)
|
|
: ASTVisitor(unit), _cplusplusDir(cplusplusDir), out(0)
|
|
{ }
|
|
|
|
void operator()(AST *ast)
|
|
{
|
|
QFileInfo fileInfo(_cplusplusDir, QLatin1String("ASTClone.cpp"));
|
|
|
|
QFile file(fileInfo.absoluteFilePath());
|
|
if (! file.open(openFlags))
|
|
return;
|
|
|
|
QTextStream output(&file);
|
|
out = &output;
|
|
|
|
*out << copyrightHeader
|
|
<< generatedHeader
|
|
<< "#include \"AST.h\"" << endl
|
|
<< "#include \"MemoryPool.h\"" << endl
|
|
<< endl
|
|
<< "using namespace CPlusPlus;" << endl
|
|
<< endl;
|
|
|
|
accept(ast);
|
|
|
|
closeAndPrintFilePath(file);
|
|
}
|
|
|
|
protected:
|
|
using ASTVisitor::visit;
|
|
|
|
QMap<QByteArray, ClassSpecifierAST *> classMap;
|
|
|
|
QByteArray id_cast(NameAST *name)
|
|
{
|
|
if (! name)
|
|
return QByteArray();
|
|
|
|
const Identifier *id = identifier(name->asSimpleName()->identifier_token);
|
|
|
|
return QByteArray::fromRawData(id->chars(), id->size());
|
|
}
|
|
|
|
void visitMembers(Class *klass)
|
|
{
|
|
for (unsigned i = 0; i < klass->memberCount(); ++i) {
|
|
Symbol *member = klass->memberAt(i);
|
|
if (! member->name())
|
|
continue;
|
|
|
|
const Identifier *id = member->name()->identifier();
|
|
|
|
if (! id)
|
|
continue;
|
|
|
|
const QByteArray memberName = QByteArray::fromRawData(id->chars(), id->size());
|
|
if (member->type().isUnsigned() && memberName.endsWith("_token")) {
|
|
*out << " ast->" << memberName << " = " << memberName << ";" << endl;
|
|
} else if (PointerType *ptrTy = member->type()->asPointerType()) {
|
|
if (NamedType *namedTy = ptrTy->elementType()->asNamedType()) {
|
|
QByteArray typeName = namedTy->name()->identifier()->chars();
|
|
|
|
if (typeName.endsWith("ListAST")) {
|
|
*out << " for (" << typeName << " *iter = " << memberName << ", **ast_iter = &ast->" << memberName << ";" << endl
|
|
<< " iter; iter = iter->next, ast_iter = &(*ast_iter)->next)" << endl
|
|
<< " *ast_iter = new (pool) " << typeName << "((iter->value) ? iter->value->clone(pool) : 0);" << endl;
|
|
} else if (typeName.endsWith("AST")) {
|
|
*out << " if (" << memberName << ")" << endl
|
|
<< " ast->" << memberName << " = " << memberName << "->clone(pool);" << endl;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for (unsigned i = 0; i < klass->baseClassCount(); ++i) {
|
|
const QByteArray baseClassName = klass->baseClassAt(i)->identifier()->chars();
|
|
|
|
if (ClassSpecifierAST *baseClassSpec = classMap.value(baseClassName, 0))
|
|
visitMembers(baseClassSpec->symbol);
|
|
}
|
|
}
|
|
|
|
bool checkMethod(Symbol *cloneMethod) const
|
|
{
|
|
Declaration *decl = cloneMethod->asDeclaration();
|
|
if (! decl)
|
|
return false;
|
|
|
|
Function *funTy = decl->type()->asFunctionType();
|
|
if (! funTy)
|
|
return false;
|
|
|
|
else if (funTy->isPureVirtual())
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
virtual bool visit(ClassSpecifierAST *ast)
|
|
{
|
|
Class *klass = ast->symbol;
|
|
const QByteArray className = id_cast(ast->name);
|
|
if (! className.endsWith("AST"))
|
|
return false;
|
|
|
|
const Identifier *clone_id = control()->identifier("clone");
|
|
Symbol *cloneMethod = klass->find(clone_id);
|
|
for (; cloneMethod; cloneMethod = cloneMethod->next()) {
|
|
if (cloneMethod->identifier() != clone_id)
|
|
continue;
|
|
|
|
if (checkMethod(cloneMethod))
|
|
break;
|
|
}
|
|
|
|
if (! cloneMethod)
|
|
return true;
|
|
|
|
classMap.insert(className, ast);
|
|
|
|
*out << className.constData() << " *" << className.constData() << "::" << "clone(MemoryPool *pool) const" << endl
|
|
<< "{" << endl
|
|
<< " " << className.constData() << " *ast = new (pool) " << className.constData() << ";" << endl;
|
|
|
|
visitMembers(klass);
|
|
|
|
*out << " return ast;" << endl
|
|
<< "}" << endl << endl;
|
|
|
|
return false;
|
|
}
|
|
};
|
|
|
|
class GenerateDumpers: protected ASTVisitor
|
|
{
|
|
QTextStream out;
|
|
|
|
public:
|
|
GenerateDumpers(QFile *file, TranslationUnit *unit)
|
|
: ASTVisitor(unit), out(file)
|
|
{ }
|
|
|
|
static void go(const QString &fileName, TranslationUnit *unit)
|
|
{
|
|
QFile file(fileName);
|
|
if (! file.open(openFlags)) {
|
|
std::cerr << "Cannot open dumpers file." << std::endl;
|
|
return;
|
|
}
|
|
|
|
GenerateDumpers d(&file, unit);
|
|
d.out << copyrightHeader
|
|
<< generatedHeader
|
|
<< endl;
|
|
|
|
|
|
d.accept(unit->ast());
|
|
|
|
closeAndPrintFilePath(file);
|
|
}
|
|
|
|
protected:
|
|
using ASTVisitor::visit;
|
|
|
|
QMap<QByteArray, ClassSpecifierAST *> classMap;
|
|
|
|
QByteArray id_cast(NameAST *name)
|
|
{
|
|
if (! name)
|
|
return QByteArray();
|
|
|
|
const Identifier *id = identifier(name->asSimpleName()->identifier_token);
|
|
|
|
return QByteArray::fromRawData(id->chars(), id->size());
|
|
}
|
|
|
|
void visitMembers(Class *klass)
|
|
{
|
|
for (unsigned i = 0; i < klass->memberCount(); ++i) {
|
|
Symbol *member = klass->memberAt(i);
|
|
if (! member->name())
|
|
continue;
|
|
|
|
const Identifier *id = member->name()->identifier();
|
|
|
|
if (! id)
|
|
continue;
|
|
|
|
const QByteArray memberName = QByteArray::fromRawData(id->chars(), id->size());
|
|
if (member->type().isUnsigned() && memberName.endsWith("_token")) {
|
|
out << " if (ast->" << memberName << ")" << endl;
|
|
out << " terminal(ast->" << memberName << ", ast);" << endl;
|
|
} else if (PointerType *ptrTy = member->type()->asPointerType()) {
|
|
if (NamedType *namedTy = ptrTy->elementType()->asNamedType()) {
|
|
QByteArray typeName = namedTy->name()->identifier()->chars();
|
|
|
|
if (typeName.endsWith("ListAST")) {
|
|
out << " for (" << typeName << " *iter = ast->" << memberName << "; iter; iter = iter->next)" << endl
|
|
<< " nonterminal(iter->value);" << endl;
|
|
} else if (typeName.endsWith("AST")) {
|
|
out << " nonterminal(ast->" << memberName << ");" << endl;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for (unsigned i = 0; i < klass->baseClassCount(); ++i) {
|
|
const QByteArray baseClassName = klass->baseClassAt(i)->identifier()->chars();
|
|
|
|
if (ClassSpecifierAST *baseClassSpec = classMap.value(baseClassName, 0))
|
|
visitMembers(baseClassSpec->symbol);
|
|
}
|
|
}
|
|
|
|
bool checkMethod(Symbol *cloneMethod) const
|
|
{
|
|
Declaration *decl = cloneMethod->asDeclaration();
|
|
if (! decl)
|
|
return false;
|
|
|
|
Function *funTy = decl->type()->asFunctionType();
|
|
if (! funTy)
|
|
return false;
|
|
|
|
else if (funTy->isPureVirtual())
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
virtual bool visit(ClassSpecifierAST *ast)
|
|
{
|
|
Class *klass = ast->symbol;
|
|
const QByteArray className = id_cast(ast->name);
|
|
if (! className.endsWith("AST"))
|
|
return false;
|
|
|
|
const Identifier *clone_id = control()->identifier("clone");
|
|
Symbol *cloneMethod = klass->find(clone_id);
|
|
for (; cloneMethod; cloneMethod = cloneMethod->next()) {
|
|
if (cloneMethod->identifier() != clone_id)
|
|
continue;
|
|
|
|
if (checkMethod(cloneMethod))
|
|
break;
|
|
}
|
|
|
|
if (! cloneMethod)
|
|
return true;
|
|
|
|
classMap.insert(className, ast);
|
|
|
|
out << "virtual bool visit(" << className.constData() << " *ast)" << endl
|
|
<< "{" << endl;
|
|
|
|
visitMembers(klass);
|
|
|
|
out << " return false;" << endl
|
|
<< "}" << endl << endl;
|
|
|
|
return false;
|
|
}
|
|
};
|
|
|
|
class RemoveCastMethods: protected ASTVisitor
|
|
{
|
|
public:
|
|
RemoveCastMethods(Document::Ptr doc, QTextDocument *document)
|
|
: ASTVisitor(doc->translationUnit()), document(document) {}
|
|
|
|
QList<QTextCursor> operator()(AST *ast)
|
|
{
|
|
_cursors.clear();
|
|
accept(ast);
|
|
return _cursors;
|
|
}
|
|
|
|
protected:
|
|
virtual bool visit(FunctionDefinitionAST *ast)
|
|
{
|
|
Function *fun = ast->symbol;
|
|
const QString functionName = oo(fun->name());
|
|
|
|
if (functionName.length() > 3 && functionName.startsWith(QLatin1String("as"))
|
|
&& functionName.at(2).isUpper()) {
|
|
|
|
QTextCursor tc = createCursor(translationUnit(), ast, document);
|
|
|
|
//qDebug() << qPrintable(tc.selectedText());
|
|
_cursors.append(tc);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private:
|
|
QTextDocument *document;
|
|
QList<QTextCursor> _cursors;
|
|
Overview oo;
|
|
};
|
|
|
|
static QList<QTextCursor> removeConstructors(ClassSpecifierAST *classAST,
|
|
TranslationUnit *translationUnit,
|
|
QTextDocument *document)
|
|
{
|
|
Overview oo;
|
|
QList<QTextCursor> cursors;
|
|
const QString className = oo(classAST->symbol->name());
|
|
|
|
for (DeclarationListAST *iter = classAST->member_specifier_list; iter; iter = iter->next) {
|
|
if (FunctionDefinitionAST *funDef = iter->value->asFunctionDefinition()) {
|
|
if (oo(funDef->symbol->name()) == className) {
|
|
// found it:
|
|
QTextCursor tc = createCursor(translationUnit, funDef, document);
|
|
tc.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
|
|
tc.setPosition(tc.position() + 1, QTextCursor::KeepAnchor);
|
|
cursors.append(tc);
|
|
}
|
|
}
|
|
}
|
|
|
|
return cursors;
|
|
}
|
|
|
|
static QStringList collectFieldNames(ClassSpecifierAST *classAST, bool onlyTokensAndASTNodes)
|
|
{
|
|
QStringList fields;
|
|
Overview oo;
|
|
Class *clazz = classAST->symbol;
|
|
for (unsigned i = 0; i < clazz->memberCount(); ++i) {
|
|
Symbol *s = clazz->memberAt(i);
|
|
if (Declaration *decl = s->asDeclaration()) {
|
|
const QString declName = oo(decl->name());
|
|
const FullySpecifiedType ty = decl->type();
|
|
if (const PointerType *ptrTy = ty->asPointerType()) {
|
|
if (onlyTokensAndASTNodes) {
|
|
if (const NamedType *namedTy = ptrTy->elementType()->asNamedType()) {
|
|
if (oo(namedTy->name()).endsWith(QLatin1String("AST")))
|
|
fields.append(declName);
|
|
}
|
|
} else {
|
|
fields.append(declName);
|
|
}
|
|
} else if (ty.isUnsigned()) {
|
|
fields.append(declName);
|
|
}
|
|
}
|
|
}
|
|
return fields;
|
|
}
|
|
|
|
static QString createConstructor(ClassSpecifierAST *classAST)
|
|
{
|
|
Overview oo;
|
|
Class *clazz = classAST->symbol;
|
|
|
|
QString result(QLatin1String(" "));
|
|
result.append(oo(clazz->name()));
|
|
result.append(QLatin1String("()\n"));
|
|
|
|
QStringList classFields = collectFieldNames(classAST, false);
|
|
for (int i = 0; i < classFields.size(); ++i) {
|
|
if (i == 0) {
|
|
result.append(QLatin1String(" : "));
|
|
result.append(classFields.at(i));
|
|
result.append(QLatin1String("(0)\n"));
|
|
} else {
|
|
result.append(QLatin1String(" , "));
|
|
result.append(classFields.at(i));
|
|
result.append(QLatin1String("(0)\n"));
|
|
}
|
|
}
|
|
|
|
result.append(QLatin1String(" {}\n"));
|
|
return result;
|
|
}
|
|
|
|
bool checkGenerated(const QTextCursor &cursor, int *doxyStart)
|
|
{
|
|
BackwardsScanner tokens(cursor, 10, QString(), false);
|
|
Token prevToken = tokens.LA(1);
|
|
if (prevToken.kind() != T_DOXY_COMMENT && prevToken.kind() != T_CPP_DOXY_COMMENT)
|
|
return false;
|
|
|
|
*doxyStart = tokens.startPosition() + prevToken.utf16charsBegin();
|
|
|
|
return tokens.text(tokens.startToken() - 1).contains(QLatin1String("\\generated"));
|
|
}
|
|
|
|
struct GenInfo {
|
|
GenInfo()
|
|
: classAST(0)
|
|
, start(0)
|
|
, end(0)
|
|
, firstToken(false)
|
|
, lastToken(false)
|
|
, remove(false)
|
|
{}
|
|
|
|
ClassSpecifierAST *classAST;
|
|
int start;
|
|
int end;
|
|
bool firstToken;
|
|
bool lastToken;
|
|
bool remove;
|
|
};
|
|
|
|
void generateFirstToken(QTextStream &os, const QString &className, const QStringList &fields)
|
|
{
|
|
os << "unsigned "<< className << "::firstToken() const" << endl
|
|
<< "{" << endl;
|
|
|
|
foreach (const QString &field, fields) {
|
|
os << " if (" << field << ")" << endl;
|
|
|
|
if (field.endsWith(QLatin1String("_token"))) {
|
|
os << " return " << field << ";" << endl;
|
|
} else {
|
|
os << " if (unsigned candidate = " << field << "->firstToken())" << endl;
|
|
os << " return candidate;" << endl;
|
|
}
|
|
}
|
|
|
|
os << " return 0;" << endl;
|
|
os << "}" << endl << endl;
|
|
}
|
|
|
|
void generateLastToken(QTextStream &os, const QString &className, const QStringList &fields)
|
|
{
|
|
os << "unsigned "<< className << "::lastToken() const" << endl
|
|
<< "{" << endl;
|
|
|
|
for (int i = fields.size() - 1; i >= 0; --i) {
|
|
const QString field = fields.at(i);
|
|
|
|
os << " if (" << field << ")" << endl;
|
|
|
|
if (field.endsWith(QLatin1String("_token"))) {
|
|
os << " return " << field << " + 1;" << endl;
|
|
} else {
|
|
os << " if (unsigned candidate = " << field << "->lastToken())" << endl;
|
|
os << " return candidate;" << endl;
|
|
}
|
|
}
|
|
|
|
os << " return 1;" << endl;
|
|
os << "}" << endl << endl;
|
|
}
|
|
|
|
void generateAST_cpp(const Snapshot &snapshot, const QDir &cplusplusDir)
|
|
{
|
|
typedef QMap<QString, ClassSpecifierAST *> StringClassSpecifierASTMap;
|
|
typedef StringClassSpecifierASTMap::ConstIterator StringClassSpecifierASTMapConstIt;
|
|
|
|
QFileInfo fileAST_cpp(cplusplusDir, QLatin1String("AST.cpp"));
|
|
Q_ASSERT(fileAST_cpp.exists());
|
|
|
|
const QString fileName = fileAST_cpp.absoluteFilePath();
|
|
|
|
QFile file(fileName);
|
|
if (! file.open(QFile::ReadOnly)) {
|
|
std::cerr << "Cannot open " << fileName.toLatin1().data() << std::endl;
|
|
return;
|
|
}
|
|
|
|
const QByteArray source = file.readAll();
|
|
file.close();
|
|
|
|
QTextDocument cpp_document;
|
|
cpp_document.setPlainText(QString::fromUtf8(source));
|
|
|
|
Document::Ptr AST_cpp_document = snapshot.preprocessedDocument(source, fileName);
|
|
AST_cpp_document->check();
|
|
|
|
Overview oo;
|
|
StringClassSpecifierASTMap classesNeedingFirstToken;
|
|
StringClassSpecifierASTMap classesNeedingLastToken;
|
|
|
|
// find all classes with method declarations for firstToken/lastToken
|
|
foreach (ClassSpecifierAST *classAST, astNodes.deriveds) {
|
|
const QString className = oo(classAST->symbol->name());
|
|
if (className.isEmpty())
|
|
continue;
|
|
|
|
for (DeclarationListAST *declIter = classAST->member_specifier_list; declIter; declIter = declIter->next) {
|
|
if (SimpleDeclarationAST *decl = declIter->value->asSimpleDeclaration()) {
|
|
if (decl->symbols && decl->symbols->value) {
|
|
if (decl->symbols->next)
|
|
std::cerr << "Found simple declaration with multiple symbols in " << className.toLatin1().data() << std::endl;
|
|
|
|
Symbol *s = decl->symbols->value;
|
|
const QString funName = oo(s->name());
|
|
if (funName == QLatin1String("firstToken")) {
|
|
// found it:
|
|
classesNeedingFirstToken.insert(className, classAST);
|
|
} else if (funName == QLatin1String("lastToken")) {
|
|
// found it:
|
|
classesNeedingLastToken.insert(className, classAST);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
QList<GenInfo> todo;
|
|
|
|
TranslationUnitAST *xUnit = AST_cpp_document->translationUnit()->ast()->asTranslationUnit();
|
|
for (DeclarationListAST *iter = xUnit->declaration_list; iter; iter = iter->next) {
|
|
if (FunctionDefinitionAST *funDef = iter->value->asFunctionDefinition()) {
|
|
if (const Name *name = funDef->symbol->name()) {
|
|
if (const QualifiedNameId *qName = name->asQualifiedNameId()) {
|
|
const QString className = oo(qName->base());
|
|
const QString methodName = oo(qName->name());
|
|
|
|
QTextCursor cursor(&cpp_document);
|
|
|
|
unsigned line = 0, column = 0;
|
|
AST_cpp_document->translationUnit()->getTokenStartPosition(funDef->firstToken(), &line, &column);
|
|
const int start = cpp_document.findBlockByNumber(line - 1).position() + column - 1;
|
|
cursor.setPosition(start);
|
|
int doxyStart = start;
|
|
|
|
const bool isGenerated = checkGenerated(cursor, &doxyStart);
|
|
|
|
AST_cpp_document->translationUnit()->getTokenEndPosition(funDef->lastToken() - 1, &line, &column);
|
|
int end = cpp_document.findBlockByNumber(line - 1).position() + column - 1;
|
|
while (cpp_document.characterAt(end).isSpace())
|
|
++end;
|
|
|
|
if (methodName == QLatin1String("firstToken")) {
|
|
ClassSpecifierAST *classAST = classesNeedingFirstToken.value(className, 0);
|
|
GenInfo info;
|
|
info.end = end;
|
|
if (classAST) {
|
|
info.classAST = classAST;
|
|
info.firstToken = true;
|
|
info.start = start;
|
|
classesNeedingFirstToken.remove(className);
|
|
} else {
|
|
info.start = doxyStart;
|
|
info.remove = true;
|
|
}
|
|
if (isGenerated)
|
|
todo.append(info);
|
|
} else if (methodName == QLatin1String("lastToken")) {
|
|
ClassSpecifierAST *classAST = classesNeedingLastToken.value(className, 0);
|
|
GenInfo info;
|
|
info.end = end;
|
|
if (classAST) {
|
|
info.classAST = classAST;
|
|
info.start = start;
|
|
info.lastToken = true;
|
|
classesNeedingLastToken.remove(className);
|
|
} else {
|
|
info.start = doxyStart;
|
|
info.remove = true;
|
|
}
|
|
if (isGenerated)
|
|
todo.append(info);
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
const int documentEnd = cpp_document.lastBlock().position() + cpp_document.lastBlock().length() - 1;
|
|
|
|
Utils::ChangeSet changes;
|
|
foreach (GenInfo info, todo) {
|
|
if (info.end > documentEnd)
|
|
info.end = documentEnd;
|
|
|
|
if (info.remove) {
|
|
changes.remove(info.start, info.end);
|
|
return;
|
|
}
|
|
|
|
Overview oo;
|
|
|
|
const QString className = oo(info.classAST->symbol->name());
|
|
|
|
QString method;
|
|
QTextStream os(&method);
|
|
const QStringList fields = collectFieldNames(info.classAST, true);
|
|
|
|
if (info.firstToken)
|
|
generateFirstToken(os, className, fields);
|
|
else if (info.lastToken)
|
|
generateLastToken(os, className, fields);
|
|
|
|
changes.replace(info.start, info.end, method);
|
|
}
|
|
|
|
QTextCursor tc(&cpp_document);
|
|
changes.apply(&tc);
|
|
|
|
QString newMethods;
|
|
QTextStream os(&newMethods);
|
|
const StringClassSpecifierASTMapConstIt cfend = classesNeedingFirstToken.constEnd();
|
|
for (StringClassSpecifierASTMapConstIt it = classesNeedingFirstToken.constBegin(); it != cfend; ++it) {
|
|
const QString &className = it.key();
|
|
const QStringList fields = collectFieldNames(it.value(), true);
|
|
os << "/** \\generated */" << endl;
|
|
generateFirstToken(os, className, fields);
|
|
if (ClassSpecifierAST *classAST = classesNeedingLastToken.value(className, 0)) {
|
|
const QStringList fields = collectFieldNames(classAST, true);
|
|
os << "/** \\generated */" << endl;
|
|
generateLastToken(os, className, fields);
|
|
classesNeedingLastToken.remove(className);
|
|
}
|
|
}
|
|
const StringClassSpecifierASTMapConstIt clend = classesNeedingLastToken.constEnd();
|
|
for (StringClassSpecifierASTMapConstIt it = classesNeedingLastToken.constBegin(); it != clend; ++it) {
|
|
os << "/** \\generated */" << endl;
|
|
generateLastToken(os, it.key(), collectFieldNames(it.value(), true));
|
|
}
|
|
tc.setPosition(documentEnd);
|
|
tc.insertText(newMethods);
|
|
|
|
if (file.open(openFlags)) {
|
|
QTextStream out(&file);
|
|
out << cpp_document.toPlainText();
|
|
closeAndPrintFilePath(file);
|
|
}
|
|
}
|
|
|
|
void generateASTVisitor_H(const Snapshot &, const QDir &cplusplusDir,
|
|
const QList<QByteArray> &classes)
|
|
{
|
|
QFileInfo fileASTVisitor_h(cplusplusDir, QLatin1String("ASTVisitor.h"));
|
|
Q_ASSERT(fileASTVisitor_h.exists());
|
|
|
|
const QString fileName = fileASTVisitor_h.absoluteFilePath();
|
|
|
|
QFile file(fileName);
|
|
if (! file.open(openFlags))
|
|
return;
|
|
|
|
QTextStream out(&file);
|
|
out << copyrightHeader <<
|
|
"\n"
|
|
"#ifndef CPLUSPLUS_ASTVISITOR_H\n"
|
|
"#define CPLUSPLUS_ASTVISITOR_H\n"
|
|
"\n"
|
|
"#include \"CPlusPlusForwardDeclarations.h\"\n"
|
|
"#include \"ASTfwd.h\"\n"
|
|
"\n"
|
|
"namespace CPlusPlus {\n"
|
|
"\n"
|
|
"class CPLUSPLUS_EXPORT ASTVisitor\n"
|
|
"{\n"
|
|
" ASTVisitor(const ASTVisitor &other);\n"
|
|
" void operator =(const ASTVisitor &other);\n"
|
|
"\n"
|
|
"public:\n"
|
|
" ASTVisitor(TranslationUnit *unit);\n"
|
|
" virtual ~ASTVisitor();\n"
|
|
"\n"
|
|
" TranslationUnit *translationUnit() const;\n"
|
|
" void setTranslationUnit(TranslationUnit *translationUnit);\n"
|
|
"\n"
|
|
" Control *control() const;\n"
|
|
" unsigned tokenCount() const;\n"
|
|
" const Token &tokenAt(unsigned index) const;\n"
|
|
" int tokenKind(unsigned index) const;\n"
|
|
" const char *spell(unsigned index) const;\n"
|
|
" const Identifier *identifier(unsigned index) const;\n"
|
|
" const Literal *literal(unsigned index) const;\n"
|
|
" const NumericLiteral *numericLiteral(unsigned index) const;\n"
|
|
" const StringLiteral *stringLiteral(unsigned index) const;\n"
|
|
"\n"
|
|
" void getPosition(unsigned offset,\n"
|
|
" unsigned *line,\n"
|
|
" unsigned *column = 0,\n"
|
|
" const StringLiteral **fileName = 0) const;\n"
|
|
"\n"
|
|
" void getTokenPosition(unsigned index,\n"
|
|
" unsigned *line,\n"
|
|
" unsigned *column = 0,\n"
|
|
" const StringLiteral **fileName = 0) const;\n"
|
|
"\n"
|
|
" void getTokenStartPosition(unsigned index, unsigned *line, unsigned *column) const;\n"
|
|
" void getTokenEndPosition(unsigned index, unsigned *line, unsigned *column) const;\n"
|
|
"\n"
|
|
" void accept(AST *ast);\n"
|
|
"\n"
|
|
" template <typename Tptr>\n"
|
|
" void accept(List<Tptr> *it)\n"
|
|
" {\n"
|
|
" for (; it; it = it->next)\n"
|
|
" accept(it->value);\n"
|
|
" }\n"
|
|
"\n"
|
|
" virtual bool preVisit(AST *) { return true; }\n"
|
|
" virtual void postVisit(AST *) {}\n";
|
|
|
|
out << "\n";
|
|
foreach (const QByteArray &klass, classes) {
|
|
out << " virtual bool visit(" << klass << " *) { return true; }\n";
|
|
}
|
|
|
|
out << "\n";
|
|
foreach (const QByteArray &klass, classes) {
|
|
out << " virtual void endVisit(" << klass << " *) {}\n";
|
|
}
|
|
out << "\n";
|
|
|
|
out <<
|
|
"private:\n"
|
|
" TranslationUnit *_translationUnit;\n"
|
|
"};\n"
|
|
"\n"
|
|
"} // namespace CPlusPlus\n"
|
|
"\n"
|
|
"#endif // CPLUSPLUS_ASTVISITOR_H\n";
|
|
|
|
closeAndPrintFilePath(file);
|
|
}
|
|
|
|
void generateASTMatcher_H(const Snapshot &, const QDir &cplusplusDir,
|
|
const QList<QByteArray> &classes)
|
|
{
|
|
QFileInfo fileASTMatcher_h(cplusplusDir, QLatin1String("ASTMatcher.h"));
|
|
Q_ASSERT(fileASTMatcher_h.exists());
|
|
|
|
const QString fileName = fileASTMatcher_h.absoluteFilePath();
|
|
|
|
QFile file(fileName);
|
|
if (! file.open(openFlags))
|
|
return;
|
|
|
|
QTextStream out(&file);
|
|
out << copyrightHeader <<
|
|
"\n"
|
|
"#ifndef ASTMATCHER_H\n"
|
|
"#define ASTMATCHER_H\n"
|
|
"\n"
|
|
"#include \"ASTfwd.h\"\n"
|
|
"\n"
|
|
"namespace CPlusPlus {\n"
|
|
"\n"
|
|
"class CPLUSPLUS_EXPORT ASTMatcher\n"
|
|
"{\n"
|
|
"public:\n"
|
|
" ASTMatcher();\n"
|
|
" virtual ~ASTMatcher();\n"
|
|
"\n";
|
|
|
|
foreach (const QByteArray &klass, classes) {
|
|
out << " virtual bool match(" << klass << " *node, " << klass << " *pattern);\n";
|
|
}
|
|
|
|
out <<
|
|
"};\n"
|
|
"\n"
|
|
"} // namespace CPlusPlus\n"
|
|
"\n"
|
|
"#endif // CPLUSPLUS_ASTMATCHER_H\n";
|
|
|
|
closeAndPrintFilePath(file);
|
|
}
|
|
|
|
QStringList generateAST_H(const Snapshot &snapshot, const QDir &cplusplusDir, const QString &dumpersFile)
|
|
{
|
|
QStringList astDerivedClasses;
|
|
|
|
QFileInfo fileAST_h(cplusplusDir, QLatin1String("AST.h"));
|
|
Q_ASSERT(fileAST_h.exists());
|
|
|
|
const QString fileName = fileAST_h.absoluteFilePath();
|
|
|
|
QFile file(fileName);
|
|
if (! file.open(QFile::ReadOnly))
|
|
return astDerivedClasses;
|
|
|
|
const QByteArray source = file.readAll();
|
|
file.close();
|
|
|
|
QTextDocument document;
|
|
document.setPlainText(QString::fromUtf8(source));
|
|
|
|
AST_h_document = snapshot.preprocessedDocument(source, fileName);
|
|
AST_h_document->check();
|
|
|
|
FindASTNodes process(AST_h_document, &document);
|
|
astNodes = process(AST_h_document->translationUnit()->ast());
|
|
|
|
RemoveCastMethods removeCastMethods(AST_h_document, &document);
|
|
|
|
QList<QTextCursor> baseCastMethodCursors = removeCastMethods(astNodes.base);
|
|
QMap<ClassSpecifierAST *, QList<QTextCursor> > cursors;
|
|
QMap<ClassSpecifierAST *, QString> replacementCastMethods;
|
|
QMap<ClassSpecifierAST *, QList<QTextCursor> > constructors;
|
|
QMap<ClassSpecifierAST *, QString> replacementConstructors;
|
|
|
|
Overview oo;
|
|
|
|
QStringList castMethods;
|
|
foreach (ClassSpecifierAST *classAST, astNodes.deriveds) {
|
|
cursors[classAST] = removeCastMethods(classAST);
|
|
const QString className = oo(classAST->symbol->name());
|
|
const QString methodName = QLatin1String("as") + className.mid(0, className.length() - 3);
|
|
replacementCastMethods[classAST]
|
|
= QString::fromLatin1(" virtual %1 *%2() { return this; }\n")
|
|
.arg(className, methodName);
|
|
castMethods.append(
|
|
QString::fromLatin1(" virtual %1 *%2() { return 0; }\n")
|
|
.arg(className, methodName));
|
|
astDerivedClasses.append(className);
|
|
|
|
constructors[classAST] = removeConstructors(classAST, AST_h_document->translationUnit(),
|
|
&document);
|
|
replacementConstructors[classAST] = createConstructor(classAST);
|
|
}
|
|
|
|
if (! baseCastMethodCursors.isEmpty()) {
|
|
castMethods.sort();
|
|
for (int i = 0; i < baseCastMethodCursors.length(); ++i) {
|
|
baseCastMethodCursors[i].removeSelectedText();
|
|
}
|
|
|
|
baseCastMethodCursors.first().insertText(castMethods.join(QLatin1String("")));
|
|
}
|
|
|
|
for (int classIndex = 0; classIndex < astNodes.deriveds.size(); ++classIndex) {
|
|
ClassSpecifierAST *classAST = astNodes.deriveds.at(classIndex);
|
|
|
|
{ // remove the cast methods.
|
|
QList<QTextCursor> c = cursors.value(classAST);
|
|
for (int i = 0; i < c.length(); ++i) {
|
|
c[i].removeSelectedText();
|
|
}
|
|
}
|
|
{ // remove the constructors.
|
|
QList<QTextCursor> c = constructors.value(classAST);
|
|
for (int i = 0; i < c.length(); ++i) {
|
|
c[i].removeSelectedText();
|
|
}
|
|
}
|
|
|
|
astNodes.endOfPublicClassSpecifiers[classIndex].insertText(
|
|
replacementConstructors.value(classAST) +
|
|
QLatin1String("\n") +
|
|
replacementCastMethods.value(classAST));
|
|
}
|
|
|
|
if (file.open(openFlags)) {
|
|
QTextStream out(&file);
|
|
out << document.toPlainText();
|
|
closeAndPrintFilePath(file);
|
|
}
|
|
|
|
Accept0CG cg(cplusplusDir, AST_h_document->translationUnit());
|
|
cg(AST_h_document->translationUnit()->ast());
|
|
const QList<QByteArray> astClasses = cg.classes();
|
|
|
|
Match0CG cg2(cplusplusDir, AST_h_document->translationUnit());
|
|
cg2(AST_h_document->translationUnit()->ast());
|
|
|
|
MatcherCPPCG cg3(cplusplusDir, AST_h_document->translationUnit());
|
|
cg3(AST_h_document->translationUnit()->ast());
|
|
|
|
CloneCPPCG cg4(cplusplusDir, AST_h_document->translationUnit());
|
|
cg4(AST_h_document->translationUnit()->ast());
|
|
|
|
generateAST_cpp(snapshot, cplusplusDir);
|
|
|
|
generateASTVisitor_H(snapshot, cplusplusDir, astClasses);
|
|
generateASTMatcher_H(snapshot, cplusplusDir, astClasses);
|
|
|
|
if (!dumpersFile.isEmpty())
|
|
GenerateDumpers::go(dumpersFile, AST_h_document->translationUnit());
|
|
|
|
return astDerivedClasses;
|
|
}
|
|
|
|
class FindASTForwards: protected ASTVisitor
|
|
{
|
|
public:
|
|
FindASTForwards(Document::Ptr doc, QTextDocument *document)
|
|
: ASTVisitor(doc->translationUnit()), document(document)
|
|
{}
|
|
|
|
QList<QTextCursor> operator()(AST *ast)
|
|
{
|
|
accept(ast);
|
|
return _cursors;
|
|
}
|
|
|
|
protected:
|
|
bool visit(SimpleDeclarationAST *ast)
|
|
{
|
|
if (! ast->decl_specifier_list)
|
|
return false;
|
|
|
|
if (ElaboratedTypeSpecifierAST *e = ast->decl_specifier_list->value->asElaboratedTypeSpecifier()) {
|
|
if (tokenKind(e->classkey_token) == T_CLASS && !ast->declarator_list) {
|
|
QString className = oo(e->name->name);
|
|
|
|
if (className.length() > 3 && className.endsWith(QLatin1String("AST"))) {
|
|
QTextCursor tc = createCursor(translationUnit(), ast, document);
|
|
_cursors.append(tc);
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private:
|
|
QTextDocument *document;
|
|
QList<QTextCursor> _cursors;
|
|
Overview oo;
|
|
};
|
|
|
|
void generateASTFwd_h(const Snapshot &snapshot, const QDir &cplusplusDir, const QStringList &astDerivedClasses)
|
|
{
|
|
QFileInfo fileASTFwd_h(cplusplusDir, QLatin1String("ASTfwd.h"));
|
|
Q_ASSERT(fileASTFwd_h.exists());
|
|
|
|
const QString fileName = fileASTFwd_h.absoluteFilePath();
|
|
|
|
QFile file(fileName);
|
|
if (! file.open(QFile::ReadOnly))
|
|
return;
|
|
|
|
const QByteArray source = file.readAll();
|
|
file.close();
|
|
|
|
QTextDocument document;
|
|
document.setPlainText(QString::fromUtf8(source));
|
|
Document::Ptr doc = snapshot.preprocessedDocument(source, fileName);
|
|
doc->check();
|
|
|
|
FindASTForwards process(doc, &document);
|
|
QList<QTextCursor> cursors = process(doc->translationUnit()->ast());
|
|
|
|
for (int i = 0; i < cursors.length(); ++i)
|
|
cursors[i].removeSelectedText();
|
|
|
|
QString replacement;
|
|
foreach (const QString &astDerivedClass, astDerivedClasses) {
|
|
replacement += QString(QLatin1String("class %1;\n")).arg(astDerivedClass);
|
|
}
|
|
|
|
cursors.first().insertText(replacement);
|
|
|
|
if (file.open(openFlags)) {
|
|
QTextStream out(&file);
|
|
out << document.toPlainText();
|
|
closeAndPrintFilePath(file);
|
|
}
|
|
}
|
|
|
|
void generateASTPatternBuilder_h(const QDir &cplusplusDir)
|
|
{
|
|
typedef QPair<QString, QString> StringPair;
|
|
|
|
QFileInfo fileInfo(cplusplusDir, QLatin1String("ASTPatternBuilder.h"));
|
|
QFile file(fileInfo.absoluteFilePath());
|
|
if (! file.open(openFlags))
|
|
return;
|
|
|
|
Overview oo;
|
|
QTextStream out(&file);
|
|
|
|
out
|
|
<< copyrightHeader
|
|
<< generatedHeader
|
|
<< "#ifndef CPLUSPLUS_AST_PATTERN_BUILDER_H" << endl
|
|
<< "#define CPLUSPLUS_AST_PATTERN_BUILDER_H" << endl
|
|
<< endl
|
|
<< "#include \"CPlusPlusForwardDeclarations.h\"" << endl
|
|
<< "#include \"AST.h\"" << endl
|
|
<< "#include \"MemoryPool.h\"" << endl
|
|
<< endl
|
|
<< "namespace CPlusPlus {" << endl
|
|
<< endl
|
|
<< "class CPLUSPLUS_EXPORT ASTPatternBuilder" << endl
|
|
<< "{" << endl
|
|
<< " MemoryPool pool;" << endl
|
|
<< endl
|
|
<< "public:" << endl
|
|
<< " ASTPatternBuilder() {}" << endl
|
|
<< endl
|
|
<< " void reset() { pool.reset(); }" << endl
|
|
<< endl;
|
|
|
|
Control *control = AST_h_document->control();
|
|
QSet<QString> classesSet;
|
|
|
|
foreach (ClassSpecifierAST *classNode, astNodes.deriveds) {
|
|
Class *klass = classNode->symbol;
|
|
|
|
const Identifier *match0_id = control->identifier("match0");
|
|
Symbol *match0Method = klass->find(match0_id);
|
|
for (; match0Method; match0Method = match0Method->next()) {
|
|
if (match0Method->identifier() != match0_id)
|
|
continue;
|
|
else break;
|
|
}
|
|
|
|
if (! match0Method)
|
|
continue;
|
|
|
|
const QString className = oo(klass->name());
|
|
|
|
if (! className.endsWith(QLatin1String("AST")))
|
|
continue;
|
|
|
|
const QString methodName = className.left(className.length() - 3);
|
|
|
|
out
|
|
<< " " << className << " *" << methodName << "(";
|
|
|
|
QList<StringPair> args;
|
|
|
|
bool first = true;
|
|
for (unsigned index = 0; index < klass->memberCount(); ++index) {
|
|
Declaration *member = klass->memberAt(index)->asDeclaration();
|
|
if (! member)
|
|
continue;
|
|
|
|
PointerType *ptrTy = member->type()->asPointerType();
|
|
if (! ptrTy)
|
|
continue;
|
|
|
|
const QString tyName = oo(ptrTy->elementType());
|
|
if (tyName.endsWith(QLatin1String("ListAST")))
|
|
classesSet.insert(tyName);
|
|
if (tyName.endsWith(QLatin1String("AST"))) {
|
|
if (! first)
|
|
out << ", ";
|
|
|
|
const QString memberName = oo(member->name());
|
|
|
|
out << tyName << " *" << memberName << " = 0";
|
|
args.append(qMakePair(tyName, memberName));
|
|
first = false;
|
|
}
|
|
}
|
|
|
|
out
|
|
<< ")" << endl
|
|
<< " {" << endl
|
|
<< " " << className << " *ast = new (&pool) " << className << ';' << endl;
|
|
|
|
|
|
foreach (const StringPair &p, args) {
|
|
out << " ast->" << p.second << " = " << p.second << ';' << endl;
|
|
}
|
|
|
|
out
|
|
<< " return ast;" << endl
|
|
<< " }" << endl
|
|
<< endl;
|
|
}
|
|
|
|
QStringList classesList = classesSet.toList();
|
|
qSort(classesList);
|
|
foreach (const QString &className, classesList) {
|
|
const QString methodName = className.left(className.length() - 3);
|
|
const QString elementName = className.left(className.length() - 7) + QLatin1String("AST");
|
|
out
|
|
<< " " << className << " *" << methodName << "("
|
|
<< elementName << " *value, " << className << " *next = 0)" << endl
|
|
<< " {" << endl
|
|
<< " " << className << " *list = new (&pool) " << className << ";" << endl
|
|
<< " list->next = next;" << endl
|
|
<< " list->value = value;" << endl
|
|
<< " return list;" << endl
|
|
<< " }" << endl
|
|
<< endl;
|
|
}
|
|
|
|
out
|
|
<< "};" << endl
|
|
<< endl
|
|
<< "} // end of namespace CPlusPlus" << endl
|
|
<< endl
|
|
<< "#endif // CPLUSPLUS_AST_PATTERN_BUILDER_H" << endl;
|
|
|
|
closeAndPrintFilePath(file);
|
|
}
|
|
|
|
void printUsage()
|
|
{
|
|
const QByteArray executable = QFileInfo(qApp->arguments().first()).fileName().toLatin1();
|
|
std::cout << "Usage: " << executable.constData() << "\n"
|
|
<< " " << executable.constData() << " <frontend-dir> <dumpers-file>"
|
|
<< "\n\n"
|
|
<< "Generate appropriate header and source files of the C++ frontend accordingly\n"
|
|
<< "to AST.h and print the paths of the written files. Run this tool after\n"
|
|
<< "modifying AST.h."
|
|
<< "\n\n";
|
|
const QString defaultPathCppFrontend
|
|
= QFileInfo(QLatin1String(PATH_CPP_FRONTEND)).canonicalFilePath();
|
|
const QString defaultPathDumpersFile
|
|
= QFileInfo(QLatin1String(PATH_DUMPERS_FILE)).canonicalFilePath();
|
|
std::cout << "Default values:" << "\n"
|
|
<< " frontend-dir: " << qPrintable(defaultPathCppFrontend) << "\n"
|
|
<< " dumpers-file: " << qPrintable(defaultPathDumpersFile) << "\n";
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
QGuiApplication app(argc, argv);
|
|
QStringList args = app.arguments();
|
|
args.removeFirst();
|
|
|
|
QString pathCppFrontend = QLatin1String(PATH_CPP_FRONTEND);
|
|
QString pathDumpersFile = QLatin1String(PATH_DUMPERS_FILE);
|
|
|
|
const bool helpRequested = args.contains(QLatin1String("-h"))
|
|
|| args.contains(QLatin1String("-help"));
|
|
if (args.count() == 1 || args.count() >= 3 || helpRequested) {
|
|
printUsage();
|
|
return helpRequested ? EXIT_SUCCESS : EXIT_FAILURE;
|
|
} else if (args.count() == 2) {
|
|
pathCppFrontend = args.at(0);
|
|
pathDumpersFile = args.at(1);
|
|
}
|
|
|
|
QDir cplusplusDir(pathCppFrontend);
|
|
if (!QFile::exists(pathCppFrontend)) {
|
|
std::cerr << "Error: Directory \"" << qPrintable(cplusplusDir.absolutePath())
|
|
<< "\" does not exist." << std::endl;
|
|
return EXIT_FAILURE;
|
|
}
|
|
if (!QFileInfo(cplusplusDir, QLatin1String("AST.h")).exists()) {
|
|
std::cerr << "Error: Cannot find AST.h in \"" << qPrintable(cplusplusDir.absolutePath())
|
|
<< "\"." << std::endl;
|
|
return EXIT_FAILURE;
|
|
}
|
|
if (!QFile::exists(pathDumpersFile)) {
|
|
std::cerr << "Error: File \"" << qPrintable(pathDumpersFile)
|
|
<< "\" does not exist." << std::endl;
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
Snapshot snapshot;
|
|
QStringList astDerivedClasses = generateAST_H(snapshot, cplusplusDir, pathDumpersFile);
|
|
astDerivedClasses.sort();
|
|
generateASTFwd_h(snapshot, cplusplusDir, astDerivedClasses);
|
|
generateASTPatternBuilder_h(cplusplusDir);
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|