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 // Attribute statements end at a semicolon outside quoted text.
23 void ReadAttributeLines(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 inQuote = !inQuote;
36 } else if (!inQuote && character == ';') {
37 return;
38 }
39 }
40 if (!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 escaped = false;
48 }
49 }
50
51 // Read through the closing unescaped quote, preserving comment whitespace.
52 void ReadCommentLines(std::ifstream &input, std::string &statement,
53 std::size_t &lineNumber) {
54 bool inQuote = false;
55 bool escaped = false;
56 std::string part = statement;
57 for (;;) {
58 for (const char character: part) {
59 if (escaped) {
60 escaped = false;
61 } else if (inQuote && character == '\\') {
62 escaped = true;
63 } else if (character == '"') {
64 if (inQuote)
65 return;
66 inQuote = true;
67 }
68 }
69 if (!inQuote || !std::getline(input, part))
70 return;
71 ++lineNumber;
72 if (!part.empty() && part.back() == '\r')
73 part.pop_back();
74 statement += '\n';
75 statement += part;
76 // A physical newline cannot escape the next line's first character.
77 escaped = false;
78 }
79 }
80
81 DbcDiagnosticCode StatementErrorCode(const std::string &keyword) {
82 if (keyword == "SG_MUL_VAL_")
83 return DbcDiagnosticCode::InvalidMultiplexing;
84 if (keyword == "VAL_")
85 return DbcDiagnosticCode::InvalidValueDescription;
86 return DbcDiagnosticCode::InvalidStatement;
87 }
88}
89
90DbcDatabase DbcParser::ParseFile(const std::string &filePath) const {
92 for (const DbcDiagnostic &diagnostic : result.diagnostics) {
93 if (diagnostic.severity == DbcDiagnosticSeverity::Error) {
94 if (diagnostic.lineNumber == 0U)
95 throw std::runtime_error(diagnostic.message);
96 throw std::runtime_error(diagnostic.filePath + ":" +
97 std::to_string(diagnostic.lineNumber) + ": " + diagnostic.message);
98 }
99 }
100 return std::move(result.database);
101}
102
103DbcParseResult DbcParser::ParseFileWithDiagnostics(const std::string &filePath) const {
104 DbcParseResult result;
105 std::ifstream input(filePath.c_str());
106 if (!input.is_open()) {
107 result.diagnostics.push_back({DbcDiagnosticSeverity::Error,
108 DbcDiagnosticCode::FileOpenError, filePath, 0U,
109 "Failed to open DBC file: " + filePath});
110 return result;
111 }
112
113 DbcDatabase database;
114 DbcParseContext context{database, nullptr};
115 context.enableJ1939 = options.enableJ1939;
116 const DbcStatementDispatcher dispatcher;
117 bool inNamespace = false;
118 std::size_t lineNumber = 0U;
119 std::string line;
120 while (std::getline(input, line)) {
121 ++lineNumber;
122 if (!line.empty() && line.back() == '\r')
123 line.pop_back();
124 const std::string rawLine = line;
125 const std::size_t statementLine = lineNumber;
126 line = dbc_detail::TrimText(line);
127 if (line.empty())
128 continue;
129
130 std::istringstream tokens(line);
131 std::string keyword;
132 std::string argument;
133 tokens >> keyword;
134 const bool hasArgument = static_cast<bool>(tokens >> argument);
135 if (inNamespace && !hasArgument && keyword != "BO_" && keyword != "SG_" &&
136 keyword != "BS_" && keyword != "BS_:" &&
137 keyword != "BU_" && keyword != "BU_:")
138 continue; // NS_ lists syntax symbols; BO_, SG_, BS_ and BU_ end the list.
139 inNamespace = keyword == "NS_" || keyword == "NS_:";
140
141 if (keyword == "CM_") {
142 line = rawLine;
143 ReadCommentLines(input, line, lineNumber);
144 } else if (keyword == "BA_DEF_" || keyword == "BA_DEF_DEF_" || keyword == "BA_") {
145 line = rawLine.substr(rawLine.find(keyword));
146 ReadAttributeLines(input, line, lineNumber);
147 }
148
149 try {
150 context.lineNumber = statementLine;
151 if (!dispatcher.Dispatch(line, context)) {
152 result.diagnostics.push_back({DbcDiagnosticSeverity::Warning,
153 DbcDiagnosticCode::UnsupportedStatement,
154 filePath, statementLine,
155 "Unsupported statement '" + keyword +
156 "' is ignored; decoding or metadata may be incomplete."});
157 }
158 } catch (const std::exception &error) {
159 result.diagnostics.push_back({DbcDiagnosticSeverity::Error,
160 StatementErrorCode(keyword), filePath,
161 statementLine, error.what()});
162 return result;
163 }
164 }
165
166 if (input.bad() || !input.eof()) {
167 result.diagnostics.push_back({DbcDiagnosticSeverity::Error,
168 DbcDiagnosticCode::FileReadError, filePath, 0U,
169 "Failed to read DBC file: " + filePath});
170 return result;
171 }
172
173 try {
174 context.ResolveComments(filePath);
175 context.ResolveValueDescriptions(filePath);
176 context.ResolveMultiplexing(filePath);
177 context.ValidateMultiplexing(filePath);
178 context.ResolveAttributes(filePath, result.diagnostics);
179 } catch (const DbcDiagnosticError &error) {
180 result.diagnostics.push_back(error.diagnostic);
181 return result;
182 }
183 result.database = std::move(database);
184 return result;
185}
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