1459 lines
42 KiB
Plaintext
1459 lines
42 KiB
Plaintext
-- 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]+(?:(?<attribute>[A-Za-z_][A-Za-z0-9_]*)[ \\t]+)?(?<name>[A-Za-z_][A-Za-z0-9_]+)[ \\t]*"
|
|
%token pod "[pod]POD[ \\t]*(?:(?<attribute>[A-Za-z_][A-Za-z0-9_]*)[ \\t]+)?(?<name>[A-Za-z_][A-Za-z0-9_]+)[ \\t]*\\((?<types>[^\\)]*)\\);?[ \\t]*"
|
|
%token pod2 "[pod2]POD[ \\t]*(?:(?<attribute>[A-Za-z_][A-Za-z0-9_]*)[ \\t]+)?(?<name>[A-Za-z_][A-Za-z0-9_]+)[ \\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 prop "[prop][ \\t]*PROP[ \\t]*\\((?<args>[^\\)]+)\\);?[ \\t]*"
|
|
%token use_enum "[use_enum]USE_ENUM[ \\t]*\\((?<name>[^\\)]*)\\);?[ \\t]*"
|
|
%token signal "[signal][ \\t]*SIGNAL[ \\t]*\\([ \\t]*(?<name>\\S+)[ \\t]*\\((?<args>[^\\)]*)\\)[ \\t]*\\);?[ \\t]*"
|
|
%token slot "[slot][ \\t]*SLOT[ \\t]*\\((?<type>[^\\(]*)\\((?<args>[^\\)]*)\\)[ \\t]*\\);?[ \\t]*"
|
|
%token model "[model][ \\t]*MODEL[ \\t]+(?<name>[A-Za-z_][A-Za-z0-9_]+)\\((?<args>[^\\)]+)\\)[ \\t]*;?[ \\t]*"
|
|
%token childrep "[childrep][ \\t]*CLASS[ \\t]+(?<name>[A-Za-z_][A-Za-z0-9_]+)\\((?<type>[^\\)]+)\\)[ \\t]*;?[ \\t]*"
|
|
%token qualifier "[qualifier][ \\t]*(?<value>const|unsigned|signed)[ \\t]*"
|
|
%token passbyqual "[passbyqual][ \\t]*(?<value>&)[ \\t]*"
|
|
%token symbol "[symbol][ \\t]*(?<symbol>[A-Za-z_][A-Za-z0-9_]*)[ \\t]*"
|
|
%token value "[value][ \\t]*(?<value>-\\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](?<comment>[ \\t]*//[^\\n]*\\n)"
|
|
%token mcomment "[mcomment,M](?<comment>/\\*(.*?)\\*/)"
|
|
%token preprocessor_directive "[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 <rep_grammar_p.h>
|
|
#include <qregexparser.h>
|
|
#include <QStringList>
|
|
#include <QList>
|
|
#include <QHash>
|
|
#include <QRegularExpression>
|
|
#include <QSet>
|
|
|
|
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<ASTDeclaration> params;
|
|
};
|
|
Q_DECLARE_TYPEINFO(ASTFunction, Q_RELOCATABLE_TYPE);
|
|
|
|
struct ASTEnumParam
|
|
{
|
|
ASTEnumParam(const QString ¶mName = QString(), int paramValue = 0)
|
|
: name(paramName),
|
|
value(paramValue)
|
|
{
|
|
}
|
|
|
|
QString asString() const;
|
|
|
|
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<ASTEnumParam> 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<ASTModelRole> 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<ASTProperty> properties;
|
|
QList<ASTFunction> signalsList;
|
|
QList<ASTFunction> slotsList;
|
|
QList<ASTEnum> enums;
|
|
QList<ASTFlag> flags;
|
|
bool hasPersisted;
|
|
QList<ASTModel> modelMetadata;
|
|
QList<int> 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<PODAttribute> attributes;
|
|
QList<ASTEnum> enums;
|
|
QList<ASTFlag> flags;
|
|
};
|
|
Q_DECLARE_TYPEINFO(POD, Q_RELOCATABLE_TYPE);
|
|
|
|
// The AST representation of a .rep file
|
|
struct AST
|
|
{
|
|
QList<ASTClass> classes;
|
|
QList<POD> pods;
|
|
QList<ASTEnum> enums;
|
|
QList<ASTFlag> flags;
|
|
QList<QString> enumUses;
|
|
QStringList preprocessorDirectives;
|
|
QStringList headerLines;
|
|
QStringList footerLines;
|
|
QHash<QString, QByteArray> typeSignatures;
|
|
QByteArray typeData(const QString &type, const QString &className) const;
|
|
QByteArray functionsData(const QList<ASTFunction> &functions, const QString &className) const;
|
|
};
|
|
Q_DECLARE_TYPEINFO(AST, Q_RELOCATABLE_TYPE);
|
|
|
|
class RepParser: public QRegexParser<RepParser, $table>
|
|
{
|
|
public:
|
|
explicit RepParser(QIODevice &outputDevice);
|
|
virtual ~RepParser() {}
|
|
|
|
bool parse() override { return QRegexParser<RepParser, $table>::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<ASTDeclaration> 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 <QCryptographicHash>
|
|
#include <QDebug>
|
|
#include <QTextStream>
|
|
|
|
// for normalizeTypeInternal
|
|
#include <private/qmetaobject_p.h>
|
|
#include <private/qmetaobject_moc_p.h>
|
|
|
|
// 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<int> 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<const char *>(&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<ASTFunction> &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<const char *>(¶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;
|
|
}
|
|
./
|