/** * @file dbc_parser.cpp * @brief * * Created: 2026-03-13 * Author: Deeaitch (Dim. Himro) * * Licensed under the MIT License. * See LICENSE file in the project root for full license text. */ #include "dbc_parser.h" #include "dbc_statement_handler.h" #include "text_utils.h" #include #include #include #include namespace { DbcDiagnosticCode StatementErrorCode(const std::string &keyword) { if (keyword == "SG_MUL_VAL_") return DbcDiagnosticCode::UnsupportedStatement; if (keyword == "VAL_") return DbcDiagnosticCode::InvalidValueDescription; return DbcDiagnosticCode::InvalidStatement; } } DbcDatabase DbcParser::ParseFile(const std::string &filePath) const { DbcParseResult result = ParseFileWithDiagnostics(filePath); for (const DbcDiagnostic &diagnostic : result.diagnostics) { if (diagnostic.severity == DbcDiagnosticSeverity::Error) { if (diagnostic.lineNumber == 0U) throw std::runtime_error(diagnostic.message); throw std::runtime_error(diagnostic.filePath + ":" + std::to_string(diagnostic.lineNumber) + ": " + diagnostic.message); } } return std::move(result.database); } DbcParseResult DbcParser::ParseFileWithDiagnostics(const std::string &filePath) const { DbcParseResult result; std::ifstream input(filePath.c_str()); if (!input.is_open()) { result.diagnostics.push_back({DbcDiagnosticSeverity::Error, DbcDiagnosticCode::FileOpenError, filePath, 0U, "Failed to open DBC file: " + filePath}); return result; } DbcDatabase database; DbcParseContext context{database, nullptr}; const DbcStatementDispatcher dispatcher; bool inNamespace = false; std::size_t lineNumber = 0U; std::string line; while (std::getline(input, line)) { ++lineNumber; line = dbc_detail::TrimText(line); if (line.empty()) continue; std::istringstream tokens(line); std::string keyword; std::string argument; tokens >> keyword; const bool hasArgument = static_cast(tokens >> argument); if (inNamespace && !hasArgument && keyword != "BO_" && keyword != "SG_" && keyword != "BS_" && keyword != "BS_:" && keyword != "BU_" && keyword != "BU_:") continue; // NS_ lists syntax symbols; BO_, SG_, BS_ and BU_ end the list. inNamespace = keyword == "NS_" || keyword == "NS_:"; try { context.lineNumber = lineNumber; if (!dispatcher.Dispatch(line, context)) { result.diagnostics.push_back({DbcDiagnosticSeverity::Warning, DbcDiagnosticCode::UnsupportedStatement, filePath, lineNumber, "Unsupported statement '" + keyword + "' is ignored; decoding or metadata may be incomplete."}); } } catch (const std::exception &error) { result.diagnostics.push_back({DbcDiagnosticSeverity::Error, StatementErrorCode(keyword), filePath, lineNumber, error.what()}); return result; } } if (input.bad() || !input.eof()) { result.diagnostics.push_back({DbcDiagnosticSeverity::Error, DbcDiagnosticCode::FileReadError, filePath, 0U, "Failed to read DBC file: " + filePath}); return result; } try { context.ResolveValueDescriptions(filePath); context.ValidateMultiplexing(filePath); } catch (const DbcDiagnosticError &error) { result.diagnostics.push_back(error.diagnostic); return result; } result.database = std::move(database); return result; }