diff options
Diffstat (limited to 'src/tools/cplusplus-update-frontend/cplusplus-update-frontend.cpp')
-rw-r--r-- | src/tools/cplusplus-update-frontend/cplusplus-update-frontend.cpp | 1751 |
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; +} |