DBC Framework
DBC parsing and CAN signal decoding framework
Loading...
Searching...
No Matches
dbc_parser.cpp
Go to the documentation of this file.
1
12#include "dbc_parser.h"
13#include "dbc_statement_handler.h"
14
15#include "text_utils.h"
16#include <fstream>
17#include <sstream>
18#include <stdexcept>
19#include <utility>
20
21namespace {
22 // Read through the closing unescaped quote, preserving comment whitespace.
23 void ReadCommentLines(std::ifstream &input, std::string &statement,
24 std::size_t &lineNumber) {
25 bool inQuote = false;
26 bool escaped = false;
27 std::string part = statement;
28 for (;;) {
29 for (const char character: part) {
30 if (escaped) {
31 escaped = false;
32 } else if (inQuote && character == '\\') {
33 escaped = true;
34 } else if (character == '"') {
35 if (inQuote)
36 return;
37 inQuote = true;
38 }
39 }
40 if (!inQuote || !std::getline(input, part))
41 return;
42 ++lineNumber;
43 if (!part.empty() && part.back() == '\r')
44 part.pop_back();
45 statement += '\n';
46 statement += part;
47 // A physical newline cannot escape the next line's first character.
48 escaped = false;
49 }
50 }
51
52 DbcDiagnosticCode StatementErrorCode(const std::string &keyword) {
53 if (keyword == "SG_MUL_VAL_")
54 return DbcDiagnosticCode::UnsupportedStatement;
55 if (keyword == "VAL_")
56 return DbcDiagnosticCode::InvalidValueDescription;
57 return DbcDiagnosticCode::InvalidStatement;
58 }
59}
60
61DbcDatabase DbcParser::ParseFile(const std::string &filePath) const {
63 for (const DbcDiagnostic &diagnostic : result.diagnostics) {
64 if (diagnostic.severity == DbcDiagnosticSeverity::Error) {
65 if (diagnostic.lineNumber == 0U)
66 throw std::runtime_error(diagnostic.message);
67 throw std::runtime_error(diagnostic.filePath + ":" +
68 std::to_string(diagnostic.lineNumber) + ": " + diagnostic.message);
69 }
70 }
71 return std::move(result.database);
72}
73
74DbcParseResult DbcParser::ParseFileWithDiagnostics(const std::string &filePath) const {
75 DbcParseResult result;
76 std::ifstream input(filePath.c_str());
77 if (!input.is_open()) {
78 result.diagnostics.push_back({DbcDiagnosticSeverity::Error,
79 DbcDiagnosticCode::FileOpenError, filePath, 0U,
80 "Failed to open DBC file: " + filePath});
81 return result;
82 }
83
84 DbcDatabase database;
85 DbcParseContext context{database, nullptr};
86 context.enableJ1939 = options.enableJ1939;
87 const DbcStatementDispatcher dispatcher;
88 bool inNamespace = false;
89 std::size_t lineNumber = 0U;
90 std::string line;
91 while (std::getline(input, line)) {
92 ++lineNumber;
93 if (!line.empty() && line.back() == '\r')
94 line.pop_back();
95 const std::string rawLine = line;
96 const std::size_t statementLine = lineNumber;
97 line = dbc_detail::TrimText(line);
98 if (line.empty())
99 continue;
100
101 std::istringstream tokens(line);
102 std::string keyword;
103 std::string argument;
104 tokens >> keyword;
105 const bool hasArgument = static_cast<bool>(tokens >> argument);
106 if (inNamespace && !hasArgument && keyword != "BO_" && keyword != "SG_" &&
107 keyword != "BS_" && keyword != "BS_:" &&
108 keyword != "BU_" && keyword != "BU_:")
109 continue; // NS_ lists syntax symbols; BO_, SG_, BS_ and BU_ end the list.
110 inNamespace = keyword == "NS_" || keyword == "NS_:";
111
112 if (keyword == "CM_") {
113 line = rawLine;
114 ReadCommentLines(input, line, lineNumber);
115 }
116
117 try {
118 context.lineNumber = statementLine;
119 if (!dispatcher.Dispatch(line, context)) {
120 result.diagnostics.push_back({DbcDiagnosticSeverity::Warning,
121 DbcDiagnosticCode::UnsupportedStatement,
122 filePath, statementLine,
123 "Unsupported statement '" + keyword +
124 "' is ignored; decoding or metadata may be incomplete."});
125 }
126 } catch (const std::exception &error) {
127 result.diagnostics.push_back({DbcDiagnosticSeverity::Error,
128 StatementErrorCode(keyword), filePath,
129 statementLine, error.what()});
130 return result;
131 }
132 }
133
134 if (input.bad() || !input.eof()) {
135 result.diagnostics.push_back({DbcDiagnosticSeverity::Error,
136 DbcDiagnosticCode::FileReadError, filePath, 0U,
137 "Failed to read DBC file: " + filePath});
138 return result;
139 }
140
141 try {
142 context.ResolveComments(filePath);
143 context.ResolveValueDescriptions(filePath);
144 context.ValidateMultiplexing(filePath);
145 context.ResolveAttributes(filePath, result.diagnostics);
146 } catch (const DbcDiagnosticError &error) {
147 result.diagnostics.push_back(error.diagnostic);
148 return result;
149 }
150 result.database = std::move(database);
151 return result;
152}
DbcParseResult ParseFileWithDiagnostics(const std::string &filePath) const
Parse with warnings and a structured first error instead of throwing.
DbcDatabase ParseFile(const std::string &filePath) const
Parse DBC file.
bool Dispatch(const std::string &line, DbcParseContext &context) const
DBC file parser and interpretation options.
Parsed DBC content stored in a simple internal form.
Diagnostic message and physical source location.
Parsed database and diagnostics; the database is empty on error.
std::vector< DbcDiagnostic > diagnostics
DbcDatabase database