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::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::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 Preprocessor::parseDefineArguments(Macro *m) { Symbols arguments; while (hasNext()) { while (test(PP_WHITESPACE)) {} Token t = next(); if (t == PP_RPAREN) break; if (t != PP_IDENTIFIER) { QByteArray l = lexem(); if (l == "...") { m->isVariadic = true; arguments += Symbol(symbol().lineNum, PP_IDENTIFIER, "__VA_ARGS__"); while (test(PP_WHITESPACE)) {} if (!test(PP_RPAREN)) error("missing ')' in macro argument list"); break; } else if (!is_identifier(l.constData(), l.length())) { qDebug() << l; error("Unexpected character in macro argument list."); } } Symbol arg = symbol(); if (arguments.contains(arg)) error("Duplicate macro parameter."); arguments += symbol(); while (test(PP_WHITESPACE)) {} t = next(); if (t == PP_RPAREN) break; if (t == PP_COMMA) continue; if (lexem() == "...") { //GCC extension: #define FOO(x, y...) x(y) // The last argument was already parsed. Just mark the macro as variadic. m->isVariadic = true; while (test(PP_WHITESPACE)) {} if (!test(PP_RPAREN)) error("missing ')' in macro argument list"); break; } error("Unexpected character in macro argument list."); } m->arguments = arguments; while (test(PP_WHITESPACE)) {} }
int PP_Expression::primary_expression() { int value; if (test(PP_LPAREN)) { value = conditional_expression(); test(PP_RPAREN); } else { next(); value = lexem().toInt(0, 0); } return value; }
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::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); }
Expression *ExpressionBuilder::primary_expression() { Expression *value; if (test('(')) { if (moreTokens(1)) value = conditional_expression(); else value = createIntLiteral(0); // Syntax error. test(')'); } else { next(); bool ok; int val = QString::fromLatin1(lexem()).toInt(&ok, 0); if(ok) value = createIntLiteral(val); else value = createMacroReference(MacroReference::ValueRef, createTokenList(i -1)); } return value; }
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; } }
void Preprocessor::preprocess(const QByteArray &filename, Symbols &preprocessed) { currentFilenames.push(filename); preprocessed.reserve(preprocessed.size() + symbols.size()); while (hasNext()) { Token token = next(); switch (token) { case PP_INCLUDE: { int lineNum = symbol().lineNum; QByteArray include; bool local = false; if (test(PP_STRING_LITERAL)) { local = lexem().startsWith('\"'); include = unquotedLexem(); } else continue; until(PP_NEWLINE); // #### stringery QFileInfo fi; if (local) fi.setFile(QFileInfo(QString::fromLocal8Bit(filename)).dir(), QString::fromLocal8Bit(include)); for (int j = 0; j < Preprocessor::includes.size() && !fi.exists(); ++j) { const IncludePath &p = Preprocessor::includes.at(j); if (p.isFrameworkPath) { const int slashPos = include.indexOf('/'); if (slashPos == -1) continue; QByteArray frameworkCandidate = include.left(slashPos); frameworkCandidate.append(".framework/Headers/"); fi.setFile(QString::fromLocal8Bit(QByteArray(p.path + '/' + frameworkCandidate)), QString::fromLocal8Bit(include.mid(slashPos + 1))); } else { fi.setFile(QString::fromLocal8Bit(p.path), QString::fromLocal8Bit(include)); } // try again, maybe there's a file later in the include paths with the same name // (186067) if (fi.isDir()) { fi = QFileInfo(); continue; } } if (!fi.exists() || fi.isDir()) continue; include = fi.canonicalFilePath().toLocal8Bit(); if (Preprocessor::preprocessedIncludes.contains(include)) continue; Preprocessor::preprocessedIncludes.insert(include); QFile file(QString::fromLocal8Bit(include)); if (!file.open(QFile::ReadOnly)) continue; QByteArray input = file.readAll(); file.close(); if (input.isEmpty()) continue; Symbols saveSymbols = symbols; int saveIndex = index; // phase 1: get rid of backslash-newlines input = cleaned(input); // phase 2: tokenize for the preprocessor symbols = tokenize(input); input.clear(); index = 0; // phase 3: preprocess conditions and substitute macros preprocessed += Symbol(0, MOC_INCLUDE_BEGIN, include); preprocess(include, preprocessed); preprocessed += Symbol(lineNum, MOC_INCLUDE_END, include); symbols = saveSymbols; index = saveIndex; continue; } case PP_DEFINE: { next(IDENTIFIER); QByteArray name = lexem(); int start = index; until(PP_NEWLINE); Macro macro; macro.symbols.reserve(index - start - 1); for (int i = start; i < index - 1; ++i) macro.symbols += symbols.at(i); macros.insert(name, macro); continue; } case PP_UNDEF: { next(IDENTIFIER); QByteArray name = lexem(); until(PP_NEWLINE); macros.remove(name); continue; } case PP_IDENTIFIER: { // if (macros.contains(symbol())) // ; } // we _could_ easily substitute macros by the following // four lines, but we choose not to. /* if (macros.contains(sym.lexem())) { preprocessed += substitute(macros, symbols, i); continue; } */ break; case PP_HASH: until(PP_NEWLINE); continue; // skip unknown preprocessor statement case PP_IFDEF: case PP_IFNDEF: case PP_IF: while (!evaluateCondition()) { if (!skipBranch()) break; if (test(PP_ELIF)) { } else { until(PP_NEWLINE); break; } } continue; case PP_ELIF: case PP_ELSE: skipUntilEndif(); // fall through case PP_ENDIF: until(PP_NEWLINE); continue; case SIGNALS: case SLOTS: { Symbol sym = symbol(); if (macros.contains("QT_NO_KEYWORDS")) sym.token = IDENTIFIER; else sym.token = (token == SIGNALS ? Q_SIGNALS_TOKEN : Q_SLOTS_TOKEN); preprocessed += sym; } continue; default: break; } preprocessed += symbol(); } currentFilenames.pop(); }
void Preprocessor::preprocess(const QByteArray &filename, Symbols &preprocessed) { currentFilenames.push(filename); preprocessed.reserve(preprocessed.size() + symbols.size()); while (hasNext()) { Token token = next(); switch (token) { case PP_INCLUDE: { int lineNum = symbol().lineNum; QByteArray include; bool local = false; if (test(PP_STRING_LITERAL)) { local = lexem().startsWith('\"'); include = unquotedLexem(); } else continue; until(PP_NEWLINE); // #### stringery QFileInfo fi; if (local) fi.setFile(QFileInfo(QString::fromLocal8Bit(filename.constData())).dir(), QString::fromLocal8Bit(include.constData())); for (int j = 0; j < Preprocessor::includes.size() && !fi.exists(); ++j) { const IncludePath &p = Preprocessor::includes.at(j); if (p.isFrameworkPath) { const int slashPos = include.indexOf('/'); if (slashPos == -1) continue; QByteArray frameworkCandidate = include.left(slashPos); frameworkCandidate.append(".framework/Headers/"); fi.setFile(QString::fromLocal8Bit(QByteArray(p.path + '/' + frameworkCandidate).constData()), QString::fromLocal8Bit(include.mid(slashPos + 1).constData())); } else { fi.setFile(QString::fromLocal8Bit(p.path.constData()), QString::fromLocal8Bit(include.constData())); } // try again, maybe there's a file later in the include paths with the same name // (186067) if (fi.isDir()) { fi = QFileInfo(); continue; } } if (!fi.exists() || fi.isDir()) continue; include = fi.canonicalFilePath().toLocal8Bit(); if (Preprocessor::preprocessedIncludes.contains(include)) continue; Preprocessor::preprocessedIncludes.insert(include); QFile file(QString::fromLocal8Bit(include.constData())); if (!file.open(QFile::ReadOnly)) continue; QByteArray input = file.readAll(); file.close(); if (input.isEmpty()) continue; Symbols saveSymbols = symbols; int saveIndex = index; // phase 1: get rid of backslash-newlines input = cleaned(input); // phase 2: tokenize for the preprocessor symbols = tokenize(input); input.clear(); index = 0; // phase 3: preprocess conditions and substitute macros preprocessed += Symbol(0, MOC_INCLUDE_BEGIN, include); preprocess(include, preprocessed); preprocessed += Symbol(lineNum, MOC_INCLUDE_END, include); symbols = saveSymbols; index = saveIndex; continue; } case PP_DEFINE: { next(IDENTIFIER); QByteArray name = lexem(); Macro macro; macro.isVariadic = false; Token t = next(); if (t == LPAREN) { // we have a function macro macro.isFunction = true; parseDefineArguments(¯o); } else if (t == PP_WHITESPACE) { macro.isFunction = false; } else { error("Moc: internal error"); } int start = index; until(PP_NEWLINE); macro.symbols.reserve(index - start - 1); // remove whitespace where there shouldn't be any: // Before and after the macro, after a # and around ## Token lastToken = HASH; // skip shitespace at the beginning for (int i = start; i < index - 1; ++i) { Token token = symbols.at(i).token; if (token == PP_WHITESPACE || token == WHITESPACE) { if (lastToken == PP_HASH || lastToken == HASH || lastToken == PP_HASHHASH || lastToken == PP_WHITESPACE || lastToken == WHITESPACE) continue; } else if (token == PP_HASHHASH) { if (!macro.symbols.isEmpty() && (lastToken == PP_WHITESPACE || lastToken == WHITESPACE)) macro.symbols.pop_back(); } macro.symbols.append(symbols.at(i)); lastToken = token; } // remove trailing whitespace while (!macro.symbols.isEmpty() && (macro.symbols.last().token == PP_WHITESPACE || macro.symbols.last().token == WHITESPACE)) macro.symbols.pop_back(); if (!macro.symbols.isEmpty()) { if (macro.symbols.first().token == PP_HASHHASH || macro.symbols.last().token == PP_HASHHASH) { error("'##' cannot appear at either end of a macro expansion"); } if (macro.symbols.last().token == HASH || macro.symbols.last().token == PP_HASH) { error("'#' is not followed by a macro parameter"); } } macros.insert(name, macro); continue; } case PP_UNDEF: { next(IDENTIFIER); QByteArray name = lexem(); until(PP_NEWLINE); macros.remove(name); continue; } case PP_IDENTIFIER: { // substitute macros preprocessed += macroExpand(this, symbols, index, symbol().lineNum, true); continue; } case PP_HASH: until(PP_NEWLINE); continue; // skip unknown preprocessor statement case PP_IFDEF: case PP_IFNDEF: case PP_IF: while (!evaluateCondition()) { if (!skipBranch()) break; if (test(PP_ELIF)) { } else { until(PP_NEWLINE); break; } } continue; case PP_ELIF: case PP_ELSE: skipUntilEndif(); // fall through case PP_ENDIF: until(PP_NEWLINE); continue; case PP_NEWLINE: continue; case SIGNALS: case SLOTS: { Symbol sym = symbol(); if (macros.contains("QT_NO_KEYWORDS")) sym.token = IDENTIFIER; else sym.token = (token == SIGNALS ? Q_SIGNALS_TOKEN : Q_SLOTS_TOKEN); preprocessed += sym; } continue; default: break; } preprocessed += symbol(); } currentFilenames.pop(); }
void Moc::createPropertyDef(PropertyDef &propDef) { QByteArray type = parseType().name; if (type.isEmpty()) error(); propDef.designable = propDef.scriptable = propDef.stored = "true"; propDef.user = "******"; /* 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); } }
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; }
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 Q_PRIVATE_PROPERTY_TOKEN: parsePrivateProperty(&def); 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); checkProperties(&def); classList += def; knownQObjectClasses.insert(def.classname); knownQObjectClasses.insert(def.qualified); } } }
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 = "******"; /* 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"; propDef.type = type; next(); propDef.name = lexem(); while (test(IDENTIFIER)) { QByteArray l = lexem(); 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.notify.isEmpty()) def->notifyableProperties++; def->propertyList += propDef; }
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) || test(ANDAND)) { type.name += ' '; type.name += lexem(); if (lookup(0) == AND) type.referenceType = Type::Reference; else if (lookup(0) == ANDAND) type.referenceType = Type::RValueReference; 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; }