Branch data Line data Source code
1 : : /**
2 : : * @file dbc_parser.cpp
3 : : * @brief DBC file reading and diagnostic collection.
4 : : *
5 : : * Created: 2026-03-13
6 : : * Author: Deeaitch (Dim. Himro)
7 : : *
8 : : * Licensed under the MIT License.
9 : : * See LICENSE file in the project root for full license text.
10 : : */
11 : :
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 : :
21 : : namespace {
22 : : // Read through the closing unescaped quote, preserving comment whitespace.
23 : 140 : void ReadCommentLines(std::ifstream &input, std::string &statement,
24 : : std::size_t &lineNumber) {
25 : 140 : bool inQuote = false;
26 : 140 : bool escaped = false;
27 [ + - ]: 140 : std::string part = statement;
28 : : for (;;) {
29 [ + + ]: 3677 : for (const char character: part) {
30 [ + + ]: 3662 : if (escaped) {
31 : 5 : escaped = false;
32 [ + + + + ]: 3657 : } else if (inQuote && character == '\\') {
33 : 7 : escaped = true;
34 [ + + ]: 3650 : } else if (character == '"') {
35 [ + + ]: 268 : if (inQuote)
36 : 132 : return;
37 : 136 : inQuote = true;
38 : : }
39 : : }
40 [ + + + - : 15 : if (!inQuote || !std::getline(input, part))
+ - + + +
+ ]
41 : 8 : return;
42 : 7 : ++lineNumber;
43 [ + - + + : 7 : if (!part.empty() && part.back() == '\r')
+ + ]
44 : 2 : part.pop_back();
45 [ + - ]: 7 : statement += '\n';
46 [ + - ]: 7 : statement += part;
47 : : // A physical newline cannot escape the next line's first character.
48 : 7 : escaped = false;
49 : 7 : }
50 : 140 : }
51 : :
52 : 349 : DbcDiagnosticCode StatementErrorCode(const std::string &keyword) {
53 [ + + ]: 349 : if (keyword == "SG_MUL_VAL_")
54 : 4 : return DbcDiagnosticCode::UnsupportedStatement;
55 [ + + ]: 345 : if (keyword == "VAL_")
56 : 25 : return DbcDiagnosticCode::InvalidValueDescription;
57 : 320 : return DbcDiagnosticCode::InvalidStatement;
58 : : }
59 : : }
60 : :
61 : 656 : DbcDatabase DbcParser::ParseFile(const std::string &filePath) const {
62 [ + - ]: 656 : DbcParseResult result = ParseFileWithDiagnostics(filePath);
63 [ + + ]: 662 : for (const DbcDiagnostic &diagnostic : result.diagnostics) {
64 [ + + ]: 545 : if (diagnostic.severity == DbcDiagnosticSeverity::Error) {
65 [ + + ]: 539 : if (diagnostic.lineNumber == 0U)
66 [ + - ]: 3 : throw std::runtime_error(diagnostic.message);
67 [ + - ]: 1072 : throw std::runtime_error(diagnostic.filePath + ":" +
68 [ + - + - : 1608 : std::to_string(diagnostic.lineNumber) + ": " + diagnostic.message);
+ - + - +
- ]
69 : : }
70 : : }
71 : 234 : return std::move(result.database);
72 : 656 : }
73 : :
74 : 951 : DbcParseResult DbcParser::ParseFileWithDiagnostics(const std::string &filePath) const {
75 : 951 : DbcParseResult result;
76 [ + - ]: 951 : std::ifstream input(filePath.c_str());
77 [ + + ]: 951 : if (!input.is_open()) {
78 : 3 : result.diagnostics.push_back({DbcDiagnosticSeverity::Error,
79 : : DbcDiagnosticCode::FileOpenError, filePath, 0U,
80 : : "Failed to open DBC file: " + filePath});
81 : 3 : return result;
82 : : }
83 : :
84 : 948 : DbcDatabase database;
85 : 948 : DbcParseContext context{database, nullptr};
86 : 948 : context.enableJ1939 = options.enableJ1939;
87 [ + - ]: 948 : const DbcStatementDispatcher dispatcher;
88 : 948 : bool inNamespace = false;
89 : 948 : std::size_t lineNumber = 0U;
90 : 948 : std::string line;
91 [ + - + - : 3097 : while (std::getline(input, line)) {
+ + ]
92 : 2498 : ++lineNumber;
93 [ + + + + : 2498 : if (!line.empty() && line.back() == '\r')
+ + ]
94 : 19 : line.pop_back();
95 [ + - ]: 2498 : const std::string rawLine = line;
96 : 2498 : const std::size_t statementLine = lineNumber;
97 [ + - ]: 2498 : line = dbc_detail::TrimText(line);
98 [ + + ]: 2498 : if (line.empty())
99 : 342 : continue;
100 : :
101 [ + - ]: 2156 : std::istringstream tokens(line);
102 : 2156 : std::string keyword;
103 : 2156 : std::string argument;
104 [ + - ]: 2156 : tokens >> keyword;
105 [ + - + - ]: 2156 : const bool hasArgument = static_cast<bool>(tokens >> argument);
106 [ + + + - : 44 : if (inNamespace && !hasArgument && keyword != "BO_" && keyword != "SG_" &&
+ + + - +
+ ]
107 [ + - + + : 36 : keyword != "BS_" && keyword != "BS_:" &&
+ - + + ]
108 [ + + + - : 2200 : keyword != "BU_" && keyword != "BU_:")
+ + + - +
+ + + ]
109 : 29 : continue; // NS_ lists syntax symbols; BO_, SG_, BS_ and BU_ end the list.
110 [ + - + + : 2127 : inNamespace = keyword == "NS_" || keyword == "NS_:";
+ - + + ]
111 : :
112 [ + - + + ]: 2127 : if (keyword == "CM_") {
113 [ + - ]: 140 : line = rawLine;
114 [ + - ]: 140 : ReadCommentLines(input, line, lineNumber);
115 : : }
116 : :
117 : : try {
118 : 2127 : context.lineNumber = statementLine;
119 [ + + + + ]: 2127 : if (!dispatcher.Dispatch(line, context)) {
120 : 37 : result.diagnostics.push_back({DbcDiagnosticSeverity::Warning,
121 : : DbcDiagnosticCode::UnsupportedStatement,
122 : : filePath, statementLine,
123 : 37 : "Unsupported statement '" + keyword +
124 : : "' is ignored; decoding or metadata may be incomplete."});
125 : : }
126 [ - + ]: 349 : } catch (const std::exception &error) {
127 : 349 : result.diagnostics.push_back({DbcDiagnosticSeverity::Error,
128 : 698 : StatementErrorCode(keyword), filePath,
129 : 0 : statementLine, error.what()});
130 : 349 : return result;
131 : 349 : }
132 [ + + + + : 3632 : }
+ + + + +
+ + + ]
133 : :
134 [ + - + + : 599 : if (input.bad() || !input.eof()) {
+ - - + +
+ ]
135 : 2 : result.diagnostics.push_back({DbcDiagnosticSeverity::Error,
136 : : DbcDiagnosticCode::FileReadError, filePath, 0U,
137 : : "Failed to read DBC file: " + filePath});
138 : 2 : return result;
139 : : }
140 : :
141 : : try {
142 [ + + ]: 597 : context.ResolveComments(filePath);
143 [ + + ]: 587 : context.ResolveValueDescriptions(filePath);
144 [ + + ]: 576 : context.ValidateMultiplexing(filePath);
145 [ + + ]: 549 : context.ResolveAttributes(filePath, result.diagnostics);
146 [ - + ]: 434 : } catch (const DbcDiagnosticError &error) {
147 [ + - ]: 434 : result.diagnostics.push_back(error.diagnostic);
148 : 434 : return result;
149 [ + - ]: 434 : }
150 : 163 : result.database = std::move(database);
151 : 163 : return result;
152 [ + - + - : 1691 : }
+ - + - +
- + - + -
+ - + - +
- - - - -
+ - + - +
- + - - -
- - ]
|