Walker test code coverage report
Current view: top level - Control - FileParser.cpp (source / functions) Hit Total Coverage
Commit: test_coverage.info Lines: 21 42 50.0 %
Date: 2022-09-21 13:52:12 Functions: 2 3 66.7 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 37 124 29.8 %

           Branch data     Line data    Source code
       1                 :            : // *****************************************************************************
       2                 :            : /*!
       3                 :            :   \file      src/Control/FileParser.cpp
       4                 :            :   \copyright 2012-2015 J. Bakosi,
       5                 :            :              2016-2018 Los Alamos National Security, LLC.,
       6                 :            :              2019-2021 Triad National Security, LLC.
       7                 :            :              All rights reserved. See the LICENSE file for details.
       8                 :            :   \brief     File parser base class definition
       9                 :            :   \details   File parser base class defintion. File parser base serves as a
      10                 :            :     base class for various file parsers, e.g., input deck parsers. It does
      11                 :            :     generic low-level I/O, e.g., testing whether the file to be parsed exits or
      12                 :            :     not and associated error handling, as well as after-parser diagnostics.
      13                 :            : */
      14                 :            : // *****************************************************************************
      15                 :            : 
      16                 :            : #include <map>
      17                 :            : 
      18                 :            : #include "FileParser.hpp"
      19                 :            : #include "Exception.hpp"
      20                 :            : #include "Reader.hpp"
      21                 :            : #include "Print.hpp"
      22                 :            : 
      23                 :            : using tk::FileParser;
      24                 :            : 
      25                 :        104 : FileParser::FileParser( const std::string& filename ) : m_filename( filename )
      26                 :            : // *****************************************************************************
      27                 :            : //  Constructor
      28                 :            : //! \param[in] filename File to be parsed by the parser
      29                 :            : //! \details This constructor does basic tests in an attempt to determine if the
      30                 :            : //!   file to be parsed exists and is in good shape and does associated error
      31                 :            : //!   handling. This file stream is local, only used for error checking, and
      32                 :            : //!   thus is not part of the object state here since the parser, inheriting
      33                 :            : //!   from FileParser, e.g., walker::InputDeckParser, parses by completely
      34                 :            : //!   outsourcing the parsing (to PEGTL), so there is no need to store the file
      35                 :            : //!   stream handle here.
      36                 :            : // *****************************************************************************
      37                 :            : {
      38                 :            :   // Make sure there is a filename
      39                 :            :   Assert( !filename.empty(), "No filename specified" );
      40                 :            : 
      41                 :            :   // Local file stream handle
      42         [ +  - ]:        208 :   std::ifstream q;
      43                 :            : 
      44                 :            :   // Check if file exists, throw exception if it does not
      45         [ +  - ]:        104 :   q.open( filename, std::ifstream::in );
      46 [ +  + ][ +  - ]:        106 :   ErrChk( q.good(), "Failed to open file: " + filename );
         [ +  - ][ +  - ]
         [ -  + ][ -  + ]
                 [ -  - ]
      47                 :            : 
      48                 :            :   // Attempt to read a character, throw if it fails
      49                 :            :   // It is curious that on some systems opening a directory instead of a file
      50                 :            :   // with the above ifstream::open() call does not set the failbit. Thus we get
      51                 :            :   // here fine, so we try to read a character from it. If it is a directory or
      52                 :            :   // an empty file the read will fail, so we throw. Read more at: http://
      53                 :            :   // stackoverflow.com/questions/9591036/
      54                 :            :   // ifstream-open-doesnt-set-error-bits-when-argument-is-a-directory.
      55         [ +  - ]:        102 :   q.get();
      56 [ +  + ][ +  - ]:        103 :   ErrChk( q.good(), "Failed to read from file: " + filename );
         [ +  - ][ +  - ]
         [ -  + ][ -  + ]
                 [ -  - ]
      57                 :            : 
      58                 :            :   // Close it
      59         [ +  - ]:        101 :   q.close();
      60 [ -  + ][ -  - ]:        101 :   ErrChk( !q.fail(), "Failed to close file: " + filename );
         [ -  - ][ -  - ]
         [ -  - ][ -  - ]
                 [ -  - ]
      61                 :        101 : }
      62                 :            : 
      63                 :            : void
      64                 :        100 : FileParser::diagnostics( const tk::Print& print,
      65                 :            :                          const std::vector< std::string >& messages )
      66                 :            : // *****************************************************************************
      67                 :            : //  Echo errors and warnings accumulated during parsing
      68                 :            : //! \param[in] print    Pretty printer
      69                 :            : //! \param[in] messages Vector of strings of errors and warnings
      70                 :            : // *****************************************************************************
      71                 :            : {
      72                 :            :   // Bundle storing multiple messages for a single errouneous line
      73                 :            :   struct ErroneousLine {
      74                 :            :     std::size_t dlnum;                       //!< number of digits of line num
      75                 :            :     std::string parsed;                      //!< original line parsed
      76                 :            :     std::string underline;                   //!< underline
      77                 :            :     std::vector< std::string > msg;          //!< error or warning messages
      78                 :          0 :     explicit ErroneousLine() : dlnum(0), parsed(), underline(), msg() {}
      79                 :          0 :     explicit ErroneousLine( const std::string& m ) :
      80 [ -  - ][ -  - ]:          0 :       dlnum(0), parsed(), underline(), msg({{m}}) {}
         [ -  - ][ -  - ]
         [ -  - ][ -  - ]
                 [ -  - ]
      81                 :            :   };
      82                 :            : 
      83                 :        200 :   Reader id( m_filename );        // file reader for extracting erroneous lines
      84                 :            :   bool err = false;               // signaling whether there were any errors
      85                 :            :   std::map< std::size_t, ErroneousLine > lines; // erroneous lines, key: lineno
      86                 :            : 
      87                 :            :   // Underline errors and warnings
      88         [ +  + ]:        103 :   for (const auto& e : messages) {
      89                 :            :     // decide if error or warning
      90                 :            :     char underchar = ' ';
      91         [ +  + ]:          3 :     if (e.find( "Error" ) != std::string::npos) { err = true; underchar = '^'; }
      92         [ +  + ]:          2 :     else if (e.find( "Warning" ) != std::string::npos) underchar = '~';
      93                 :            : 
      94         [ +  + ]:          3 :     if (underchar == '^' || underchar == '~') {
      95                 :            :       auto sloc = e.find( "at " );
      96         [ -  + ]:          2 :       if (sloc != std::string::npos) {  // if we have location info
      97                 :            :         // skip "at "
      98                 :          0 :         sloc += 3;
      99                 :            :         // find a comma starting from after "at "
     100                 :            :         auto eloc = e.find_first_of( ',', sloc );
     101                 :            :         // extract line number of error from error message
     102 [ -  - ][ -  - ]:          0 :         const std::size_t lnum = std::stoul( e.substr( sloc, eloc-sloc ) );
     103                 :            :         // store number of digits in line number
     104                 :            :         const auto dlnum = eloc - sloc;
     105                 :            :         // find a dot starting from after "at "
     106                 :            :         eloc = e.find_first_of( '.', sloc );
     107                 :            :         // skip line number
     108                 :          0 :         sloc = e.find_first_of( ',', sloc ) + 1;
     109                 :            :         // extract column number of error from error message
     110         [ -  - ]:          0 :         const decltype(sloc) cnum = std::stoul( e.substr( sloc, eloc-sloc ) )-1;
     111                 :            :         // store erroneous line information in map
     112         [ -  - ]:          0 :         auto& l = lines[ lnum ];
     113                 :            :         // store number of digits in line number
     114                 :          0 :         l.dlnum = dlnum;
     115                 :            :         // get erroneous line from file and store
     116         [ -  - ]:          0 :         l.parsed = id.line( lnum );
     117                 :            :         // store message
     118         [ -  - ]:          0 :         l.msg.push_back( e );
     119                 :            :         // start constructing underline (from scratch if first error on line)
     120         [ -  - ]:          0 :         if (l.underline.empty()) l.underline = std::string(l.parsed.size(),' ');
     121                 :            :         // find beginning of erroneous argument, this can be found in either e
     122                 :            :         // (the full error message which may contain the erroneous substring
     123                 :            :         // between single quotes and can also contain white space), or in
     124                 :            :         // l.parsed (the erroneouss line from the file), if we find a
     125                 :            :         // singly-quoted substring in e, we find the location of that substring
     126                 :            :         // in l.parsed, if we don't find a singly-quoted substring in e,
     127                 :            :         // we reverse-search l.parsed until a white space, in which case the
     128                 :            :         // error that will be underlined will be a single word
     129                 :          0 :         sloc = e.find( '\'' );
     130         [ -  - ]:          0 :         if (sloc == std::string::npos) {
     131                 :          0 :           sloc = l.parsed.rfind( ' ', cnum-1 );
     132                 :            :         } else {
     133                 :          0 :           auto sloc2 = e.find( '\'', sloc+1 );
     134 [ -  - ][ -  - ]:          0 :           sloc = l.parsed.find( e.substr( sloc+1, sloc2-sloc-1 ) ) - 1;
     135                 :            :         }
     136                 :            :         // special-handle the beginning of the line with no space in front of it
     137         [ -  - ]:          0 :         if (sloc == std::string::npos) sloc = 0; else ++sloc;
     138                 :            :         // underline error and warning differently
     139         [ -  - ]:          0 :         for (auto i=sloc; i<l.underline.size(); ++i)
     140                 :          0 :           l.underline[i] = underchar;
     141                 :            :       }
     142 [ -  + ][ -  - ]:          1 :     } else if (!e.empty()) lines.emplace( 0, ErroneousLine(e) );
                 [ -  - ]
     143                 :            :   }
     144                 :            : 
     145                 :            :   // Output errors and warnings underlined to quiet stream and message
     146         [ -  + ]:        100 :   for (const auto& l : lines) {
     147                 :            :     const auto& e = l.second;
     148                 :            :     print % '\n';
     149                 :            :     print % ">>> Line " % l.first % ": '" % e.parsed % "'\n";
     150         [ -  - ]:          1 :     print % ">>>" % std::string( e.dlnum+9, ' ' ) % e.underline % "\n";
     151         [ -  - ]:          0 :     for (const auto& m : e.msg) print % ">>> " % m % '\n';
     152                 :            :     print % '\n';
     153                 :            :   }
     154                 :            : 
     155                 :            :   // Exit if there were any errors
     156 [ +  + ][ +  - ]:        101 :   if (err) Throw( "Error(s) occurred while parsing file " + m_filename );
         [ +  - ][ +  - ]
         [ -  + ][ -  + ]
                 [ -  - ]
     157                 :         99 : }

Generated by: LCOV version 1.14