This commit is contained in:
deeaitch committed 2026-10-14 10:32:44 -04:00
1 parent 0b05152bcf
commit d87c100398
9 files changed
+265 -65

No files matched your search

+44 -4
View File
@@ -19,6 +19,36 @@
#include <utility>
namespace {
// Read through the closing unescaped quote, preserving comment whitespace.
void ReadCommentLines(std::ifstream &input, std::string &statement,
std::size_t &lineNumber) {
bool inQuote = false;
bool escaped = false;
std::string part = statement;
for (;;) {
for (const char character: part) {
if (escaped) {
escaped = false;
} else if (inQuote && character == '\\') {
escaped = true;
} else if (character == '"') {
if (inQuote)
return;
inQuote = true;
}
}
if (!inQuote || !std::getline(input, part))
return;
++lineNumber;
if (!part.empty() && part.back() == '\r')
part.pop_back();
statement += '\n';
statement += part;
// A physical newline cannot escape the next line's first character.
escaped = false;
}
}
DbcDiagnosticCode StatementErrorCode(const std::string &keyword) {
if (keyword == "SG_MUL_VAL_")
return DbcDiagnosticCode::UnsupportedStatement;
@@ -59,6 +89,10 @@ DbcParseResult DbcParser::ParseFileWithDiagnostics(const std::string &filePath)
std::string line;
while (std::getline(input, line)) {
++lineNumber;
if (!line.empty() && line.back() == '\r')
line.pop_back();
const std::string rawLine = line;
const std::size_t statementLine = lineNumber;
line = dbc_detail::TrimText(line);
if (line.empty())
continue;
@@ -74,19 +108,24 @@ DbcParseResult DbcParser::ParseFileWithDiagnostics(const std::string &filePath)
continue; // NS_ lists syntax symbols; BO_, SG_, BS_ and BU_ end the list.
inNamespace = keyword == "NS_" || keyword == "NS_:";
if (keyword == "CM_") {
line = rawLine;
ReadCommentLines(input, line, lineNumber);
}
try {
context.lineNumber = lineNumber;
context.lineNumber = statementLine;
if (!dispatcher.Dispatch(line, context)) {
result.diagnostics.push_back({DbcDiagnosticSeverity::Warning,
DbcDiagnosticCode::UnsupportedStatement,
filePath, lineNumber,
filePath, statementLine,
"Unsupported statement '" + keyword +
"' is ignored; decoding or metadata may be incomplete."});
"' is ignored; decoding or metadata may be incomplete."});
}
} catch (const std::exception &error) {
result.diagnostics.push_back({DbcDiagnosticSeverity::Error,
StatementErrorCode(keyword), filePath,
lineNumber, error.what()});
statementLine, error.what()});
return result;
}
}
@@ -99,6 +138,7 @@ DbcParseResult DbcParser::ParseFileWithDiagnostics(const std::string &filePath)
}
try {
context.ResolveComments(filePath);
context.ResolveValueDescriptions(filePath);
context.ValidateMultiplexing(filePath);
} catch (const DbcDiagnosticError &error) {
+2
View File
@@ -27,6 +27,8 @@
* - basic M / mN multiplexing
* - VAL_ signal value descriptions
*
* Frame/signal comments support multiline quoted text and deferred references.
*
* Reports unsupported statements as warnings, except SG_MUL_VAL_ which is
* an error. VERSION, NS_, BS_, BU_ and NS_ symbol declarations stay quiet.
*/
+71 -50
View File
@@ -21,6 +21,15 @@ namespace {
return value;
}
std::uint32_t ParseCanId(const std::string &text) {
const std::uint32_t rawId = ParseDecimal(
text, std::numeric_limits<std::uint32_t>::max(), "CAN ID");
// Only bit 31 may accompany the actual 29-bit identifier.
if ((rawId & 0x60000000U) != 0U)
throw std::runtime_error("CAN ID exceeds the 29-bit identifier range.");
return rawId;
}
double ParsePhysicalValue(const std::string &text, const char *field) {
static const std::regex number(
R"(^[+-]?(?:[0-9]+(?:\.[0-9]*)?|\.[0-9]+)(?:[eE][+-]?[0-9]+)?$)");
@@ -193,11 +202,7 @@ FrameInfo DbcFrameHandler::ParseFrameLine(const std::string &line) {
if (!(stream >> keyword >> idText) || keyword != "BO_")
throw std::runtime_error("Missing CAN ID in frame definition.");
const std::uint32_t rawCanId = ParseDecimal(
idText, std::numeric_limits<std::uint32_t>::max(), "CAN ID");
// Only bit 31 may accompany the actual 29-bit identifier.
if ((rawCanId & 0x60000000U) != 0U)
throw std::runtime_error("CAN ID exceeds the 29-bit identifier range.");
const std::uint32_t rawCanId = ParseCanId(idText);
std::string nameText;
if (!std::getline(stream, nameText, ':') || stream.eof())
@@ -273,50 +278,6 @@ SignalInfo DbcSignalHandler::ParseSignalLine(const std::string &line) {
return signal;
}
void DbcCommentHandler::ParseCommentLine(const std::string &line, DbcDatabase &database) {
/*
* Examples:
* CM_ BO_ 256 "Frame comment";
* CM_ SG_ 256 EngineSpeed "Signal comment";
*/
std::istringstream stream(line);
std::string token;
stream >> token;
if (token != "CM_")
return;
stream >> token;
if (token != "BO_" && token != "SG_")
return;
std::uint32_t rawCanId = 0U;
std::uint32_t canId = 0U;
bool isExtended = false;
std::string signalName;
stream >> rawCanId;
if (token == "SG_")
stream >> signalName;
NormalizeCanId(rawCanId, canId, isExtended);
const std::string::size_type quoteBegin = line.find('"');
const std::string::size_type quoteEnd = line.rfind('"');
if (quoteBegin == std::string::npos || quoteEnd == std::string::npos || quoteEnd <= quoteBegin)
return;
FrameInfo *frame = FindFrameById(database, canId, isExtended);
if (frame == nullptr)
return;
const std::string comment = line.substr(quoteBegin + 1U, quoteEnd - quoteBegin - 1U);
if (token == "BO_") {
frame->comment = comment;
return;
}
SignalInfo *signal = FindSignalByName(*frame, signalName);
if (signal != nullptr)
signal->comment = comment;
}
namespace {
FrameInfo *FindFrameById(DbcDatabase &database,
@@ -365,7 +326,67 @@ void DbcSignalHandler::Handle(const std::string &line, DbcParseContext &context)
}
void DbcCommentHandler::Handle(const std::string &line, DbcParseContext &context) const {
ParseCommentLine(line, context.database);
std::istringstream stream(line);
std::string keyword;
std::string target;
stream >> keyword >> target;
if (keyword != "CM_" || (target != "BO_" && target != "SG_"))
return;
PendingComment pending;
pending.isSignal = target == "SG_";
pending.lineNumber = context.lineNumber;
std::string idText;
if (!(stream >> idText))
throw std::runtime_error("Missing CAN ID in CM_ statement.");
NormalizeCanId(ParseCanId(idText), pending.canId, pending.isExtended);
if (pending.isSignal && (!(stream >> pending.signalName) ||
pending.signalName.find_first_of("\";") != std::string::npos))
throw std::runtime_error("Missing or invalid signal name in CM_ statement.");
stream >> std::ws;
if (stream.get() != '"')
throw std::runtime_error("Missing opening quote in CM_ statement.");
bool closed = false;
char character;
while (stream.get(character)) {
if (character == '"') {
closed = true;
break;
}
if (character == '\\') {
if (!stream.get(character) || (character != '"' && character != '\\'))
throw std::runtime_error("Invalid escape in CM_ statement.");
}
pending.comment += character;
}
if (!closed)
throw std::runtime_error("Unterminated CM_ comment.");
stream >> std::ws;
if (stream.get() != ';')
throw std::runtime_error("Missing semicolon in CM_ statement.");
stream >> std::ws;
if (!stream.eof())
throw std::runtime_error("Unexpected trailing text in CM_ statement.");
context.pendingComments.push_back(std::move(pending));
}
void DbcParseContext::ResolveComments(const std::string &filePath) {
for (const PendingComment &pending: pendingComments) {
FrameInfo *frame = FindFrameById(database, pending.canId, pending.isExtended);
if (!frame)
throw DbcDiagnosticError(DbcDiagnosticCode::InvalidStatement, filePath,
pending.lineNumber, "Unresolved CM_ frame reference.");
if (pending.isSignal) {
SignalInfo *signal = FindSignalByName(*frame, pending.signalName);
if (!signal)
throw DbcDiagnosticError(DbcDiagnosticCode::InvalidStatement, filePath,
pending.lineNumber, "Unresolved CM_ signal reference: " + pending.signalName);
signal->comment = pending.comment;
} else {
frame->comment = pending.comment;
}
}
}
DbcStatementDispatcher::DbcStatementDispatcher() {
+12 -2
View File
@@ -17,6 +17,15 @@ struct PendingValueDescriptions {
std::size_t lineNumber;
};
struct PendingComment {
std::uint32_t canId;
bool isExtended;
bool isSignal;
std::string signalName;
std::string comment;
std::size_t lineNumber;
};
/** Carries deferred validation locations without parsing exception text. */
class DbcDiagnosticError : public std::runtime_error {
public:
@@ -38,6 +47,9 @@ struct DbcParseContext {
std::vector<std::vector<std::size_t>> signalLines = {};
void ValidateMultiplexing(const std::string &filePath) const;
std::vector<PendingComment> pendingComments = {};
void ResolveComments(const std::string &filePath);
void ResolveValueDescriptions(const std::string &filePath);
};
@@ -67,8 +79,6 @@ class DbcCommentHandler : public DbcStatementHandler {
public:
void Handle(const std::string &line, DbcParseContext &context) const override;
private:
static void ParseCommentLine(const std::string &line, DbcDatabase &database);
};
class DbcValueDescriptionHandler : public DbcStatementHandler {