#include "LogViewTetraTelegramsParser.hpp" #include #include 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 &structs) { if (si.baseStruct.isEmpty()) return; StructInformation base = structs.value(si.baseStruct); fillBasemembers(base, structs); si.member = (base.member + si.member); } bool LogViewTetraTelegramsParser::parse(const QString &filename) { m_telegramIdEnum = EnumInformation(); m_enums.clear(); m_structs.clear(); 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 lines = content.splitRef(QLatin1Char('\n')); QVector 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 &lines, int iStartPos) { const QVector tokens = tokenize(lines.at(iStartPos)); if (tokens.size() < 2) return -1; const QStringRef &structType = tokens.at(1); if (structType.endsWith(QLatin1Char(';'))) return iStartPos; 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('>'))) { si.isVisible = true; continue; } si.baseStruct = tokens.at(i).toString(); } } int i = iStartPos + 1; while (i < lines.size()) { const QVector 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("::"))) { 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 &lines, int iStartPos, const QString &ns, StructInformation *si) { const QVector 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(';'))) return iStartPos; EnumInformation ei; ei.type = enumName.toString(); ei.ns = ns; int i = iStartPos + 1; while (i < lines.size()) { const QVector 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) { 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 &lines, int iStartPos) { const QVector 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 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")) { ++i; continue; } const QVector 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") || 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 LogViewTetraTelegramsParser::tokenize(const QStringRef &line) const { QVector ret = line.split(QLatin1Char(' '), QString::SkipEmptyParts); int i = 0; while (i < ret.size()) { const QStringRef &token = ret.at(i); if (token.startsWith(QLatin1String("//"))) { 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 &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; } }