Files
qt-creator/src/tools/cplusplus/generate-ast.cpp
Roberto Raggi fac625cf48 Compile.
2010-08-12 13:38:07 +02:00

1553 lines
48 KiB
C++

/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** 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.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/
#include <QCoreApplication>
#include <QStringList>
#include <QTextDocument>
#include <QTextCursor>
#include <QTextBlock>
#include <QDir>
#include <QDebug>
#include <Control.h>
#include <Parser.h>
#include <AST.h>
#include <ASTVisitor.h>
#include <Symbols.h>
#include <CoreTypes.h>
#include <Literals.h>
#include <CppDocument.h>
#include <Overview.h>
#include <Names.h>
#include <Scope.h>
#include <BackwardsScanner.h>
#include <utils/changeset.h>
#include <iostream>
#include <cstdlib>
using namespace CPlusPlus;
static const char copyrightHeader[] =
"/**************************************************************************\n"
"**\n"
"** This file is part of Qt Creator\n"
"**\n"
"** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).\n"
"**\n"
"** Contact: Nokia Corporation (qt-info@nokia.com)\n"
"**\n"
"** Commercial Usage\n"
"**\n"
"** Licensees holding valid Qt Commercial licenses may use this file in\n"
"** accordance with the Qt Commercial License Agreement provided with the\n"
"** Software or, alternatively, in accordance with the terms contained in\n"
"** a written agreement between you and Nokia.\n"
"**\n"
"** GNU Lesser General Public License Usage\n"
"**\n"
"** Alternatively, this file may be used under the terms of the GNU Lesser\n"
"** General Public License version 2.1 as published by the Free Software\n"
"** Foundation and appearing in the file LICENSE.LGPL included in the\n"
"** packaging of this file. Please review the following information to\n"
"** ensure the GNU Lesser General Public License version 2.1 requirements\n"
"** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.\n"
"**\n"
"** If you are unsure which license is appropriate for your use, please\n"
"** contact the sales department at http://qt.nokia.com/contact.\n"
"**\n"
"**************************************************************************/\n"
;
static const char generatedHeader[] =
"\n"
"//\n"
"// W A R N I N G\n"
"// -------------\n"
"//\n"
"// This file is automatically generated.\n"
"// Changes will be lost.\n"
"//\n"
"\n"
;
class ASTNodes
{
public:
ASTNodes(): base(0) {}
ClassSpecifierAST *base; // points to "class AST"
QList<ClassSpecifierAST *> deriveds; // n where n extends AST
QList<QTextCursor> endOfPublicClassSpecifiers;
};
static Document::Ptr AST_h_document;
static ASTNodes astNodes;
static QTextCursor createCursor(TranslationUnit *unit, AST *ast, QTextDocument *document)
{
unsigned startLine, startColumn, endLine, endColumn;
unit->getTokenStartPosition(ast->firstToken(), &startLine, &startColumn);
unit->getTokenEndPosition(ast->lastToken() - 1, &endLine, &endColumn);
QTextCursor tc(document);
tc.setPosition(document->findBlockByNumber(startLine - 1).position());
tc.setPosition(document->findBlockByNumber(endLine - 1).position() + endColumn - 1,
QTextCursor::KeepAnchor);
int charsToSkip = 0;
forever {
QChar ch = document->characterAt(tc.position() + charsToSkip);
if (! ch.isSpace())
break;
++charsToSkip;
if (ch == QChar::ParagraphSeparator)
break;
}
tc.setPosition(tc.position() + charsToSkip, QTextCursor::KeepAnchor);
return tc;
}
class FindASTNodes: protected ASTVisitor
{
public:
FindASTNodes(Document::Ptr doc, QTextDocument *document)
: ASTVisitor(doc->translationUnit()), document(document)
{
}
ASTNodes operator()(AST *ast)
{
accept(ast);
return _nodes;
}
protected:
virtual bool visit(ClassSpecifierAST *ast)
{
Class *klass = ast->symbol;
Q_ASSERT(klass != 0);
const QString className = oo(klass->name());
if (className.endsWith("AST")) {
if (className == QLatin1String("AST"))
_nodes.base = ast;
else {
_nodes.deriveds.append(ast);
AccessDeclarationAST *accessDeclaration = 0;
for (DeclarationListAST *it = ast->member_specifier_list; it; it = it->next) {
if (AccessDeclarationAST *decl = it->value->asAccessDeclaration()) {
if (tokenKind(decl->access_specifier_token) == T_PUBLIC)
accessDeclaration = decl;
}
}
if (! accessDeclaration)
qDebug() << "no access declaration for class:" << className;
Q_ASSERT(accessDeclaration != 0);
QTextCursor tc = createCursor(translationUnit(), accessDeclaration, document);
tc.setPosition(tc.position());
_nodes.endOfPublicClassSpecifiers.append(tc);
}
}
return true;
}
private:
QTextDocument *document;
ASTNodes _nodes;
Overview oo;
};
class Accept0CG: protected ASTVisitor
{
QDir _cplusplusDir;
QTextStream *out;
public:
Accept0CG(const QDir &cplusplusDir, TranslationUnit *unit)
: ASTVisitor(unit), _cplusplusDir(cplusplusDir), out(0)
{ }
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);
}
protected:
using ASTVisitor::visit;
QMap<QByteArray, ClassSpecifierAST *> classMap;
QByteArray id_cast(NameAST *name)
{
if (! name)
return QByteArray();
const Identifier *id = identifier(name->asSimpleName()->identifier_token);
return QByteArray::fromRawData(id->chars(), id->size());
}
void visitMembers(Class *klass)
{
// *out << " // visit " << className.constData() << endl;
for (unsigned i = 0; i < klass->memberCount(); ++i) {
Symbol *member = klass->memberAt(i);
if (! member->name())
continue;
const Identifier *id = member->name()->identifier();
if (! id)
continue;
const QByteArray memberName = QByteArray::fromRawData(id->chars(), id->size());
if (member->type().isUnsigned() && memberName.endsWith("_token")) {
// nothing to do. The member is a token.
} else if (PointerType *ptrTy = member->type()->asPointerType()) {
if (NamedType *namedTy = ptrTy->elementType()->asNamedType()) {
QByteArray typeName = namedTy->name()->identifier()->chars();
if (typeName.endsWith("AST") && memberName != "next") {
*out << " accept(" << memberName.constData() << ", visitor);" << endl;
}
}
}
}
for (unsigned i = 0; i < klass->baseClassCount(); ++i) {
const QByteArray baseClassName = klass->baseClassAt(i)->identifier()->chars();
if (ClassSpecifierAST *baseClassSpec = classMap.value(baseClassName, 0)) {
visitMembers(baseClassSpec->symbol);
}
}
}
bool checkMethod(Symbol *accept0Method) const
{
Declaration *decl = accept0Method->asDeclaration();
if (! decl)
return false;
Function *funTy = decl->type()->asFunctionType();
if (! funTy)
return false;
else if (funTy->isPureVirtual())
return false;
return true;
}
virtual bool visit(ClassSpecifierAST *ast)
{
Class *klass = ast->symbol;
const QByteArray className = id_cast(ast->name);
const Identifier *visit_id = control()->identifier("accept0");
Symbol *accept0Method = klass->find(visit_id);
for (; accept0Method; accept0Method = accept0Method->next()) {
if (accept0Method->identifier() != visit_id)
continue;
if (checkMethod(accept0Method))
break;
}
if (! accept0Method)
return true;
classMap.insert(className, ast);
*out
<< "void " << className.constData() << "::accept0(ASTVisitor *visitor)" << endl
<< "{" << endl
<< " if (visitor->visit(this)) {" << endl;
visitMembers(klass);
*out
<< " }" << endl
<< " visitor->endVisit(this);" << endl
<< "}" << endl
<< endl;
return true;
}
};
class Match0CG: protected ASTVisitor
{
QDir _cplusplusDir;
QTextStream *out;
public:
Match0CG(const QDir &cplusplusDir, TranslationUnit *unit)
: ASTVisitor(unit), _cplusplusDir(cplusplusDir), out(0)
{ }
void operator()(AST *ast)
{
QFileInfo fileInfo(_cplusplusDir, QLatin1String("ASTMatch0.cpp"));
QFile file(fileInfo.absoluteFilePath());
if (! file.open(QFile::WriteOnly))
return;
QTextStream output(&file);
out = &output;
*out << copyrightHeader << generatedHeader <<
"\n"
"#include \"AST.h\"\n"
"#include \"ASTMatcher.h\"\n"
"\n"
"using namespace CPlusPlus;\n" << endl;
accept(ast);
}
protected:
using ASTVisitor::visit;
QMap<QByteArray, ClassSpecifierAST *> classMap;
QByteArray id_cast(NameAST *name)
{
if (! name)
return QByteArray();
const Identifier *id = identifier(name->asSimpleName()->identifier_token);
return QByteArray::fromRawData(id->chars(), id->size());
}
void visitMembers(Class *klass)
{
Overview oo;
const QString className = oo(klass->name());
*out << " if (" << className << " *_other = pattern->as" << className.left(className.length() - 3) << "())" << endl;
*out << " return matcher->match(this, _other);" << endl;
*out << endl;
}
bool checkMethod(Symbol *accept0Method) const
{
Declaration *decl = accept0Method->asDeclaration();
if (! decl)
return false;
Function *funTy = decl->type()->asFunctionType();
if (! funTy)
return false;
else if (funTy->isPureVirtual())
return false;
return true;
}
virtual bool visit(ClassSpecifierAST *ast)
{
Class *klass = ast->symbol;
const QByteArray className = id_cast(ast->name);
const Identifier *match0_id = control()->identifier("match0");
Symbol *accept0Method = klass->find(match0_id);
for (; accept0Method; accept0Method = accept0Method->next()) {
if (accept0Method->identifier() != match0_id)
continue;
if (checkMethod(accept0Method))
break;
}
if (! accept0Method)
return true;
classMap.insert(className, ast);
*out
<< "bool " << className.constData() << "::match0(AST *pattern, ASTMatcher *matcher)" << endl
<< "{" << endl;
visitMembers(klass);
*out
<< " return false;" << endl
<< "}" << endl
<< endl;
return true;
}
};
class MatcherCPPCG: protected ASTVisitor
{
QDir _cplusplusDir;
QTextStream *out;
public:
MatcherCPPCG(const QDir &cplusplusDir, TranslationUnit *unit)
: ASTVisitor(unit), _cplusplusDir(cplusplusDir), out(0)
{ }
void operator()(AST *ast)
{
QFileInfo fileInfo(_cplusplusDir, QLatin1String("ASTMatcher.cpp"));
QFile file(fileInfo.absoluteFilePath());
if (! file.open(QFile::WriteOnly))
return;
QTextStream output(&file);
out = &output;
*out << copyrightHeader << endl
<< generatedHeader
<< "#include \"AST.h\"" << endl
<< "#include \"ASTMatcher.h\"" << endl
<< endl
<< "using namespace CPlusPlus;" << endl
<< endl
<< "ASTMatcher::ASTMatcher()" << endl
<< "{ }" << endl
<< endl
<< "ASTMatcher::~ASTMatcher()" << endl
<< "{ }" << endl
<< endl;
accept(ast);
}
protected:
using ASTVisitor::visit;
QMap<QByteArray, ClassSpecifierAST *> classMap;
QByteArray id_cast(NameAST *name)
{
if (! name)
return QByteArray();
const Identifier *id = identifier(name->asSimpleName()->identifier_token);
return QByteArray::fromRawData(id->chars(), id->size());
}
void visitMembers(Class *klass)
{
for (unsigned i = 0; i < klass->memberCount(); ++i) {
Symbol *member = klass->memberAt(i);
if (! member->name())
continue;
const Identifier *id = member->name()->identifier();
if (! id)
continue;
const QByteArray memberName = QByteArray::fromRawData(id->chars(), id->size());
if (member->type().isUnsigned() && memberName.endsWith("_token")) {
*out
<< " pattern->" << memberName << " = node->" << memberName << ";" << endl
<< endl;
} else if (PointerType *ptrTy = member->type()->asPointerType()) {
if (NamedType *namedTy = ptrTy->elementType()->asNamedType()) {
QByteArray typeName = namedTy->name()->identifier()->chars();
if (typeName.endsWith("AST")) {
*out
<< " if (! pattern->" << memberName << ")" << endl
<< " pattern->" << memberName << " = node->" << memberName << ";" << endl
<< " else if (! AST::match(node->" << memberName << ", pattern->" << memberName << ", this))" << endl
<< " return false;" << endl
<< endl;
}
}
}
}
for (unsigned i = 0; i < klass->baseClassCount(); ++i) {
const QByteArray baseClassName = klass->baseClassAt(i)->identifier()->chars();
if (ClassSpecifierAST *baseClassSpec = classMap.value(baseClassName, 0)) {
visitMembers(baseClassSpec->symbol);
}
}
}
bool checkMethod(Symbol *accept0Method) const
{
Declaration *decl = accept0Method->asDeclaration();
if (! decl)
return false;
Function *funTy = decl->type()->asFunctionType();
if (! funTy)
return false;
else if (funTy->isPureVirtual())
return false;
return true;
}
virtual bool visit(ClassSpecifierAST *ast)
{
Class *klass = ast->symbol;
const QByteArray className = id_cast(ast->name);
const Identifier *match0_id = control()->identifier("match0");
Symbol *match0Method = klass->find(match0_id);
for (; match0Method; match0Method = match0Method->next()) {
if (match0Method->identifier() != match0_id)
continue;
if (checkMethod(match0Method))
break;
}
if (! match0Method)
return true;
classMap.insert(className, ast);
*out
<< "bool ASTMatcher::match(" << className.constData() << " *node, " << className.constData() << " *pattern)" << endl
<< "{" << endl
<< " (void) node;" << endl
<< " (void) pattern;" << endl
<< endl;
visitMembers(klass);
*out
<< " return true;" << endl
<< "}" << endl
<< endl;
return true;
}
};
class CloneCPPCG: protected ASTVisitor
{
QDir _cplusplusDir;
QTextStream *out;
public:
CloneCPPCG(const QDir &cplusplusDir, TranslationUnit *unit)
: ASTVisitor(unit), _cplusplusDir(cplusplusDir), out(0)
{ }
void operator()(AST *ast)
{
QFileInfo fileInfo(_cplusplusDir, QLatin1String("ASTClone.cpp"));
QFile file(fileInfo.absoluteFilePath());
if (! file.open(QFile::WriteOnly))
return;
QTextStream output(&file);
out = &output;
*out << copyrightHeader
<< generatedHeader
<< "#include \"AST.h\"" << endl
<< "#include \"MemoryPool.h\"" << endl
<< endl
<< "using namespace CPlusPlus;" << endl
<< endl;
accept(ast);
file.close();
}
protected:
using ASTVisitor::visit;
QMap<QByteArray, ClassSpecifierAST *> classMap;
QByteArray id_cast(NameAST *name)
{
if (! name)
return QByteArray();
const Identifier *id = identifier(name->asSimpleName()->identifier_token);
return QByteArray::fromRawData(id->chars(), id->size());
}
void visitMembers(Class *klass)
{
for (unsigned i = 0; i < klass->memberCount(); ++i) {
Symbol *member = klass->memberAt(i);
if (! member->name())
continue;
const Identifier *id = member->name()->identifier();
if (! id)
continue;
const QByteArray memberName = QByteArray::fromRawData(id->chars(), id->size());
if (member->type().isUnsigned() && memberName.endsWith("_token")) {
*out << " ast->" << memberName << " = " << memberName << ";" << endl;
} else if (PointerType *ptrTy = member->type()->asPointerType()) {
if (NamedType *namedTy = ptrTy->elementType()->asNamedType()) {
QByteArray typeName = namedTy->name()->identifier()->chars();
if (typeName.endsWith("ListAST")) {
*out << " for (" << typeName << " *iter = " << memberName << ", **ast_iter = &ast->" << memberName << ";" << endl
<< " iter; iter = iter->next, ast_iter = &(*ast_iter)->next)" << endl
<< " *ast_iter = new (pool) " << typeName << "((iter->value) ? iter->value->clone(pool) : 0);" << endl;
} else if (typeName.endsWith("AST")) {
*out << " if (" << memberName << ")" << endl
<< " ast->" << memberName << " = " << memberName << "->clone(pool);" << endl;
}
}
}
}
for (unsigned i = 0; i < klass->baseClassCount(); ++i) {
const QByteArray baseClassName = klass->baseClassAt(i)->identifier()->chars();
if (ClassSpecifierAST *baseClassSpec = classMap.value(baseClassName, 0)) {
visitMembers(baseClassSpec->symbol);
}
}
}
bool checkMethod(Symbol *cloneMethod) const
{
Declaration *decl = cloneMethod->asDeclaration();
if (! decl)
return false;
Function *funTy = decl->type()->asFunctionType();
if (! funTy)
return false;
else if (funTy->isPureVirtual())
return false;
return true;
}
virtual bool visit(ClassSpecifierAST *ast)
{
Class *klass = ast->symbol;
const QByteArray className = id_cast(ast->name);
if (! className.endsWith("AST"))
return false;
const Identifier *clone_id = control()->identifier("clone");
Symbol *cloneMethod = klass->find(clone_id);
for (; cloneMethod; cloneMethod = cloneMethod->next()) {
if (cloneMethod->identifier() != clone_id)
continue;
if (checkMethod(cloneMethod))
break;
}
if (! cloneMethod)
return true;
classMap.insert(className, ast);
*out << className.constData() << " *" << className.constData() << "::" << "clone(MemoryPool *pool) const" << endl
<< "{" << endl
<< " " << className.constData() << " *ast = new (pool) " << className.constData() << ";" << endl;
visitMembers(klass);
*out << " return ast;" << endl
<< "}" << endl << endl;
return false;
}
};
class GenerateDumpers: protected ASTVisitor
{
QTextStream out;
public:
GenerateDumpers(QFile *file, TranslationUnit *unit)
: ASTVisitor(unit), out(file)
{ }
static void go(const QString &fileName, TranslationUnit *unit)
{
QFile file(fileName);
if (! file.open(QFile::WriteOnly)) {
std::cerr << "Cannot open dumpers file." << std::endl;
return;
}
GenerateDumpers d(&file, unit);
d.out << copyrightHeader
<< generatedHeader
<< endl;
d.accept(unit->ast());
file.close();
}
protected:
using ASTVisitor::visit;
QMap<QByteArray, ClassSpecifierAST *> classMap;
QByteArray id_cast(NameAST *name)
{
if (! name)
return QByteArray();
const Identifier *id = identifier(name->asSimpleName()->identifier_token);
return QByteArray::fromRawData(id->chars(), id->size());
}
void visitMembers(Class *klass)
{
for (unsigned i = 0; i < klass->memberCount(); ++i) {
Symbol *member = klass->memberAt(i);
if (! member->name())
continue;
const Identifier *id = member->name()->identifier();
if (! id)
continue;
const QByteArray memberName = QByteArray::fromRawData(id->chars(), id->size());
if (member->type().isUnsigned() && memberName.endsWith("_token")) {
out << " if (ast->" << memberName << ")" << endl;
out << " terminal(ast->" << memberName << ", ast);" << endl;
} else if (PointerType *ptrTy = member->type()->asPointerType()) {
if (NamedType *namedTy = ptrTy->elementType()->asNamedType()) {
QByteArray typeName = namedTy->name()->identifier()->chars();
if (typeName.endsWith("ListAST")) {
out << " for (" << typeName << " *iter = ast->" << memberName << "; iter; iter = iter->next)" << endl
<< " nonterminal(iter->value);" << endl;
} else if (typeName.endsWith("AST")) {
out << " nonterminal(ast->" << memberName << ");" << endl;
}
}
}
}
for (unsigned i = 0; i < klass->baseClassCount(); ++i) {
const QByteArray baseClassName = klass->baseClassAt(i)->identifier()->chars();
if (ClassSpecifierAST *baseClassSpec = classMap.value(baseClassName, 0)) {
visitMembers(baseClassSpec->symbol);
}
}
}
bool checkMethod(Symbol *cloneMethod) const
{
Declaration *decl = cloneMethod->asDeclaration();
if (! decl)
return false;
Function *funTy = decl->type()->asFunctionType();
if (! funTy)
return false;
else if (funTy->isPureVirtual())
return false;
return true;
}
virtual bool visit(ClassSpecifierAST *ast)
{
Class *klass = ast->symbol;
const QByteArray className = id_cast(ast->name);
if (! className.endsWith("AST"))
return false;
const Identifier *clone_id = control()->identifier("clone");
Symbol *cloneMethod = klass->find(clone_id);
for (; cloneMethod; cloneMethod = cloneMethod->next()) {
if (cloneMethod->identifier() != clone_id)
continue;
if (checkMethod(cloneMethod))
break;
}
if (! cloneMethod)
return true;
classMap.insert(className, ast);
out << "virtual bool visit(" << className.constData() << " *ast)" << endl
<< "{" << endl;
visitMembers(klass);
out << " return false;" << endl
<< "}" << endl << endl;
return false;
}
};
class RemoveCastMethods: protected ASTVisitor
{
public:
RemoveCastMethods(Document::Ptr doc, QTextDocument *document)
: ASTVisitor(doc->translationUnit()), document(document) {}
QList<QTextCursor> operator()(AST *ast)
{
_cursors.clear();
accept(ast);
return _cursors;
}
protected:
virtual bool visit(FunctionDefinitionAST *ast)
{
Function *fun = ast->symbol;
const QString functionName = oo(fun->name());
if (functionName.length() > 3 && functionName.startsWith(QLatin1String("as"))
&& functionName.at(2).isUpper()) {
QTextCursor tc = createCursor(translationUnit(), ast, document);
//qDebug() << qPrintable(tc.selectedText());
_cursors.append(tc);
}
return true;
}
private:
QTextDocument *document;
QList<QTextCursor> _cursors;
Overview oo;
};
static QList<QTextCursor> removeConstructors(ClassSpecifierAST *classAST,
TranslationUnit *translationUnit,
QTextDocument *document)
{
Overview oo;
QList<QTextCursor> cursors;
const QString className = oo(classAST->symbol->name());
for (DeclarationListAST *iter = classAST->member_specifier_list; iter; iter = iter->next) {
if (FunctionDefinitionAST *funDef = iter->value->asFunctionDefinition()) {
if (oo(funDef->symbol->name()) == className) {
// found it:
QTextCursor tc = createCursor(translationUnit, funDef, document);
tc.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
tc.setPosition(tc.position() + 1, QTextCursor::KeepAnchor);
cursors.append(tc);
}
}
}
return cursors;
}
static QStringList collectFieldNames(ClassSpecifierAST *classAST, bool onlyTokensAndASTNodes)
{
QStringList fields;
Overview oo;
Class *clazz = classAST->symbol;
for (unsigned i = 0; i < clazz->memberCount(); ++i) {
Symbol *s = clazz->memberAt(i);
if (Declaration *decl = s->asDeclaration()) {
const QString declName = oo(decl->name());
const FullySpecifiedType ty = decl->type();
if (const PointerType *ptrTy = ty->asPointerType()) {
if (onlyTokensAndASTNodes) {
if (const NamedType *namedTy = ptrTy->elementType()->asNamedType()) {
if (oo(namedTy->name()).endsWith(QLatin1String("AST")))
fields.append(declName);
}
} else {
fields.append(declName);
}
} else if (ty.isUnsigned()) {
fields.append(declName);
}
}
}
return fields;
}
static QString createConstructor(ClassSpecifierAST *classAST)
{
Overview oo;
Class *clazz = classAST->symbol;
QString result(QLatin1String(" "));
result.append(oo(clazz->name()));
result.append(QLatin1String("()\n"));
QStringList classFields = collectFieldNames(classAST, false);
for (int i = 0; i < classFields.size(); ++i) {
if (i == 0) {
result.append(QLatin1String(" : "));
result.append(classFields.at(i));
result.append(QLatin1String("(0)\n"));
} else {
result.append(QLatin1String(" , "));
result.append(classFields.at(i));
result.append(QLatin1String("(0)\n"));
}
}
result.append(QLatin1String(" {}\n"));
return result;
}
bool checkGenerated(const QTextCursor &cursor, int *doxyStart)
{
BackwardsScanner tokens(cursor);
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 0;" << endl;
os << "}" << endl << endl;
}
void generateAST_cpp(const Snapshot &snapshot, const QDir &cplusplusDir)
{
QFileInfo fileAST_cpp(cplusplusDir, QLatin1String("AST.cpp"));
Q_ASSERT(fileAST_cpp.exists());
const QString fileName = fileAST_cpp.absoluteFilePath();
QFile file(fileName);
if (! file.open(QFile::ReadOnly)) {
std::cerr << "Cannot open " << fileName.toLatin1().data() << std::endl;
return;
}
const QString source = QTextStream(&file).readAll();
file.close();
QTextDocument cpp_document;
cpp_document.setPlainText(source);
Document::Ptr AST_cpp_document = Document::create(fileName);
const QByteArray preprocessedCode = snapshot.preprocessedCode(source, fileName);
AST_cpp_document->setSource(preprocessedCode);
AST_cpp_document->check();
Overview oo;
QMap<QString, ClassSpecifierAST *> classesNeedingFirstToken;
QMap<QString, ClassSpecifierAST *> classesNeedingLastToken;
// find all classes with method declarations for firstToken/lastToken
foreach (ClassSpecifierAST *classAST, astNodes.deriveds) {
const QString className = oo(classAST->symbol->name());
if (className.isEmpty())
continue;
for (DeclarationListAST *declIter = classAST->member_specifier_list; declIter; declIter = declIter->next) {
if (SimpleDeclarationAST *decl = declIter->value->asSimpleDeclaration()) {
if (decl->symbols && decl->symbols->value) {
if (decl->symbols->next)
std::cerr << "Found simple declaration with multiple symbols in " << className.toLatin1().data() << std::endl;
Symbol *s = decl->symbols->value;
const QString funName = oo(s->name());
if (funName == QLatin1String("firstToken")) {
// found it:
classesNeedingFirstToken.insert(className, classAST);
} else if (funName == QLatin1String("lastToken")) {
// found it:
classesNeedingLastToken.insert(className, classAST);
}
}
}
}
}
QList<GenInfo> todo;
TranslationUnitAST *xUnit = AST_cpp_document->translationUnit()->ast()->asTranslationUnit();
for (DeclarationListAST *iter = xUnit->declaration_list; iter; iter = iter->next) {
if (FunctionDefinitionAST *funDef = iter->value->asFunctionDefinition()) {
if (const QualifiedNameId *qName = funDef->symbol->name()->asQualifiedNameId()) {
const QString className = oo(qName->base());
const QString methodName = oo(qName->name());
QTextCursor cursor(&cpp_document);
unsigned line = 0, column = 0;
AST_cpp_document->translationUnit()->getTokenStartPosition(funDef->firstToken(), &line, &column);
const int start = cpp_document.findBlockByNumber(line - 1).position() + column - 1;
cursor.setPosition(start);
int doxyStart = start;
const bool isGenerated = checkGenerated(cursor, &doxyStart);
AST_cpp_document->translationUnit()->getTokenEndPosition(funDef->lastToken() - 1, &line, &column);
int end = cpp_document.findBlockByNumber(line - 1).position() + column - 1;
while (cpp_document.characterAt(end).isSpace())
++end;
if (methodName == QLatin1String("firstToken")) {
ClassSpecifierAST *classAST = classesNeedingFirstToken.value(className, 0);
GenInfo info;
info.end = end;
if (classAST) {
info.classAST = classAST;
info.firstToken = true;
info.start = start;
classesNeedingFirstToken.remove(className);
} else {
info.start = doxyStart;
info.remove = true;
}
if (isGenerated)
todo.append(info);
} else if (methodName == QLatin1String("lastToken")) {
ClassSpecifierAST *classAST = classesNeedingLastToken.value(className, 0);
GenInfo info;
info.end = end;
if (classAST) {
info.classAST = classAST;
info.start = start;
info.lastToken = true;
classesNeedingLastToken.remove(className);
} else {
info.start = doxyStart;
info.remove = true;
}
if (isGenerated)
todo.append(info);
}
}
}
}
const int documentEnd = cpp_document.lastBlock().position() + cpp_document.lastBlock().length() - 1;
Utils::ChangeSet changes;
foreach (GenInfo info, todo) {
if (info.end > documentEnd)
info.end = documentEnd;
if (info.remove) {
changes.remove(info.start, info.end);
return;
}
Overview oo;
const QString className = oo(info.classAST->symbol->name());
QString method;
QTextStream os(&method);
const QStringList fields = collectFieldNames(info.classAST, true);
if (info.firstToken) {
generateFirstToken(os, className, fields);
} else if (info.lastToken) {
generateLastToken(os, className, fields);
}
changes.replace(info.start, info.end, method);
}
QTextCursor tc(&cpp_document);
changes.apply(&tc);
QString newMethods;
QTextStream os(&newMethods);
foreach (const QString &className, classesNeedingFirstToken.keys()) {
const QStringList fields = collectFieldNames(classesNeedingFirstToken.value(className), true);
os << "/** \\generated */" << endl;
generateFirstToken(os, className, fields);
if (ClassSpecifierAST *classAST = classesNeedingLastToken.value(className, 0)) {
const QStringList fields = collectFieldNames(classAST, true);
os << "/** \\generated */" << endl;
generateLastToken(os, className, fields);
classesNeedingLastToken.remove(className);
}
}
foreach (const QString &className, classesNeedingLastToken.keys()) {
const QStringList fields = collectFieldNames(classesNeedingLastToken.value(className), true);
os << "/** \\generated */" << endl;
generateLastToken(os, className, fields);
}
tc.setPosition(documentEnd);
tc.insertText(newMethods);
if (file.open(QFile::WriteOnly)) {
QTextStream out(&file);
out << cpp_document.toPlainText();
}
}
QStringList generateAST_H(const Snapshot &snapshot, const QDir &cplusplusDir, const QString &dumpersFile)
{
QStringList astDerivedClasses;
QFileInfo fileAST_h(cplusplusDir, QLatin1String("AST.h"));
Q_ASSERT(fileAST_h.exists());
const QString fileName = fileAST_h.absoluteFilePath();
QFile file(fileName);
if (! file.open(QFile::ReadOnly))
return astDerivedClasses;
const QString source = QTextStream(&file).readAll();
file.close();
QTextDocument document;
document.setPlainText(source);
AST_h_document = Document::create(fileName);
const QByteArray preprocessedCode = snapshot.preprocessedCode(source, fileName);
AST_h_document->setSource(preprocessedCode);
AST_h_document->check();
FindASTNodes process(AST_h_document, &document);
astNodes = process(AST_h_document->translationUnit()->ast());
RemoveCastMethods removeCastMethods(AST_h_document, &document);
QList<QTextCursor> baseCastMethodCursors = removeCastMethods(astNodes.base);
QMap<ClassSpecifierAST *, QList<QTextCursor> > cursors;
QMap<ClassSpecifierAST *, QString> replacementCastMethods;
QMap<ClassSpecifierAST *, QList<QTextCursor> > constructors;
QMap<ClassSpecifierAST *, QString> replacementConstructors;
Overview oo;
QStringList castMethods;
foreach (ClassSpecifierAST *classAST, astNodes.deriveds) {
cursors[classAST] = removeCastMethods(classAST);
const QString className = oo(classAST->symbol->name());
const QString methodName = QLatin1String("as") + className.mid(0, className.length() - 3);
replacementCastMethods[classAST] = QString(" virtual %1 *%2() { return this; }\n").arg(className, methodName);
castMethods.append(QString(" virtual %1 *%2() { return 0; }\n").arg(className, methodName));
astDerivedClasses.append(className);
constructors[classAST] = removeConstructors(classAST, AST_h_document->translationUnit(), &document);
replacementConstructors[classAST] = createConstructor(classAST);
}
if (! baseCastMethodCursors.isEmpty()) {
castMethods.sort();
for (int i = 0; i < baseCastMethodCursors.length(); ++i) {
baseCastMethodCursors[i].removeSelectedText();
}
baseCastMethodCursors.first().insertText(castMethods.join(QLatin1String("")));
}
for (int classIndex = 0; classIndex < astNodes.deriveds.size(); ++classIndex) {
ClassSpecifierAST *classAST = astNodes.deriveds.at(classIndex);
{ // remove the cast methods.
QList<QTextCursor> c = cursors.value(classAST);
for (int i = 0; i < c.length(); ++i) {
c[i].removeSelectedText();
}
}
{ // remove the constructors.
QList<QTextCursor> c = constructors.value(classAST);
for (int i = 0; i < c.length(); ++i) {
c[i].removeSelectedText();
}
}
astNodes.endOfPublicClassSpecifiers[classIndex].insertText(
replacementConstructors.value(classAST) +
QLatin1String("\n") +
replacementCastMethods.value(classAST));
}
if (file.open(QFile::WriteOnly)) {
QTextStream out(&file);
out << document.toPlainText();
}
Accept0CG cg(cplusplusDir, AST_h_document->translationUnit());
cg(AST_h_document->translationUnit()->ast());
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);
if (!dumpersFile.isEmpty())
GenerateDumpers::go(dumpersFile, AST_h_document->translationUnit());
return astDerivedClasses;
}
class FindASTForwards: protected ASTVisitor
{
public:
FindASTForwards(Document::Ptr doc, QTextDocument *document)
: ASTVisitor(doc->translationUnit()), document(document)
{}
QList<QTextCursor> operator()(AST *ast)
{
accept(ast);
return _cursors;
}
protected:
bool visit(SimpleDeclarationAST *ast)
{
if (! ast->decl_specifier_list)
return false;
if (ElaboratedTypeSpecifierAST *e = ast->decl_specifier_list->value->asElaboratedTypeSpecifier()) {
if (tokenKind(e->classkey_token) == T_CLASS && !ast->declarator_list) {
QString className = oo(e->name->name);
if (className.length() > 3 && className.endsWith(QLatin1String("AST"))) {
QTextCursor tc = createCursor(translationUnit(), ast, document);
_cursors.append(tc);
}
}
}
return true;
}
private:
QTextDocument *document;
QList<QTextCursor> _cursors;
Overview oo;
};
void generateASTFwd_h(const Snapshot &snapshot, const QDir &cplusplusDir, const QStringList &astDerivedClasses)
{
QFileInfo fileASTFwd_h(cplusplusDir, QLatin1String("ASTfwd.h"));
Q_ASSERT(fileASTFwd_h.exists());
const QString fileName = fileASTFwd_h.absoluteFilePath();
QFile file(fileName);
if (! file.open(QFile::ReadOnly))
return;
const QString source = QTextStream(&file).readAll();
file.close();
QTextDocument document;
document.setPlainText(source);
Document::Ptr doc = Document::create(fileName);
const QByteArray preprocessedCode = snapshot.preprocessedCode(source, fileName);
doc->setSource(preprocessedCode);
doc->check();
FindASTForwards process(doc, &document);
QList<QTextCursor> cursors = process(doc->translationUnit()->ast());
for (int i = 0; i < cursors.length(); ++i)
cursors[i].removeSelectedText();
QString replacement;
foreach (const QString &astDerivedClass, astDerivedClasses) {
replacement += QString(QLatin1String("class %1;\n")).arg(astDerivedClass);
}
cursors.first().insertText(replacement);
if (file.open(QFile::WriteOnly)) {
QTextStream out(&file);
out << document.toPlainText();
}
}
void generateASTPatternBuilder_h(const QDir &cplusplusDir)
{
QFileInfo fileInfo(cplusplusDir, QLatin1String("ASTPatternBuilder.h"));
QFile file(fileInfo.absoluteFilePath());
if (! file.open(QFile::WriteOnly))
return;
Overview oo;
QTextStream out(&file);
out
<< copyrightHeader
<< generatedHeader
<< "#ifndef CPLUSPLUS_AST_PATTERN_BUILDER_H" << endl
<< "#define CPLUSPLUS_AST_PATTERN_BUILDER_H" << endl
<< endl
<< "#include \"CPlusPlusForwardDeclarations.h\"" << endl
<< "#include \"AST.h\"" << endl
<< "#include \"MemoryPool.h\"" << endl
<< endl
<< "namespace CPlusPlus {" << endl
<< endl
<< "class CPLUSPLUS_EXPORT ASTPatternBuilder" << endl
<< "{" << endl
<< " MemoryPool pool;" << endl
<< endl
<< "public:" << endl
<< " ASTPatternBuilder() {}" << endl
<< endl
<< " void reset() { pool.reset(); };" << endl
<< endl;
Control *control = AST_h_document->control();
foreach (ClassSpecifierAST *classNode, astNodes.deriveds) {
Class *klass = classNode->symbol;
const Identifier *match0_id = control->identifier("match0");
Symbol *match0Method = klass->find(match0_id);
for (; match0Method; match0Method = match0Method->next()) {
if (match0Method->identifier() != match0_id)
continue;
else break;
}
if (! match0Method)
continue;
const QString className = oo(klass->name());
if (! className.endsWith("AST"))
continue;
const QString methodName = className.left(className.length() - 3);
out
<< " " << className << " *" << methodName << "(";
QList<QPair<QString, QString> > args;
bool first = true;
for (unsigned index = 0; index < klass->memberCount(); ++index) {
Declaration *member = klass->memberAt(index)->asDeclaration();
if (! member)
continue;
PointerType *ptrTy = member->type()->asPointerType();
if (! ptrTy)
continue;
const QString tyName = oo(ptrTy->elementType());
if (tyName.endsWith("ListAST"))
continue;
else if (tyName.endsWith("AST")) {
if (! first)
out << ", ";
const QString memberName = oo(member->name());
out << tyName << " *" << memberName << " = 0";
args.append(qMakePair(tyName, memberName));
first = false;
}
}
out
<< ")" << endl
<< " {" << endl
<< " " << className << " *__ast = new (&pool) " << className << ";" << endl;
QPair<QString, QString> p;
foreach (p, args) {
out
<< " __ast->" << p.second << " = " << p.second << ";" << endl;
}
out
<< " return __ast;" << endl
<< " }" << endl
<< endl;
}
out
<< "};" << endl
<< endl
<< "} // end of namespace CPlusPlus" << endl
<< endl
<< "#endif // CPLUSPLUS_AST_PATTERN_BUILDER_H" << endl;
}
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
QStringList files = app.arguments();
files.removeFirst();
if (files.size() != 1 && files.size() != 2) {
std::cerr << "Usage: cplusplus [path to C++ front-end]" << std::endl;
std::cerr << " or: cplusplus [path to C++ front-end] [dumpers file name]" << std::endl;
return EXIT_FAILURE;
}
QDir cplusplusDir(files.first());
if (!QFileInfo(cplusplusDir, QLatin1String("AST.h")).exists()) {
std::cerr << "Cannot find AST.h in " << qPrintable(cplusplusDir.absolutePath())
<< std::endl;
return EXIT_FAILURE;
}
QString dumpersFile;
if (files.size() == 2)
dumpersFile = files.last();
Snapshot snapshot;
QStringList astDerivedClasses = generateAST_H(snapshot, cplusplusDir, dumpersFile);
astDerivedClasses.sort();
generateASTFwd_h(snapshot, cplusplusDir, astDerivedClasses);
//generateASTPatternBuilder_h(cplusplusDir);
}