diff options
-rw-r--r-- | src/repparser/parser.g | 76 | ||||
-rw-r--r-- | tests/auto/repparser/tst_parser.cpp | 36 | ||||
-rw-r--r-- | tools/repc/repcodegenerator.cpp | 43 | ||||
-rw-r--r-- | tools/repc/repcodegenerator.h | 3 |
4 files changed, 138 insertions, 20 deletions
diff --git a/src/repparser/parser.g b/src/repparser/parser.g index 671ee00..ef942ca 100644 --- a/src/repparser/parser.g +++ b/src/repparser/parser.g @@ -45,6 +45,7 @@ %token semicolon "[semicolon];" %token class "[class]class[ \\t]+(?<name>[A-Za-z_][A-Za-z0-9_]+)[ \\t]*" %token pod "[pod]POD[ \\t]*(?<name>[A-Za-z_][A-Za-z0-9_]+)[ \\t]*\\((?<types>[^\\)]*)\\);?[ \\t]*" +%token flag "[flag][ \\t]*FLAG[ \t]*\\([ \t]*(?<name>[A-Za-z_][A-Za-z0-9_]*)[ \t]+(?<enum>[A-Za-z_][A-Za-z0-9_]*)[ \t]*\\)[ \t]*" %token enum "[enum][ \\t]*ENUM[ \t]+(?:(?<class>class[ \t]+))?(?<name>[A-Za-z_][A-Za-z0-9_]*)[ \t]*(?::[ \t]*(?<type>[a-zA-Z0-9 _:]*[a-zA-Z0-9_])[ \t]*)?" %token enum_param "[enum_param][ \\t]*(?<name>[A-Za-z_][A-Za-z0-9_]*)[ \\t]*(=[ \\t]*(?<value>-\\d+|0[xX][0-9A-Fa-f]+|\\d+))?[ \\t]*" %token prop "[prop][ \\t]*PROP[ \\t]*\\((?<args>[^\\)]+)\\);?[ \\t]*" @@ -168,9 +169,20 @@ struct ASTEnum bool isSigned; bool isScoped; int max; + int flagIndex = -1; }; Q_DECLARE_TYPEINFO(ASTEnum, Q_RELOCATABLE_TYPE); +struct ASTFlag +{ + explicit ASTFlag(const QString &name = {}, const QString &_enum = {}); + + bool isValid() const; + QString name; + QString _enum; +}; +Q_DECLARE_TYPEINFO(ASTFlag, Q_RELOCATABLE_TYPE); + struct ASTModelRole { ASTModelRole(const QString &roleName = QString()) @@ -204,6 +216,7 @@ struct ASTClass QList<ASTFunction> signalsList; QList<ASTFunction> slotsList; QList<ASTEnum> enums; + QList<ASTFlag> flags; bool hasPersisted; QList<ASTModel> modelMetadata; QList<int> subClassPropertyIndices; @@ -236,6 +249,7 @@ struct AST QList<ASTClass> classes; QList<POD> pods; QList<ASTEnum> enums; + QList<ASTFlag> flags; QList<QString> enumUses; QStringList preprocessorDirectives; }; @@ -384,6 +398,16 @@ ASTEnum::ASTEnum(const QString &name) { } +ASTFlag::ASTFlag(const QString &name, const QString &_enum) + : name(name), _enum(_enum) +{ +} + +bool ASTFlag::isValid() const +{ + return !name.isEmpty(); +} + ASTClass::ASTClass(const QString &name) : name(name), hasPersisted(false) { @@ -682,6 +706,7 @@ Type: Enum; } break; ./ +Type: Flag | Flag Newlines; Comma: comma | comma Newlines; @@ -733,7 +758,7 @@ Class: ClassStart Start Stop; ./ ClassTypes: ClassType | ClassType ClassTypes; -ClassType: DecoratedProp | DecoratedSignal | DecoratedSlot | DecoratedModel | DecoratedClass | Comments; +ClassType: DecoratedProp | DecoratedSignal | DecoratedSlot | DecoratedModel | DecoratedClass | DecoratedClassFlag | Comments; ClassType: Enum; /. case $rule_number: @@ -749,6 +774,7 @@ DecoratedProp: Prop | Comments Prop | Prop Newlines | Comments Prop Newlines; DecoratedModel: Model | Comments Model | Model Newlines | Comments Model Newlines; DecoratedClass: ChildRep | Comments ChildRep | ChildRep Newlines | Comments ChildRep Newlines; DecoratedEnumParam: EnumParam | Comments EnumParam | EnumParam Newlines | Comments EnumParam Newlines; +DecoratedClassFlag: ClassFlag | Comments ClassFlag | ClassFlag Newlines | Comments ClassFlag Newlines; Start: start | Comments start | start Newlines | Comments start Newlines; Stop: stop | stop Newlines; @@ -803,6 +829,29 @@ EnumParam: enum_param; break; ./ +ClassFlag: flag; +/. + case $rule_number: + { + const QString name = captured().value(QLatin1String("name")); + const QString _enum = captured().value(QLatin1String("enum")); + int enumIndex = 0; + for (auto &en : m_astClass.enums) { + if (en.name == _enum) { + en.flagIndex = m_astClass.flags.count(); + break; + } + enumIndex++; + } + if (enumIndex == m_astClass.enums.count()) { + setErrorString(QLatin1String("FLAG: Unknown (class) enum: %1").arg(_enum)); + return false; + } + m_astClass.flags.append(ASTFlag(name, _enum)); + } + break; +./ + Prop: prop; /. case $rule_number: @@ -915,6 +964,31 @@ UseEnum: use_enum; break; ./ +Flag: flag; +/. + case $rule_number: + { + const QString name = captured().value(QLatin1String("name")); + const QString _enum = captured().value(QLatin1String("enum")); + int enumIndex = 0; + for (auto &en : m_ast.enums) { + if (en.name == _enum) { + en.flagIndex = m_ast.flags.count(); + break; + } + if (en.name == _enum) + break; + enumIndex++; + } + if (enumIndex == m_ast.enums.count()) { + setErrorString(QLatin1String("FLAG: Unknown (global) enum: %1").arg(_enum)); + return false; + } + m_ast.flags.append(ASTFlag(name, _enum)); + } + break; +./ + --Error conditions/messages ClassType: ClassStart; /. diff --git a/tests/auto/repparser/tst_parser.cpp b/tests/auto/repparser/tst_parser.cpp index 0c15002..5d2e818 100644 --- a/tests/auto/repparser/tst_parser.cpp +++ b/tests/auto/repparser/tst_parser.cpp @@ -402,16 +402,19 @@ void tst_Parser::testTypedEnums_data() QTest::addColumn<QString>("expectedtype"); QTest::addColumn<bool>("inclass"); QTest::addColumn<bool>("isscoped"); + QTest::addColumn<bool>("isflag"); - for (int i = 0; i <= 3; ++i) { + for (int i = 0; i <= 7; ++i) { bool inclass = i % 2 == 1; - bool isscoped = i > 1; - QString identifier = inclass ? QLatin1String("%1 %2 in class") : QLatin1String("%1 %2 outside class"); + bool isscoped = i % 4 > 1; + bool isflag = i > 3; + QString identifier = inclass ? QLatin1String("%1 %2 %3 in class") : QLatin1String("%1 %2 %3 outside class"); QString scopeString = isscoped ? QLatin1String("Scoped") : QLatin1String("Non-scoped"); - QTest::newRow(identifier.arg(scopeString, "no type").toLatin1()) << "preset {presetNumber}" << QString() << inclass << isscoped; - QTest::newRow(identifier.arg(scopeString, "quint16").toLatin1()) << "preset : quint16 {presetNumber}" << "quint16" << inclass << isscoped; - QTest::newRow(identifier.arg(scopeString, "qint64").toLatin1()) << "preset : qint64 {presetNumber}" << "qint64" << inclass << isscoped; - QTest::newRow(identifier.arg(scopeString, "unsigned char").toLatin1()) << "preset: unsigned char {presetNumber}" << "unsigned char" << inclass << isscoped; + QString flagString = isflag ? QLatin1String("Flag") : QLatin1String("Enum"); + QTest::newRow(identifier.arg(scopeString, flagString, "no type").toLatin1()) << "preset {presetNumber}" << QString() << inclass << isscoped << isflag; + QTest::newRow(identifier.arg(scopeString, flagString, "quint16").toLatin1()) << "preset : quint16 {presetNumber}" << "quint16" << inclass << isscoped << isflag; + QTest::newRow(identifier.arg(scopeString, flagString, "qint64").toLatin1()) << "preset : qint64 {presetNumber}" << "qint64" << inclass << isscoped << isflag; + QTest::newRow(identifier.arg(scopeString, flagString, "unsigned char").toLatin1()) << "preset: unsigned char {presetNumber}" << "unsigned char" << inclass << isscoped << isflag; } } @@ -421,16 +424,23 @@ void tst_Parser::testTypedEnums() QFETCH(QString, expectedtype); QFETCH(bool, inclass); QFETCH(bool, isscoped); + QFETCH(bool, isflag); QTemporaryFile file; file.open(); QTextStream stream(&file); - if (!inclass) + if (!inclass) { stream << "ENUM " << (isscoped ? "class " : "") << enumdeclaration << Qt::endl; + if (isflag) + stream << "FLAG(MyFlags preset)" << Qt::endl; + } stream << "class TestClass" << Qt::endl; stream << "{" << Qt::endl; - if (inclass) + if (inclass) { stream << "ENUM " << (isscoped ? "class " : "") << enumdeclaration << Qt::endl; + if (isflag) + stream << "FLAG(MyFlags preset)" << Qt::endl; + } stream << "};" << Qt::endl; file.seek(0); @@ -440,15 +450,23 @@ void tst_Parser::testTypedEnums() const AST ast = parser.ast(); QCOMPARE(ast.classes.count(), 1); ASTEnum enums; + ASTFlag flags; if (inclass) { const ASTClass astClass = ast.classes.first(); QCOMPARE(astClass.enums.count(), 1); enums = astClass.enums.first(); + if (isflag) + flags = astClass.flags.first(); } else { QCOMPARE(ast.enums.count(), 1); enums = ast.enums.first(); + if (isflag) + flags = ast.flags.first(); } QVERIFY(enums.isScoped == isscoped); + QVERIFY(enums.flagIndex == (isflag ? 0 : -1)); + QVERIFY(flags.name == (isflag ? "MyFlags" : QString{})); + QVERIFY(flags._enum == (isflag ? "preset" : QString{})); QCOMPARE(enums.type, expectedtype); const QList<ASTEnumParam> paramList = enums.params; QVERIFY(paramList.count() == 1); diff --git a/tools/repc/repcodegenerator.cpp b/tools/repc/repcodegenerator.cpp index 47e9ff7..c4913f8 100644 --- a/tools/repc/repcodegenerator.cpp +++ b/tools/repc/repcodegenerator.cpp @@ -225,8 +225,12 @@ void RepCodeGenerator::generate(const AST &ast, Mode mode, QString fileName) } generateHeader(mode, stream, ast); - for (const ASTEnum &en : ast.enums) - generateEnumGadget(stream, en, QStringLiteral("%1Enum").arg(en.name)); + for (const ASTEnum &en : ast.enums) { + ASTFlag flag; + if (en.flagIndex >= 0) + flag = ast.flags.at(en.flagIndex); + generateEnumGadget(stream, en, flag, QStringLiteral("%1Enum").arg(en.name)); + } for (const POD &pod : ast.pods) generatePOD(stream, pod); @@ -499,7 +503,7 @@ void RepCodeGenerator::generatePOD(QTextStream &out, const POD &pod) QString getEnumType(const ASTEnum &en) { - if (en.isScoped && !en.type.isEmpty()) + if (!en.type.isEmpty()) return en.type; if (en.isSigned) { if (en.max < 0x7F) @@ -532,13 +536,12 @@ void RepCodeGenerator::generateDeclarationsForEnums(QTextStream &out, const QLis out << " };\n"; - if (generateQENUM) { + if (generateQENUM) out << " Q_ENUM(" << en.name << ")\n"; - } } } -void RepCodeGenerator::generateEnumGadget(QTextStream &out, const ASTEnum &en, const QString &className) +void RepCodeGenerator::generateEnumGadget(QTextStream &out, const ASTEnum &en, const ASTFlag &flag, const QString &className) { out << "class " << className << "\n" "{\n" @@ -551,9 +554,15 @@ void RepCodeGenerator::generateEnumGadget(QTextStream &out, const ASTEnum &en, c auto enums = QList<ASTEnum>() << en; generateDeclarationsForEnums(out, enums); + if (flag.isValid()) { + out << " Q_DECLARE_FLAGS(" << flag.name << ", " << flag._enum << ")\n"; + out << " Q_FLAG(" << flag.name << ")\n"; + } out << "};\n\n"; + if (flag.isValid()) + out << "Q_DECLARE_OPERATORS_FOR_FLAGS(" << className << "::" << flag.name << ")\n\n"; } QString RepCodeGenerator::generateMetaTypeRegistration(const QSet<QString> &metaTypes) @@ -642,6 +651,10 @@ void RepCodeGenerator::generateClass(Mode mode, QTextStream &out, const ASTClass out << "" << Qt::endl; out << "public:" << Qt::endl; generateDeclarationsForEnums(out, astClass.enums); + for (const auto &flag : astClass.flags) { + out << " Q_DECLARE_FLAGS(" << flag.name << ", " << flag._enum << ")\n"; + out << " Q_FLAG(" << flag.name << ")\n"; + } } } @@ -948,6 +961,10 @@ void RepCodeGenerator::generateClass(Mode mode, QTextStream &out, const ASTClass out << " friend class QT_PREPEND_NAMESPACE(QRemoteObjectNode);" << Qt::endl; out << "};\n\n"; + if (mode != SIMPLE_SOURCE) { + for (const ASTFlag &flag : astClass.flags) + out << "Q_DECLARE_OPERATORS_FOR_FLAGS(" << className << "::" << flag.name << ")\n\n"; + } } void RepCodeGenerator::generateSourceAPI(QTextStream &out, const ASTClass &astClass) @@ -960,6 +977,8 @@ void RepCodeGenerator::generateSourceAPI(QTextStream &out, const ASTClass &astCl // Include enum definition in SourceAPI generateDeclarationsForEnums(out, astClass.enums, false); } + for (const auto &flag : astClass.flags) + out << QLatin1String(" typedef QFlags<typename ObjectType::%1> %2;").arg(flag._enum, flag.name) << Qt::endl; out << QString::fromLatin1(" %1(ObjectType *object, const QString &name = QLatin1String(\"%2\"))").arg(className, astClass.name) << Qt::endl; out << QStringLiteral(" : SourceApiMap(), m_name(name)") << Qt::endl; out << QStringLiteral(" {") << Qt::endl; @@ -967,16 +986,22 @@ void RepCodeGenerator::generateSourceAPI(QTextStream &out, const ASTClass &astCl out << QStringLiteral(" Q_UNUSED(object)") << Qt::endl; const auto enumCount = astClass.enums.count(); + const auto totalCount = enumCount + astClass.flags.count(); for (int i : astClass.subClassPropertyIndices) { const ASTProperty &child = astClass.properties.at(i); out << QString::fromLatin1(" using %1_type_t = typename std::remove_pointer<decltype(object->%1())>::type;") .arg(child.name) << Qt::endl; } - out << QString::fromLatin1(" m_enums[0] = %1;").arg(enumCount) << Qt::endl; + out << QString::fromLatin1(" m_enums[0] = %1;").arg(totalCount) << Qt::endl; for (qsizetype i = 0; i < enumCount; ++i) { const auto enumerator = astClass.enums.at(i); out << QString::fromLatin1(" m_enums[%1] = ObjectType::staticMetaObject.indexOfEnumerator(\"%2\");") - .arg(i+1).arg(enumerator.name) << Qt::endl; + .arg(i+1).arg(enumerator.name) << Qt::endl; + } + for (qsizetype i = enumCount; i < totalCount; ++i) { + const auto flag = astClass.flags.at(i - enumCount); + out << QString::fromLatin1(" m_enums[%1] = ObjectType::staticMetaObject.indexOfEnumerator(\"%2\");") + .arg(i+1).arg(flag.name) << Qt::endl; } const auto propCount = astClass.properties.count(); out << QString::fromLatin1(" m_properties[0] = %1;").arg(propCount) << Qt::endl; @@ -1282,7 +1307,7 @@ void RepCodeGenerator::generateSourceAPI(QTextStream &out, const ASTClass &astCl << QStringLiteral("\"}; }") << Qt::endl; out << QStringLiteral("") << Qt::endl; - out << QString::fromLatin1(" int m_enums[%1];").arg(enumCount + 1) << Qt::endl; + out << QString::fromLatin1(" int m_enums[%1];").arg(totalCount + 1) << Qt::endl; out << QString::fromLatin1(" int m_properties[%1];").arg(propCount+1) << Qt::endl; out << QString::fromLatin1(" int m_signals[%1];").arg(signalCount+changedCount+1) << Qt::endl; out << QString::fromLatin1(" int m_methods[%1];").arg(methodCount+1) << Qt::endl; diff --git a/tools/repc/repcodegenerator.h b/tools/repc/repcodegenerator.h index e918729..b48bf30 100644 --- a/tools/repc/repcodegenerator.h +++ b/tools/repc/repcodegenerator.h @@ -38,6 +38,7 @@ struct AST; struct ASTClass; struct POD; struct ASTEnum; +struct ASTFlag; struct ASTProperty; class QIODevice; @@ -66,7 +67,7 @@ private: void generateSimpleSetter(QTextStream &out, const ASTProperty &property, bool generateOverride = true); void generatePOD(QTextStream &out, const POD &pod); - void generateEnumGadget(QTextStream &out, const ASTEnum &en, const QString &className); + void generateEnumGadget(QTextStream &out, const ASTEnum &en, const ASTFlag &flag, const QString &className); void generateDeclarationsForEnums(QTextStream &out, const QList<ASTEnum> &enums, bool generateQENUM=true); QString formatQPropertyDeclarations(const POD &pod); QString formatConstructors(const POD &pod); |