Closes #13
This commit is contained in:
1 parent
bce707daf6
commit
51fcdb88ac
174 files changed
+5220
-2213
No files matched your search
@@ -7,6 +7,7 @@ SOURCES += \
|
||||
$$PWD/tree_node.cpp
|
||||
|
||||
HEADERS += \
|
||||
$$PWD/dbc_diagnostic.h \
|
||||
$$PWD/multiplexing.h \
|
||||
$$PWD/dbc_database.h \
|
||||
$$PWD/dbc_decode_builder.h \
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
#ifndef DBC_DIAGNOSTIC_H
|
||||
#define DBC_DIAGNOSTIC_H
|
||||
|
||||
#include "dbc_database.h"
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
enum class DbcDiagnosticSeverity {
|
||||
Warning,
|
||||
Error
|
||||
};
|
||||
|
||||
/** Stable categories; messages provide the details. */
|
||||
enum class DbcDiagnosticCode {
|
||||
FileOpenError,
|
||||
FileReadError,
|
||||
InvalidStatement,
|
||||
InvalidValueDescription,
|
||||
InvalidMultiplexing,
|
||||
UnsupportedStatement
|
||||
};
|
||||
|
||||
struct DbcDiagnostic {
|
||||
DbcDiagnosticSeverity severity;
|
||||
DbcDiagnosticCode code;
|
||||
std::string filePath;
|
||||
std::size_t lineNumber; // One-based physical line; zero for file-level errors.
|
||||
std::string message;
|
||||
};
|
||||
|
||||
struct DbcParseResult {
|
||||
DbcDatabase database;
|
||||
std::vector<DbcDiagnostic> diagnostics;
|
||||
|
||||
bool HasErrors() const {
|
||||
for (const DbcDiagnostic &diagnostic : diagnostics) {
|
||||
if (diagnostic.severity == DbcDiagnosticSeverity::Error)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* DBC_DIAGNOSTIC_H */
|
||||
+71
-9
@@ -14,17 +14,47 @@
|
||||
|
||||
#include "text_utils.h"
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
|
||||
namespace {
|
||||
DbcDiagnosticCode StatementErrorCode(const std::string &keyword) {
|
||||
if (keyword == "SG_MUL_VAL_")
|
||||
return DbcDiagnosticCode::UnsupportedStatement;
|
||||
if (keyword == "VAL_")
|
||||
return DbcDiagnosticCode::InvalidValueDescription;
|
||||
return DbcDiagnosticCode::InvalidStatement;
|
||||
}
|
||||
}
|
||||
|
||||
DbcDatabase DbcParser::ParseFile(const std::string &filePath) const {
|
||||
DbcParseResult result = ParseFileWithDiagnostics(filePath);
|
||||
for (const DbcDiagnostic &diagnostic : result.diagnostics) {
|
||||
if (diagnostic.severity == DbcDiagnosticSeverity::Error) {
|
||||
if (diagnostic.lineNumber == 0U)
|
||||
throw std::runtime_error(diagnostic.message);
|
||||
throw std::runtime_error(diagnostic.filePath + ":" +
|
||||
std::to_string(diagnostic.lineNumber) + ": " + diagnostic.message);
|
||||
}
|
||||
}
|
||||
return std::move(result.database);
|
||||
}
|
||||
|
||||
DbcParseResult DbcParser::ParseFileWithDiagnostics(const std::string &filePath) const {
|
||||
DbcParseResult result;
|
||||
std::ifstream input(filePath.c_str());
|
||||
if (!input.is_open())
|
||||
throw std::runtime_error("Failed to open DBC file: " + filePath);
|
||||
if (!input.is_open()) {
|
||||
result.diagnostics.push_back({DbcDiagnosticSeverity::Error,
|
||||
DbcDiagnosticCode::FileOpenError, filePath, 0U,
|
||||
"Failed to open DBC file: " + filePath});
|
||||
return result;
|
||||
}
|
||||
|
||||
DbcDatabase database;
|
||||
DbcParseContext context{database, nullptr};
|
||||
const DbcStatementDispatcher dispatcher;
|
||||
|
||||
bool inNamespace = false;
|
||||
std::size_t lineNumber = 0U;
|
||||
std::string line;
|
||||
while (std::getline(input, line)) {
|
||||
@@ -33,16 +63,48 @@ DbcDatabase DbcParser::ParseFile(const std::string &filePath) const {
|
||||
if (line.empty())
|
||||
continue;
|
||||
|
||||
std::istringstream tokens(line);
|
||||
std::string keyword;
|
||||
std::string argument;
|
||||
tokens >> keyword;
|
||||
const bool hasArgument = static_cast<bool>(tokens >> argument);
|
||||
if (inNamespace && !hasArgument && keyword != "BO_" && keyword != "SG_" &&
|
||||
keyword != "BS_" && keyword != "BS_:" &&
|
||||
keyword != "BU_" && keyword != "BU_:")
|
||||
continue; // NS_ lists syntax symbols; BO_, SG_, BS_ and BU_ end the list.
|
||||
inNamespace = keyword == "NS_" || keyword == "NS_:";
|
||||
|
||||
try {
|
||||
context.lineNumber = lineNumber;
|
||||
dispatcher.Dispatch(line, context);
|
||||
if (!dispatcher.Dispatch(line, context)) {
|
||||
result.diagnostics.push_back({DbcDiagnosticSeverity::Warning,
|
||||
DbcDiagnosticCode::UnsupportedStatement,
|
||||
filePath, lineNumber,
|
||||
"Unsupported statement '" + keyword +
|
||||
"' is ignored; decoding or metadata may be incomplete."});
|
||||
}
|
||||
} catch (const std::exception &error) {
|
||||
throw std::runtime_error(filePath + ":" + std::to_string(lineNumber) +
|
||||
": " + error.what());
|
||||
result.diagnostics.push_back({DbcDiagnosticSeverity::Error,
|
||||
StatementErrorCode(keyword), filePath,
|
||||
lineNumber, error.what()});
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
context.ResolveValueDescriptions(filePath);
|
||||
context.ValidateMultiplexing(filePath);
|
||||
return database;
|
||||
if (input.bad() || !input.eof()) {
|
||||
result.diagnostics.push_back({DbcDiagnosticSeverity::Error,
|
||||
DbcDiagnosticCode::FileReadError, filePath, 0U,
|
||||
"Failed to read DBC file: " + filePath});
|
||||
return result;
|
||||
}
|
||||
|
||||
try {
|
||||
context.ResolveValueDescriptions(filePath);
|
||||
context.ValidateMultiplexing(filePath);
|
||||
} catch (const DbcDiagnosticError &error) {
|
||||
result.diagnostics.push_back(error.diagnostic);
|
||||
return result;
|
||||
}
|
||||
result.database = std::move(database);
|
||||
return result;
|
||||
}
|
||||
+11
-4
@@ -14,7 +14,7 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "dbc_database.h"
|
||||
#include "dbc_diagnostic.h"
|
||||
|
||||
/**
|
||||
* @brief Minimal DBC parser.
|
||||
@@ -27,9 +27,8 @@
|
||||
* - basic M / mN multiplexing
|
||||
* - VAL_ signal value descriptions
|
||||
*
|
||||
* Ignores:
|
||||
* - attributes
|
||||
* - named VAL_TABLE_ tables
|
||||
* Reports unsupported statements as warnings, except SG_MUL_VAL_ which is
|
||||
* an error. VERSION, NS_, BS_, BU_ and NS_ symbol declarations stay quiet.
|
||||
*/
|
||||
class DbcParser {
|
||||
public:
|
||||
@@ -44,6 +43,14 @@ public:
|
||||
*/
|
||||
DbcDatabase ParseFile(const std::string &filePath) const;
|
||||
|
||||
/**
|
||||
* @brief Parse with warnings and a structured first error instead of throwing.
|
||||
* @return Valid database with diagnostics; empty database on error.
|
||||
* File errors use line zero; statement errors use physical one-based lines.
|
||||
* Warnings indicate ignored syntax and possible incomplete decoding.
|
||||
*/
|
||||
DbcParseResult ParseFileWithDiagnostics(const std::string &filePath) const;
|
||||
|
||||
};
|
||||
|
||||
#endif /* DBC_PARSER_H */
|
||||
@@ -372,6 +372,13 @@ void DbcCommentHandler::Handle(const std::string &line, DbcParseContext &context
|
||||
}
|
||||
|
||||
DbcStatementDispatcher::DbcStatementDispatcher() {
|
||||
RegisterType<DbcHeaderHandler>("VERSION");
|
||||
RegisterType<DbcHeaderHandler>("NS_");
|
||||
RegisterType<DbcHeaderHandler>("NS_:");
|
||||
RegisterType<DbcHeaderHandler>("BS_");
|
||||
RegisterType<DbcHeaderHandler>("BS_:");
|
||||
RegisterType<DbcHeaderHandler>("BU_");
|
||||
RegisterType<DbcHeaderHandler>("BU_:");
|
||||
RegisterType<DbcUnsupportedMultiplexingHandler>("SG_MUL_VAL_");
|
||||
RegisterType<DbcFrameHandler>("BO_");
|
||||
RegisterType<DbcSignalHandler>("SG_");
|
||||
@@ -379,6 +386,8 @@ DbcStatementDispatcher::DbcStatementDispatcher() {
|
||||
RegisterType<DbcValueDescriptionHandler>("VAL_");
|
||||
}
|
||||
|
||||
void DbcHeaderHandler::Handle(const std::string &, DbcParseContext &) const {}
|
||||
|
||||
bool DbcStatementDispatcher::Dispatch(const std::string &line,
|
||||
DbcParseContext &context) const {
|
||||
std::istringstream stream(line);
|
||||
@@ -434,7 +443,8 @@ void DbcParseContext::ResolveValueDescriptions(const std::string &filePath) {
|
||||
throw std::runtime_error("Duplicate VAL_ value key for signal: " + pending.signalName);
|
||||
}
|
||||
} catch (const std::exception &error) {
|
||||
throw std::runtime_error(filePath + ":" + std::to_string(pending.lineNumber) + ": " + error.what());
|
||||
throw DbcDiagnosticError(DbcDiagnosticCode::InvalidValueDescription,
|
||||
filePath, pending.lineNumber, error.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -479,8 +489,9 @@ namespace {
|
||||
if (signal.multiplexing.role != MultiplexRole::Selector)
|
||||
continue;
|
||||
if (selector != nullptr || signal.isSigned)
|
||||
throw std::runtime_error(filePath + ":" + std::to_string(lines[index]) +
|
||||
": Multiplexing requires one unsigned selector per frame.");
|
||||
throw DbcDiagnosticError(DbcDiagnosticCode::InvalidMultiplexing,
|
||||
filePath, lines[index],
|
||||
"Multiplexing requires one unsigned selector per frame.");
|
||||
selector = &signal;
|
||||
}
|
||||
return selector;
|
||||
@@ -512,7 +523,8 @@ void DbcParseContext::ValidateMultiplexing(const std::string &filePath) const {
|
||||
error = "Signals overlap without mutually exclusive multiplexing conditions.";
|
||||
}
|
||||
if (!error.empty())
|
||||
throw std::runtime_error(filePath + ":" + std::to_string(signalLines[frameIndex][index]) + ": " + error);
|
||||
throw DbcDiagnosticError(DbcDiagnosticCode::InvalidMultiplexing,
|
||||
filePath, signalLines[frameIndex][index], error);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,9 @@
|
||||
#define DBC_STATEMENT_HANDLER_H
|
||||
|
||||
#include "dbc_database.h"
|
||||
#include "dbc_diagnostic.h"
|
||||
#include "typefactory.h"
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
@@ -15,6 +17,17 @@ struct PendingValueDescriptions {
|
||||
std::size_t lineNumber;
|
||||
};
|
||||
|
||||
/** Carries deferred validation locations without parsing exception text. */
|
||||
class DbcDiagnosticError : public std::runtime_error {
|
||||
public:
|
||||
DbcDiagnosticError(DbcDiagnosticCode code, const std::string &filePath,
|
||||
std::size_t lineNumber, const std::string &message)
|
||||
: std::runtime_error(filePath + ":" + std::to_string(lineNumber) + ": " + message),
|
||||
diagnostic{DbcDiagnosticSeverity::Error, code, filePath, lineNumber, message} {}
|
||||
|
||||
DbcDiagnostic diagnostic;
|
||||
};
|
||||
|
||||
/** Mutable state belonging to one file parse. Handlers do not retain it. */
|
||||
struct DbcParseContext {
|
||||
DbcDatabase &database;
|
||||
@@ -68,6 +81,12 @@ public:
|
||||
void Handle(const std::string &line, DbcParseContext &context) const override;
|
||||
};
|
||||
|
||||
/** Common header metadata does not change the supported decode definitions. */
|
||||
class DbcHeaderHandler : public DbcStatementHandler {
|
||||
public:
|
||||
void Handle(const std::string &line, DbcParseContext &context) const override;
|
||||
};
|
||||
|
||||
/** Exact-key dispatch to handlers created once per registration via TypeFactory. */
|
||||
class DbcStatementDispatcher {
|
||||
public:
|
||||
|
||||
Reference in new issue
Block a user