-- Copyright (C) 2014-2020 Ford Motor Company. -- SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only %parser rep_grammar %decl repparser.h %impl repparser.cpp %token_prefix Token_ %token semicolon "[semicolon];" %token class "[class]class[ \\t]+(?:(?[A-Za-z_][A-Za-z0-9_]*)[ \\t]+)?(?[A-Za-z_][A-Za-z0-9_]+)[ \\t]*" %token pod "[pod]POD[ \\t]*(?:(?[A-Za-z_][A-Za-z0-9_]*)[ \\t]+)?(?[A-Za-z_][A-Za-z0-9_]+)[ \\t]*\\((?[^\\)]*)\\);?[ \\t]*" %token pod2 "[pod2]POD[ \\t]*(?:(?[A-Za-z_][A-Za-z0-9_]*)[ \\t]+)?(?[A-Za-z_][A-Za-z0-9_]+)[ \\t]*" %token flag "[flag][ \\t]*FLAG[ \t]*\\([ \t]*(?[A-Za-z_][A-Za-z0-9_]*)[ \t]+(?[A-Za-z_][A-Za-z0-9_]*)[ \t]*\\)[ \t]*" %token enum "[enum][ \\t]*ENUM[ \t]+(?:(?class[ \t]+))?(?[A-Za-z_][A-Za-z0-9_]*)[ \t]*(?::[ \t]*(?[a-zA-Z0-9 _:]*[a-zA-Z0-9_])[ \t]*)?" %token prop "[prop][ \\t]*PROP[ \\t]*\\((?[^\\)]+)\\);?[ \\t]*" %token use_enum "[use_enum]USE_ENUM[ \\t]*\\((?[^\\)]*)\\);?[ \\t]*" %token signal "[signal][ \\t]*SIGNAL[ \\t]*\\([ \\t]*(?\\S+)[ \\t]*\\((?[^\\)]*)\\)[ \\t]*\\);?[ \\t]*" %token slot "[slot][ \\t]*SLOT[ \\t]*\\((?[^\\(]*)\\((?[^\\)]*)\\)[ \\t]*\\);?[ \\t]*" %token model "[model][ \\t]*MODEL[ \\t]+(?[A-Za-z_][A-Za-z0-9_]+)\\((?[^\\)]+)\\)[ \\t]*;?[ \\t]*" %token childrep "[childrep][ \\t]*CLASS[ \\t]+(?[A-Za-z_][A-Za-z0-9_]+)\\((?[^\\)]+)\\)[ \\t]*;?[ \\t]*" %token qualifier "[qualifier][ \\t]*(?const|unsigned|signed)[ \\t]*" %token passbyqual "[passbyqual][ \\t]*(?&)[ \\t]*" %token symbol "[symbol][ \\t]*(?[A-Za-z_][A-Za-z0-9_]*)[ \\t]*" %token value "[value][ \\t]*(?-\\d+|0[xX][0-9A-Fa-f]+|\\d+)[ \\t]*" %token start "[start][ \\t]*\\{[ \\t]*" %token stop "[stop][ \\t]*\\};?[ \\t]*" %token comma "[comma]," %token equals "[equals]=" %token comment "[comment](?[ \\t]*//[^\\n]*\\n)" %token mcomment "[mcomment,M](?/\\*(.*?)\\*/)" %token preprocessor_directive "[preprocessor_directive](?#[ \\t]*[^\\n]*\\n)" %token newline "[newline](\\r)?\\n" %token tstart "[tstart]<" %token tstop "[tstop]>[ \\t]*" -- Define associativity for newline to resolve ambiguity when there are multiple %right newline %start TopLevel /: #ifndef REPPARSER_H #define REPPARSER_H #include #include #include #include #include #include #include QT_BEGIN_NAMESPACE class QIODevice; class QCryptographicHash; struct AST; struct SignedType { SignedType(const QString &name = QString()); virtual ~SignedType() {} void generateSignature(AST &ast); virtual QString typeName() const; virtual void signature_impl(const AST &ast, QCryptographicHash &checksum) = 0; QString name; QString compilerAttribute; }; /// A property of a Class declaration struct ASTProperty { enum Modifier { Constant, ReadOnly, ReadPush, ReadWrite, SourceOnlySetter }; ASTProperty(); ASTProperty(const QString &type, const QString &name, const QString &defaultValue, Modifier modifier, bool persisted, bool isPointer=false); QString type; QString name; QString defaultValue; Modifier modifier; bool persisted; bool isPointer; }; Q_DECLARE_TYPEINFO(ASTProperty, Q_RELOCATABLE_TYPE); struct ASTDeclaration { enum VariableType { None = 0, Constant = 1, Reference = 2, }; Q_DECLARE_FLAGS(VariableTypes, VariableType) ASTDeclaration(const QString &declarationType = QString(), const QString &declarationName = QString(), VariableTypes declarationVariableType = None) : type(declarationType), name(declarationName), variableType(declarationVariableType) { } QString asString(bool withName) const; QString type; QString name; VariableTypes variableType; }; Q_DECLARE_TYPEINFO(ASTDeclaration, Q_RELOCATABLE_TYPE); struct ASTFunction { enum ParamsAsStringFormat { Default, Normalized }; explicit ASTFunction(const QString &name = QString(), const QString &returnType = QLatin1String("void")); QString paramsAsString(ParamsAsStringFormat format = Default) const; QStringList paramNames() const; QString returnType; QString name; QList params; }; Q_DECLARE_TYPEINFO(ASTFunction, Q_RELOCATABLE_TYPE); struct ASTEnumParam { ASTEnumParam(const QString ¶mName = QString(), int paramValue = 0) : name(paramName), value(paramValue) { } QString name; int value; }; Q_DECLARE_TYPEINFO(ASTEnumParam, Q_RELOCATABLE_TYPE); struct ASTEnum : public SignedType { explicit ASTEnum(const QString &name = QString()); void signature_impl(const AST &ast, QCryptographicHash &checksum) override; QString typeName() const override; QString type; QString scope; QList params; bool isSigned; bool isScoped; int max; int flagIndex = -1; }; Q_DECLARE_TYPEINFO(ASTEnum, Q_RELOCATABLE_TYPE); struct ASTFlag : public SignedType { explicit ASTFlag(const QString &name = {}, const QString &_enum = {}); void signature_impl(const AST &ast, QCryptographicHash &checksum) override; QString typeName() const override; bool isValid() const; QString _enum; QString scope; }; Q_DECLARE_TYPEINFO(ASTFlag, Q_RELOCATABLE_TYPE); struct ASTModelRole { ASTModelRole(const QString &roleName = QString()) : name(roleName) { } QString name; }; Q_DECLARE_TYPEINFO(ASTModelRole, Q_RELOCATABLE_TYPE); struct ASTModel : public SignedType { ASTModel(const QString &name, const QString &scope, int index = -1) : SignedType(name), scope(scope), propertyIndex(index) {} void signature_impl(const AST &ast, QCryptographicHash &checksum) override; QString typeName() const override; QList roles; QString scope; int propertyIndex; }; Q_DECLARE_TYPEINFO(ASTModel, Q_RELOCATABLE_TYPE); /// A Class declaration struct ASTClass : public SignedType { explicit ASTClass(const QString& name = QString()); void signature_impl(const AST &ast, QCryptographicHash &checksum) override; bool isValid() const; bool hasPointerObjects() const; QList properties; QList signalsList; QList slotsList; QList enums; QList flags; bool hasPersisted; QList modelMetadata; QList subClassPropertyIndices; }; Q_DECLARE_TYPEINFO(ASTClass, Q_RELOCATABLE_TYPE); // The attribute of a POD struct PODAttribute { explicit PODAttribute(const QString &type_ = QString(), const QString &name_ = QString()) : type(type_), name(name_) {} QString type; QString name; }; Q_DECLARE_TYPEINFO(PODAttribute, Q_RELOCATABLE_TYPE); // A POD declaration struct POD : public SignedType { void signature_impl(const AST &ast, QCryptographicHash &checksum) override; QList attributes; QList enums; QList flags; }; Q_DECLARE_TYPEINFO(POD, Q_RELOCATABLE_TYPE); // The AST representation of a .rep file struct AST { QList classes; QList pods; QList enums; QList flags; QList enumUses; QStringList preprocessorDirectives; QStringList headerLines; QStringList footerLines; QHash typeSignatures; QByteArray typeData(const QString &type, const QString &className) const; QByteArray functionsData(const QList &functions, const QString &className) const; }; Q_DECLARE_TYPEINFO(AST, Q_RELOCATABLE_TYPE); class RepParser: public QRegexParser { public: explicit RepParser(QIODevice &outputDevice); virtual ~RepParser() {} bool parse() override { return QRegexParser::parse(); } void reset() override; int nextToken(); bool consumeRule(int ruleno); AST ast() const; private: struct TypeParser { void parseArguments(const QString &arguments); void appendParams(ASTFunction &slot); void appendPods(POD &pods); void generateFunctionParameter(QString variableName, const QString &propertyType, int &variableNameIndex, ASTDeclaration::VariableTypes variableType); //Type, Variable QList arguments; }; bool parseProperty(ASTClass &astClass, const QString &propertyDeclaration); /// A helper function to parse modifier flag of property declaration bool parseModifierFlag(const QString &flag, ASTProperty::Modifier &modifier, bool &persisted); bool parseRoles(ASTModel &astModel, const QString &modelRoles); AST m_ast; ASTClass m_astClass; POD m_astPod; QString m_symbol; QString m_argString; ASTEnum m_astEnum; int m_astEnumValue; }; QT_END_NAMESPACE #endif :/ /. #include "repparser.h" #include #include #include // for normalizeTypeInternal #include #include // Code copied from moc.cpp // We cannot depend on QMetaObject::normalizedSignature, // since repc is linked against Qt5Bootstrap (which doesn't offer QMetaObject) when cross-compiling // Thus, just use internal API which is exported in private headers, as moc does static QByteArray normalizeType(const QByteArray &ba) { const char *s = ba.constData(); int len = ba.size(); 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 = normalizeTypeInternal(buf, d); if (buf != stackbuf) delete [] buf; return result; } SignedType::SignedType(const QString &name) : name(name) { } void SignedType::generateSignature(AST &ast) { QCryptographicHash checksum(QCryptographicHash::Sha1); signature_impl(ast, checksum); ast.typeSignatures[typeName()] = checksum.result().toHex(); } QString SignedType::typeName() const { return name; } ASTProperty::ASTProperty() : modifier(ReadPush), persisted(false), isPointer(false) { } ASTProperty::ASTProperty(const QString &type, const QString &name, const QString &defaultValue, Modifier modifier, bool persisted, bool isPointer) : type(type), name(name), defaultValue(defaultValue), modifier(modifier), persisted(persisted), isPointer(isPointer) { } QString ASTDeclaration::asString(bool withName) const { QString str; if (variableType & ASTDeclaration::Constant) str += QLatin1String("const "); str += type; if (variableType & ASTDeclaration::Reference) str += QLatin1String(" &"); if (withName) str += QString::fromLatin1(" %1").arg(name); return str; } ASTFunction::ASTFunction(const QString &name, const QString &returnType) : returnType(returnType), name(name) { } QString ASTFunction::paramsAsString(ParamsAsStringFormat format) const { QString str; for (const ASTDeclaration ¶m : params) { QString paramStr = param.asString(format != Normalized); if (format == Normalized) { paramStr = QString::fromLatin1(::normalizeType(paramStr.toLatin1().constData())); str += paramStr + QLatin1Char(','); } else { str += paramStr + QLatin1String(", "); } } str.chop((format == Normalized ? 1 : 2)); // chop trailing ',' or ', ' return str; } QStringList ASTFunction::paramNames() const { QStringList names; names.reserve(params.size()); for (const ASTDeclaration ¶m : params) names << param.name; return names; } ASTEnum::ASTEnum(const QString &name) : SignedType(name), isSigned(false), isScoped(false), max(0) { } void ASTEnum::signature_impl(const AST &ast, QCryptographicHash &checksum) { Q_UNUSED(ast) checksum.addData(name.toLatin1()); if (isScoped) checksum.addData("class"); if (!type.isEmpty()) checksum.addData(type.toLatin1()); for (const ASTEnumParam ¶m : params) { checksum.addData(param.name.toLatin1()); checksum.addData(QByteArray::number(param.value)); } } QString ASTEnum::typeName() const { if (scope.isEmpty()) return name; return QLatin1String("%1::%2").arg(scope, name); } ASTFlag::ASTFlag(const QString &name, const QString &_enum) : SignedType(name), _enum(_enum) { } void ASTFlag::signature_impl(const AST &ast, QCryptographicHash &checksum) { checksum.addData(name.toLatin1()); checksum.addData(ast.typeData(_enum, scope)); } QString ASTFlag::typeName() const { if (scope.isEmpty()) return name; return QLatin1String("%1::%2").arg(scope, name); } bool ASTFlag::isValid() const { return !name.isEmpty(); } void ASTModel::signature_impl(const AST &ast, QCryptographicHash &checksum) { Q_UNUSED(ast) QByteArrayList _roles; for (const auto &role : roles) _roles << role.name.toLatin1(); std::sort(_roles.begin(), _roles.end()); checksum.addData(_roles.join('_')); } QString ASTModel::typeName() const { return QLatin1String("%1::%2").arg(scope, name); } ASTClass::ASTClass(const QString &name) : SignedType(name), hasPersisted(false) { } void ASTClass::signature_impl(const AST &ast, QCryptographicHash &checksum) { checksum.addData(name.toLatin1()); // Checksum properties QSet classIndices{ subClassPropertyIndices.begin(), subClassPropertyIndices.end() }; int propertyIndex = -1; int modelIndex = 0; for (const ASTProperty &p : properties) { propertyIndex++; checksum.addData(p.name.toLatin1()); if (p.type == QLatin1String("QAbstractItemModel")) checksum.addData(ast.typeSignatures[modelMetadata[modelIndex++].typeName()]); else if (classIndices.contains(propertyIndex)) checksum.addData(ast.typeSignatures[p.type]); else checksum.addData(ast.typeData(p.type, name)); ASTProperty::Modifier m = p.modifier; // Treat ReadOnly and SourceOnlySetter the same (interface-wise they are) if (m == ASTProperty::SourceOnlySetter) m = ASTProperty::ReadOnly; checksum.addData({reinterpret_cast(&m), sizeof(m)}); } // Checksum signals checksum.addData(ast.functionsData(signalsList, name)); // Checksum slots checksum.addData(ast.functionsData(slotsList, name)); } void POD::signature_impl(const AST &ast, QCryptographicHash &checksum) { checksum.addData(name.toLatin1()); for (const PODAttribute &attr : attributes) { checksum.addData(attr.name.toLatin1()); checksum.addData(ast.typeData(attr.type, name)); } } bool ASTClass::isValid() const { return !name.isEmpty(); } bool ASTClass::hasPointerObjects() const { int count = modelMetadata.size() + subClassPropertyIndices.size(); return count > 0; } QByteArray AST::typeData(const QString &type, const QString &className) const { static const QRegularExpression re = QRegularExpression(QLatin1String("([^<>,\\s]+)")); if (type.contains(QLatin1Char('<'))) { // templated type QByteArray result; for (const QRegularExpressionMatch &match : re.globalMatch(type)) result += typeData(match.captured(1), className); return result; } // Try enum/flags within the class first if (!className.isEmpty()) { auto classType = QLatin1String("%1::%2").arg(className, type); auto it = typeSignatures.find(classType); if (it != typeSignatures.end()) return it.value(); } auto it = typeSignatures.find(type); if (it != typeSignatures.end()) return it.value(); const auto pos = type.lastIndexOf(QLatin1String("::")); if (pos > 0) return typeData(type.mid(pos + 2), className); return type.toLatin1(); } QByteArray AST::functionsData(const QList &functions, const QString &className) const { QByteArray ret; for (const ASTFunction &func : functions) { ret += func.name.toLatin1(); for (const ASTDeclaration ¶m : func.params) { ret += param.name.toLatin1(); ret += typeData(param.type, className); ret += QByteArray(reinterpret_cast(¶m.variableType), sizeof(param.variableType)); } ret += typeData(func.returnType, className); } return ret; } RepParser::RepParser(QIODevice &outputDevice) : QRegexParser(), m_astEnumValue(-1) { setBufferFromDevice(&outputDevice); } void RepParser::reset() { m_ast = AST(); m_astClass = ASTClass(); m_astPod = POD(); m_argString.clear(); m_astEnum = ASTEnum(); //setDebug(); } bool RepParser::parseModifierFlag(const QString &flag, ASTProperty::Modifier &modifier, bool &persisted) { QRegularExpression regex(QStringLiteral("\\s*,\\s*")); QStringList flags = flag.split(regex); persisted = flags.removeAll(QStringLiteral("PERSISTED")) > 0; if (flags.length() == 0) return true; if (flags.length() > 1) { // Only valid combination is "READONLY" and "CONSTANT" if (flags.length() == 2 && flags.contains(QStringLiteral("READONLY")) && flags.contains(QStringLiteral("CONSTANT"))) { // If we have READONLY and CONSTANT that means CONSTANT modifier = ASTProperty::Constant; return true; } else { setErrorString(QLatin1String("Invalid property declaration: combination not allowed (%1)").arg(flag)); return false; } } const QString &f = flags.at(0); if (f == QLatin1String("READONLY")) modifier = ASTProperty::ReadOnly; else if (f == QLatin1String("CONSTANT")) modifier = ASTProperty::Constant; else if (f == QLatin1String("READPUSH")) modifier = ASTProperty::ReadPush; else if (f == QLatin1String("READWRITE")) modifier = ASTProperty::ReadWrite; else if (f == QLatin1String("SOURCEONLYSETTER")) modifier = ASTProperty::SourceOnlySetter; else { setErrorString(QLatin1String("Invalid property declaration: flag %1 is unknown").arg(flag)); return false; } return true; } QString stripArgs(const QString &arguments) { // This repc parser searches for the longest possible matches, which can be multiline. // This method "cleans" the string input, removing comments and converting to a single // line for subsequent parsing. QStringList lines = arguments.split(QRegularExpression(QStringLiteral("\r?\n"))); for (auto & line : lines) line.replace(QRegularExpression(QStringLiteral("//.*")),QString()); return lines.join(QString()); } bool RepParser::parseProperty(ASTClass &astClass, const QString &propertyDeclaration) { QString input = stripArgs(propertyDeclaration).trimmed(); const QRegularExpression whitespace(QStringLiteral("\\s")); QString propertyType; QString propertyName; QString propertyDefaultValue; ASTProperty::Modifier propertyModifier = ASTProperty::ReadPush; bool persisted = false; // parse type declaration which could be a nested template as well bool inTemplate = false; int templateDepth = 0; int nameIndex = -1; for (int i = 0; i < input.size(); ++i) { const QChar inputChar(input.at(i)); if (inputChar == QLatin1Char('<')) { propertyType += inputChar; inTemplate = true; ++templateDepth; } else if (inputChar == QLatin1Char('>')) { propertyType += inputChar; --templateDepth; if (templateDepth == 0) inTemplate = false; } else if (inputChar.isSpace()) { if (!inTemplate) { nameIndex = i; break; } else { propertyType += inputChar; } } else { propertyType += inputChar; } } if (nameIndex == -1) { setErrorString(QLatin1String("PROP: Invalid property declaration: %1").arg(propertyDeclaration)); return false; } // parse the name of the property input = input.mid(nameIndex).trimmed(); const int equalSignIndex = input.indexOf(QLatin1Char('=')); if (equalSignIndex != -1) { // we have a default value propertyName = input.left(equalSignIndex).trimmed(); input = input.mid(equalSignIndex + 1).trimmed(); const int lastQuoteIndex = input.lastIndexOf(QLatin1Char('"')); if (lastQuoteIndex != -1) { propertyDefaultValue = input.left(lastQuoteIndex + 1); input = input.mid(lastQuoteIndex + 1); } const int whitespaceIndex = input.indexOf(whitespace); if (whitespaceIndex == -1) { // no flag given if (propertyDefaultValue.isEmpty()) propertyDefaultValue = input; propertyModifier = ASTProperty::ReadPush; } else { // flag given if (propertyDefaultValue.isEmpty()) propertyDefaultValue = input.left(whitespaceIndex).trimmed(); const QString flag = input.mid(whitespaceIndex + 1).trimmed(); if (!parseModifierFlag(flag, propertyModifier, persisted)) return false; } } else { // there is no default value const int whitespaceIndex = input.indexOf(whitespace); if (whitespaceIndex == -1) { // no flag given propertyName = input; propertyModifier = ASTProperty::ReadPush; } else { // flag given propertyName = input.left(whitespaceIndex).trimmed(); const QString flag = input.mid(whitespaceIndex + 1).trimmed(); if (!parseModifierFlag(flag, propertyModifier, persisted)) return false; } } astClass.properties << ASTProperty(propertyType, propertyName, propertyDefaultValue, propertyModifier, persisted); if (persisted) astClass.hasPersisted = true; return true; } bool RepParser::parseRoles(ASTModel &astModel, const QString &modelRoles) { const QString input = modelRoles.trimmed(); if (input.isEmpty()) return true; const QStringList roleStrings = input.split(QChar(QLatin1Char(','))); for (auto role : roleStrings) astModel.roles << ASTModelRole(role.trimmed()); return true; } AST RepParser::ast() const { return m_ast; } void RepParser::TypeParser::parseArguments(const QString &arguments) { const QString strippedArgs = stripArgs(arguments); int templateDepth = 0; bool inTemplate = false; bool inVariable = false; QString propertyType; QString variableName; ASTDeclaration::VariableTypes variableType = ASTDeclaration::None; int variableNameIndex = 0; for (int i = 0; i < strippedArgs.size(); ++i) { const QChar inputChar(strippedArgs.at(i)); if (inputChar == QLatin1Char('<')) { propertyType += inputChar; inTemplate = true; ++templateDepth; } else if (inputChar == QLatin1Char('>')) { propertyType += inputChar; --templateDepth; if (templateDepth == 0) inTemplate = false; } else if (inputChar.isSpace()) { if (inTemplate) propertyType += inputChar; else if (!propertyType.isEmpty()) { if (propertyType == QLatin1String("const")) { propertyType.clear(); variableType |= ASTDeclaration::Constant; } else { inVariable = true; } } } else if (inputChar == QLatin1Char('&')) { variableType |= ASTDeclaration::Reference; } else if (inputChar == QLatin1Char(',')) { if (!inTemplate) { RepParser::TypeParser::generateFunctionParameter(variableName, propertyType, variableNameIndex, variableType); propertyType.clear(); variableName.clear(); variableType = ASTDeclaration::None; inVariable = false; } else { propertyType += inputChar; } } else { if (inVariable) variableName += inputChar; else propertyType += inputChar; } } if (!propertyType.isEmpty()) { RepParser::TypeParser::generateFunctionParameter(variableName, propertyType, variableNameIndex, variableType); } } void RepParser::TypeParser::generateFunctionParameter(QString variableName, const QString &propertyType, int &variableNameIndex, ASTDeclaration::VariableTypes variableType) { if (!variableName.isEmpty()) variableName = variableName.trimmed(); else variableName = QString::fromLatin1("__repc_variable_%1").arg(++variableNameIndex); arguments.append(ASTDeclaration(propertyType, variableName, variableType)); } void RepParser::TypeParser::appendParams(ASTFunction &slot) { for (const ASTDeclaration &arg : std::as_const(arguments)) slot.params << arg; } void RepParser::TypeParser::appendPods(POD &pods) { for (const ASTDeclaration &arg : std::as_const(arguments)) { PODAttribute attr; attr.type = arg.type; attr.name = arg.name; pods.attributes.append(qMove(attr)); } } bool RepParser::consumeRule(int ruleno) { if (isDebug()) { qDebug() << "consumeRule:" << ruleno << spell[rule_info[rule_index[ruleno]]]; } switch (ruleno) { ./ TopLevel: Types | Newlines Types; Types: Type | Types Type; Newlines: newline | Newlines newline; Comments: Comment | Comments Comment; Comment: comment | comment Newlines | mcomment | mcomment Newlines; Type: PreprocessorDirective | PreprocessorDirective Newlines; Type: Pod | Pod Newlines; Type: Pod2; Type: Class; Type: UseEnum | UseEnum Newlines; Type: Comment | Comment Newlines; Type: Enum; /. case $rule_number: { m_astEnum.generateSignature(m_ast); m_ast.enums.append(m_astEnum); } break; ./ Type: Flag | Flag Newlines; Comma: comma | comma Newlines; Equals: equals; PreprocessorDirective: preprocessor_directive; /. case $rule_number: { const QString line = captured().value(QStringLiteral("preprocessor_directive")); if (line.startsWith(QStringLiteral("#HEADER"))) m_ast.headerLines.append(line.sliced(8)); else if (line.startsWith(QStringLiteral("#FOOTER"))) m_ast.footerLines.append(line.sliced(8)); else m_ast.preprocessorDirectives.append(captured().value(QStringLiteral("preprocessor_directive"))); } break; ./ Pod: pod; /. case $rule_number: { POD pod; pod.name = captured().value(QStringLiteral("name")).trimmed(); pod.compilerAttribute = captured().value(QStringLiteral("attribute")).trimmed(); const QString argString = captured().value(QLatin1String("types")).trimmed(); if (argString.isEmpty()) { qWarning() << "[repc] - Ignoring POD with no data members. POD name: " << qPrintable(pod.name); return true; } for (const QStringView &token : argString.tokenize(u' ')) { if (token.startsWith(QLatin1String("ENUM"))) { if (token.size() == 4 || !(token[4].isLetterOrNumber() || token[4] == u'_')) { setErrorString(QLatin1String("ENUMs are only available in PODs using bracket syntax ('{'), not parentheses")); return false; } } } RepParser::TypeParser parseType; parseType.parseArguments(argString); parseType.appendPods(pod); pod.generateSignature(m_ast); m_ast.pods.append(pod); } break; ./ Class: ClassStart Start ClassContent Stop; /. case $rule_number: ./ Class: ClassStart Start Stop; /. case $rule_number: { m_astClass.generateSignature(m_ast); m_ast.classes.append(m_astClass); } break; ./ --"Decorated" types capture comments _before_ the type and newlines _after_ the type --This leaves newlines at the start of the scope and comments at the end of the scope still to be handled ClassContent: ClassTypes | ClassTypes Comments | Newlines ClassTypes | Newlines ClassTypes Comments; --Allow empty/ignored content in the scope for testing purposes ClassContent: Comments | Newlines | Newlines Comments | Comments Newlines | Newlines Comments Newlines; ClassTypes: ClassType | ClassTypes ClassType; ClassType: DecoratedProp | DecoratedSignal | DecoratedSlot | DecoratedModel | DecoratedClass | DecoratedClassFlag; ClassType: DecoratedEnum; /. case $rule_number: { m_astEnum.scope = m_astClass.name; m_astEnum.generateSignature(m_ast); m_astClass.enums.append(m_astEnum); } break; ./ Pod2: PodStart Start PodContent Stop; /. case $rule_number: ./ Pod2: PodStart Start Stop; /. case $rule_number: { RepParser::TypeParser parseType; parseType.parseArguments(m_argString); parseType.appendPods(m_astPod); m_astPod.generateSignature(m_ast); m_ast.pods.append(m_astPod); } break; ./ --"Decorated" types capture comments _before_ the type and newlines _after_ the type --This leaves newlines at the start of the scope and comments at the end of the scope still to be handled PodContent: PodTypes | PodTypes Comments | Newlines PodTypes | Newlines PodTypes Comments; --Allow empty/ignored content in the scope for testing purposes PodContent: Comments | Newlines | Newlines Comments | Comments Newlines | Newlines Comments Newlines; PodTypes: PodType | PodTypes CaptureComma PodType; --Enums are now legal in PODs, but don't usually have a comma afterwards so we need to accept matches without --separating commas unless we expand the language to distinguish enums from other PodTypes. PodTypes: PodTypes PodType; PodType: DecoratedPODFlag; PodType: DecoratedEnum; /. case $rule_number: { m_astEnum.generateSignature(m_ast); m_astPod.enums.append(m_astEnum); } break; ./ PodType: Parameter | Parameter Newlines; Parameter: DecoratedParameterType ParameterName; DecoratedParameterType: ParameterType | Qualified ParameterType | ParameterType PassByReference | Qualified ParameterType PassByReference; ParameterType: SimpleType | TemplateType; TemplateType: TemplateTypename TStart ParameterTypes TStop; TemplateTypename: Symbol; /. case $rule_number: { m_argString += m_symbol; } break; ./ TStart: tstart; /. case $rule_number: { m_argString += QLatin1Char('<'); } break; ./ TStop: tstop; /. case $rule_number: { m_argString += QLatin1Char('>'); } break; ./ Qualified: qualifier; /. case $rule_number: { m_argString += captured().value(QLatin1String("value")).trimmed() + QLatin1Char(' '); } break; ./ PassByReference: passbyqual; /. case $rule_number: { m_argString += QLatin1Char(' ') + captured().value(QLatin1String("value")).trimmed(); } break; ./ ParameterTypes: DecoratedParameterType | ParameterTypes CaptureComma DecoratedParameterType; CaptureComma: comma; /. case $rule_number: ./ CaptureComma: comma Newlines; /. case $rule_number: { m_argString += QLatin1Char(','); } break; ./ SimpleType: Symbol; /. case $rule_number: { m_argString += m_symbol; } break; ./ ParameterName: Symbol; /. case $rule_number: { m_argString += QLatin1Char(' ') + m_symbol; } break; ./ DecoratedSlot: Slot | Comments Slot | Slot Newlines | Comments Slot Newlines; DecoratedSignal: Signal | Comments Signal | Signal Newlines | Comments Signal Newlines; 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; DecoratedPODFlag: PODFlag | Comments PODFlag | PODFlag Newlines | Comments PODFlag Newlines; DecoratedEnum: Enum | Comments Enum | Enum Newlines | Comments Enum Newlines; Start: start | Comments start | start Newlines | Comments start Newlines; Stop: stop | stop Newlines; Enum: EnumStart Start EnumParams Stop; EnumStart: enum; /. case $rule_number: { const QString name = captured().value(QLatin1String("name")); const QString type = captured().value(QLatin1String("type")); const QString _class = captured().value(QLatin1String("class")); // new Class declaration m_astEnum = ASTEnum(name); if (!_class.isEmpty()) m_astEnum.isScoped = true; if (!type.isEmpty()) m_astEnum.type = type; m_astEnumValue = -1; } break; ./ EnumParams: DecoratedEnumParam | EnumParams Comma DecoratedEnumParam; EnumParam: Symbol; /. case $rule_number: { ASTEnumParam param; param.name = m_symbol; param.value = ++m_astEnumValue; if (m_astEnum.max < param.value) m_astEnum.max = param.value; m_astEnum.params << param; } break; ./ EnumParam: Symbol Equals Value; /. case $rule_number: { ASTEnumParam param; param.name = m_symbol; param.value = m_astEnumValue; if (param.value < 0) { m_astEnum.isSigned = true; if (m_astEnum.max < -param.value) m_astEnum.max = -param.value; } else if (m_astEnum.max < param.value) m_astEnum.max = param.value; m_astEnum.params << param; } break; ./ Symbol: symbol; /. case $rule_number: { m_symbol = captured().value(QStringLiteral("symbol")).trimmed(); } break; ./ Value: value; /. case $rule_number: { QString value = captured().value(QStringLiteral("value")).trimmed(); if (value.startsWith(QLatin1String("0x"), Qt::CaseInsensitive)) m_astEnumValue = value.toInt(0,16); else m_astEnumValue = value.toInt(); } 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; } auto flag = ASTFlag(name, _enum); flag.scope = m_astClass.name; flag.generateSignature(m_ast); m_astClass.flags.append(flag); } break; ./ PODFlag: 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_astPod.enums) { if (en.name == _enum) { en.flagIndex = m_astPod.flags.count(); break; } enumIndex++; } if (enumIndex == m_astPod.enums.count()) { setErrorString(QLatin1String("FLAG: Unknown (pod) enum: %1").arg(_enum)); return false; } auto flag = ASTFlag(name, _enum); flag.scope = m_astPod.name; flag.generateSignature(m_ast); m_astPod.flags.append(flag); } break; ./ Prop: prop; /. case $rule_number: { const QString args = captured().value(QLatin1String("args")); if (!parseProperty(m_astClass, args)) return false; } break; ./ Signal: signal; /. case $rule_number: { ASTFunction signal; signal.name = captured().value(QLatin1String("name")).trimmed(); const QString argString = captured().value(QLatin1String("args")).trimmed(); RepParser::TypeParser parseType; parseType.parseArguments(argString); parseType.appendParams(signal); m_astClass.signalsList << signal; } break; ./ Slot: slot; /. case $rule_number: { QString returnTypeAndName = captured().value(QLatin1String("type")).trimmed(); const QString argString = captured().value(QLatin1String("args")).trimmed(); // compat code with old SLOT declaration: "SLOT(func(...))" const bool hasWhitespace = returnTypeAndName.indexOf(u' ') != -1; if (!hasWhitespace) { qWarning() << "[repc] - Adding 'void' for unspecified return type on" << qPrintable(returnTypeAndName); returnTypeAndName.prepend(QLatin1String("void ")); } const int startOfFunctionName = returnTypeAndName.lastIndexOf(u' ') + 1; ASTFunction slot; slot.returnType = returnTypeAndName.mid(0, startOfFunctionName-1); slot.name = returnTypeAndName.mid(startOfFunctionName); RepParser::TypeParser parseType; parseType.parseArguments(argString); parseType.appendParams(slot); m_astClass.slotsList << slot; } break; ./ Model: model; /. case $rule_number: { const QString name = captured().value(QLatin1String("name")).trimmed(); const QString argString = captured().value(QLatin1String("args")).trimmed(); ASTModel model(name, m_astClass.name, m_astClass.properties.size()); if (!parseRoles(model, argString)) return false; model.generateSignature(m_ast); m_astClass.modelMetadata << model; m_astClass.properties << ASTProperty(QStringLiteral("QAbstractItemModel"), name, QStringLiteral("nullptr"), ASTProperty::SourceOnlySetter, false, true); } break; ./ ChildRep: childrep; /. case $rule_number: { const QString name = captured().value(QLatin1String("name")).trimmed(); const QString type = captured().value(QLatin1String("type")).trimmed(); m_astClass.subClassPropertyIndices << m_astClass.properties.size(); m_astClass.properties << ASTProperty(type, name, QStringLiteral("nullptr"), ASTProperty::SourceOnlySetter, false, true); } break; ./ ClassStart: class Newlines; /. case $rule_number: ./ ClassStart: class; /. case $rule_number: { const QString attribute = captured().value(QLatin1String("attribute")); const QString name = captured().value(QLatin1String("name")); // new Class declaration m_astClass = ASTClass(name); m_astClass.compilerAttribute = attribute; } break; ./ PodStart: pod2; /. case $rule_number: ./ PodStart: pod2 Newlines; /. case $rule_number: { // new POD declaration m_astPod = POD(); m_astPod.name = captured().value(QLatin1String("name")).trimmed(); m_astPod.compilerAttribute = captured().value(QLatin1String("attribute")); m_argString.clear(); } break; ./ UseEnum: use_enum; /. case $rule_number: { const QString name = captured().value(QLatin1String("name")); m_ast.enumUses.append(name); } 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; } auto flag = ASTFlag(name, _enum); flag.generateSignature(m_ast); m_ast.flags.append(flag); } break; ./ --Error conditions/messages ClassType: ClassStart; /. case $rule_number: { setErrorString(QStringLiteral("class: Cannot be nested")); return false; } break; ./ ClassType: Pod; /. case $rule_number: { setErrorString(QStringLiteral("POD: Can only be used in global scope")); return false; } break; ./ ClassType: UseEnum; /. case $rule_number: { setErrorString(QStringLiteral("USE_ENUM: Can only be used in global scope")); return false; } break; ./ Type: Signal; /. case $rule_number: { setErrorString(QStringLiteral("SIGNAL: Can only be used in class scope")); return false; } break; ./ Type: Slot; /. case $rule_number: { setErrorString(QStringLiteral("SLOT: Can only be used in class scope")); return false; } break; ./ Type: Prop; /. case $rule_number: { setErrorString(QStringLiteral("PROP: Can only be used in class scope")); return false; } break; ./ Type: Model; /. case $rule_number: { setErrorString(QStringLiteral("MODEL: Can only be used in class scope")); return false; } break; ./ Type: ChildRep; /. case $rule_number: { setErrorString(QStringLiteral("CLASS: Can only be used in class scope")); return false; } break; ./ /. } // switch return true; } ./