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 if (keyword == "SIG_VALTYPE_")
87 return DbcDiagnosticCode::UnsupportedSignalType;
88 return DbcDiagnosticCode::InvalidStatement;
89 }
90}
91
92DbcDatabase DbcParser::ParseFile(const std::string &filePath) const {
94 for (const DbcDiagnostic &diagnostic : result.diagnostics) {
95 if (diagnostic.severity == DbcDiagnosticSeverity::Error) {
96 if (diagnostic.lineNumber == 0U)
97 throw std::runtime_error(diagnostic.message);
98 throw std::runtime_error(diagnostic.filePath + ":" +
99 std::to_string(diagnostic.lineNumber) + ": " + diagnostic.message);
100 }
101 }
102 return std::move(result.database);
103}
104
105DbcParseResult DbcParser::ParseFileWithDiagnostics(const std::string &filePath) const {
106 DbcParseResult result;
107 std::ifstream input(filePath.c_str());
108 if (!input.is_open()) {
109 result.diagnostics.push_back({DbcDiagnosticSeverity::Error,
110 DbcDiagnosticCode::FileOpenError, filePath, 0U,
111 "Failed to open DBC file: " + filePath});
112 return result;
113 }
114
115 DbcDatabase database;
116 DbcParseContext context{database, nullptr};
117 context.enableJ1939 = options.enableJ1939;
118 const DbcStatementDispatcher dispatcher;
119 bool inNamespace = false;
120 std::size_t lineNumber = 0U;
121 std::string line;
122 while (std::getline(input, line)) {
123 ++lineNumber;
124 if (!line.empty() && line.back() == '\r')
125 line.pop_back();
126 const std::string rawLine = line;
127 const std::size_t statementLine = lineNumber;
128 line = dbc_detail::TrimText(line);
129 if (line.empty())
130 continue;
131
132 std::istringstream tokens(line);
133 std::string keyword;
134 std::string argument;
135 tokens >> keyword;
136 const bool hasArgument = static_cast<bool>(tokens >> argument);
137 if (inNamespace && !hasArgument && keyword != "BO_" && keyword != "SG_" &&
138 keyword != "BS_" && keyword != "BS_:" &&
139 keyword != "BU_" && keyword != "BU_:")
140 continue; // NS_ lists syntax symbols; BO_, SG_, BS_ and BU_ end the list.
141 inNamespace = keyword == "NS_" || keyword == "NS_:";
142
143 if (keyword == "CM_") {
144 line = rawLine;
145 ReadCommentLines(input, line, lineNumber);
146 } else if (keyword == "BA_DEF_" || keyword == "BA_DEF_DEF_" || keyword == "BA_") {
147 line = rawLine.substr(rawLine.find(keyword));
148 ReadAttributeLines(input, line, lineNumber);
149 }
150
151 try {
152 context.lineNumber = statementLine;
153 if (!dispatcher.Dispatch(line, context)) {
154 result.diagnostics.push_back({DbcDiagnosticSeverity::Warning,
155 DbcDiagnosticCode::UnsupportedStatement,
156 filePath, statementLine,
157 "Unsupported statement '" + keyword +
158 "' is ignored; decoding or metadata may be incomplete."});
159 }
160 } catch (const std::exception &error) {
161 result.diagnostics.push_back({DbcDiagnosticSeverity::Error,
162 StatementErrorCode(keyword), filePath,
163 statementLine, error.what()});
164 return result;
165 }
166 }
167
168 if (input.bad() || !input.eof()) {
169 result.diagnostics.push_back({DbcDiagnosticSeverity::Error,
170 DbcDiagnosticCode::FileReadError, filePath, 0U,
171 "Failed to read DBC file: " + filePath});
172 return result;
173 }
174
175 try {
176 context.ResolveComments(filePath);
177 context.ResolveValueDescriptions(filePath);
178 context.ResolveMultiplexing(filePath);
179 context.ValidateMultiplexing(filePath);
180 context.ResolveAttributes(filePath, result.diagnostics);
181 } catch (const DbcDiagnosticError &error) {
182 result.diagnostics.push_back(error.diagnostic);
183 return result;
184 }
185 result.database = std::move(database);
186 return result;
187}
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