diff options
Diffstat (limited to 'src/tools/cplusplus-mkvisitor/cplusplus-mkvisitor.cpp')
-rw-r--r-- | src/tools/cplusplus-mkvisitor/cplusplus-mkvisitor.cpp | 488 |
1 files changed, 488 insertions, 0 deletions
diff --git a/src/tools/cplusplus-mkvisitor/cplusplus-mkvisitor.cpp b/src/tools/cplusplus-mkvisitor/cplusplus-mkvisitor.cpp new file mode 100644 index 00000000000..ae21f90d94a --- /dev/null +++ b/src/tools/cplusplus-mkvisitor/cplusplus-mkvisitor.cpp @@ -0,0 +1,488 @@ +/**************************************************************************** +** +** 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 <AST.h> +#include <ASTVisitor.h> +#include <ASTPatternBuilder.h> +#include <ASTMatcher.h> +#include <Control.h> +#include <Scope.h> +#include <Bind.h> +#include <TranslationUnit.h> +#include <Literals.h> +#include <Symbols.h> +#include <Names.h> +#include <CoreTypes.h> +#include <CppDocument.h> +#include <SymbolVisitor.h> +#include <Overview.h> +#include <LookupContext.h> + +#include "cplusplus-tools-utils.h" + +#include <QFile> +#include <QList> +#include <QCoreApplication> +#include <QStringList> +#include <QFileInfo> +#include <QTime> +#include <QDebug> + +#include <cstdio> +#include <cstdlib> +#include <iostream> + +using namespace CPlusPlus; + +class MkVisitor: protected SymbolVisitor +{ + const LookupContext &context; + Overview oo; + QList<ClassOrNamespace *> interfaces; + QList<ClassOrNamespace *> nodes; + + bool isMiscNode(ClassOrNamespace *b) const + { + foreach (ClassOrNamespace *u, b->usings()) { + if (oo(u->symbols().first()->name()) == QLatin1String("AST")) + return true; + } + + return false; + } + + QString getAcceptFunctionName(ClassOrNamespace *b, QString *retType) const + { + Q_ASSERT(b != 0); + + retType->clear(); + + if (interfaces.contains(b) || isMiscNode(b)) { + QString className = oo(b->symbols().first()->name()); + + if (className.endsWith("AST")) { + className.chop(3); + + QString funcName = className; + if (! funcName.isEmpty()) + funcName[0] = funcName[0].toLower(); + + if (funcName == QLatin1String("operator")) + funcName = QLatin1String("cppOperator"); + + if (interfaces.contains(b)) + *retType = className + QLatin1String("Ty"); + + return funcName; + } + } + + if (! b->usings().isEmpty()) + return getAcceptFunctionName(b->usings().first(), retType); + + Q_ASSERT(!"wtf?"); + return QString(); + } + +public: + MkVisitor(const LookupContext &context): context(context) { + accept(context.thisDocument()->globalNamespace()); + + // declaration + + std::cout << "#include <CPlusPlus.h>" << std::endl + << "#include <memory>" << std::endl + << "#include <cassert>" << std::endl + << std::endl + << "using namespace CPlusPlus;" << std::endl + << std::endl; + + + std::cout << "class Semantic: protected ASTVisitor" << std::endl + << "{" << std::endl + << "public:" << std::endl + << " Semantic(TranslationUnit *unit): ASTVisitor(unit) { translationUnit(unit->ast()->asTranslationUnit()); }" << std::endl + << std::endl; + + foreach (ClassOrNamespace *b, interfaces) { + Q_ASSERT(! b->symbols().isEmpty()); + + Class *klass = 0; + foreach (Symbol *s, b->symbols()) + if ((klass = s->asClass()) != 0) + break; + + Q_ASSERT(klass != 0); + + QString className = oo(klass->name()); + if (className == "AST") + continue; + + QString baseClassName = className; + baseClassName.chop(3); + + QString retTy = false; + QString funcName = getAcceptFunctionName(b, &retTy); + + std::cout + << " typedef void *" << qPrintable(baseClassName) << "Ty;" << std::endl + << " " << qPrintable(baseClassName) << "Ty " << qPrintable(funcName) + << "(" << qPrintable(className) << " *ast);" << std::endl + << std::endl; + } + + std::cout << "protected:" << std::endl; + std::cout << " using ASTVisitor::translationUnit;" << std::endl + << std::endl; + + QHash<ClassOrNamespace *, QList<ClassOrNamespace *> > implements; + foreach (ClassOrNamespace *b, nodes) { + ClassOrNamespace *iface = 0; + foreach (ClassOrNamespace *u, b->usings()) { + if (interfaces.contains(u)) { + iface = u; + break; + } + } + Q_ASSERT(iface != 0); + implements[iface].append(b); + } + + foreach (ClassOrNamespace *iface, interfaces) { + foreach (ClassOrNamespace *b, implements.value(iface)) { + if (! isMiscNode(b)) + continue; + + Class *klass = 0; + foreach (Symbol *s, b->symbols()) + if ((klass = s->asClass()) != 0) + break; + + Q_ASSERT(klass != 0); + + QString retTy ; + QString className = oo(klass->name()); + std::cout << " void " << qPrintable(getAcceptFunctionName(b, &retTy)) << "(" << qPrintable(className) << " *ast);" << std::endl; + } + } + + std::cout << std::endl; + + foreach (ClassOrNamespace *iface, interfaces) { + std::cout << " // " << qPrintable(oo(iface->symbols().first()->name())) << std::endl; + foreach (ClassOrNamespace *b, implements.value(iface)) { + Class *klass = 0; + foreach (Symbol *s, b->symbols()) + if ((klass = s->asClass()) != 0) + break; + + Q_ASSERT(klass != 0); + + QString className = oo(klass->name()); + std::cout << " virtual bool visit(" << qPrintable(className) << " *ast);" << std::endl; + } + std::cout << std::endl; + } + + std::cout << "private:" << std::endl; + foreach (ClassOrNamespace *b, interfaces) { + Q_ASSERT(! b->symbols().isEmpty()); + + Class *klass = 0; + foreach (Symbol *s, b->symbols()) + if ((klass = s->asClass()) != 0) + break; + + Q_ASSERT(klass != 0); + + QString className = oo(klass->name()); + if (className == "AST") + continue; + + QString baseClassName = className; + baseClassName.chop(3); + + QString current = "_current"; + current += baseClassName; + + std::cout << " " << qPrintable(baseClassName) << "Ty " << qPrintable(current) << ";" << std::endl; + } + + std::cout << "};" << std::endl + << std::endl; + + + std::cout << "namespace { bool debug_todo = false; }" << std::endl + << std::endl; + + + // implementation + + foreach (ClassOrNamespace *b, interfaces) { + Q_ASSERT(! b->symbols().isEmpty()); + + Class *klass = 0; + foreach (Symbol *s, b->symbols()) + if ((klass = s->asClass()) != 0) + break; + + Q_ASSERT(klass != 0); + + QString className = oo(klass->name()); + if (className == "AST") + continue; + + QString baseClassName = className; + baseClassName.chop(3); + + QString retTy; + QString funcName = getAcceptFunctionName(b, &retTy); + + QString current = "_current"; + current += baseClassName; + + std::cout << "Semantic::" << qPrintable(baseClassName) << "Ty Semantic::" + << qPrintable(funcName) << "(" << qPrintable(className) << " *ast)" << std::endl + << "{" << std::endl + << " " << qPrintable(baseClassName) << "Ty value = " << qPrintable(baseClassName) << "Ty();" << std::endl + << " std::swap(" << qPrintable(current) << ", value);" << std::endl + << " accept(ast);" << std::endl + << " std::swap(" << qPrintable(current) << ", value);" << std::endl + << " return value;" << std::endl + << "}" << std::endl + << std::endl; + } + + foreach (ClassOrNamespace *iface, interfaces) { + std::cout << "// " << qPrintable(oo(iface->symbols().first()->name())) << std::endl; + foreach (ClassOrNamespace *b, implements.value(iface)) { + Class *klass = 0; + foreach (Symbol *s, b->symbols()) + if ((klass = s->asClass()) != 0) + break; + + Q_ASSERT(klass != 0); + + QString className = oo(klass->name()); + std::cout << "bool Semantic::visit(" << qPrintable(className) << " *ast)" << std::endl + << "{" << std::endl; + + if (isMiscNode(b)) { + std::cout << " (void) ast;" << std::endl + << " assert(!\"unreachable\");" << std::endl + << " return false;" << std::endl + << "}" << std::endl + << std::endl; + + QString retTy; + std::cout << "void Semantic::" << qPrintable(getAcceptFunctionName(b, &retTy)) << "(" << qPrintable(className) << " *ast)" << std::endl + << "{" << std::endl + << " if (! ast)" << std::endl + << " return;" << std::endl + << std::endl; + } + + std::cout + << " if (debug_todo)" << std::endl + << " translationUnit()->warning(ast->firstToken(), \"TODO: %s\", __func__);" << std::endl; + + for (unsigned i = 0; i < klass->memberCount(); ++i) { + Declaration *decl = klass->memberAt(i)->asDeclaration(); + if (! decl) + continue; + if (decl->type()->isFunctionType()) + continue; + const QString declName = oo(decl->name()); + if (PointerType *ptrTy = decl->type()->asPointerType()) { + if (NamedType *namedTy = ptrTy->elementType()->asNamedType()) { + const QString eltTyName = oo(namedTy->name()); + if (eltTyName.endsWith("ListAST")) { + QString name = eltTyName; + name.chop(7); + name += "AST"; + + Control *control = context.thisDocument()->control(); + const Name *n = control->identifier(name.toLatin1().constData()); + + if (ClassOrNamespace *bb = context.lookupType(n, klass)) { + QString retTy; + QString funcName = getAcceptFunctionName(bb, &retTy); + Q_ASSERT(! funcName.isEmpty()); + + QString var; + if (! retTy.isEmpty()) + var += retTy + QLatin1String(" value = "); + //std::cout << " // ### accept with function " << qPrintable(getAcceptFunctionName(bb, &hasRetValue)) << std::endl; + std::cout << " for (" << qPrintable(eltTyName) << " *it = ast->" << qPrintable(declName) << "; it; it = it->next) {" << std::endl + << " " << qPrintable(var) << "this->" << qPrintable(funcName) << "(it->value);" << std::endl + << " }" << std::endl; + continue; + } + + std::cout << " // ### process list of " << qPrintable(name) << std::endl; + continue; + } + + if (ClassOrNamespace *ty = context.lookupType(namedTy->name(), klass)) { + QString className = oo(ty->symbols().first()->name()); + QString baseClassName = className; + if (baseClassName.endsWith("AST")) { + baseClassName.chop(3); + + QString retTy; + QString funcName = getAcceptFunctionName(ty, &retTy); + QString var; + if (! retTy.isEmpty()) + var += retTy + QLatin1String(" ") + declName + QLatin1String(" = "); + std::cout << " " << qPrintable(var) << "this->" << qPrintable(funcName) << "(ast->" << qPrintable(declName) << ");" << std::endl; + continue; + } + } + } + } + + std::cout << " // " << qPrintable(oo.prettyType(decl->type(), declName)) << " = ast->" << qPrintable(declName) << ";" << std::endl; + } + + if (! isMiscNode(b)) + std::cout << " return false;" << std::endl; + + std::cout + << "}" << std::endl + << std::endl; + } + std::cout << std::endl; + } + + } + +protected: + using SymbolVisitor::visit; + + QList<ClassOrNamespace *> baseClasses(ClassOrNamespace *b) { + QList<ClassOrNamespace *> usings = b->usings(); + foreach (ClassOrNamespace *u, usings) + usings += baseClasses(u); + return usings; + } + + virtual bool visit(Class *klass) { + const QString className = oo(klass->name()); + if (! className.endsWith("AST")) + return false; + + ClassOrNamespace *b = context.lookupType(klass); + Q_ASSERT(b != 0); + + const Identifier *accept0 = context.thisDocument()->control()->identifier("accept0"); + if (Symbol *s = klass->find(accept0)) { + if (Function *meth = s->type()->asFunctionType()) { + if (! meth->isPureVirtual()) { + foreach (ClassOrNamespace *u, b->usings()) { + if (interfaces.contains(u)) { + // qDebug() << oo(klass->name()) << "implements" << oo(u->symbols().first()->name()); + } else { + Q_ASSERT(!"no way"); + } + } + nodes.append(b); + return false; + } + } + } + + //qDebug() << oo(klass->name()) << "is a base ast node"; + interfaces.append(b); + + return false; + } +}; + +void printUsage() +{ + std::cout << "Usage: " << qPrintable(QFileInfo(qApp->arguments().at(0)).fileName()) + << " [-v] [path to AST.h]\n\n" + << "Print a visitor class based on AST.h to stdout.\n\n"; + const QString defaulPath = QFileInfo(PATH_AST_H).canonicalFilePath(); + std::cout << "Default path: " << qPrintable(defaulPath) << '.' << "\n"; +} + +int main(int argc, char *argv[]) +{ + QCoreApplication app(argc, argv); + QStringList args = app.arguments(); + args.removeFirst(); + + bool optionVerbose = false; + + // Process options & arguments + if (args.contains("-v")) { + optionVerbose = true; + args.removeOne("-v"); + } + const bool helpRequested = args.contains("-h") || args.contains("-help"); + if (helpRequested || args.count() >= 2) { + printUsage(); + return helpRequested ? EXIT_SUCCESS : EXIT_FAILURE; + } + + // Run the preprocessor + QString fileName = PATH_AST_H; + if (!args.isEmpty()) + fileName = args.first(); + + const QString fileNamePreprocessed = fileName + QLatin1String(".preprocessed"); + CplusplusToolsUtils::SystemPreprocessor preprocessor(optionVerbose); + preprocessor.preprocessFile(fileName, fileNamePreprocessed); + + QFile file(fileNamePreprocessed); + if (! file.open(QFile::ReadOnly)) { + std::cerr << "Error: Could not open file \"" << qPrintable(file.fileName()) << "\"." + << std::endl; + return EXIT_FAILURE; + } + + const QByteArray source = file.readAll(); + file.close(); + + Document::Ptr doc = Document::create(fileName); + //doc->control()->setDiagnosticClient(0); + doc->setUtf8Source(source); + doc->parse(); + doc->translationUnit()->blockErrors(true); + doc->check(); + + Snapshot snapshot; + snapshot.insert(doc); + + LookupContext context(doc, snapshot); + MkVisitor mkVisitor(context); + + return EXIT_SUCCESS; +} |