summaryrefslogtreecommitdiff
path: root/src/tools/cplusplus-update-frontend/cplusplus-update-frontend.cpp
diff options
context:
space:
mode:
authorNikolai Kosjar <nikolai.kosjar@digia.com>2012-10-29 13:54:33 +0100
committerNikolai Kosjar <nikolai.kosjar@digia.com>2012-11-22 14:11:58 +0100
commitd0f3d7cb89a234f88b06bc19a41e50c41b1eab0a (patch)
tree31c22e52a2f35be42d7298fab1cfc6a92e112550 /src/tools/cplusplus-update-frontend/cplusplus-update-frontend.cpp
parent1a003ed29bd72009ed9c619d6af92804d099dbfc (diff)
downloadqt-creator-d0f3d7cb89a234f88b06bc19a41e50c41b1eab0a.tar.gz
C++: Clean up dev tools.
* Add -h and -help options describing the tools and their usage. * Make the tools compile and run on Windows (MinGW, MSVC). * Rename project dirs, executables and main source files to more meaningful names: - Use same base name for project dir, *.pro file, main source file and (if applicable) script file. - Use the prefix "cplusplus-". - The names are now: - gen-cpp-ast/generate-ast --> cplusplus-update-frontend - mkvisitor --> cplusplus-mkvisitor - cplusplus-dump/cplusplus0 --> cplusplus-ast2png * Get rid of 'c++' shell scripts. * Get rid of duplicates of 'conf.c++'. Rename to 'pp-configuration.inc'. * Introduce src/tools/cplusplus-tools-utils containing common stuff that is used at least in two tools. 'pp-configuration.inc' can also be found here. * cplusplus-update-frontend: - Print file paths of written files to stdout. - Convenience: Use default values referencing the appropriate dirs and files. * cplusplus-mkvisitor: - Take only one argument, namely the path to AST.h. - Convenience: Use default path to AST.h. * cplusplus-ast2png: - Make it run without LD_LIBRARY_PATH. - As the name suggests, generate image files in png format (needs 'dot' from graphviz). - Convenience: Read from stdin, which useful for small snippets. Change-Id: I79c4061fce4a1571c0588dfedd50d4a70715d9df Reviewed-by: Erik Verbruggen <erik.verbruggen@digia.com>
Diffstat (limited to 'src/tools/cplusplus-update-frontend/cplusplus-update-frontend.cpp')
-rw-r--r--src/tools/cplusplus-update-frontend/cplusplus-update-frontend.cpp1751
1 files changed, 1751 insertions, 0 deletions
diff --git a/src/tools/cplusplus-update-frontend/cplusplus-update-frontend.cpp b/src/tools/cplusplus-update-frontend/cplusplus-update-frontend.cpp
new file mode 100644
index 0000000000..6d8a61dc7f
--- /dev/null
+++ b/src/tools/cplusplus-update-frontend/cplusplus-update-frontend.cpp
@@ -0,0 +1,1751 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 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://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 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, 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 <QStringList>
+#include <QTextDocument>
+#include <QTextCursor>
+#include <QTextBlock>
+#include <QDir>
+#include <QDebug>
+
+#if QT_VERSION >= 0x050000
+ // Qt5: QTextDocument needs access to Fonts via QGuiApplication.
+ #include <QGuiApplication>
+ typedef QGuiApplication MyQApplication;
+#else
+ #include <QCoreApplication>
+ typedef QCoreApplication MyQApplication;
+#endif
+
+#include <Control.h>
+#include <Parser.h>
+#include <AST.h>
+#include <ASTVisitor.h>
+#include <Symbols.h>
+#include <CoreTypes.h>
+#include <Literals.h>
+#include <CppDocument.h>
+#include <Overview.h>
+#include <Names.h>
+#include <Scope.h>
+#include <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 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("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(QFile::WriteOnly))
+ 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(QFile::WriteOnly))
+ 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(QFile::WriteOnly))
+ 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(QFile::WriteOnly))
+ 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(QFile::WriteOnly)) {
+ 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.begin();
+
+ 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)
+{
+ 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 QString source = QTextStream(&file).readAll();
+ file.close();
+
+ QTextDocument cpp_document;
+ cpp_document.setPlainText(source);
+
+ Document::Ptr AST_cpp_document = snapshot.preprocessedDocument(source, fileName);
+ AST_cpp_document->check();
+
+ Overview oo;
+ QMap<QString, ClassSpecifierAST *> classesNeedingFirstToken;
+ QMap<QString, ClassSpecifierAST *> 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 QualifiedNameId *qName = funDef->symbol->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);
+ foreach (const QString &className, classesNeedingFirstToken.keys()) {
+ const QStringList fields = collectFieldNames(classesNeedingFirstToken.value(className), 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);
+ }
+ }
+ foreach (const QString &className, classesNeedingLastToken.keys()) {
+ const QStringList fields = collectFieldNames(classesNeedingLastToken.value(className), true);
+ os << "/** \\generated */" << endl;
+ generateLastToken(os, className, fields);
+ }
+ tc.setPosition(documentEnd);
+ tc.insertText(newMethods);
+
+ if (file.open(QFile::WriteOnly)) {
+ 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(QFile::WriteOnly))
+ 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 _Tp>\n"
+" void accept(List<_Tp> *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(QFile::WriteOnly))
+ 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 QString source = QTextStream(&file).readAll();
+ file.close();
+
+ QTextDocument document;
+ document.setPlainText(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(" virtual %1 *%2() { return this; }\n").arg(className, methodName);
+ castMethods.append(QString(" 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(QFile::WriteOnly)) {
+ 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 QString source = QTextStream(&file).readAll();
+ file.close();
+
+ QTextDocument document;
+ document.setPlainText(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(QFile::WriteOnly)) {
+ QTextStream out(&file);
+ out << document.toPlainText();
+ closeAndPrintFilePath(file);
+ }
+}
+
+void generateASTPatternBuilder_h(const QDir &cplusplusDir)
+{
+ QFileInfo fileInfo(cplusplusDir, QLatin1String("ASTPatternBuilder.h"));
+ QFile file(fileInfo.absoluteFilePath());
+ if (! file.open(QFile::WriteOnly))
+ 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> listClasses;
+
+ 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("AST"))
+ continue;
+
+ const QString methodName = className.left(className.length() - 3);
+
+ out
+ << " " << className << " *" << methodName << "(";
+
+ QList<QPair<QString, QString> > 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("ListAST"))
+ listClasses.insert(tyName);
+ if (tyName.endsWith("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;
+
+
+ QPair<QString, QString> p;
+ foreach (p, args) {
+ out << " __ast->" << p.second << " = " << p.second << ';' << endl;
+ }
+
+ out
+ << " return __ast;" << endl
+ << " }" << endl
+ << endl;
+ }
+
+ foreach (const QString &className, listClasses) {
+ 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(PATH_CPP_FRONTEND).canonicalFilePath();
+ const QString defaultPathDumpersFile = QFileInfo(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[])
+{
+ MyQApplication app(argc, argv);
+ QStringList args = app.arguments();
+ args.removeFirst();
+
+ QString pathCppFrontend = PATH_CPP_FRONTEND;
+ QString pathDumpersFile = PATH_DUMPERS_FILE;
+
+ const bool helpRequested = args.contains("-h") || args.contains("-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;
+}