LCOV - code coverage report
Current view: top level - core - dbc_parser.cpp (source / functions) Coverage Total Hit
Test: coverage.info Lines: 99.0 % 99 98
Test Date: 2026-10-20 13:34:58 Functions: 100.0 % 4 4
Branches: 70.4 % 216 152

             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 : }
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  -  -  -  -  
          +  -  +  -  +  
          -  +  -  -  -  
                   -  - ]
        

Generated by: LCOV version 2.0-1