Files
qt5-legatus-legacy/TetraIF/LogView/LogViewTetraTelegramsParser.cpp
T
2026-08-13 18:33:10 +02:00

522 lines
15 KiB
C++

/**
* Parses training_area_telegrams.h
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Ehrlicher, SLSE, 2016-04-14
*/
#include "LogViewTetraTelegramsParser.hpp"
#include <QFile>
#include <QVector>
namespace N_LogView
{
LogViewTetraTelegramsParser::LogViewTetraTelegramsParser()
: m_logger(getLogger("LogViewTetraTelegramsParser"))
{
insertPod(QStringLiteral("bool"), 1, true, true, true);
insertPod(QStringLiteral("int"), 4, true, true);
insertPod(QStringLiteral("int32_t"), 4, true, true);
insertPod(QStringLiteral("qint64"), 8, true, true);
insertPod(QStringLiteral("float"), 4, true, false);
insertPod(QStringLiteral("double"), 8, true, false);
}
EnumInformation LogViewTetraTelegramsParser::enumFromType(const QString &type) const
{
for (const auto &ei : m_enums)
{
const QString fqType = ei.ns.isEmpty() ? ei.type : (ei.ns + QStringLiteral("::") + ei.type);
if (fqType == type)
return ei;
}
return EnumInformation();
}
StructInformation LogViewTetraTelegramsParser::structFromType(const QString &type) const
{
return m_structs.value(type);
}
PodInformation LogViewTetraTelegramsParser::podFromType(const QString &type) const
{
return m_pods.value(type);
}
bool LogViewTetraTelegramsParser::isEnum(const QString &type) const
{
for (const auto &ei : m_enums)
if (ei.type == type)
return true;
return false;
}
bool LogViewTetraTelegramsParser::isStruct(const QString &type) const
{
return m_structs.contains(type);
}
bool LogViewTetraTelegramsParser::isPod(const QString &type) const
{
return m_pods.contains(type);
}
static void fillBasemembers(StructInformation &si, const QHash<QString, StructInformation> &structs)
{
if (si.baseStruct.isEmpty())
return;
StructInformation base = structs.value(si.baseStruct);
fillBasemembers(base, structs);
si.member = (base.member + si.member); // base first
}
bool LogViewTetraTelegramsParser::parse(const QString &filename)
{
m_telegramIdEnum = EnumInformation();
m_enums.clear();
m_structs.clear();
//m_pods.clear(); // only set once
m_visibleTelegrams.clear();
QFile f(filename);
if (!f.open(QIODevice::ReadOnly))
{
WARNING_LOG(m_logger) << "Can not open file " << filename << " for reading: " << f.errorString();
return false;
}
const QString content = QString::fromUtf8(f.readAll());
f.close();
const QVector<QStringRef> lines = content.splitRef(QLatin1Char('\n'));
QVector<QStringRef> linesTrimmed;
linesTrimmed.reserve(lines.size());
for (const QStringRef &line : lines)
linesTrimmed += line.trimmed();
int i = 0;
while (i < linesTrimmed.size())
{
const QStringRef &line = linesTrimmed.at(i);
if (line.startsWith(QLatin1String("struct")))
{
i = parseStruct(linesTrimmed, i);
if (i < 0)
return false;
}
else if (line.startsWith(QLatin1String("enum")))
{
i = parseEnum(linesTrimmed, i, QString());
if (i < 0)
return false;
}
else if (line.startsWith(QLatin1String("template <> struct Constraints")))
{
i = parseConstraint(linesTrimmed, i);
if (i < 0)
return false;
}
++i;
}
const auto completeStructs = m_structs;
for (StructInformation &si : m_structs)
fillBasemembers(si, completeStructs);
return true;
}
void LogViewTetraTelegramsParser::insertPod(const QString &type, int byteLen, bool isSigned, bool isIntegral, bool isBoolean)
{
PodInformation pod;
pod.type = type;
pod.byteLen = byteLen;
pod.isBoolean = isBoolean;
pod.isIntegral = isIntegral;
pod.isSigned = isSigned;
m_pods[type] = pod;
}
int LogViewTetraTelegramsParser::parseStruct(const QVector<QStringRef> &lines, int iStartPos)
{
const QVector<QStringRef> tokens = tokenize(lines.at(iStartPos));
if (tokens.size() < 2)
return -1;
const QStringRef &structType = tokens.at(1);
if (structType.endsWith(QLatin1Char(';'))) // forward decl
return iStartPos; // move to next line
StructInformation si;
si.type = structType.toString();
if (tokens.size() > 2)
{
if (tokens.at(2) != QLatin1String(":"))
{
WARNING_LOG(m_logger) << "Could not parse struct: '" << lines.at(iStartPos).toString() << "', line: " << iStartPos + 1;
return -1;
}
for (int i = 3; i < tokens.size(); ++i)
{
if (!si.baseStruct.isEmpty())
{
WARNING_LOG(m_logger) << "Could not parse struct: '" << lines.at(iStartPos).toString() << "', line: " << iStartPos + 1;
return -1;
}
QStringRef base = tokens.at(i);
if (base == QLatin1String("public") ||
base == QLatin1String("protected") ||
base == QLatin1String("private"))
continue;
if (base.endsWith(QLatin1Char(',')))
base.chop(1);
if (base.endsWith(QLatin1Char('>'))) // helper template - ignore
{
si.isVisible = true;
continue;
}
si.baseStruct = tokens.at(i).toString();
}
}
int i = iStartPos + 1;
while (i < lines.size())
{
const QVector<QStringRef> tokens = tokenize(lines.at(i));
if (!validStructTokens(tokens))
{
++i;
continue;
}
if (tokens.at(0) == QLatin1String("};"))
break;
if (tokens.at(0) == QLatin1String("enum"))
{
i = parseEnum(lines, i, si.type, &si);
if (i < 0)
return -1;
++i;
continue;
}
MemberInformation mi;
if (tokens.size() > 2)
{
const int parserCommentPos = tokens.at(2) == QLatin1String("=") ? 4 : 2;
if (tokens.size() > parserCommentPos)
{
const QStringRef &comment = tokens.at(parserCommentPos);
DEBUG_LOG(m_logger) << "comment: " << comment.toString();
if (comment.startsWith(QLatin1String("//@array: ")))
{
const QStringRef hint = comment.mid(QLatin1String("//@array: ").size());
QString structStr = QLatin1String("struct ") + si.type + QLatin1String("::") + tokens.at(0).toString() + QLatin1Char('\n') + hint.toString();
structStr.replace(QLatin1Char(';'), QLatin1String(";\n"));
DEBUG_LOG(m_logger) << "structStr: " << structStr;
parseStruct(structStr.splitRef(QLatin1Char('\n')), 0);
mi.type = si.type + QLatin1String("::") + tokens.at(0).toString();
mi.isArray = true;
}
if (comment == QLatin1String("//@invisible"))
mi.isVisible = false;
}
}
QStringRef name = tokens.at(1);
if (name.endsWith(QLatin1Char(';')))
name.chop(1);
if (mi.type.isEmpty())
mi.type += tokens.at(0).toString();
categorizeMember(mi);
mi.name = name.toString();
if (mi.isEnum && !mi.type.contains(QLatin1String("::")))
{
// fixup ns
for (const auto &ei : qAsConst(m_enums))
{
if (ei.type == mi.type)
{
if (!ei.ns.isEmpty())
mi.type = ei.ns + QLatin1String("::") + mi.type;
break;
}
}
}
si.member.push_back(std::move(mi));
++i;
}
if (m_logger.isDebugEnabled())
{
if (si.baseStruct.isEmpty())
DEBUG_LOG(m_logger) << "Found Struct: " << si.type;
else
DEBUG_LOG(m_logger) << "Found Struct: " << si.type << ", base: " << si.baseStruct;
for (int j = 0; j < si.member.size(); ++j)
{
const MemberInformation &mi = si.member.at(j);
DEBUG_LOG(m_logger) << "Member[" << j << "]: " << mi.type << " - " << mi.name;
}
}
m_structs[si.type] = si;
return i;
}
int LogViewTetraTelegramsParser::parseEnum(const QVector<QStringRef> &lines, int iStartPos, const QString &ns, StructInformation *si)
{
const QVector<QStringRef> tokens = tokenize(lines.at(iStartPos));
if (tokens.size() < 2)
return -1;
QStringRef enumName = tokens.at(1);
if (enumName == QLatin1String("class"))
{
if (tokens.size() < 3)
return -1;
enumName = tokens.at(2);
}
if (enumName.endsWith(QLatin1Char(';'))) // forward decl
return iStartPos; // move to next line
EnumInformation ei;
ei.type = enumName.toString();
ei.ns = ns;
int i = iStartPos + 1;
while (i < lines.size())
{
const QVector<QStringRef> tokens = tokenize(lines.at(i).split(QStringLiteral("//")).first());
if (tokens.isEmpty() ||
(tokens.at(0) == QLatin1String("{")))
{
++i;
continue;
}
if (tokens.at(0) == QLatin1String("};"))
break;
if ((tokens.size() != 1 && tokens.size() != 3) ||
(tokens.size() == 3 && tokens.at(1) != QLatin1String("=")))
{
if (tokens.size() == 4 && tokens.at(0) == QLatin1String("}") && tokens.at(3).endsWith(QLatin1Char(';')) && si)
{
// enum in struct with direct decl of member
MemberInformation mi;
if (ns.isEmpty())
mi.type = ei.type;
else
mi.type = ns + QStringLiteral("::") + ei.type;
mi.isEnum = true;
mi.name = tokens.at(1).toString();
si->member.push_back(std::move(mi));
break;
}
WARNING_LOG(m_logger) << "Could not parse enum: '" << lines.at(i).toString() << "', line: " << i + 1;
return -1;
}
EnumValue ev;
ev.key = tokens.at(0).toString();
if (tokens.size() == 3)
{
QStringRef value = tokens.at(2);
if (value.endsWith(QLatin1Char(',')))
value.chop(1);
ev.value = value.toInt();
}
else
{
int val = ei.values.isEmpty() ? 0 : (ei.values.last().value + 1);
ev.value = val;
}
ei.values.push_back(std::move(ev));
++i;
}
DEBUG_LOG(m_logger) << "Found Enum: " << ei.type;
for (const EnumValue &ev : qAsConst(ei.values))
DEBUG_LOG(m_logger) << "Member[" << i << "]: " << ev.key << " = " << ev.value;
if (ei.type == QLatin1String("TelegramId"))
m_telegramIdEnum = ei;
m_enums.push_back(std::move(ei));
return i;
}
int LogViewTetraTelegramsParser::parseConstraint(const QVector<QStringRef> &lines, int iStartPos)
{
const QVector<QStringRef> tokens = tokenize(lines.at(iStartPos));
if (tokens.size() != 4)
{
WARNING_LOG(m_logger) << "Could not parse constraint: '" << lines.at(iStartPos).toString() << "'";
return -1;
}
const QStringRef &cs = tokens.at(3);
if (!cs.startsWith(QLatin1String("Constraints<")) || !cs.endsWith(QLatin1Char('>')))
{
WARNING_LOG(m_logger) << "Could not parse constraint: '" << lines.at(iStartPos).toString() << "'";
return -1;
}
QStringRef structName = cs.mid(QLatin1String("Constraints<").size());
structName.chop(1);
DEBUG_LOG(m_logger) << "Constraint: " << structName.toString();
int i = iStartPos + 1;
while (i < lines.size())
{
const QVector<QStringRef> tokens = tokenize(lines.at(i));
if (tokens.isEmpty() ||
(tokens.at(0) == QLatin1String("{")))
{
++i;
continue;
}
if (tokens.at(0) == QLatin1String("};"))
break;
if (tokens.size() != 8 || tokens.at(2) != QLatin1String("TelegramId")) // static constexpr TelegramId id() { return TelegramId::INIT; }
{
++i;
continue;
}
const QVector<QStringRef> parts = tokens.at(6).split(QLatin1String("::"));
if (parts.size() != 2)
{
WARNING_LOG(m_logger) << "Could not parse constraint: '" << lines.at(i).toString() << "'";
return -1;
}
QStringRef enumKey = parts.at(1);
if (enumKey.endsWith(QLatin1Char(';')))
enumKey.chop(1);
bool bFound = false;
for (const auto &v : qAsConst(m_telegramIdEnum.values))
{
if (v.key == enumKey)
{
m_visibleTelegrams.insert(v.value, structName.toString());
bFound = true;
break;
}
}
if (!bFound)
{
WARNING_LOG(m_logger) << "Could not find enum value '" << enumKey.toString() << "' in enum TelegramId";
return -1;
}
++i;
}
return i;
}
QString LogViewTetraTelegramsParser::getInnerType(const QString &type)
{
int idxLt = type.lastIndexOf(QLatin1Char('>'));
if (idxLt < 0)
return QString();
int idxGt = type.indexOf(QLatin1Char('<'));
if (idxGt < 0)
return QString();
return type.mid(idxGt + 1, idxLt - idxGt - 1);
}
void LogViewTetraTelegramsParser::categorizeMember(MemberInformation &mi) const
{
mi.isPod = isPod(mi.type);
mi.isEnum = mi.isPod ? false : isEnum(mi.type);
if (mi.type.startsWith(QLatin1String("std::vector<")))
mi.isArray = true;
mi.isStruct = mi.isArray ? false : isStruct(mi.type);
mi.isString = (mi.type == QLatin1String("std::string") || mi.type == QLatin1String("QString"));
mi.isBinary = (mi.type == QLatin1String("std::vector<unsigned char>") || mi.type == QLatin1String("QByteArray"));
}
static inline void bracketCount(const QStringRef &str, int &cntLt, int &cntGt)
{
cntLt = 0;
cntGt = 0;
for (int i = 0; i < str.size(); ++i)
{
if (str.at(i) == QLatin1Char('<'))
cntLt++;
else if (str.at(i) == QLatin1Char('>'))
cntGt++;
}
}
QVector<QStringRef> LogViewTetraTelegramsParser::tokenize(const QStringRef &line) const
{
QVector<QStringRef> ret = line.split(QLatin1Char(' '), QString::SkipEmptyParts);
int i = 0;
while (i < ret.size())
{
const QStringRef &token = ret.at(i);
if (token.startsWith(QLatin1String("//")))
{
// all comment after this - merge into one
if (i == ret.size() - 1)
break;
const QStringRef &lastToken = ret.last();
const QStringRef newToken = QStringRef(token.string(), token.position(), lastToken.position() - token.position() + lastToken.size());
DEBUG_LOG(m_logger) << "New Token: " << newToken.toString();
ret.resize(i + 1);
ret[i] = newToken;
break;
}
if (i == ret.size() - 1)
break;
int cntLt;
int cntGt;
bracketCount(token, cntLt, cntGt);
if (cntLt == cntGt)
{
++i;
continue;
}
const QStringRef &nextToken = ret.at(i + 1);
const QStringRef newToken = QStringRef(token.string(), token.position(), nextToken.position() - token.position() + nextToken.size());
DEBUG_LOG(m_logger) << "New Token: " << newToken.toString();
ret.removeAt(i + 1);
ret[i] = newToken;
}
return ret;
}
bool LogViewTetraTelegramsParser::validStructTokens(const QVector<QStringRef> &tokens)
{
if (tokens.isEmpty() ||
(tokens.at(0) == QLatin1String("{")) ||
(tokens.at(0) == QLatin1String("{}")) ||
(tokens.at(0) == QLatin1String("}")) ||
(tokens.at(0) == QLatin1String(":")) ||
(tokens.at(0) == QLatin1String(",")) ||
(tokens.at(0) == QLatin1String("virtual")))
return false;
if (tokens.at(0) == QLatin1String("};") || tokens.at(0) == QLatin1String("enum"))
return true;
const int len = tokens.last().startsWith(QLatin1String("//")) ? tokens.size() - 1 : tokens.size();
if (len == 2)
return true;
if (len < 4)
return false;
if (len >= 4 && tokens.at(2) == QLatin1String("="))
return true;
return false;
}
} // namespace N_LogView