2#include "dbc_statement_handler.h"
14 std::vector<Token> Tokenize(
const std::string &text) {
15 std::vector<Token> tokens;
16 std::size_t index = 0U;
17 while (index < text.size()) {
18 const char character = text[index++];
19 if (character ==
' ' || character ==
'\t')
22 if (character ==
'"') {
25 while (index < text.size()) {
26 char next = text[index++];
32 if (index == text.size())
33 throw std::runtime_error(
"Incomplete attribute escape.");
35 if (next !=
'"' && next !=
'\\')
36 throw std::runtime_error(
"Invalid attribute escape.");
41 throw std::runtime_error(
"Unterminated attribute string.");
42 if (index < text.size() && text[index] !=
' ' && text[index] !=
'\t' &&
43 text[index] !=
',' && text[index] !=
';')
44 throw std::runtime_error(
"Missing separator after attribute string.");
45 }
else if (character ==
';' || character ==
',') {
46 token.text = character;
48 token.text = character;
49 while (index < text.size() && text[index] !=
' ' && text[index] !=
'\t' &&
50 text[index] !=
';' && text[index] !=
',') {
51 if (text[index] ==
'"')
52 throw std::runtime_error(
"Missing separator before attribute string.");
53 token.text += text[index++];
56 tokens.push_back(token);
58 if (tokens.empty() || tokens.back().quoted || tokens.back().text !=
";")
59 throw std::runtime_error(
"Attribute statement requires a semicolon.");
64 class AttributeTokens {
66 explicit AttributeTokens(
const std::string &line) : tokens(Tokenize(line)) {}
68 if (position == tokens.size())
69 throw std::runtime_error(
"Incomplete attribute statement.");
70 return tokens[position++];
73 const Token token = Take();
74 if (token.quoted || token.text ==
"," || token.text ==
";")
75 throw std::runtime_error(
"Expected an attribute keyword or number.");
78 std::string Quoted() {
79 const Token token = Take();
81 throw std::runtime_error(
"Expected a quoted attribute name or string.");
84 bool Empty()
const {
return position == tokens.size(); }
87 throw std::runtime_error(
"Unexpected trailing attribute fields.");
91 std::vector<Token> tokens;
92 std::size_t position = 0U;
95 bool IsScope(
const std::string &text) {
96 return text ==
"BO_" || text ==
"SG_" || text ==
"BU_" || text ==
"EV_";
99 AttributeScope ParseScope(
const std::string &text) {
100 if (text ==
"BO_")
return AttributeScope::Frame;
101 if (text ==
"SG_")
return AttributeScope::Signal;
102 if (text ==
"BU_")
return AttributeScope::Node;
103 if (text ==
"EV_")
return AttributeScope::Environment;
104 throw std::runtime_error(
"Unsupported attribute scope: " + text);
107 template<
class Numeric>
108 Numeric ParseNumber(
const std::string &text) {
109 const char *begin = text.data();
110 const char *end = begin + text.size();
111 if (begin != end && *begin ==
'+') {
113 if (begin != end && (*begin ==
'-' || *begin ==
'+'))
114 throw std::runtime_error(
"Invalid attribute number: " + text);
117 const std::from_chars_result parsed = std::from_chars(begin, end, value);
118 if (begin == end || parsed.ec != std::errc() || parsed.ptr != end || !std::isfinite(value))
119 throw std::runtime_error(
"Invalid attribute number: " + text);
123 AttributeValue Value(
const Token &token,
const AttributeDefinition &definition,
bool isDefault) {
124 if (definition.
type == AttributeType::String) {
125 if (!token.quoted)
throw std::runtime_error(
"Attribute STRING requires quoted text.");
128 if (definition.
type == AttributeType::Enum && isDefault && token.quoted) {
129 for (std::size_t index = 0U; index < definition.
enumValues.size(); ++index) {
130 if (definition.
enumValues[index] == token.text)
131 return static_cast<std::int64_t
>(index);
133 throw std::runtime_error(
"Unknown attribute ENUM default label.");
135 if (token.quoted)
throw std::runtime_error(
"Numeric attribute requires an unquoted number.");
136 if (definition.
type == AttributeType::Float) {
137 const double value = ParseNumber<double>(token.text);
138 if (value < definition.minimum || value > definition.
maximum)
139 throw std::runtime_error(
"Attribute FLOAT value outside definition range.");
142 const std::int64_t value = ParseNumber<std::int64_t>(token.text);
143 if (value < definition.integerMinimum || value > definition.
integerMaximum)
144 throw std::runtime_error(
"Attribute integer/ENUM value outside definition range.");
148 void Define(AttributeTokens &tokens,
DbcDatabase &database) {
150 const Token first = tokens.Take();
151 std::string name = first.text;
153 definition.
scope = ParseScope(first.text);
154 name = tokens.Quoted();
156 if (name.empty())
throw std::runtime_error(
"Empty attribute name.");
157 const std::string type = tokens.Word();
158 if (type ==
"INT" || type ==
"HEX") {
159 definition.
type = type ==
"INT" ? AttributeType::Integer : AttributeType::Hex;
160 definition.
integerMinimum = ParseNumber<std::int64_t>(tokens.Word());
161 definition.
integerMaximum = ParseNumber<std::int64_t>(tokens.Word());
163 throw std::runtime_error(
"Reversed attribute integer range.");
164 }
else if (type ==
"FLOAT") {
165 definition.
type = AttributeType::Float;
166 definition.
minimum = ParseNumber<double>(tokens.Word());
167 definition.
maximum = ParseNumber<double>(tokens.Word());
169 throw std::runtime_error(
"Reversed attribute FLOAT range.");
170 }
else if (type ==
"STRING") {
171 definition.
type = AttributeType::String;
172 }
else if (type ==
"ENUM") {
173 definition.
type = AttributeType::Enum;
174 std::set<std::string> labels;
176 const std::string label = tokens.Quoted();
177 if (!labels.insert(label).second)
178 throw std::runtime_error(
"Duplicate attribute ENUM label.");
180 if (tokens.Empty())
break;
181 const Token separator = tokens.Take();
182 if (separator.quoted || separator.text !=
",")
throw std::runtime_error(
"Expected ENUM comma.");
186 throw std::runtime_error(
"Unsupported attribute definition type: " + type);
189 if (name ==
"VFrameFormat" && (definition.
scope != AttributeScope::Frame || definition.
type != AttributeType::Enum))
190 throw std::runtime_error(
"VFrameFormat requires a BO_ ENUM definition.");
191 if (name ==
"GenMsgCycleTime" && (definition.
scope != AttributeScope::Frame ||
192 (definition.
type != AttributeType::Integer && definition.
type != AttributeType::Hex)))
193 throw std::runtime_error(
"GenMsgCycleTime requires a BO_ integer definition.");
195 throw std::runtime_error(
"Duplicate attribute definition: " + name);
199 const std::map<std::string, AttributeDefinition>::iterator found = database.
attributeDefinitions.find(name);
201 throw std::runtime_error(
"Undefined attribute: " + name);
202 return found->second;
206 const std::int64_t raw = ParseNumber<std::int64_t>(text);
207 if (raw < 0 || raw > std::numeric_limits<std::uint32_t>::max() || (raw & 0x60000000U) != 0)
208 throw std::runtime_error(
"Invalid attribute CAN ID.");
209 const std::uint32_t
id =
static_cast<std::uint32_t
>(raw);
210 assignment.
canId =
id & 0x1fffffffU;
211 assignment.
isExtended = (
id & 0x80000000U) != 0U ||
id > 0x7ffU;
215 if (assignment.
scope == AttributeScope::Database)
return database.
attributes;
216 for (
FrameInfo &frame: database.frames) {
217 if (frame.canId != assignment.
canId || frame.isExtended != assignment.
isExtended)
continue;
218 if (assignment.
scope == AttributeScope::Frame)
return frame.attributes;
220 if (signal.name == assignment.
targetName)
return signal.attributes;
222 throw std::runtime_error(
"Unresolved attribute signal: " + assignment.
targetName);
224 throw std::runtime_error(
"Unresolved attribute frame.");
227 void ValidateCycleTime(
const std::string &name,
const AttributeValue &value) {
228 if (name !=
"GenMsgCycleTime")
return;
229 const std::int64_t milliseconds = std::get<std::int64_t>(value);
230 if (milliseconds < 0 || milliseconds > std::numeric_limits<std::uint32_t>::max())
231 throw std::runtime_error(
"GenMsgCycleTime must fit unsigned 32-bit milliseconds.");
236 if (definition.
scope == AttributeScope::Database) {
240 if (definition.
scope != AttributeScope::Frame && definition.
scope != AttributeScope::Signal)
242 for (
FrameInfo &frame: database.frames) {
243 if (definition.
scope == AttributeScope::Frame) frame.attributes[name] = *definition.
defaultValue;
244 if (definition.
scope == AttributeScope::Signal) {
245 for (
SignalInfo &signal: frame.signals) signal.attributes[name] = *definition.defaultValue;
256 std::map<std::string, std::size_t> defaultLines;
257 std::vector<std::size_t> assignmentLines;
259 const char *keywords[] = {
"BA_DEF_",
"BA_DEF_DEF_",
"BA_"};
260 for (
const char *phase: keywords) {
262 const std::string keyword = pending.statement.substr(0U, pending.statement.find_first_of(
" \t"));
263 if (keyword != phase)
continue;
265 AttributeTokens tokens(pending.statement);
267 if (keyword ==
"BA_DEF_") {
271 const std::string name = tokens.Quoted();
273 if (keyword ==
"BA_DEF_DEF_") {
274 if (definition.
defaultValue)
throw std::runtime_error(
"Duplicate attribute default: " + name);
275 definition.
defaultValue = Value(tokens.Take(), definition,
true);
278 defaultLines[name] = pending.lineNumber;
279 ApplyDefault(
database, name, definition);
283 assignment.
name = name;
284 Token value = tokens.Take();
285 if (!value.quoted && IsScope(value.text)) {
286 assignment.
scope = ParseScope(value.text);
287 if (assignment.
scope == AttributeScope::Frame || assignment.
scope == AttributeScope::Signal)
288 ParseId(tokens.Word(), assignment);
289 if (assignment.
scope != AttributeScope::Frame)
291 value = tokens.Take();
293 if (assignment.
scope != definition.
scope)
throw std::runtime_error(
"Attribute assignment scope differs from definition.");
294 assignment.
value = Value(value, definition,
false);
295 ValidateCycleTime(name, assignment.
value);
298 if (assignment.
scope != AttributeScope::Node && assignment.
scope != AttributeScope::Environment)
301 assignmentLines.push_back(pending.lineNumber);
302 }
catch (
const std::exception &error) {
303 throw DbcDiagnosticError(DbcDiagnosticCode::InvalidAttribute, filePath, pending.lineNumber, error.what());
308 for (
const AttributeMap::value_type &attribute: frame.attributes) {
309 if (attribute.first !=
"VFrameFormat" && attribute.first !=
"GenMsgCycleTime")
continue;
310 std::size_t line = defaultLines[attribute.first];
313 if (assignment.
name == attribute.first && assignment.
scope == AttributeScope::Frame &&
314 assignment.
canId == frame.canId && assignment.
isExtended == frame.isExtended)
315 line = assignmentLines[index];
317 const std::int64_t value = std::get<std::int64_t>(attribute.second);
318 if (attribute.first ==
"GenMsgCycleTime") {
319 frame.cycleTimeMs =
static_cast<std::uint32_t
>(value);
323 const std::string &format = *frame.frameFormat;
325 if (format !=
"StandardCAN" && format !=
"ExtendedCAN")
326 warning =
"Unsupported VFrameFormat '" + format +
"' is preserved; decoding may be incomplete.";
327 else if ((format ==
"ExtendedCAN") != frame.isExtended)
328 warning =
"VFrameFormat '" + format +
"' conflicts with the BO_ CAN ID; ID interpretation is unchanged.";
329 if (!warning.empty())
330 diagnostics.push_back({DbcDiagnosticSeverity::Warning, DbcDiagnosticCode::UnsupportedAttribute, filePath, line, warning});
void Handle(const std::string &line, DbcParseContext &context) const override
Process a trimmed statement using state belonging to this parse.
Explicit assignment; defaults remain on definitions.
Attribute type, permitted range, and optional default.
std::vector< std::string > enumValues
std::int64_t integerMaximum
std::int64_t integerMinimum
std::optional< AttributeValue > defaultValue
Parsed DBC content stored in a simple internal form.
std::map< std::string, AttributeDefinition > attributeDefinitions
std::vector< FrameInfo > frames
std::vector< AttributeAssignment > attributeAssignments
void ResolveAttributes(const std::string &filePath, std::vector< DbcDiagnostic > &diagnostics)
Resolve definitions, defaults, then assignments; append format warnings.
std::vector< PendingAttribute > pendingAttributes
Describes one CAN frame from a DBC file.
Attribute statement retained for ordered resolution.
Describes one signal inside a DBC frame.