Skip to content

Commit

Permalink
completed refactore class for parse signature. Also extracted tests f…
Browse files Browse the repository at this point in the history
…or signature parsing to the separate class.
  • Loading branch information
vt4a2h committed Aug 29, 2015
1 parent 37f80c1 commit 5811862
Show file tree
Hide file tree
Showing 13 changed files with 178 additions and 219 deletions.
2 changes: 1 addition & 1 deletion entity/components/components_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ namespace components {
using Tokens = QVector<QString>;

class ComponentSignatureParser;
using SharedSignatureParser = std::shared_ptr<ComponentSignatureParser>;
using UniqueSignatureParser = std::unique_ptr<ComponentSignatureParser>;

class ComponentsMaker;
using UniqueComponentsMaker = std::unique_ptr<ComponentsMaker>;
Expand Down
13 changes: 12 additions & 1 deletion entity/components/componentsignatureparser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,6 @@ namespace components {
Tokens &out)
{
const int groupsCount = int(componentsGroupCount[display]);
out.clear();
out.resize(groupsCount);

const Forbidden &forbidden = forbiddenMap[display];
Expand All @@ -144,8 +143,16 @@ namespace components {
}
}

/**
* @brief ComponentSignatureParser::parse
* @param signature
* @param display
* @return
*/
bool ComponentSignatureParser::parse(const QString &signature, models::DisplayPart display)
{
m_Tokens.clear();

const auto &pattern = componentPatternMap[display];
if (pattern.isEmpty())
return false;
Expand All @@ -158,6 +165,10 @@ namespace components {
return false;
}

/**
* @brief ComponentSignatureParser::tokens
* @return
*/
Tokens ComponentSignatureParser::tokens() const
{
return m_Tokens;
Expand Down
183 changes: 22 additions & 161 deletions entity/components/componentsmaker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,119 +39,8 @@

namespace components {

namespace {
using Keywords = QSet<QString>;

const Keywords types = {"bool", "char16_t", "char32_t", "float", "int", "long", "short", "signed",
"wchar_t", "double", "void" };
const Keywords reservedKeywords = { "alignas", "alignof", "and", "and_eq", "asm", "auto", "bitand", "bitor",
"break", "case", "catch", "char", "class",
"compl", "const", "constexpr", "const_cast", "continue", "decltype",
"default", "delete", "do", "dynamic_cast", "else", "enum",
"explicit", "export", "extern", "false", "for", "friend", "goto",
"if", "inline", "mutable", "namespace", "new", "noexcept",
"not", "not_eq", "nullptr", "operator", "or", "or_eq", "private",
"protected", "public", "register", "reinterpret_cast", "return",
"sizeof", "static", "static_assert", "static_cast", "struct",
"switch", "template", "this", "thread_local", "throw", "true", "try",
"typedef", "typeid", "typename", "union", "unsigned", "using", "virtual",
"volatile", "while", "xor", "xor_eq" };

// TODO: Just simple patterns now, must be improved in future (prefer to use simple parser)
// TODO: 6 section may contains wrong combination of "*&const" it must be fixed.
// TODO: for 5 required recursive parsing to detect nested types with templates
const QString fieldPattern = "^((?:volatile|static|mutable)\\s)?" // 1 -- lhs keywords
"(const\\s)?" // 2 -- const
"((?:\\w*:{2,})*)" // 3 -- namespaces
"(\\w+)" // 4 -- typename
"(?:\\s*<\\s*((?:\\w+(?:\\w+:{2,})*(?:\\s*,\\s*)?)+)\\s*>\\s*)?" // 5 -- template args
"\\s+([\\*\\s\\&const]*)" // 6 -- &*const
"\\s*(\\w+)$"; // 7 -- field name

const QString propertyPattern = "^\\s*(\\w+)\\s+" // 1 -- type
"(\\w+)" // 2 -- name
"(?:\\s+(?:MEMBER)\\s+(\\w+))?" // 3 -- member
"(?:\\s+(?:READ)\\s+(\\w+))?" // 4 -- getter
"(?:\\s+(?:WRITE)\\s+(\\w+))?" // 5 -- setter
"(?:\\s+(?:RESET)\\s+(\\w+))?" // 6 -- resetter
"(?:\\s+(?:NOTIFY)\\s+(\\w+))?" // 7 -- notifier
"(?:\\s+(?:REVISION)\\s+(\\d+))?" // 8 -- revision
"(?:\\s+(?:DESIGNABLE)\\s+(\\w+))?" // 9 -- designable
"(?:\\s+(?:SCRIPTABLE)\\s+(\\w+))?" // 10 -- scriptable
"(?:\\s+(?:STORED)\\s+(true|false))?" // 11 -- stored
"(?:\\s+(?:USER)\\s+(true|false))?" // 12 -- user
"(?:\\s+(CONSTANT))?" // 13 -- constant
"(?:\\s+(FINAL))?"; // 14 -- final

const QMap<models::DisplayPart, QString> componentPatternMap =
{
{models::DisplayPart::Fields, fieldPattern},
{models::DisplayPart::Properties, propertyPattern},
};

const QMap<models::DisplayPart, int> componentsGroupCount =
{
{models::DisplayPart::Fields, int(FieldGroupNames::GroupsCount)},
{models::DisplayPart::Properties, int(PropGroupNames::GroupsCount)},
};

// Capture index, keywords which MUST NOT contains in captured text
using NumberKeywords = std::pair<int, Keywords>;
using Forbidden = QVector<NumberKeywords>;

QMap<models::DisplayPart, Forbidden> forbiddenMap = {
{models::DisplayPart::Fields,
{
{int(FieldGroupNames::Namespaces), reservedKeywords|types},
{int(FieldGroupNames::Typename), reservedKeywords},
{int(FieldGroupNames::Name), reservedKeywords|types},
{int(FieldGroupNames::TemplateArgs), reservedKeywords}
}
},
{models::DisplayPart::Properties,
{
{int(PropGroupNames::Type), reservedKeywords},
{int(PropGroupNames::Name), reservedKeywords|types},
{int(PropGroupNames::Member), reservedKeywords|types},
{int(PropGroupNames::Getter), reservedKeywords|types},
{int(PropGroupNames::Setter), reservedKeywords|types},
{int(PropGroupNames::Resetter), reservedKeywords|types},
{int(PropGroupNames::Notifier), reservedKeywords|types},
{int(PropGroupNames::Designable), reservedKeywords|types},
{int(PropGroupNames::Scriptable), reservedKeywords|types}
}
}
};


using MakerFunction = std::function<OptionalEntity()>;
QMap<models::DisplayPart, MakerFunction> componentMakerMap;

bool notContainsInvalidKeyword(const QRegularExpressionMatch &match, models::DisplayPart display,
QVector<QString> &captured)
{
const int groupsCount = int(componentsGroupCount[display]);
captured.resize(groupsCount);

const Forbidden &forbidden = forbiddenMap[display];
for (int groupIndex = 1; groupIndex < groupsCount; ++groupIndex)
{
QString cap = match.captured(groupIndex).trimmed();
captured[groupIndex] = cap;

auto it = utility::find_if(forbidden, [&](const auto &c){ return int(c.first) == groupIndex; });
if (it != cend(forbidden)) {
const QStringList &tmpList = cap.remove(QChar::Space).split(QRegExp("::|,"), QString::SkipEmptyParts);
if (!(tmpList.toSet() & it->second).isEmpty()) {
captured.clear();
return false;
}
}
}

return true;
}
}
using MakerFunction = std::function<OptionalEntity(const components::Tokens &)>;
QMap<models::DisplayPart, MakerFunction> componentMakerMap;

/**
* @brief ComponentsMaker::ComponentsMaker
Expand All @@ -172,49 +61,19 @@ namespace components {
: m_Model(model)
, m_Entity(entity)
, m_Scope(scope)
, m_LastSignature("")
{
componentMakerMap[models::DisplayPart::Fields] = [&]{ return makeField(); };
}

/**
* @brief ComponentsMaker::signatureValid
* @param signature
* @return
*/
bool ComponentsMaker::signatureValid(const QString &signature, models::DisplayPart display)
{
const QString &pattern = componentPatternMap[display];
if (pattern.isEmpty())
return false;

const QRegularExpression re(pattern);
const auto match = re.match(signature.trimmed());
if (match.hasMatch()) {
m_LastCaptured.clear();
m_LastSignature.clear();

const bool result = notContainsInvalidKeyword(match, display, m_LastCaptured);
if (result) {
m_LastSignature = signature;
return result;
}
}

return false;
componentMakerMap[models::DisplayPart::Fields] = [&](auto tokens){ return this->makeField(tokens); };
}

/**
* @brief ComponentsMaker::makeComponent
* @param signature
* @return
*/
OptionalEntity ComponentsMaker::makeComponent(const QString &signature, models::DisplayPart display)
OptionalEntity ComponentsMaker::makeComponent(const components::Tokens &tokens, models::DisplayPart display)
{
if ((signature.isEmpty() && !m_LastSignature.isEmpty() && !m_LastCaptured.isEmpty()) ||
(signature == m_LastSignature && !m_LastCaptured.isEmpty()) ||
signatureValid(signature, display))
return componentMakerMap[display]();
if (!tokens.isEmpty())
return componentMakerMap[display](tokens);

return {tr("Wrong signature"), entity::SharedBasicEntity()};
}
Expand Down Expand Up @@ -277,29 +136,31 @@ namespace components {
* @brief ComponentsMaker::makeField
* @return
*/
OptionalEntity ComponentsMaker::makeField()
OptionalEntity ComponentsMaker::makeField(const Tokens &tokens)
{
Q_ASSERT(!m_LastCaptured.isEmpty() && !m_LastCaptured[int(FieldGroupNames::Typename)].isEmpty() &&
!m_LastCaptured[int(FieldGroupNames::Name)].isEmpty());
Q_ASSERT(!tokens.isEmpty() && !tokens[int(FieldGroupNames::Typename)].isEmpty() &&
!tokens[int(FieldGroupNames::Name)].isEmpty());

Q_ASSERT(m_Model);
Q_ASSERT(m_Model->globalDatabase());
Q_ASSERT(m_Model->currentProject());
Q_ASSERT(m_Model->currentProject()->database());

auto tmpTokens = tokens;

auto newField = std::make_shared<entity::Field>();
newField->setName(m_LastCaptured[int(FieldGroupNames::Name)]);
if (!m_LastCaptured[int(FieldGroupNames::LhsKeywords)].isEmpty()) {
auto keyword = utility::fieldKeywordFromString(m_LastCaptured[int(FieldGroupNames::LhsKeywords)]);
newField->setName(tmpTokens[int(FieldGroupNames::Name)]);
if (!tmpTokens[int(FieldGroupNames::LhsKeywords)].isEmpty()) {
auto keyword = utility::fieldKeywordFromString(tmpTokens[int(FieldGroupNames::LhsKeywords)]);
Q_ASSERT(keyword != entity::FieldKeyword::Invalid);
newField->addKeyword(keyword);
}

const QString &typeName = m_LastCaptured[int(FieldGroupNames::Typename)];
const QString &typeName = tmpTokens[int(FieldGroupNames::Typename)];
entity::SharedType type;

if (!m_LastCaptured[int(FieldGroupNames::Namespaces)].isEmpty()) {
auto names = m_LastCaptured[int(FieldGroupNames::Namespaces)].split("::", QString::SkipEmptyParts);
if (!tmpTokens[int(FieldGroupNames::Namespaces)].isEmpty()) {
auto names = tmpTokens[int(FieldGroupNames::Namespaces)].split("::", QString::SkipEmptyParts);
auto scope = m_Model->globalDatabase()->chainScopeSearch(names);
if (!scope)
scope = m_Model->currentProject()->database()->chainScopeSearch(names);
Expand All @@ -324,10 +185,10 @@ namespace components {
entity::SharedExtendedType extendedType = std::make_shared<entity::ExtendedType>();
extendedType->setTypeId(type->id());
extendedType->setScopeId(m_Scope->id());
extendedType->setConstStatus(!m_LastCaptured[int(FieldGroupNames::ConstStatus)].isEmpty());
extendedType->setConstStatus(!tmpTokens[int(FieldGroupNames::ConstStatus)].isEmpty());

if (!m_LastCaptured[int(FieldGroupNames::PLC)].isEmpty()) {
QString plc = m_LastCaptured[int(FieldGroupNames::PLC)];
if (!tmpTokens[int(FieldGroupNames::PLC)].isEmpty()) {
QString plc = tmpTokens[int(FieldGroupNames::PLC)];
plc.remove(QChar::Space);

if (plc.startsWith("const")) {
Expand All @@ -351,8 +212,8 @@ namespace components {
}
}

if (!m_LastCaptured[int(FieldGroupNames::TemplateArgs)].isEmpty()) {
QStringList arguments = m_LastCaptured[int(FieldGroupNames::TemplateArgs)]
if (!tmpTokens[int(FieldGroupNames::TemplateArgs)].isEmpty()) {
QStringList arguments = tmpTokens[int(FieldGroupNames::TemplateArgs)]
.remove(QChar::Space)
.split(",", QString::SkipEmptyParts);
entity::ScopesList scopes = m_Model->currentProject()->database()->scopes();
Expand Down
10 changes: 4 additions & 6 deletions entity/components/componentsmaker.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
#include <models/models_types.hpp>
#include <models/componentsmodel.h>

#include <entity/components/components_types.h>

namespace components {

/// The optional entity
Expand All @@ -48,8 +50,7 @@ namespace components {
ComponentsMaker(const models::SharedApplicationModel &model, const entity::SharedType &entity,
const entity::SharedScope &scope);

bool signatureValid(const QString &signature, models::DisplayPart display);
OptionalEntity makeComponent(const QString &signature, models::DisplayPart display);
OptionalEntity makeComponent(const components::Tokens &tokens, models::DisplayPart display);

models::SharedApplicationModel model() const;
void setModel(const models::SharedApplicationModel &model);
Expand All @@ -65,10 +66,7 @@ namespace components {
entity::SharedType m_Entity;
entity::SharedScope m_Scope;

QVector<QString> m_LastCaptured;
QString m_LastSignature;

OptionalEntity makeField();
OptionalEntity makeField(const components::Tokens &tokens);
};

} // namespace components
10 changes: 7 additions & 3 deletions gui/signatureeditdelegate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include <models/componentsmodel.h>

#include <entity/components/componentsmaker.h>
#include <entity/components/componentsignatureparser.h>

namespace gui {

Expand All @@ -43,6 +44,7 @@ namespace gui {
SignatureEditDelegate::SignatureEditDelegate(QObject *parent)
: QStyledItemDelegate(parent)
, m_ComponentsMaker(std::make_unique<components::ComponentsMaker>())
, m_SignatureParser(std::make_unique<components::ComponentSignatureParser>())
, m_DisplayPart(models::DisplayPart::Invalid)
{}

Expand Down Expand Up @@ -116,8 +118,10 @@ namespace gui {
auto le = static_cast<QLineEdit*>(editor);
auto currentSignature = model->data(index, models::ComponentsModel::ShortSignature).toString();
if (currentSignature != le->text()) {
auto e = static_cast<QLineEdit*>(editor);
auto optionalEntity = m_ComponentsMaker->makeComponent(e->text(), m_DisplayPart);
// Parse operation called each onTextEdited()
const auto &tokens = m_SignatureParser->tokens();
auto optionalEntity = m_ComponentsMaker->makeComponent(tokens, m_DisplayPart);

if (optionalEntity.resultEntity){
model->setData(index, QVariant::fromValue(optionalEntity.resultEntity),
models::ComponentsModel::UpdateSignature);
Expand All @@ -132,7 +136,7 @@ namespace gui {
void SignatureEditDelegate::onTextEdited()
{
auto editor = qobject_cast<QLineEdit*>(sender());
if (m_ComponentsMaker->signatureValid(editor->text(), m_DisplayPart))
if (m_SignatureParser->parse(editor->text(), m_DisplayPart))
editor->setStyleSheet(validSignature);
else
editor->setStyleSheet(invalidSignature);
Expand Down
1 change: 1 addition & 0 deletions gui/signatureeditdelegate.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ namespace gui {

private:
components::UniqueComponentsMaker m_ComponentsMaker;
components::UniqueSignatureParser m_SignatureParser;
mutable models::DisplayPart m_DisplayPart;
};

Expand Down

0 comments on commit 5811862

Please sign in to comment.