src/tools/moc/moc.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/tools/moc/moc.cpp	Mon Jan 11 14:00:40 2010 +0000
@@ -0,0 +1,1260 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "moc.h"
+#include "generator.h"
+#include "qdatetime.h"
+#include "utils.h"
+#include "outputrevision.h"
+
+// for normalizeTypeInternal
+#include <private/qmetaobject_p.h>
+
+QT_BEGIN_NAMESPACE
+
+// only moc needs this function
+static QByteArray normalizeType(const char *s, bool fixScope = false)
+{
+    int len = qstrlen(s);
+    char stackbuf[64];
+    char *buf = (len >= 64 ? new char[len + 1] : stackbuf);
+    char *d = buf;
+    char last = 0;
+    while(*s && is_space(*s))
+        s++;
+    while (*s) {
+        while (*s && !is_space(*s))
+            last = *d++ = *s++;
+        while (*s && is_space(*s))
+            s++;
+        if (*s && ((is_ident_char(*s) && is_ident_char(last))
+                   || ((*s == ':') && (last == '<')))) {
+            last = *d++ = ' ';
+        }
+    }
+    *d = '\0';
+    QByteArray result;
+    if (strncmp("void", buf, d - buf) != 0)
+        result = normalizeTypeInternal(buf, d, fixScope);
+    if (buf != stackbuf)
+        delete [] buf;
+    return result;
+}
+
+bool Moc::parseClassHead(ClassDef *def)
+{
+    // figure out whether this is a class declaration, or only a
+    // forward or variable declaration.
+    int i = 0;
+    Token token;
+    do {
+        token = lookup(i++);
+        if (token == COLON || token == LBRACE)
+            break;
+        if (token == SEMIC || token == RANGLE)
+            return false;
+    } while (token);
+
+    if (!test(IDENTIFIER)) // typedef struct { ... }
+        return false;
+    QByteArray name = lexem();
+
+    // support "class IDENT name" and "class IDENT(IDENT) name"
+    if (test(LPAREN)) {
+        until(RPAREN);
+        if (!test(IDENTIFIER))
+            return false;
+        name = lexem();
+    } else  if (test(IDENTIFIER)) {
+        name = lexem();
+    }
+
+    def->qualified += name;
+    while (test(SCOPE)) {
+        def->qualified += lexem();
+        if (test(IDENTIFIER)) {
+            name = lexem();
+            def->qualified += name;
+        }
+    }
+    def->classname = name;
+    if (test(COLON)) {
+        do {
+            test(VIRTUAL);
+            FunctionDef::Access access = FunctionDef::Public;
+            if (test(PRIVATE))
+                access = FunctionDef::Private;
+            else if (test(PROTECTED))
+                access = FunctionDef::Protected;
+            else
+                test(PUBLIC);
+            test(VIRTUAL);
+            const QByteArray type = parseType().name;
+            // ignore the 'class Foo : BAR(Baz)' case
+            if (test(LPAREN)) {
+                until(RPAREN);
+            } else {
+                def->superclassList += qMakePair(type, access);
+            }
+        } while (test(COMMA));
+    }
+    if (!test(LBRACE))
+        return false;
+    def->begin = index - 1;
+    bool foundRBrace = until(RBRACE);
+    def->end = index;
+    index = def->begin + 1;
+    return foundRBrace;
+}
+
+Type Moc::parseType()
+{
+    Type type;
+    bool hasSignedOrUnsigned = false;
+    bool isVoid = false;
+    type.firstToken = lookup();
+    for (;;) {
+        switch (next()) {
+            case SIGNED:
+            case UNSIGNED:
+                hasSignedOrUnsigned = true;
+                // fall through
+            case CONST:
+            case VOLATILE:
+                type.name += lexem();
+                type.name += ' ';
+                if (lookup(0) == VOLATILE)
+                    type.isVolatile = true;
+                continue;
+            case Q_MOC_COMPAT_TOKEN:
+            case Q_QT3_SUPPORT_TOKEN:
+            case Q_INVOKABLE_TOKEN:
+            case Q_SCRIPTABLE_TOKEN:
+            case Q_SIGNALS_TOKEN:
+            case Q_SLOTS_TOKEN:
+            case Q_SIGNAL_TOKEN:
+            case Q_SLOT_TOKEN:
+                type.name += lexem();
+                return type;
+            default:
+                prev();
+                break;
+        }
+        break;
+    }
+    test(ENUM) || test(CLASS) || test(STRUCT);
+    for(;;) {
+        switch (next()) {
+        case IDENTIFIER:
+            // void mySlot(unsigned myArg)
+            if (hasSignedOrUnsigned) {
+                prev();
+                break;
+            }
+        case CHAR:
+        case SHORT:
+        case INT:
+        case LONG:
+            type.name += lexem();
+            // preserve '[unsigned] long long', 'short int', 'long int', 'long double'
+            if (test(LONG) || test(INT) || test(DOUBLE)) {
+                type.name += ' ';
+                prev();
+                continue;
+            }
+            break;
+        case FLOAT:
+        case DOUBLE:
+        case VOID:
+        case BOOL:
+            type.name += lexem();
+            isVoid |= (lookup(0) == VOID);
+            break;
+        default:
+            prev();
+            ;
+        }
+        if (test(LANGLE)) {
+            QByteArray templ = lexemUntil(RANGLE);
+            for (int i = 0; i < templ.size(); ++i) {
+                type.name += templ.at(i);
+                if ((templ.at(i) == '<' && i < templ.size()-1 && templ.at(i+1) == ':')
+                    || (templ.at(i) == '>' && i < templ.size()-1 && templ.at(i+1) == '>')) {
+                    type.name += ' ';
+                }
+            }
+        }
+        if (test(SCOPE)) {
+            type.name += lexem();
+            type.isScoped = true;
+        } else {
+            break;
+        }
+    }
+    while (test(CONST) || test(VOLATILE) || test(SIGNED) || test(UNSIGNED)
+           || test(STAR) || test(AND)) {
+        type.name += ' ';
+        type.name += lexem();
+        if (lookup(0) == AND)
+            type.referenceType = Type::Reference;
+        else if (lookup(0) == STAR)
+            type.referenceType = Type::Pointer;
+    }
+    // transform stupid things like 'const void' or 'void const' into 'void'
+    if (isVoid && type.referenceType == Type::NoReference) {
+        type.name = "void";
+    }
+    return type;
+}
+
+bool Moc::parseEnum(EnumDef *def)
+{
+    bool isTypdefEnum = false; // typedef enum { ... } Foo;
+
+    if (test(IDENTIFIER)) {
+        def->name = lexem();
+    } else {
+        if (lookup(-1) != TYPEDEF)
+            return false; // anonymous enum
+        isTypdefEnum = true;
+    }
+    if (!test(LBRACE))
+        return false;
+    do {
+        if (lookup() == RBRACE) // accept trailing comma
+            break;
+        next(IDENTIFIER);
+        def->values += lexem();
+    } while (test(EQ) ? until(COMMA) : test(COMMA));
+    next(RBRACE);
+    if (isTypdefEnum) {
+        if (!test(IDENTIFIER))
+            return false;
+        def->name = lexem();
+    }
+    return true;
+}
+
+void Moc::parseFunctionArguments(FunctionDef *def)
+{
+    Q_UNUSED(def);
+    while (hasNext()) {
+        ArgumentDef  arg;
+        arg.type = parseType();
+        if (arg.type.name == "void")
+            break;
+        if (test(IDENTIFIER))
+            arg.name = lexem();
+        while (test(LBRACK)) {
+            arg.rightType += lexemUntil(RBRACK);
+        }
+        if (test(CONST) || test(VOLATILE)) {
+            arg.rightType += ' ';
+            arg.rightType += lexem();
+        }
+        arg.normalizedType = normalizeType(arg.type.name + ' ' + arg.rightType);
+        arg.typeNameForCast = normalizeType(noRef(arg.type.name) + "(*)" + arg.rightType);
+        if (test(EQ))
+            arg.isDefault = true;
+        def->arguments += arg;
+        if (!until(COMMA))
+            break;
+    }
+}
+
+bool Moc::testFunctionAttribute(FunctionDef *def)
+{
+    if (index < symbols.size() && testFunctionAttribute(symbols.at(index).token, def)) {
+        ++index;
+        return true;
+    }
+    return false;
+}
+
+bool Moc::testFunctionAttribute(Token tok, FunctionDef *def)
+{
+    switch (tok) {
+        case Q_MOC_COMPAT_TOKEN:
+        case Q_QT3_SUPPORT_TOKEN:
+            def->isCompat = true;
+            return true;
+        case Q_INVOKABLE_TOKEN:
+            def->isInvokable = true;
+            return true;
+        case Q_SIGNAL_TOKEN:
+            def->isSignal = true;
+            return true;
+        case Q_SLOT_TOKEN:
+            def->isSlot = true;
+            return true;
+        case Q_SCRIPTABLE_TOKEN:
+            def->isInvokable = def->isScriptable = true;
+            return true;
+        default: break;
+    }
+    return false;
+}
+
+// returns false if the function should be ignored
+bool Moc::parseFunction(FunctionDef *def, bool inMacro)
+{
+    def->isVirtual = false;
+    //skip modifiers and attributes
+    while (test(INLINE) || test(STATIC) ||
+        (test(VIRTUAL) && (def->isVirtual = true)) //mark as virtual
+        || testFunctionAttribute(def)) {}
+    bool templateFunction = (lookup() == TEMPLATE);
+    def->type = parseType();
+    if (def->type.name.isEmpty()) {
+        if (templateFunction)
+            error("Template function as signal or slot");
+        else
+            error();
+    }
+    bool scopedFunctionName = false;
+    if (test(LPAREN)) {
+        def->name = def->type.name;
+        scopedFunctionName = def->type.isScoped;
+        def->type = Type("int");
+    } else {
+        Type tempType = parseType();;
+        while (!tempType.name.isEmpty() && lookup() != LPAREN) {
+            if (testFunctionAttribute(def->type.firstToken, def))
+                ; // fine
+            else if (def->type.firstToken == Q_SIGNALS_TOKEN)
+                error();
+            else if (def->type.firstToken == Q_SLOTS_TOKEN)
+                error();
+            else {
+                if (!def->tag.isEmpty())
+                    def->tag += ' ';
+                def->tag += def->type.name;
+            }
+            def->type = tempType;
+            tempType = parseType();
+        }
+        next(LPAREN, "Not a signal or slot declaration");
+        def->name = tempType.name;
+        scopedFunctionName = tempType.isScoped;
+    }
+
+    // we don't support references as return types, it's too dangerous
+    if (def->type.referenceType == Type::Reference)
+        def->type = Type("void");
+
+    def->normalizedType = normalizeType(def->type.name);
+
+    if (!test(RPAREN)) {
+        parseFunctionArguments(def);
+        next(RPAREN);
+    }
+
+    // support optional macros with compiler specific options
+    while (test(IDENTIFIER))
+        ;
+
+    def->isConst = test(CONST);
+
+    while (test(IDENTIFIER))
+        ;
+
+    if (inMacro) {
+        next(RPAREN);
+        prev();
+    } else {
+        if (test(THROW)) {
+            next(LPAREN);
+            until(RPAREN);
+        }
+        if (test(SEMIC))
+            ;
+        else if ((def->inlineCode = test(LBRACE)))
+            until(RBRACE);
+        else if ((def->isAbstract = test(EQ)))
+            until(SEMIC);
+        else
+            error();
+    }
+
+    if (scopedFunctionName) {
+        QByteArray msg("Function declaration ");
+        msg += def->name;
+        msg += " contains extra qualification. Ignoring as signal or slot.";
+        warning(msg.constData());
+        return false;
+    }
+    return true;
+}
+
+// like parseFunction, but never aborts with an error
+bool Moc::parseMaybeFunction(const ClassDef *cdef, FunctionDef *def)
+{
+    def->isVirtual = false;
+    //skip modifiers and attributes
+    while (test(EXPLICIT) || test(INLINE) || test(STATIC) ||
+        (test(VIRTUAL) && (def->isVirtual = true)) //mark as virtual
+        || testFunctionAttribute(def)) {}
+    bool tilde = test(TILDE);
+    def->type = parseType();
+    if (def->type.name.isEmpty())
+        return false;
+    bool scopedFunctionName = false;
+    if (test(LPAREN)) {
+        def->name = def->type.name;
+        scopedFunctionName = def->type.isScoped;
+        if (def->name == cdef->classname) {
+            def->isDestructor = tilde;
+            def->isConstructor = !tilde;
+            def->type = Type();
+        } else {
+            def->type = Type("int");
+        }
+    } else {
+        Type tempType = parseType();;
+        while (!tempType.name.isEmpty() && lookup() != LPAREN) {
+            if (testFunctionAttribute(def->type.firstToken, def))
+                ; // fine
+            else if (def->type.name == "Q_SIGNAL")
+                def->isSignal = true;
+            else if (def->type.name == "Q_SLOT")
+                def->isSlot = true;
+            else {
+                if (!def->tag.isEmpty())
+                    def->tag += ' ';
+                def->tag += def->type.name;
+            }
+            def->type = tempType;
+            tempType = parseType();
+        }
+        if (!test(LPAREN))
+            return false;
+        def->name = tempType.name;
+        scopedFunctionName = tempType.isScoped;
+    }
+
+    // we don't support references as return types, it's too dangerous
+    if (def->type.referenceType == Type::Reference)
+        def->type = Type("void");
+
+    def->normalizedType = normalizeType(def->type.name);
+
+    if (!test(RPAREN)) {
+        parseFunctionArguments(def);
+        if (!test(RPAREN))
+            return false;
+    }
+    def->isConst = test(CONST);
+    if (scopedFunctionName
+        && (def->isSignal || def->isSlot || def->isInvokable)) {
+        QByteArray msg("parsemaybe: Function declaration ");
+        msg += def->name;
+        msg += " contains extra qualification. Ignoring as signal or slot.";
+        warning(msg.constData());
+        return false;
+    }
+    return true;
+}
+
+
+void Moc::parse()
+{
+    QList<NamespaceDef> namespaceList;
+    bool templateClass = false;
+    while (hasNext()) {
+        Token t = next();
+        switch (t) {
+            case NAMESPACE: {
+                int rewind = index;
+                if (test(IDENTIFIER)) {
+                    if (test(EQ)) {
+                        // namespace Foo = Bar::Baz;
+                        until(SEMIC);
+                    } else if (!test(SEMIC)) {
+                        NamespaceDef def;
+                        def.name = lexem();
+                        next(LBRACE);
+                        def.begin = index - 1;
+                        until(RBRACE);
+                        def.end = index;
+                        index = def.begin + 1;
+                        namespaceList += def;
+                        index = rewind;
+                    }
+                }
+                break;
+            }
+            case SEMIC:
+            case RBRACE:
+                templateClass = false;
+                break;
+            case TEMPLATE:
+                templateClass = true;
+                break;
+            case MOC_INCLUDE_BEGIN:
+                currentFilenames.push(symbol().unquotedLexem());
+                break;
+            case MOC_INCLUDE_END:
+                currentFilenames.pop();
+                break;
+            case Q_DECLARE_INTERFACE_TOKEN:
+                parseDeclareInterface();
+                break;
+            case Q_DECLARE_METATYPE_TOKEN:
+                parseDeclareMetatype();
+                break;
+            case USING:
+                if (test(NAMESPACE)) {
+                    while (test(SCOPE) || test(IDENTIFIER))
+                        ;
+                    next(SEMIC);
+                }
+                break;
+            case CLASS:
+            case STRUCT: {
+                if (currentFilenames.size() <= 1)
+                    break;
+
+                ClassDef def;
+                if (!parseClassHead(&def))
+                    continue;
+
+                while (inClass(&def) && hasNext()) {
+                    if (next() == Q_OBJECT_TOKEN) {
+                        def.hasQObject = true;
+                        break;
+                    }
+                }
+
+                if (!def.hasQObject)
+                    continue;
+
+                for (int i = namespaceList.size() - 1; i >= 0; --i)
+                    if (inNamespace(&namespaceList.at(i)))
+                        def.qualified.prepend(namespaceList.at(i).name + "::");
+
+                knownQObjectClasses.insert(def.classname);
+                knownQObjectClasses.insert(def.qualified);
+
+                continue; }
+            default: break;
+        }
+        if ((t != CLASS && t != STRUCT)|| currentFilenames.size() > 1)
+            continue;
+        ClassDef def;
+        if (parseClassHead(&def)) {
+            FunctionDef::Access access = FunctionDef::Private;
+            for (int i = namespaceList.size() - 1; i >= 0; --i)
+                if (inNamespace(&namespaceList.at(i)))
+                    def.qualified.prepend(namespaceList.at(i).name + "::");
+            while (inClass(&def) && hasNext()) {
+                switch ((t = next())) {
+                case PRIVATE:
+                    access = FunctionDef::Private;
+                    if (test(Q_SIGNALS_TOKEN))
+                        error("Signals cannot have access specifier");
+                    break;
+                case PROTECTED:
+                    access = FunctionDef::Protected;
+                    if (test(Q_SIGNALS_TOKEN))
+                        error("Signals cannot have access specifier");
+                    break;
+                case PUBLIC:
+                    access = FunctionDef::Public;
+                    if (test(Q_SIGNALS_TOKEN))
+                        error("Signals cannot have access specifier");
+                    break;
+                case CLASS: {
+                    ClassDef nestedDef;
+                    if (parseClassHead(&nestedDef)) {
+                        while (inClass(&nestedDef) && inClass(&def)) {
+                            t = next();
+                            if (t >= Q_META_TOKEN_BEGIN && t < Q_META_TOKEN_END)
+                                error("Meta object features not supported for nested classes");
+                        }
+                    }
+                } break;
+                case Q_SIGNALS_TOKEN:
+                    parseSignals(&def);
+                    break;
+                case Q_SLOTS_TOKEN:
+                    switch (lookup(-1)) {
+                    case PUBLIC:
+                    case PROTECTED:
+                    case PRIVATE:
+                        parseSlots(&def, access);
+                        break;
+                    default:
+                        error("Missing access specifier for slots");
+                    }
+                    break;
+                case Q_OBJECT_TOKEN:
+                    def.hasQObject = true;
+                    if (templateClass)
+                        error("Template classes not supported by Q_OBJECT");
+                    if (def.classname != "Qt" && def.classname != "QObject" && def.superclassList.isEmpty())
+                        error("Class contains Q_OBJECT macro but does not inherit from QObject");
+                    break;
+                case Q_GADGET_TOKEN:
+                    def.hasQGadget = true;
+                    if (templateClass)
+                        error("Template classes not supported by Q_GADGET");
+                    break;
+                case Q_PROPERTY_TOKEN:
+                    parseProperty(&def);
+                    break;
+                case Q_ENUMS_TOKEN:
+                    parseEnumOrFlag(&def, false);
+                    break;
+                case Q_FLAGS_TOKEN:
+                    parseEnumOrFlag(&def, true);
+                    break;
+                case Q_DECLARE_FLAGS_TOKEN:
+                    parseFlag(&def);
+                    break;
+                case Q_CLASSINFO_TOKEN:
+                    parseClassInfo(&def);
+                    break;
+                case Q_INTERFACES_TOKEN:
+                    parseInterfaces(&def);
+                    break;
+                case Q_PRIVATE_SLOT_TOKEN:
+                    parseSlotInPrivate(&def, access);
+                    break;
+                case ENUM: {
+                    EnumDef enumDef;
+                    if (parseEnum(&enumDef))
+                        def.enumList += enumDef;
+                } break;
+                default:
+                    FunctionDef funcDef;
+                    funcDef.access = access;
+                    int rewind = index;
+                    if (parseMaybeFunction(&def, &funcDef)) {
+                        if (funcDef.isConstructor) {
+                            if ((access == FunctionDef::Public) && funcDef.isInvokable) {
+                                def.constructorList += funcDef;
+                                while (funcDef.arguments.size() > 0 && funcDef.arguments.last().isDefault) {
+                                    funcDef.wasCloned = true;
+                                    funcDef.arguments.removeLast();
+                                    def.constructorList += funcDef;
+                                }
+                            }
+                        } else if (funcDef.isDestructor) {
+                            // don't care about destructors
+                        } else {
+                            if (access == FunctionDef::Public)
+                                def.publicList += funcDef;
+                            if (funcDef.isSlot) {
+                                def.slotList += funcDef;
+                                while (funcDef.arguments.size() > 0 && funcDef.arguments.last().isDefault) {
+                                    funcDef.wasCloned = true;
+                                    funcDef.arguments.removeLast();
+                                    def.slotList += funcDef;
+                                }
+                            } else if (funcDef.isSignal) {
+                                def.signalList += funcDef;
+                                while (funcDef.arguments.size() > 0 && funcDef.arguments.last().isDefault) {
+                                    funcDef.wasCloned = true;
+                                    funcDef.arguments.removeLast();
+                                    def.signalList += funcDef;
+                                }
+                            } else if (funcDef.isInvokable) {
+                                def.methodList += funcDef;
+                                while (funcDef.arguments.size() > 0 && funcDef.arguments.last().isDefault) {
+                                    funcDef.wasCloned = true;
+                                    funcDef.arguments.removeLast();
+                                    def.methodList += funcDef;
+                                }
+                            }
+                        }
+                    } else {
+                        index = rewind;
+                    }
+                }
+            }
+
+            next(RBRACE);
+
+            if (!def.hasQObject && !def.hasQGadget && def.signalList.isEmpty() && def.slotList.isEmpty()
+                && def.propertyList.isEmpty() && def.enumDeclarations.isEmpty())
+                continue; // no meta object code required
+
+
+            if (!def.hasQObject && !def.hasQGadget)
+                error("Class declarations lacks Q_OBJECT macro.");
+
+            checkSuperClasses(&def);
+
+            classList += def;
+            knownQObjectClasses.insert(def.classname);
+            knownQObjectClasses.insert(def.qualified);
+        }
+    }
+}
+
+void Moc::generate(FILE *out)
+{
+
+    QDateTime dt = QDateTime::currentDateTime();
+    QByteArray dstr = dt.toString().toLatin1();
+    QByteArray fn = filename;
+    int i = filename.length()-1;
+    while (i>0 && filename[i-1] != '/' && filename[i-1] != '\\')
+        --i;                                // skip path
+    if (i >= 0)
+        fn = filename.mid(i);
+    fprintf(out, "/****************************************************************************\n"
+            "** Meta object code from reading C++ file '%s'\n**\n" , (const char*)fn);
+    fprintf(out, "** Created: %s\n"
+            "**      by: The Qt Meta Object Compiler version %d (Qt %s)\n**\n" , dstr.data(), mocOutputRevision, QT_VERSION_STR);
+    fprintf(out, "** WARNING! All changes made in this file will be lost!\n"
+            "*****************************************************************************/\n\n");
+
+
+    if (!noInclude) {
+        if (includePath.size() && !includePath.endsWith('/'))
+            includePath += '/';
+        for (int i = 0; i < includeFiles.size(); ++i) {
+            QByteArray inc = includeFiles.at(i);
+            if (inc[0] != '<' && inc[0] != '"') {
+                if (includePath.size() && includePath != "./")
+                    inc.prepend(includePath);
+                inc = '\"' + inc + '\"';
+            }
+            fprintf(out, "#include %s\n", inc.constData());
+        }
+    }
+    if (classList.size() && classList.first().classname == "Qt")
+        fprintf(out, "#include <QtCore/qobject.h>\n");
+
+    if (mustIncludeQMetaTypeH)
+        fprintf(out, "#include <QtCore/qmetatype.h>\n");
+
+    fprintf(out, "#if !defined(Q_MOC_OUTPUT_REVISION)\n"
+            "#error \"The header file '%s' doesn't include <QObject>.\"\n", (const char *)fn);
+    fprintf(out, "#elif Q_MOC_OUTPUT_REVISION != %d\n", mocOutputRevision);
+    fprintf(out, "#error \"This file was generated using the moc from %s."
+            " It\"\n#error \"cannot be used with the include files from"
+            " this version of Qt.\"\n#error \"(The moc has changed too"
+            " much.)\"\n", QT_VERSION_STR);
+    fprintf(out, "#endif\n\n");
+
+    fprintf(out, "QT_BEGIN_MOC_NAMESPACE\n");
+
+    for (i = 0; i < classList.size(); ++i) {
+        Generator generator(&classList[i], metaTypes, out);
+        generator.generateCode();
+    }
+
+    fprintf(out, "QT_END_MOC_NAMESPACE\n");
+}
+
+
+QList<QMetaObject*> Moc::generate(bool ignoreProperties)
+{
+    QList<QMetaObject*> result;
+    for (int i = 0; i < classList.size(); ++i) {
+        Generator generator(&classList[i], metaTypes);
+        result << generator.generateMetaObject(ignoreProperties);
+    }
+    return result;
+}
+
+void Moc::parseSlots(ClassDef *def, FunctionDef::Access access)
+{
+    next(COLON);
+    while (inClass(def) && hasNext()) {
+        switch (next()) {
+        case PUBLIC:
+        case PROTECTED:
+        case PRIVATE:
+        case Q_SIGNALS_TOKEN:
+        case Q_SLOTS_TOKEN:
+            prev();
+            return;
+        case SEMIC:
+            continue;
+        case FRIEND:
+            until(SEMIC);
+            continue;
+        case USING:
+            error("'using' directive not supported in 'slots' section");
+        default:
+            prev();
+        }
+
+        FunctionDef funcDef;
+        funcDef.access = access;
+        if (!parseFunction(&funcDef))
+            continue;
+        def->slotList += funcDef;
+        while (funcDef.arguments.size() > 0 && funcDef.arguments.last().isDefault) {
+            funcDef.wasCloned = true;
+            funcDef.arguments.removeLast();
+            def->slotList += funcDef;
+        }
+    }
+}
+
+void Moc::parseSignals(ClassDef *def)
+{
+    next(COLON);
+    while (inClass(def) && hasNext()) {
+        switch (next()) {
+        case PUBLIC:
+        case PROTECTED:
+        case PRIVATE:
+        case Q_SIGNALS_TOKEN:
+        case Q_SLOTS_TOKEN:
+            prev();
+            return;
+        case SEMIC:
+            continue;
+        case FRIEND:
+            until(SEMIC);
+            continue;
+        case USING:
+            error("'using' directive not supported in 'signals' section");
+        default:
+            prev();
+        }
+        FunctionDef funcDef;
+        funcDef.access = FunctionDef::Protected;
+        parseFunction(&funcDef);
+        if (funcDef.isVirtual)
+            warning("Signals cannot be declared virtual");
+        if (funcDef.inlineCode)
+            error("Not a signal declaration");
+        def->signalList += funcDef;
+        while (funcDef.arguments.size() > 0 && funcDef.arguments.last().isDefault) {
+            funcDef.wasCloned = true;
+            funcDef.arguments.removeLast();
+            def->signalList += funcDef;
+        }
+    }
+}
+
+
+void Moc::parseProperty(ClassDef *def)
+{
+    next(LPAREN);
+    PropertyDef propDef;
+    QByteArray type = parseType().name;
+    if (type.isEmpty())
+        error();
+    propDef.designable = propDef.scriptable = propDef.stored = "true";
+    propDef.user = "false";
+    /*
+      The Q_PROPERTY construct cannot contain any commas, since
+      commas separate macro arguments. We therefore expect users
+      to type "QMap" instead of "QMap<QString, QVariant>". For
+      coherence, we also expect the same for
+      QValueList<QVariant>, the other template class supported by
+      QVariant.
+    */
+    type = normalizeType(type);
+    if (type == "QMap")
+        type = "QMap<QString,QVariant>";
+    else if (type == "QValueList")
+        type = "QValueList<QVariant>";
+    else if (type == "LongLong")
+        type = "qlonglong";
+    else if (type == "ULongLong")
+        type = "qulonglong";
+    else if (type == "qreal")
+        mustIncludeQMetaTypeH = true;
+
+    propDef.type = type;
+
+    next();
+    propDef.name = lexem();
+    while (test(IDENTIFIER)) {
+        QByteArray l = lexem();
+
+        if (l[0] == 'C' && l == "CONSTANT") {
+            propDef.constant = true;
+            continue;
+        } else if(l[0] == 'F' && l == "FINAL") {
+            propDef.final = true;
+            continue;
+        }
+
+        QByteArray v, v2;
+        if (test(LPAREN)) {
+            v = lexemUntil(RPAREN);
+        } else {
+            next(IDENTIFIER);
+            v = lexem();
+            if (test(LPAREN))
+                v2 = lexemUntil(RPAREN);
+            else if (v != "true" && v != "false")
+                v2 = "()";
+        }
+        switch (l[0]) {
+        case 'R':
+            if (l == "READ")
+                propDef.read = v;
+            else if (l == "RESET")
+                propDef.reset = v + v2;
+            else
+                error(2);
+            break;
+        case 'S':
+            if (l == "SCRIPTABLE")
+                propDef.scriptable = v + v2;
+            else if (l == "STORED")
+                propDef.stored = v + v2;
+            else
+                error(2);
+            break;
+        case 'W': if (l != "WRITE") error(2);
+            propDef.write = v;
+            break;
+        case 'D': if (l != "DESIGNABLE") error(2);
+            propDef.designable = v + v2;
+            break;
+        case 'E': if (l != "EDITABLE") error(2);
+            propDef.editable = v + v2;
+            break;
+        case 'N': if (l != "NOTIFY") error(2);
+            propDef.notify = v;
+            break;
+        case 'U': if (l != "USER") error(2);
+            propDef.user = v + v2;
+            break;
+        default:
+            error(2);
+        }
+    }
+    next(RPAREN);
+    if (propDef.read.isNull()) {
+        QByteArray msg;
+        msg += "Property declaration ";
+        msg += propDef.name;
+        msg += " has no READ accessor function. The property will be invalid.";
+        warning(msg.constData());
+    }
+    if (propDef.constant && !propDef.write.isNull()) {
+        QByteArray msg;
+        msg += "Property declaration ";
+        msg += propDef.name;
+        msg += " is both WRITEable and CONSTANT. CONSTANT will be ignored.";
+        propDef.constant = false;
+        warning(msg.constData());
+    }
+    if (propDef.constant && !propDef.notify.isNull()) {
+        QByteArray msg;
+        msg += "Property declaration ";
+        msg += propDef.name;
+        msg += " is both NOTIFYable and CONSTANT. CONSTANT will be ignored.";
+        propDef.constant = false;
+        warning(msg.constData());
+    }
+
+    if(!propDef.notify.isEmpty())
+        def->notifyableProperties++;
+
+    def->propertyList += propDef;
+}
+
+void Moc::parseEnumOrFlag(ClassDef *def, bool isFlag)
+{
+    next(LPAREN);
+    QByteArray identifier;
+    while (test(IDENTIFIER)) {
+        identifier = lexem();
+        while (test(SCOPE) && test(IDENTIFIER)) {
+            identifier += "::";
+            identifier += lexem();
+        }
+        def->enumDeclarations[identifier] = isFlag;
+    }
+    next(RPAREN);
+}
+
+void Moc::parseFlag(ClassDef *def)
+{
+    next(LPAREN);
+    QByteArray flagName, enumName;
+    while (test(IDENTIFIER)) {
+        flagName = lexem();
+        while (test(SCOPE) && test(IDENTIFIER)) {
+            flagName += "::";
+            flagName += lexem();
+        }
+    }
+    next(COMMA);
+    while (test(IDENTIFIER)) {
+        enumName = lexem();
+        while (test(SCOPE) && test(IDENTIFIER)) {
+            enumName += "::";
+            enumName += lexem();
+        }
+    }
+
+    def->flagAliases.insert(enumName, flagName);
+    next(RPAREN);
+}
+
+void Moc::parseClassInfo(ClassDef *def)
+{
+    next(LPAREN);
+    ClassInfoDef infoDef;
+    next(STRING_LITERAL);
+    infoDef.name = symbol().unquotedLexem();
+    next(COMMA);
+    if (test(STRING_LITERAL)) {
+        infoDef.value = symbol().unquotedLexem();
+    } else {
+        // support Q_CLASSINFO("help", QT_TR_NOOP("blah"))
+        next(IDENTIFIER);
+        next(LPAREN);
+        next(STRING_LITERAL);
+        infoDef.value = symbol().unquotedLexem();
+        next(RPAREN);
+    }
+    next(RPAREN);
+    def->classInfoList += infoDef;
+}
+
+void Moc::parseInterfaces(ClassDef *def)
+{
+    next(LPAREN);
+    while (test(IDENTIFIER)) {
+        QList<ClassDef::Interface> iface;
+        iface += ClassDef::Interface(lexem());
+        while (test(SCOPE)) {
+            iface.last().className += lexem();
+            next(IDENTIFIER);
+            iface.last().className += lexem();
+        }
+        while (test(COLON)) {
+            next(IDENTIFIER);
+            iface += ClassDef::Interface(lexem());
+            while (test(SCOPE)) {
+                iface.last().className += lexem();
+                next(IDENTIFIER);
+                iface.last().className += lexem();
+            }
+        }
+        // resolve from classnames to interface ids
+        for (int i = 0; i < iface.count(); ++i) {
+            const QByteArray iid = interface2IdMap.value(iface.at(i).className);
+            if (iid.isEmpty())
+                error("Undefined interface");
+
+            iface[i].interfaceId = iid;
+        }
+        def->interfaceList += iface;
+    }
+    next(RPAREN);
+}
+
+void Moc::parseDeclareInterface()
+{
+    next(LPAREN);
+    QByteArray interface;
+    next(IDENTIFIER);
+    interface += lexem();
+    while (test(SCOPE)) {
+        interface += lexem();
+        next(IDENTIFIER);
+        interface += lexem();
+    }
+    next(COMMA);
+    QByteArray iid;
+    if (test(STRING_LITERAL)) {
+        iid = lexem();
+    } else {
+        next(IDENTIFIER);
+        iid = lexem();
+    }
+    interface2IdMap.insert(interface, iid);
+    next(RPAREN);
+}
+
+void Moc::parseDeclareMetatype()
+{
+    next(LPAREN);
+    QByteArray typeName = lexemUntil(RPAREN);
+    typeName.remove(0, 1);
+    typeName.chop(1);
+    metaTypes.append(typeName);
+}
+
+void Moc::parseSlotInPrivate(ClassDef *def, FunctionDef::Access access)
+{
+    next(LPAREN);
+    FunctionDef funcDef;
+    next(IDENTIFIER);
+    funcDef.inPrivateClass = lexem();
+    // also allow void functions
+    if (test(LPAREN)) {
+        next(RPAREN);
+        funcDef.inPrivateClass += "()";
+    }
+    next(COMMA);
+    funcDef.access = access;
+    parseFunction(&funcDef, true);
+    def->slotList += funcDef;
+    while (funcDef.arguments.size() > 0 && funcDef.arguments.last().isDefault) {
+        funcDef.wasCloned = true;
+        funcDef.arguments.removeLast();
+        def->slotList += funcDef;
+    }
+}
+
+QByteArray Moc::lexemUntil(Token target)
+{
+    int from = index;
+    until(target);
+    QByteArray s;
+    while (from <= index) {
+        QByteArray n = symbols.at(from++-1).lexem();
+        if (s.size() && n.size()
+            && is_ident_char(s.at(s.size()-1))
+            && is_ident_char(n.at(0)))
+            s += ' ';
+        s += n;
+    }
+    return s;
+}
+
+bool Moc::until(Token target) {
+    int braceCount = 0;
+    int brackCount = 0;
+    int parenCount = 0;
+    int angleCount = 0;
+    if (index) {
+        switch(symbols.at(index-1).token) {
+        case LBRACE: ++braceCount; break;
+        case LBRACK: ++brackCount; break;
+        case LPAREN: ++parenCount; break;
+        case LANGLE: ++angleCount; break;
+        default: break;
+        }
+    }
+    while (index < symbols.size()) {
+        Token t = symbols.at(index++).token;
+        switch (t) {
+        case LBRACE: ++braceCount; break;
+        case RBRACE: --braceCount; break;
+        case LBRACK: ++brackCount; break;
+        case RBRACK: --brackCount; break;
+        case LPAREN: ++parenCount; break;
+        case RPAREN: --parenCount; break;
+        case LANGLE: ++angleCount; break;
+        case RANGLE: --angleCount; break;
+        case GTGT: angleCount -= 2; t = RANGLE; break;
+        default: break;
+        }
+        if (t == target
+            && braceCount <= 0
+            && brackCount <= 0
+            && parenCount <= 0
+            && (target != RANGLE || angleCount <= 0))
+            return true;
+
+        if (braceCount < 0 || brackCount < 0 || parenCount < 0
+            || (target == RANGLE && angleCount < 0)) {
+            --index;
+            break;
+        }
+    }
+    return false;
+}
+
+void Moc::checkSuperClasses(ClassDef *def)
+{
+    const QByteArray firstSuperclass = def->superclassList.value(0).first;
+
+    if (!knownQObjectClasses.contains(firstSuperclass)) {
+        // enable once we /require/ include paths
+#if 0
+        QByteArray msg;
+        msg += "Class ";
+        msg += def->className;
+        msg += " contains the Q_OBJECT macro and inherits from ";
+        msg += def->superclassList.value(0);
+        msg += " but that is not a known QObject subclass. You may get compilation errors.";
+        warning(msg.constData());
+#endif
+        return;
+    }
+    for (int i = 1; i < def->superclassList.count(); ++i) {
+        const QByteArray superClass = def->superclassList.at(i).first;
+        if (knownQObjectClasses.contains(superClass)) {
+            QByteArray msg;
+            msg += "Class ";
+            msg += def->classname;
+            msg += " inherits from two QObject subclasses ";
+            msg += firstSuperclass;
+            msg += " and ";
+            msg += superClass;
+            msg += ". This is not supported!";
+            warning(msg.constData());
+        }
+
+        if (interface2IdMap.contains(superClass)) {
+            bool registeredInterface = false;
+            for (int i = 0; i < def->interfaceList.count(); ++i)
+                if (def->interfaceList.at(i).first().className == superClass) {
+                    registeredInterface = true;
+                    break;
+                }
+
+            if (!registeredInterface) {
+                QByteArray msg;
+                msg += "Class ";
+                msg += def->classname;
+                msg += " implements the interface ";
+                msg += superClass;
+                msg += " but does not list it in Q_INTERFACES. qobject_cast to ";
+                msg += superClass;
+                msg += " will not work!";
+                warning(msg.constData());
+            }
+        }
+    }
+}
+
+
+QT_END_NAMESPACE