/**************************************************************************** ** ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of Qt Creator. ** ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://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 #include #include #include #include #include #if QT_VERSION >= 0x050000 // Qt5: QTextDocument needs access to Fonts via QGuiApplication. #include typedef QGuiApplication MyQApplication; #else #include typedef QCoreApplication MyQApplication; #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace CPlusPlus; static const char copyrightHeader[] = "// Copyright (c) 2008 Roberto Raggi \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 deriveds; // n where n extends AST QList endOfPublicClassSpecifiers; }; static Document::Ptr AST_h_document; static ASTNodes astNodes; static QTextCursor createCursor(TranslationUnit *unit, AST *ast, QTextDocument *document) { unsigned startLine, startColumn, endLine, endColumn; unit->getTokenStartPosition(ast->firstToken(), &startLine, &startColumn); unit->getTokenEndPosition(ast->lastToken() - 1, &endLine, &endColumn); QTextCursor tc(document); tc.setPosition(document->findBlockByNumber(startLine - 1).position()); tc.setPosition(document->findBlockByNumber(endLine - 1).position() + endColumn - 1, QTextCursor::KeepAnchor); int charsToSkip = 0; forever { QChar ch = document->characterAt(tc.position() + charsToSkip); if (! ch.isSpace()) break; ++charsToSkip; if (ch == QChar::ParagraphSeparator) break; } tc.setPosition(tc.position() + charsToSkip, QTextCursor::KeepAnchor); return tc; } class FindASTNodes: protected ASTVisitor { public: FindASTNodes(Document::Ptr doc, QTextDocument *document) : ASTVisitor(doc->translationUnit()), document(document) { } ASTNodes operator()(AST *ast) { accept(ast); return _nodes; } protected: virtual bool visit(ClassSpecifierAST *ast) { Class *klass = ast->symbol; Q_ASSERT(klass != 0); const QString className = oo(klass->name()); if (className.endsWith(QLatin1String("AST"))) { if (className == QLatin1String("AST")) _nodes.base = ast; else { _nodes.deriveds.append(ast); AccessDeclarationAST *accessDeclaration = 0; for (DeclarationListAST *it = ast->member_specifier_list; it; it = it->next) { if (AccessDeclarationAST *decl = it->value->asAccessDeclaration()) { if (tokenKind(decl->access_specifier_token) == T_PUBLIC) accessDeclaration = decl; } } if (! accessDeclaration) qDebug() << "no access declaration for class:" << className; Q_ASSERT(accessDeclaration != 0); QTextCursor tc = createCursor(translationUnit(), accessDeclaration, document); tc.setPosition(tc.position()); _nodes.endOfPublicClassSpecifiers.append(tc); } } return true; } private: QTextDocument *document; ASTNodes _nodes; Overview oo; }; class Accept0CG: protected ASTVisitor { QDir _cplusplusDir; QTextStream *out; public: Accept0CG(const QDir &cplusplusDir, TranslationUnit *unit) : ASTVisitor(unit), _cplusplusDir(cplusplusDir), out(0) { } QList 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 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 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 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 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 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 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 _cursors; Overview oo; }; static QList removeConstructors(ClassSpecifierAST *classAST, TranslationUnit *translationUnit, QTextDocument *document) { Overview oo; QList 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) { typedef QMap StringClassSpecifierASTMap; typedef StringClassSpecifierASTMap::ConstIterator StringClassSpecifierASTMapConstIt; QFileInfo fileAST_cpp(cplusplusDir, QLatin1String("AST.cpp")); Q_ASSERT(fileAST_cpp.exists()); const QString fileName = fileAST_cpp.absoluteFilePath(); QFile file(fileName); if (! file.open(QFile::ReadOnly)) { std::cerr << "Cannot open " << fileName.toLatin1().data() << std::endl; return; } const QByteArray source = file.readAll(); file.close(); QTextDocument cpp_document; cpp_document.setPlainText(QString::fromUtf8(source)); Document::Ptr AST_cpp_document = snapshot.preprocessedDocument(source, fileName); AST_cpp_document->check(); Overview oo; StringClassSpecifierASTMap classesNeedingFirstToken; StringClassSpecifierASTMap classesNeedingLastToken; // find all classes with method declarations for firstToken/lastToken foreach (ClassSpecifierAST *classAST, astNodes.deriveds) { const QString className = oo(classAST->symbol->name()); if (className.isEmpty()) continue; for (DeclarationListAST *declIter = classAST->member_specifier_list; declIter; declIter = declIter->next) { if (SimpleDeclarationAST *decl = declIter->value->asSimpleDeclaration()) { if (decl->symbols && decl->symbols->value) { if (decl->symbols->next) std::cerr << "Found simple declaration with multiple symbols in " << className.toLatin1().data() << std::endl; Symbol *s = decl->symbols->value; const QString funName = oo(s->name()); if (funName == QLatin1String("firstToken")) { // found it: classesNeedingFirstToken.insert(className, classAST); } else if (funName == QLatin1String("lastToken")) { // found it: classesNeedingLastToken.insert(className, classAST); } } } } } QList 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); const StringClassSpecifierASTMapConstIt cfend = classesNeedingFirstToken.constEnd(); for (StringClassSpecifierASTMapConstIt it = classesNeedingFirstToken.constBegin(); it != cfend; ++it) { const QString &className = it.key(); const QStringList fields = collectFieldNames(it.value(), true); os << "/** \\generated */" << endl; generateFirstToken(os, className, fields); if (ClassSpecifierAST *classAST = classesNeedingLastToken.value(className, 0)) { const QStringList fields = collectFieldNames(classAST, true); os << "/** \\generated */" << endl; generateLastToken(os, className, fields); classesNeedingLastToken.remove(className); } } const StringClassSpecifierASTMapConstIt clend = classesNeedingLastToken.constEnd(); for (StringClassSpecifierASTMapConstIt it = classesNeedingLastToken.constBegin(); it != clend; ++it) { os << "/** \\generated */" << endl; generateLastToken(os, it.key(), collectFieldNames(it.value(), true)); } tc.setPosition(documentEnd); tc.insertText(newMethods); if (file.open(QFile::WriteOnly)) { QTextStream out(&file); out << cpp_document.toPlainText(); closeAndPrintFilePath(file); } } void generateASTVisitor_H(const Snapshot &, const QDir &cplusplusDir, const QList &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 \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 &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 QByteArray source = file.readAll(); file.close(); QTextDocument document; document.setPlainText(QString::fromUtf8(source)); AST_h_document = snapshot.preprocessedDocument(source, fileName); AST_h_document->check(); FindASTNodes process(AST_h_document, &document); astNodes = process(AST_h_document->translationUnit()->ast()); RemoveCastMethods removeCastMethods(AST_h_document, &document); QList baseCastMethodCursors = removeCastMethods(astNodes.base); QMap > cursors; QMap replacementCastMethods; QMap > constructors; QMap replacementConstructors; Overview oo; QStringList castMethods; foreach (ClassSpecifierAST *classAST, astNodes.deriveds) { cursors[classAST] = removeCastMethods(classAST); const QString className = oo(classAST->symbol->name()); const QString methodName = QLatin1String("as") + className.mid(0, className.length() - 3); replacementCastMethods[classAST] = QString::fromLatin1(" virtual %1 *%2() { return this; }\n") .arg(className, methodName); castMethods.append( QString::fromLatin1(" virtual %1 *%2() { return 0; }\n") .arg(className, methodName)); astDerivedClasses.append(className); constructors[classAST] = removeConstructors(classAST, AST_h_document->translationUnit(), &document); replacementConstructors[classAST] = createConstructor(classAST); } if (! baseCastMethodCursors.isEmpty()) { castMethods.sort(); for (int i = 0; i < baseCastMethodCursors.length(); ++i) { baseCastMethodCursors[i].removeSelectedText(); } baseCastMethodCursors.first().insertText(castMethods.join(QLatin1String(""))); } for (int classIndex = 0; classIndex < astNodes.deriveds.size(); ++classIndex) { ClassSpecifierAST *classAST = astNodes.deriveds.at(classIndex); { // remove the cast methods. QList c = cursors.value(classAST); for (int i = 0; i < c.length(); ++i) { c[i].removeSelectedText(); } } { // remove the constructors. QList 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 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 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 _cursors; Overview oo; }; void generateASTFwd_h(const Snapshot &snapshot, const QDir &cplusplusDir, const QStringList &astDerivedClasses) { QFileInfo fileASTFwd_h(cplusplusDir, QLatin1String("ASTfwd.h")); Q_ASSERT(fileASTFwd_h.exists()); const QString fileName = fileASTFwd_h.absoluteFilePath(); QFile file(fileName); if (! file.open(QFile::ReadOnly)) return; const QByteArray source = file.readAll(); file.close(); QTextDocument document; document.setPlainText(QString::fromUtf8(source)); Document::Ptr doc = snapshot.preprocessedDocument(source, fileName); doc->check(); FindASTForwards process(doc, &document); QList 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) { typedef QPair StringPair; 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 classesSet; foreach (ClassSpecifierAST *classNode, astNodes.deriveds) { Class *klass = classNode->symbol; const Identifier *match0_id = control->identifier("match0"); Symbol *match0Method = klass->find(match0_id); for (; match0Method; match0Method = match0Method->next()) { if (match0Method->identifier() != match0_id) continue; else break; } if (! match0Method) continue; const QString className = oo(klass->name()); if (! className.endsWith(QLatin1String("AST"))) continue; const QString methodName = className.left(className.length() - 3); out << " " << className << " *" << methodName << "("; QList args; bool first = true; for (unsigned index = 0; index < klass->memberCount(); ++index) { Declaration *member = klass->memberAt(index)->asDeclaration(); if (! member) continue; PointerType *ptrTy = member->type()->asPointerType(); if (! ptrTy) continue; const QString tyName = oo(ptrTy->elementType()); if (tyName.endsWith(QLatin1String("ListAST"))) classesSet.insert(tyName); if (tyName.endsWith(QLatin1String("AST"))) { if (! first) out << ", "; const QString memberName = oo(member->name()); out << tyName << " *" << memberName << " = 0"; args.append(qMakePair(tyName, memberName)); first = false; } } out << ")" << endl << " {" << endl << " " << className << " *__ast = new (&pool) " << className << ';' << endl; foreach (const StringPair &p, args) { out << " __ast->" << p.second << " = " << p.second << ';' << endl; } out << " return __ast;" << endl << " }" << endl << endl; } QStringList classesList = classesSet.toList(); qSort(classesList); foreach (const QString &className, classesList) { const QString methodName = className.left(className.length() - 3); const QString elementName = className.left(className.length() - 7) + QLatin1String("AST"); out << " " << className << " *" << methodName << "(" << elementName << " *value, " << className << " *next = 0)" << endl << " {" << endl << " " << className << " *__list = new (&pool) " << className << ";" << endl << " __list->next = next;" << endl << " __list->value = value;" << endl << " return __list;" << endl << " }" << endl << endl; } out << "};" << endl << endl << "} // end of namespace CPlusPlus" << endl << endl << "#endif // CPLUSPLUS_AST_PATTERN_BUILDER_H" << endl; closeAndPrintFilePath(file); } void printUsage() { const QByteArray executable = QFileInfo(qApp->arguments().first()).fileName().toLatin1(); std::cout << "Usage: " << executable.constData() << "\n" << " " << executable.constData() << " " << "\n\n" << "Generate appropriate header and source files of the C++ frontend accordingly\n" << "to AST.h and print the paths of the written files. Run this tool after\n" << "modifying AST.h." << "\n\n"; const QString defaultPathCppFrontend = QFileInfo(QLatin1String(PATH_CPP_FRONTEND)).canonicalFilePath(); const QString defaultPathDumpersFile = QFileInfo(QLatin1String(PATH_DUMPERS_FILE)).canonicalFilePath(); std::cout << "Default values:" << "\n" << " frontend-dir: " << qPrintable(defaultPathCppFrontend) << "\n" << " dumpers-file: " << qPrintable(defaultPathDumpersFile) << "\n"; } int main(int argc, char *argv[]) { MyQApplication app(argc, argv); QStringList args = app.arguments(); args.removeFirst(); QString pathCppFrontend = QLatin1String(PATH_CPP_FRONTEND); QString pathDumpersFile = QLatin1String(PATH_DUMPERS_FILE); const bool helpRequested = args.contains(QLatin1String("-h")) || args.contains(QLatin1String("-help")); if (args.count() == 1 || args.count() >= 3 || helpRequested) { printUsage(); return helpRequested ? EXIT_SUCCESS : EXIT_FAILURE; } else if (args.count() == 2) { pathCppFrontend = args.at(0); pathDumpersFile = args.at(1); } QDir cplusplusDir(pathCppFrontend); if (!QFile::exists(pathCppFrontend)) { std::cerr << "Error: Directory \"" << qPrintable(cplusplusDir.absolutePath()) << "\" does not exist." << std::endl; return EXIT_FAILURE; } if (!QFileInfo(cplusplusDir, QLatin1String("AST.h")).exists()) { std::cerr << "Error: Cannot find AST.h in \"" << qPrintable(cplusplusDir.absolutePath()) << "\"." << std::endl; return EXIT_FAILURE; } if (!QFile::exists(pathDumpersFile)) { std::cerr << "Error: File \"" << qPrintable(pathDumpersFile) << "\" does not exist." << std::endl; return EXIT_FAILURE; } Snapshot snapshot; QStringList astDerivedClasses = generateAST_H(snapshot, cplusplusDir, pathDumpersFile); astDerivedClasses.sort(); generateASTFwd_h(snapshot, cplusplusDir, astDerivedClasses); generateASTPatternBuilder_h(cplusplusDir); return EXIT_SUCCESS; }