1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
16 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Nokia gives you certain additional
26 ** rights. These rights are described in the Nokia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
32 **************************************************************************/
34 #include <QCoreApplication>
35 #include <QStringList>
36 #include <QTextDocument>
37 #include <QTextCursor>
45 #include <ASTVisitor.h>
47 #include <CoreTypes.h>
49 #include <CppDocument.h>
53 #include <BackwardsScanner.h>
55 #include <utils/changeset.h>
60 using namespace CPlusPlus;
62 static const char copyrightHeader[] =
63 "/**************************************************************************\n"
65 "** This file is part of Qt Creator\n"
67 "** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).\n"
69 "** Contact: Nokia Corporation (qt-info@nokia.com)\n"
71 "** No Commercial Usage\n"
73 "** This file contains pre-release code and may not be distributed.\n"
74 "** You may use this file in accordance with the terms and conditions\n"
75 "** contained in the Technology Preview License Agreement accompanying\n"
78 "** GNU Lesser General Public License Usage\n"
80 "** Alternatively, this file may be used under the terms of the GNU Lesser\n"
81 "** General Public License version 2.1 as published by the Free Software\n"
82 "** Foundation and appearing in the file LICENSE.LGPL included in the\n"
83 "** packaging of this file. Please review the following information to\n"
84 "** ensure the GNU Lesser General Public License version 2.1 requirements\n"
85 "** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.\n"
87 "** In addition, as a special exception, Nokia gives you certain additional\n"
88 "** rights. These rights are described in the Nokia Qt LGPL Exception\n"
89 "** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.\n"
91 "** If you have questions regarding the use of this file, please contact\n"
92 "** Nokia at qt-info@nokia.com.\n"
94 "**************************************************************************/\n"
97 static const char generatedHeader[] =
103 "// This file is automatically generated.\n"
104 "// Changes will be lost.\n"
112 ASTNodes(): base(0) {}
114 ClassSpecifierAST *base; // points to "class AST"
115 QList<ClassSpecifierAST *> deriveds; // n where n extends AST
116 QList<QTextCursor> endOfPublicClassSpecifiers;
119 static Document::Ptr AST_h_document;
120 static ASTNodes astNodes;
122 static QTextCursor createCursor(TranslationUnit *unit, AST *ast, QTextDocument *document)
124 unsigned startLine, startColumn, endLine, endColumn;
125 unit->getTokenStartPosition(ast->firstToken(), &startLine, &startColumn);
126 unit->getTokenEndPosition(ast->lastToken() - 1, &endLine, &endColumn);
128 QTextCursor tc(document);
129 tc.setPosition(document->findBlockByNumber(startLine - 1).position());
130 tc.setPosition(document->findBlockByNumber(endLine - 1).position() + endColumn - 1,
131 QTextCursor::KeepAnchor);
135 QChar ch = document->characterAt(tc.position() + charsToSkip);
142 if (ch == QChar::ParagraphSeparator)
146 tc.setPosition(tc.position() + charsToSkip, QTextCursor::KeepAnchor);
150 class FindASTNodes: protected ASTVisitor
153 FindASTNodes(Document::Ptr doc, QTextDocument *document)
154 : ASTVisitor(doc->translationUnit()), document(document)
158 ASTNodes operator()(AST *ast)
165 virtual bool visit(ClassSpecifierAST *ast)
167 Class *klass = ast->symbol;
168 Q_ASSERT(klass != 0);
170 const QString className = oo(klass->name());
172 if (className.endsWith("AST")) {
173 if (className == QLatin1String("AST"))
176 _nodes.deriveds.append(ast);
178 AccessDeclarationAST *accessDeclaration = 0;
179 for (DeclarationListAST *it = ast->member_specifier_list; it; it = it->next) {
180 if (AccessDeclarationAST *decl = it->value->asAccessDeclaration()) {
181 if (tokenKind(decl->access_specifier_token) == T_PUBLIC)
182 accessDeclaration = decl;
186 if (! accessDeclaration)
187 qDebug() << "no access declaration for class:" << className;
189 Q_ASSERT(accessDeclaration != 0);
191 QTextCursor tc = createCursor(translationUnit(), accessDeclaration, document);
192 tc.setPosition(tc.position());
194 _nodes.endOfPublicClassSpecifiers.append(tc);
202 QTextDocument *document;
207 class Accept0CG: protected ASTVisitor
213 Accept0CG(const QDir &cplusplusDir, TranslationUnit *unit)
214 : ASTVisitor(unit), _cplusplusDir(cplusplusDir), out(0)
217 void operator()(AST *ast)
219 QFileInfo fileInfo(_cplusplusDir, QLatin1String("ASTVisit.cpp"));
221 QFile file(fileInfo.absoluteFilePath());
222 if (! file.open(QFile::WriteOnly))
225 QTextStream output(&file);
228 *out << copyrightHeader << generatedHeader <<
230 "#include \"AST.h\"\n"
231 "#include \"ASTVisitor.h\"\n"
233 "using namespace CPlusPlus;\n" << endl;
239 using ASTVisitor::visit;
241 QMap<QByteArray, ClassSpecifierAST *> classMap;
243 QByteArray id_cast(NameAST *name)
248 const Identifier *id = identifier(name->asSimpleName()->identifier_token);
250 return QByteArray::fromRawData(id->chars(), id->size());
253 void visitMembers(Class *klass)
255 // *out << " // visit " << className.constData() << endl;
256 for (unsigned i = 0; i < klass->memberCount(); ++i) {
257 Symbol *member = klass->memberAt(i);
258 if (! member->name())
261 const Identifier *id = member->name()->identifier();
266 const QByteArray memberName = QByteArray::fromRawData(id->chars(), id->size());
267 if (member->type().isUnsigned() && memberName.endsWith("_token")) {
268 // nothing to do. The member is a token.
270 } else if (PointerType *ptrTy = member->type()->asPointerType()) {
272 if (NamedType *namedTy = ptrTy->elementType()->asNamedType()) {
273 QByteArray typeName = namedTy->name()->identifier()->chars();
275 if (typeName.endsWith("AST") && memberName != "next") {
276 *out << " accept(" << memberName.constData() << ", visitor);" << endl;
282 for (unsigned i = 0; i < klass->baseClassCount(); ++i) {
283 const QByteArray baseClassName = klass->baseClassAt(i)->identifier()->chars();
285 if (ClassSpecifierAST *baseClassSpec = classMap.value(baseClassName, 0)) {
286 visitMembers(baseClassSpec->symbol);
291 bool checkMethod(Symbol *accept0Method) const
293 Declaration *decl = accept0Method->asDeclaration();
297 Function *funTy = decl->type()->asFunctionType();
301 else if (funTy->isPureVirtual())
307 virtual bool visit(ClassSpecifierAST *ast)
309 Class *klass = ast->symbol;
310 const QByteArray className = id_cast(ast->name);
312 const Identifier *visit_id = control()->identifier("accept0");
313 Symbol *accept0Method = klass->find(visit_id);
314 for (; accept0Method; accept0Method = accept0Method->next()) {
315 if (accept0Method->identifier() != visit_id)
318 if (checkMethod(accept0Method))
325 classMap.insert(className, ast);
328 << "void " << className.constData() << "::accept0(ASTVisitor *visitor)" << endl
330 << " if (visitor->visit(this)) {" << endl;
336 << " visitor->endVisit(this);" << endl
344 class Match0CG: protected ASTVisitor
350 Match0CG(const QDir &cplusplusDir, TranslationUnit *unit)
351 : ASTVisitor(unit), _cplusplusDir(cplusplusDir), out(0)
354 void operator()(AST *ast)
356 QFileInfo fileInfo(_cplusplusDir, QLatin1String("ASTMatch0.cpp"));
358 QFile file(fileInfo.absoluteFilePath());
359 if (! file.open(QFile::WriteOnly))
362 QTextStream output(&file);
365 *out << copyrightHeader << generatedHeader <<
367 "#include \"AST.h\"\n"
368 "#include \"ASTMatcher.h\"\n"
370 "using namespace CPlusPlus;\n" << endl;
376 using ASTVisitor::visit;
378 QMap<QByteArray, ClassSpecifierAST *> classMap;
380 QByteArray id_cast(NameAST *name)
385 const Identifier *id = identifier(name->asSimpleName()->identifier_token);
387 return QByteArray::fromRawData(id->chars(), id->size());
390 void visitMembers(Class *klass)
393 const QString className = oo(klass->name());
395 *out << " if (" << className << " *_other = pattern->as" << className.left(className.length() - 3) << "())" << endl;
397 *out << " return matcher->match(this, _other);" << endl;
402 bool checkMethod(Symbol *accept0Method) const
404 Declaration *decl = accept0Method->asDeclaration();
408 Function *funTy = decl->type()->asFunctionType();
412 else if (funTy->isPureVirtual())
418 virtual bool visit(ClassSpecifierAST *ast)
420 Class *klass = ast->symbol;
421 const QByteArray className = id_cast(ast->name);
423 const Identifier *match0_id = control()->identifier("match0");
424 Symbol *accept0Method = klass->find(match0_id);
425 for (; accept0Method; accept0Method = accept0Method->next()) {
426 if (accept0Method->identifier() != match0_id)
429 if (checkMethod(accept0Method))
436 classMap.insert(className, ast);
439 << "bool " << className.constData() << "::match0(AST *pattern, ASTMatcher *matcher)" << endl
445 << " return false;" << endl
454 class MatcherCPPCG: protected ASTVisitor
460 MatcherCPPCG(const QDir &cplusplusDir, TranslationUnit *unit)
461 : ASTVisitor(unit), _cplusplusDir(cplusplusDir), out(0)
464 void operator()(AST *ast)
466 QFileInfo fileInfo(_cplusplusDir, QLatin1String("ASTMatcher.cpp"));
468 QFile file(fileInfo.absoluteFilePath());
469 if (! file.open(QFile::WriteOnly))
472 QTextStream output(&file);
475 *out << copyrightHeader << endl
477 << "#include \"AST.h\"" << endl
478 << "#include \"ASTMatcher.h\"" << endl
480 << "using namespace CPlusPlus;" << endl
482 << "ASTMatcher::ASTMatcher()" << endl
485 << "ASTMatcher::~ASTMatcher()" << endl
493 using ASTVisitor::visit;
495 QMap<QByteArray, ClassSpecifierAST *> classMap;
497 QByteArray id_cast(NameAST *name)
502 const Identifier *id = identifier(name->asSimpleName()->identifier_token);
504 return QByteArray::fromRawData(id->chars(), id->size());
507 void visitMembers(Class *klass)
509 for (unsigned i = 0; i < klass->memberCount(); ++i) {
510 Symbol *member = klass->memberAt(i);
511 if (! member->name())
514 const Identifier *id = member->name()->identifier();
519 const QByteArray memberName = QByteArray::fromRawData(id->chars(), id->size());
520 if (member->type().isUnsigned() && memberName.endsWith("_token")) {
523 << " pattern->" << memberName << " = node->" << memberName << ";" << endl
526 } else if (PointerType *ptrTy = member->type()->asPointerType()) {
528 if (NamedType *namedTy = ptrTy->elementType()->asNamedType()) {
529 QByteArray typeName = namedTy->name()->identifier()->chars();
531 if (typeName.endsWith("AST")) {
533 << " if (! pattern->" << memberName << ")" << endl
534 << " pattern->" << memberName << " = node->" << memberName << ";" << endl
535 << " else if (! AST::match(node->" << memberName << ", pattern->" << memberName << ", this))" << endl
536 << " return false;" << endl
543 for (unsigned i = 0; i < klass->baseClassCount(); ++i) {
544 const QByteArray baseClassName = klass->baseClassAt(i)->identifier()->chars();
546 if (ClassSpecifierAST *baseClassSpec = classMap.value(baseClassName, 0)) {
547 visitMembers(baseClassSpec->symbol);
552 bool checkMethod(Symbol *accept0Method) const
554 Declaration *decl = accept0Method->asDeclaration();
558 Function *funTy = decl->type()->asFunctionType();
562 else if (funTy->isPureVirtual())
568 virtual bool visit(ClassSpecifierAST *ast)
570 Class *klass = ast->symbol;
571 const QByteArray className = id_cast(ast->name);
573 const Identifier *match0_id = control()->identifier("match0");
574 Symbol *match0Method = klass->find(match0_id);
575 for (; match0Method; match0Method = match0Method->next()) {
576 if (match0Method->identifier() != match0_id)
579 if (checkMethod(match0Method))
586 classMap.insert(className, ast);
589 << "bool ASTMatcher::match(" << className.constData() << " *node, " << className.constData() << " *pattern)" << endl
591 << " (void) node;" << endl
592 << " (void) pattern;" << endl
598 << " return true;" << endl
606 class CloneCPPCG: protected ASTVisitor
612 CloneCPPCG(const QDir &cplusplusDir, TranslationUnit *unit)
613 : ASTVisitor(unit), _cplusplusDir(cplusplusDir), out(0)
616 void operator()(AST *ast)
618 QFileInfo fileInfo(_cplusplusDir, QLatin1String("ASTClone.cpp"));
620 QFile file(fileInfo.absoluteFilePath());
621 if (! file.open(QFile::WriteOnly))
624 QTextStream output(&file);
627 *out << copyrightHeader
629 << "#include \"AST.h\"" << endl
630 << "#include \"MemoryPool.h\"" << endl
632 << "using namespace CPlusPlus;" << endl
641 using ASTVisitor::visit;
643 QMap<QByteArray, ClassSpecifierAST *> classMap;
645 QByteArray id_cast(NameAST *name)
650 const Identifier *id = identifier(name->asSimpleName()->identifier_token);
652 return QByteArray::fromRawData(id->chars(), id->size());
655 void visitMembers(Class *klass)
657 for (unsigned i = 0; i < klass->memberCount(); ++i) {
658 Symbol *member = klass->memberAt(i);
659 if (! member->name())
662 const Identifier *id = member->name()->identifier();
667 const QByteArray memberName = QByteArray::fromRawData(id->chars(), id->size());
668 if (member->type().isUnsigned() && memberName.endsWith("_token")) {
669 *out << " ast->" << memberName << " = " << memberName << ";" << endl;
670 } else if (PointerType *ptrTy = member->type()->asPointerType()) {
671 if (NamedType *namedTy = ptrTy->elementType()->asNamedType()) {
672 QByteArray typeName = namedTy->name()->identifier()->chars();
674 if (typeName.endsWith("ListAST")) {
675 *out << " for (" << typeName << " *iter = " << memberName << ", **ast_iter = &ast->" << memberName << ";" << endl
676 << " iter; iter = iter->next, ast_iter = &(*ast_iter)->next)" << endl
677 << " *ast_iter = new (pool) " << typeName << "((iter->value) ? iter->value->clone(pool) : 0);" << endl;
678 } else if (typeName.endsWith("AST")) {
679 *out << " if (" << memberName << ")" << endl
680 << " ast->" << memberName << " = " << memberName << "->clone(pool);" << endl;
686 for (unsigned i = 0; i < klass->baseClassCount(); ++i) {
687 const QByteArray baseClassName = klass->baseClassAt(i)->identifier()->chars();
689 if (ClassSpecifierAST *baseClassSpec = classMap.value(baseClassName, 0)) {
690 visitMembers(baseClassSpec->symbol);
695 bool checkMethod(Symbol *cloneMethod) const
697 Declaration *decl = cloneMethod->asDeclaration();
701 Function *funTy = decl->type()->asFunctionType();
705 else if (funTy->isPureVirtual())
711 virtual bool visit(ClassSpecifierAST *ast)
713 Class *klass = ast->symbol;
714 const QByteArray className = id_cast(ast->name);
715 if (! className.endsWith("AST"))
718 const Identifier *clone_id = control()->identifier("clone");
719 Symbol *cloneMethod = klass->find(clone_id);
720 for (; cloneMethod; cloneMethod = cloneMethod->next()) {
721 if (cloneMethod->identifier() != clone_id)
724 if (checkMethod(cloneMethod))
731 classMap.insert(className, ast);
733 *out << className.constData() << " *" << className.constData() << "::" << "clone(MemoryPool *pool) const" << endl
735 << " " << className.constData() << " *ast = new (pool) " << className.constData() << ";" << endl;
739 *out << " return ast;" << endl
740 << "}" << endl << endl;
746 class GenerateDumpers: protected ASTVisitor
751 GenerateDumpers(QFile *file, TranslationUnit *unit)
752 : ASTVisitor(unit), out(file)
755 static void go(const QString &fileName, TranslationUnit *unit)
757 QFile file(fileName);
758 if (! file.open(QFile::WriteOnly)) {
759 std::cerr << "Cannot open dumpers file." << std::endl;
763 GenerateDumpers d(&file, unit);
764 d.out << copyrightHeader
769 d.accept(unit->ast());
775 using ASTVisitor::visit;
777 QMap<QByteArray, ClassSpecifierAST *> classMap;
779 QByteArray id_cast(NameAST *name)
784 const Identifier *id = identifier(name->asSimpleName()->identifier_token);
786 return QByteArray::fromRawData(id->chars(), id->size());
789 void visitMembers(Class *klass)
791 for (unsigned i = 0; i < klass->memberCount(); ++i) {
792 Symbol *member = klass->memberAt(i);
793 if (! member->name())
796 const Identifier *id = member->name()->identifier();
801 const QByteArray memberName = QByteArray::fromRawData(id->chars(), id->size());
802 if (member->type().isUnsigned() && memberName.endsWith("_token")) {
803 out << " if (ast->" << memberName << ")" << endl;
804 out << " terminal(ast->" << memberName << ", ast);" << endl;
805 } else if (PointerType *ptrTy = member->type()->asPointerType()) {
806 if (NamedType *namedTy = ptrTy->elementType()->asNamedType()) {
807 QByteArray typeName = namedTy->name()->identifier()->chars();
809 if (typeName.endsWith("ListAST")) {
810 out << " for (" << typeName << " *iter = ast->" << memberName << "; iter; iter = iter->next)" << endl
811 << " nonterminal(iter->value);" << endl;
812 } else if (typeName.endsWith("AST")) {
813 out << " nonterminal(ast->" << memberName << ");" << endl;
819 for (unsigned i = 0; i < klass->baseClassCount(); ++i) {
820 const QByteArray baseClassName = klass->baseClassAt(i)->identifier()->chars();
822 if (ClassSpecifierAST *baseClassSpec = classMap.value(baseClassName, 0)) {
823 visitMembers(baseClassSpec->symbol);
828 bool checkMethod(Symbol *cloneMethod) const
830 Declaration *decl = cloneMethod->asDeclaration();
834 Function *funTy = decl->type()->asFunctionType();
838 else if (funTy->isPureVirtual())
844 virtual bool visit(ClassSpecifierAST *ast)
846 Class *klass = ast->symbol;
847 const QByteArray className = id_cast(ast->name);
848 if (! className.endsWith("AST"))
851 const Identifier *clone_id = control()->identifier("clone");
852 Symbol *cloneMethod = klass->find(clone_id);
853 for (; cloneMethod; cloneMethod = cloneMethod->next()) {
854 if (cloneMethod->identifier() != clone_id)
857 if (checkMethod(cloneMethod))
864 classMap.insert(className, ast);
866 out << "virtual bool visit(" << className.constData() << " *ast)" << endl
871 out << " return false;" << endl
872 << "}" << endl << endl;
878 class RemoveCastMethods: protected ASTVisitor
881 RemoveCastMethods(Document::Ptr doc, QTextDocument *document)
882 : ASTVisitor(doc->translationUnit()), document(document) {}
884 QList<QTextCursor> operator()(AST *ast)
892 virtual bool visit(FunctionDefinitionAST *ast)
894 Function *fun = ast->symbol;
895 const QString functionName = oo(fun->name());
897 if (functionName.length() > 3 && functionName.startsWith(QLatin1String("as"))
898 && functionName.at(2).isUpper()) {
900 QTextCursor tc = createCursor(translationUnit(), ast, document);
902 //qDebug() << qPrintable(tc.selectedText());
910 QTextDocument *document;
911 QList<QTextCursor> _cursors;
915 static QList<QTextCursor> removeConstructors(ClassSpecifierAST *classAST,
916 TranslationUnit *translationUnit,
917 QTextDocument *document)
920 QList<QTextCursor> cursors;
921 const QString className = oo(classAST->symbol->name());
923 for (DeclarationListAST *iter = classAST->member_specifier_list; iter; iter = iter->next) {
924 if (FunctionDefinitionAST *funDef = iter->value->asFunctionDefinition()) {
925 if (oo(funDef->symbol->name()) == className) {
927 QTextCursor tc = createCursor(translationUnit, funDef, document);
928 tc.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
929 tc.setPosition(tc.position() + 1, QTextCursor::KeepAnchor);
938 static QStringList collectFieldNames(ClassSpecifierAST *classAST, bool onlyTokensAndASTNodes)
942 Class *clazz = classAST->symbol;
943 for (unsigned i = 0; i < clazz->memberCount(); ++i) {
944 Symbol *s = clazz->memberAt(i);
945 if (Declaration *decl = s->asDeclaration()) {
946 const QString declName = oo(decl->name());
947 const FullySpecifiedType ty = decl->type();
948 if (const PointerType *ptrTy = ty->asPointerType()) {
949 if (onlyTokensAndASTNodes) {
950 if (const NamedType *namedTy = ptrTy->elementType()->asNamedType()) {
951 if (oo(namedTy->name()).endsWith(QLatin1String("AST")))
952 fields.append(declName);
955 fields.append(declName);
957 } else if (ty.isUnsigned()) {
958 fields.append(declName);
965 static QString createConstructor(ClassSpecifierAST *classAST)
968 Class *clazz = classAST->symbol;
970 QString result(QLatin1String(" "));
971 result.append(oo(clazz->name()));
972 result.append(QLatin1String("()\n"));
974 QStringList classFields = collectFieldNames(classAST, false);
975 for (int i = 0; i < classFields.size(); ++i) {
977 result.append(QLatin1String(" : "));
978 result.append(classFields.at(i));
979 result.append(QLatin1String("(0)\n"));
981 result.append(QLatin1String(" , "));
982 result.append(classFields.at(i));
983 result.append(QLatin1String("(0)\n"));
987 result.append(QLatin1String(" {}\n"));
991 bool checkGenerated(const QTextCursor &cursor, int *doxyStart)
993 BackwardsScanner tokens(cursor, 10, QString(), false);
994 Token prevToken = tokens.LA(1);
995 if (prevToken.kind() != T_DOXY_COMMENT && prevToken.kind() != T_CPP_DOXY_COMMENT)
998 *doxyStart = tokens.startPosition() + prevToken.begin();
1000 return tokens.text(tokens.startToken() - 1).contains(QLatin1String("\\generated"));
1013 ClassSpecifierAST *classAST;
1021 void generateFirstToken(QTextStream &os, const QString &className, const QStringList &fields)
1023 os << "unsigned "<< className << "::firstToken() const" << endl
1026 foreach (const QString &field, fields) {
1027 os << " if (" << field << ")" << endl;
1029 if (field.endsWith(QLatin1String("_token"))) {
1030 os << " return " << field << ";" << endl;
1032 os << " if (unsigned candidate = " << field << "->firstToken())" << endl;
1033 os << " return candidate;" << endl;
1037 os << " return 0;" << endl;
1038 os << "}" << endl << endl;
1041 void generateLastToken(QTextStream &os, const QString &className, const QStringList &fields)
1043 os << "unsigned "<< className << "::lastToken() const" << endl
1046 for (int i = fields.size() - 1; i >= 0; --i) {
1047 const QString field = fields.at(i);
1049 os << " if (" << field << ")" << endl;
1051 if (field.endsWith(QLatin1String("_token"))) {
1052 os << " return " << field << " + 1;" << endl;
1054 os << " if (unsigned candidate = " << field << "->lastToken())" << endl;
1055 os << " return candidate;" << endl;
1059 os << " return 1;" << endl;
1060 os << "}" << endl << endl;
1063 void generateAST_cpp(const Snapshot &snapshot, const QDir &cplusplusDir)
1065 QFileInfo fileAST_cpp(cplusplusDir, QLatin1String("AST.cpp"));
1066 Q_ASSERT(fileAST_cpp.exists());
1068 const QString fileName = fileAST_cpp.absoluteFilePath();
1070 QFile file(fileName);
1071 if (! file.open(QFile::ReadOnly)) {
1072 std::cerr << "Cannot open " << fileName.toLatin1().data() << std::endl;
1076 const QString source = QTextStream(&file).readAll();
1079 QTextDocument cpp_document;
1080 cpp_document.setPlainText(source);
1082 Document::Ptr AST_cpp_document = Document::create(fileName);
1083 const QByteArray preprocessedCode = snapshot.preprocessedCode(source, fileName);
1084 AST_cpp_document->setSource(preprocessedCode);
1085 AST_cpp_document->check();
1088 QMap<QString, ClassSpecifierAST *> classesNeedingFirstToken;
1089 QMap<QString, ClassSpecifierAST *> classesNeedingLastToken;
1091 // find all classes with method declarations for firstToken/lastToken
1092 foreach (ClassSpecifierAST *classAST, astNodes.deriveds) {
1093 const QString className = oo(classAST->symbol->name());
1094 if (className.isEmpty())
1097 for (DeclarationListAST *declIter = classAST->member_specifier_list; declIter; declIter = declIter->next) {
1098 if (SimpleDeclarationAST *decl = declIter->value->asSimpleDeclaration()) {
1099 if (decl->symbols && decl->symbols->value) {
1100 if (decl->symbols->next)
1101 std::cerr << "Found simple declaration with multiple symbols in " << className.toLatin1().data() << std::endl;
1103 Symbol *s = decl->symbols->value;
1104 const QString funName = oo(s->name());
1105 if (funName == QLatin1String("firstToken")) {
1107 classesNeedingFirstToken.insert(className, classAST);
1108 } else if (funName == QLatin1String("lastToken")) {
1110 classesNeedingLastToken.insert(className, classAST);
1117 QList<GenInfo> todo;
1119 TranslationUnitAST *xUnit = AST_cpp_document->translationUnit()->ast()->asTranslationUnit();
1120 for (DeclarationListAST *iter = xUnit->declaration_list; iter; iter = iter->next) {
1121 if (FunctionDefinitionAST *funDef = iter->value->asFunctionDefinition()) {
1122 if (const QualifiedNameId *qName = funDef->symbol->name()->asQualifiedNameId()) {
1123 const QString className = oo(qName->base());
1124 const QString methodName = oo(qName->name());
1126 QTextCursor cursor(&cpp_document);
1128 unsigned line = 0, column = 0;
1129 AST_cpp_document->translationUnit()->getTokenStartPosition(funDef->firstToken(), &line, &column);
1130 const int start = cpp_document.findBlockByNumber(line - 1).position() + column - 1;
1131 cursor.setPosition(start);
1132 int doxyStart = start;
1134 const bool isGenerated = checkGenerated(cursor, &doxyStart);
1136 AST_cpp_document->translationUnit()->getTokenEndPosition(funDef->lastToken() - 1, &line, &column);
1137 int end = cpp_document.findBlockByNumber(line - 1).position() + column - 1;
1138 while (cpp_document.characterAt(end).isSpace())
1141 if (methodName == QLatin1String("firstToken")) {
1142 ClassSpecifierAST *classAST = classesNeedingFirstToken.value(className, 0);
1146 info.classAST = classAST;
1147 info.firstToken = true;
1149 classesNeedingFirstToken.remove(className);
1151 info.start = doxyStart;
1156 } else if (methodName == QLatin1String("lastToken")) {
1157 ClassSpecifierAST *classAST = classesNeedingLastToken.value(className, 0);
1161 info.classAST = classAST;
1163 info.lastToken = true;
1164 classesNeedingLastToken.remove(className);
1166 info.start = doxyStart;
1176 const int documentEnd = cpp_document.lastBlock().position() + cpp_document.lastBlock().length() - 1;
1178 Utils::ChangeSet changes;
1179 foreach (GenInfo info, todo) {
1180 if (info.end > documentEnd)
1181 info.end = documentEnd;
1184 changes.remove(info.start, info.end);
1190 const QString className = oo(info.classAST->symbol->name());
1193 QTextStream os(&method);
1194 const QStringList fields = collectFieldNames(info.classAST, true);
1196 if (info.firstToken) {
1197 generateFirstToken(os, className, fields);
1198 } else if (info.lastToken) {
1199 generateLastToken(os, className, fields);
1202 changes.replace(info.start, info.end, method);
1205 QTextCursor tc(&cpp_document);
1209 QTextStream os(&newMethods);
1210 foreach (const QString &className, classesNeedingFirstToken.keys()) {
1211 const QStringList fields = collectFieldNames(classesNeedingFirstToken.value(className), true);
1212 os << "/** \\generated */" << endl;
1213 generateFirstToken(os, className, fields);
1214 if (ClassSpecifierAST *classAST = classesNeedingLastToken.value(className, 0)) {
1215 const QStringList fields = collectFieldNames(classAST, true);
1216 os << "/** \\generated */" << endl;
1217 generateLastToken(os, className, fields);
1218 classesNeedingLastToken.remove(className);
1221 foreach (const QString &className, classesNeedingLastToken.keys()) {
1222 const QStringList fields = collectFieldNames(classesNeedingLastToken.value(className), true);
1223 os << "/** \\generated */" << endl;
1224 generateLastToken(os, className, fields);
1226 tc.setPosition(documentEnd);
1227 tc.insertText(newMethods);
1229 if (file.open(QFile::WriteOnly)) {
1230 QTextStream out(&file);
1231 out << cpp_document.toPlainText();
1235 QStringList generateAST_H(const Snapshot &snapshot, const QDir &cplusplusDir, const QString &dumpersFile)
1237 QStringList astDerivedClasses;
1239 QFileInfo fileAST_h(cplusplusDir, QLatin1String("AST.h"));
1240 Q_ASSERT(fileAST_h.exists());
1242 const QString fileName = fileAST_h.absoluteFilePath();
1244 QFile file(fileName);
1245 if (! file.open(QFile::ReadOnly))
1246 return astDerivedClasses;
1248 const QString source = QTextStream(&file).readAll();
1251 QTextDocument document;
1252 document.setPlainText(source);
1254 AST_h_document = Document::create(fileName);
1255 const QByteArray preprocessedCode = snapshot.preprocessedCode(source, fileName);
1256 AST_h_document->setSource(preprocessedCode);
1257 AST_h_document->check();
1259 FindASTNodes process(AST_h_document, &document);
1260 astNodes = process(AST_h_document->translationUnit()->ast());
1262 RemoveCastMethods removeCastMethods(AST_h_document, &document);
1264 QList<QTextCursor> baseCastMethodCursors = removeCastMethods(astNodes.base);
1265 QMap<ClassSpecifierAST *, QList<QTextCursor> > cursors;
1266 QMap<ClassSpecifierAST *, QString> replacementCastMethods;
1267 QMap<ClassSpecifierAST *, QList<QTextCursor> > constructors;
1268 QMap<ClassSpecifierAST *, QString> replacementConstructors;
1272 QStringList castMethods;
1273 foreach (ClassSpecifierAST *classAST, astNodes.deriveds) {
1274 cursors[classAST] = removeCastMethods(classAST);
1275 const QString className = oo(classAST->symbol->name());
1276 const QString methodName = QLatin1String("as") + className.mid(0, className.length() - 3);
1277 replacementCastMethods[classAST] = QString(" virtual %1 *%2() { return this; }\n").arg(className, methodName);
1278 castMethods.append(QString(" virtual %1 *%2() { return 0; }\n").arg(className, methodName));
1279 astDerivedClasses.append(className);
1281 constructors[classAST] = removeConstructors(classAST, AST_h_document->translationUnit(), &document);
1282 replacementConstructors[classAST] = createConstructor(classAST);
1285 if (! baseCastMethodCursors.isEmpty()) {
1287 for (int i = 0; i < baseCastMethodCursors.length(); ++i) {
1288 baseCastMethodCursors[i].removeSelectedText();
1291 baseCastMethodCursors.first().insertText(castMethods.join(QLatin1String("")));
1294 for (int classIndex = 0; classIndex < astNodes.deriveds.size(); ++classIndex) {
1295 ClassSpecifierAST *classAST = astNodes.deriveds.at(classIndex);
1297 { // remove the cast methods.
1298 QList<QTextCursor> c = cursors.value(classAST);
1299 for (int i = 0; i < c.length(); ++i) {
1300 c[i].removeSelectedText();
1303 { // remove the constructors.
1304 QList<QTextCursor> c = constructors.value(classAST);
1305 for (int i = 0; i < c.length(); ++i) {
1306 c[i].removeSelectedText();
1310 astNodes.endOfPublicClassSpecifiers[classIndex].insertText(
1311 replacementConstructors.value(classAST) +
1312 QLatin1String("\n") +
1313 replacementCastMethods.value(classAST));
1316 if (file.open(QFile::WriteOnly)) {
1317 QTextStream out(&file);
1318 out << document.toPlainText();
1321 Accept0CG cg(cplusplusDir, AST_h_document->translationUnit());
1322 cg(AST_h_document->translationUnit()->ast());
1324 Match0CG cg2(cplusplusDir, AST_h_document->translationUnit());
1325 cg2(AST_h_document->translationUnit()->ast());
1327 MatcherCPPCG cg3(cplusplusDir, AST_h_document->translationUnit());
1328 cg3(AST_h_document->translationUnit()->ast());
1330 CloneCPPCG cg4(cplusplusDir, AST_h_document->translationUnit());
1331 cg4(AST_h_document->translationUnit()->ast());
1333 generateAST_cpp(snapshot, cplusplusDir);
1335 if (!dumpersFile.isEmpty())
1336 GenerateDumpers::go(dumpersFile, AST_h_document->translationUnit());
1338 return astDerivedClasses;
1341 class FindASTForwards: protected ASTVisitor
1344 FindASTForwards(Document::Ptr doc, QTextDocument *document)
1345 : ASTVisitor(doc->translationUnit()), document(document)
1348 QList<QTextCursor> operator()(AST *ast)
1355 bool visit(SimpleDeclarationAST *ast)
1357 if (! ast->decl_specifier_list)
1360 if (ElaboratedTypeSpecifierAST *e = ast->decl_specifier_list->value->asElaboratedTypeSpecifier()) {
1361 if (tokenKind(e->classkey_token) == T_CLASS && !ast->declarator_list) {
1362 QString className = oo(e->name->name);
1364 if (className.length() > 3 && className.endsWith(QLatin1String("AST"))) {
1365 QTextCursor tc = createCursor(translationUnit(), ast, document);
1366 _cursors.append(tc);
1375 QTextDocument *document;
1376 QList<QTextCursor> _cursors;
1380 void generateASTFwd_h(const Snapshot &snapshot, const QDir &cplusplusDir, const QStringList &astDerivedClasses)
1382 QFileInfo fileASTFwd_h(cplusplusDir, QLatin1String("ASTfwd.h"));
1383 Q_ASSERT(fileASTFwd_h.exists());
1385 const QString fileName = fileASTFwd_h.absoluteFilePath();
1387 QFile file(fileName);
1388 if (! file.open(QFile::ReadOnly))
1391 const QString source = QTextStream(&file).readAll();
1394 QTextDocument document;
1395 document.setPlainText(source);
1397 Document::Ptr doc = Document::create(fileName);
1398 const QByteArray preprocessedCode = snapshot.preprocessedCode(source, fileName);
1399 doc->setSource(preprocessedCode);
1402 FindASTForwards process(doc, &document);
1403 QList<QTextCursor> cursors = process(doc->translationUnit()->ast());
1405 for (int i = 0; i < cursors.length(); ++i)
1406 cursors[i].removeSelectedText();
1408 QString replacement;
1409 foreach (const QString &astDerivedClass, astDerivedClasses) {
1410 replacement += QString(QLatin1String("class %1;\n")).arg(astDerivedClass);
1413 cursors.first().insertText(replacement);
1415 if (file.open(QFile::WriteOnly)) {
1416 QTextStream out(&file);
1417 out << document.toPlainText();
1421 void generateASTPatternBuilder_h(const QDir &cplusplusDir)
1423 QFileInfo fileInfo(cplusplusDir, QLatin1String("ASTPatternBuilder.h"));
1424 QFile file(fileInfo.absoluteFilePath());
1425 if (! file.open(QFile::WriteOnly))
1429 QTextStream out(&file);
1434 << "#ifndef CPLUSPLUS_AST_PATTERN_BUILDER_H" << endl
1435 << "#define CPLUSPLUS_AST_PATTERN_BUILDER_H" << endl
1437 << "#include \"CPlusPlusForwardDeclarations.h\"" << endl
1438 << "#include \"AST.h\"" << endl
1439 << "#include \"MemoryPool.h\"" << endl
1441 << "namespace CPlusPlus {" << endl
1443 << "class CPLUSPLUS_EXPORT ASTPatternBuilder" << endl
1445 << " MemoryPool pool;" << endl
1447 << "public:" << endl
1448 << " ASTPatternBuilder() {}" << endl
1450 << " void reset() { pool.reset(); }" << endl
1453 Control *control = AST_h_document->control();
1454 QSet<QString> listClasses;
1456 foreach (ClassSpecifierAST *classNode, astNodes.deriveds) {
1457 Class *klass = classNode->symbol;
1459 const Identifier *match0_id = control->identifier("match0");
1460 Symbol *match0Method = klass->find(match0_id);
1461 for (; match0Method; match0Method = match0Method->next()) {
1462 if (match0Method->identifier() != match0_id)
1470 const QString className = oo(klass->name());
1472 if (! className.endsWith("AST"))
1475 const QString methodName = className.left(className.length() - 3);
1478 << " " << className << " *" << methodName << "(";
1480 QList<QPair<QString, QString> > args;
1483 for (unsigned index = 0; index < klass->memberCount(); ++index) {
1484 Declaration *member = klass->memberAt(index)->asDeclaration();
1488 PointerType *ptrTy = member->type()->asPointerType();
1492 const QString tyName = oo(ptrTy->elementType());
1493 if (tyName.endsWith("ListAST"))
1494 listClasses.insert(tyName);
1495 if (tyName.endsWith("AST")) {
1499 const QString memberName = oo(member->name());
1501 out << tyName << " *" << memberName << " = 0";
1502 args.append(qMakePair(tyName, memberName));
1510 << " " << className << " *__ast = new (&pool) " << className << ";" << endl;
1513 QPair<QString, QString> p;
1516 << " __ast->" << p.second << " = " << p.second << ";" << endl;
1520 << " return __ast;" << endl
1525 foreach (const QString &className, listClasses) {
1526 const QString methodName = className.left(className.length() - 3);
1527 const QString elementName = className.left(className.length() - 7) + QLatin1String("AST");
1529 << " " << className << " *" << methodName << "("
1530 << elementName << " *value, " << className << " *next = 0)" << endl
1532 << " " << className << " *__list = new (&pool) " << className << ";" << endl
1533 << " __list->next = next;" << endl
1534 << " __list->value = value;" << endl
1535 << " return __list;" << endl
1543 << "} // end of namespace CPlusPlus" << endl
1545 << "#endif // CPLUSPLUS_AST_PATTERN_BUILDER_H" << endl;
1548 int main(int argc, char *argv[])
1550 QCoreApplication app(argc, argv);
1551 QStringList files = app.arguments();
1552 files.removeFirst();
1554 if (files.size() != 1 && files.size() != 2) {
1555 std::cerr << "Usage: cplusplus [path to C++ front-end]" << std::endl;
1556 std::cerr << " or: cplusplus [path to C++ front-end] [dumpers file name]" << std::endl;
1557 return EXIT_FAILURE;
1560 QDir cplusplusDir(files.first());
1561 if (!QFileInfo(cplusplusDir, QLatin1String("AST.h")).exists()) {
1562 std::cerr << "Cannot find AST.h in " << qPrintable(cplusplusDir.absolutePath())
1564 return EXIT_FAILURE;
1567 QString dumpersFile;
1568 if (files.size() == 2)
1569 dumpersFile = files.last();
1572 QStringList astDerivedClasses = generateAST_H(snapshot, cplusplusDir, dumpersFile);
1573 astDerivedClasses.sort();
1574 generateASTFwd_h(snapshot, cplusplusDir, astDerivedClasses);
1576 generateASTPatternBuilder_h(cplusplusDir);