LCOV - code coverage report
Current view: top level - atermpp/source - aterm_io_text.cpp (source / functions) Hit Total Coverage
Test: mcrl2_coverage.info.cleaned Lines: 146 197 74.1 %
Date: 2024-04-26 03:18:02 Functions: 17 18 94.4 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // Author(s): Jan Friso Groote
       2             : // Copyright: see the accompanying file COPYING or copy at
       3             : // https://github.com/mCRL2org/mCRL2/blob/master/COPYING
       4             : //
       5             : // Distributed under the Boost Software License, Version 1.0.
       6             : // (See accompanying file LICENSE_1_0.txt or copy at
       7             : // http://www.boost.org/LICENSE_1_0.txt)
       8             : //
       9             : 
      10             : #include "mcrl2/atermpp/aterm_io_text.h"
      11             : 
      12             : #include <fstream>
      13             : 
      14             : namespace atermpp
      15             : {
      16             : 
      17             : // utility functions
      18             : 
      19      211271 : static void write_string_with_escape_symbols(const std::string& s, std::ostream& os)
      20             : {
      21             :   // Check whether the string starts with a - or a number, or contains the symbols
      22             :   // \, ", (, ), [, ], comma, space, \t, \n or \r. If yes, the string will be
      23             :   // surrounded by quotes, and the symbols \, ", \t, \n, \r
      24             :   // will be preceded by an escape symbol.
      25             : 
      26      211271 :   char c = s[0];
      27      211271 :   bool contains_special_symbols = ((c =='-') || isdigit(c));
      28             : 
      29     1413919 :   for(std::string::const_iterator i=s.begin(); !contains_special_symbols && i!=s.end(); ++i)
      30             :   {
      31     1202648 :     if (*i=='\\' || *i=='"' || *i=='(' || *i==')' || *i=='[' || *i==']' || *i==',' || *i==' ' || *i=='\n' || *i=='\t' || *i=='\r')
      32             :     {
      33        6682 :       contains_special_symbols=true;
      34             :     }
      35             :   }
      36             : 
      37      211271 :   if (contains_special_symbols)
      38             :   {
      39             :     // This function symbol needs quotes.
      40        6682 :     os << "\"";
      41       20046 :     for(std::string::const_iterator i=s.begin(); i!=s.end(); ++i)
      42             :     {
      43             :       // We need to escape special characters.
      44       13364 :       switch (*i)
      45             :       {
      46           0 :         case '\\':
      47             :         case '"':
      48           0 :           os << "\\" << *i;
      49           0 :           break;
      50           0 :         case '\n':
      51           0 :           os << "\\n";
      52           0 :           break;
      53           0 :         case '\t':
      54           0 :           os << "\\t";
      55           0 :           break;
      56           0 :         case '\r':
      57           0 :           os << "\\r";
      58           0 :           break;
      59       13364 :         default:
      60       13364 :           os << *i;
      61       13364 :           break;
      62             :       }
      63             :     }
      64        6682 :     os << "\"";
      65             :   }
      66             :   else
      67             :   {
      68      204589 :     os << s;
      69             :   }
      70      211271 : }
      71             : 
      72             : // Public functions
      73             : 
      74       12586 : text_aterm_ostream::text_aterm_ostream(std::ostream& os, bool newline)
      75       12586 :   : m_stream(os),
      76       12586 :     m_newline(newline)
      77       12586 : {}
      78             : 
      79       12586 : void text_aterm_ostream::put(const aterm& term)
      80             : {
      81       12586 :   write_term_line(term);
      82             : 
      83       12586 :   if (m_newline)
      84             :   {
      85           0 :     m_stream << "\n";
      86             :   }
      87       12586 : }
      88             : 
      89       10107 : text_aterm_istream::text_aterm_istream(std::istream& is)
      90       10107 :   : m_stream(is)
      91             : {
      92       10107 :   character = next_char();
      93       10107 : }
      94             : 
      95       10107 : void text_aterm_istream::get(aterm& term)
      96             : {
      97             :   try
      98             :   {
      99       10107 :     if (character != EOF)
     100             :     {
     101       10107 :       term = parse_aterm(character);
     102             :     }
     103             :   }
     104           0 :   catch (std::runtime_error& e)
     105             :   {
     106           0 :     throw std::runtime_error(e.what() + std::string("\n") + print_parse_error_position());
     107           0 :   }
     108             : 
     109             :   // Reset the parsing error buffers.
     110       10107 :   m_column = 0;
     111       10107 :   m_history.clear();
     112             : 
     113       10107 :   return;
     114             : }
     115             : 
     116             : // Private functions
     117             : 
     118      222300 : void text_aterm_ostream::write_term_line(const aterm& t)
     119             : {
     120      222300 :   if (t.type_is_int())
     121             :   {
     122             :     // Write a single integer as is.
     123        2253 :     m_stream << atermpp::down_cast<aterm_int>(t).value();
     124             :   }
     125      220047 :   else if (t.type_is_list())
     126             :   {
     127             :     // A list l0...ln is formatted as [l0, ..., ln].
     128        8776 :     m_stream << "[";
     129        8776 :     const aterm_list& list = down_cast<aterm_list>(t);
     130       26299 :     for (aterm_list::const_iterator it = list.begin(); it != list.end(); ++it)
     131             :     {
     132       17523 :       if (it!=list.begin())
     133             :       {
     134        8750 :         m_stream << ",";
     135             :       }
     136       17523 :       write_term_line(*it);
     137             :     }
     138             : 
     139        8776 :     m_stream << "]";
     140             :   }
     141             :   else
     142             :   {
     143             :     // An aterm_appl f(t0, ..., tn) is written as f(t0, ..., tn)
     144      211271 :     assert(t.type_is_appl());
     145             : 
     146      211271 :     aterm_appl appl = m_transformer(down_cast<aterm_appl>(t));
     147             : 
     148      211271 :     write_string_with_escape_symbols(appl.function().name(), m_stream);
     149             : 
     150      211271 :     if (appl.function().arity() > 0)
     151             :     {
     152      114216 :       m_stream << "(";
     153      114216 :       write_term_line(appl[0]);
     154      192191 :       for (std::size_t i = 1; i < appl.function().arity(); i++)
     155             :       {
     156       77975 :         m_stream << ",";
     157       77975 :         write_term_line(appl[i]);
     158             :       }
     159      114216 :       m_stream << ")";
     160             :     }
     161      211271 :   }
     162      222300 : }
     163             : 
     164      221083 : aterm text_aterm_istream::parse_aterm(int& character)
     165             : {
     166             :   // Parse the term.
     167      221083 :   switch (character)
     168             :   {
     169        6689 :     case '"':
     170             :     {
     171        6689 :       std::string function_name = parse_quoted_string(character);
     172        6689 :       return parse_aterm_appl(function_name, character);
     173        6689 :     }
     174        8855 :     case '[':
     175             :     {
     176        8855 :       return parse_aterm_list(character, '[', ']');
     177             :     }
     178      205539 :     default:
     179             :     {
     180      205539 :       if (isdigit(character) || character == '-')
     181             :       {
     182         204 :         return parse_aterm_int(character);
     183             :       }
     184             : 
     185      205335 :       std::string function_symbol = parse_unquoted_string(character);
     186      205335 :       return parse_aterm_appl(function_symbol, character);
     187      205335 :     }
     188             :   }
     189             : }
     190             : 
     191      212024 : aterm_appl text_aterm_istream::parse_aterm_appl(const std::string& function_name, int& character)
     192             : {
     193             :   // Parse the arguments.
     194      212024 :   aterm_list arguments = parse_aterm_list(character, '(', ')');
     195             : 
     196             :   // Wrap up this function application.
     197      212024 :   function_symbol symbol(function_name, arguments.size());
     198      636072 :   return m_transformer(aterm_appl(symbol, arguments.begin(), arguments.end()));
     199      212024 : }
     200             : 
     201         204 : aterm_int text_aterm_istream::parse_aterm_int(int& character)
     202             : {
     203             :   std::array<char, 32> number;
     204         204 :   auto it = number.begin();
     205             : 
     206         204 :   if (character == '-')
     207             :   {
     208           0 :     *it = static_cast<char>(character);
     209           0 :     ++it;
     210           0 :     character = next_char(true, true);
     211             :   }
     212             : 
     213         536 :   while (isdigit(character) && it != number.end())
     214             :   {
     215         332 :     *it = static_cast<char>(character);
     216         332 :     ++it;
     217         332 :     character = next_char();
     218             :   }
     219             : 
     220         204 :   *it = '\0';
     221         408 :   return aterm_int(static_cast<std::size_t>(atol(number.data())));
     222             : }
     223             : 
     224      220879 : aterm_list text_aterm_istream::parse_aterm_list(int& character, char begin, char end)
     225             : {
     226      220879 :   aterm_list list;
     227             : 
     228             :   // A list is [t0, ..., tn] or surrounded by ().
     229      220879 :   if (character == begin)
     230             :   {
     231      123667 :     character = next_char(true, true);
     232      123667 :     if (character != end)
     233             :     {
     234      123656 :       list.push_front(parse_aterm(character));
     235             : 
     236      210976 :       while (character == ',')
     237             :       {
     238       87320 :         character = next_char(true, true);
     239       87320 :         list.push_front(parse_aterm(character));
     240             :       }
     241             : 
     242      123656 :       if (character != end)
     243             :       {
     244           0 :         throw std::runtime_error(std::string("Missing ") + end + " while parsing a list term");
     245             :       }
     246             :     }
     247             : 
     248      123667 :     character = next_char(true);
     249             :   }
     250             : 
     251      441758 :   return reverse(list);
     252      220879 : }
     253             : 
     254             : 
     255           0 : std::string text_aterm_istream::print_parse_error_position()
     256             : {
     257           0 :   std::stringstream s;
     258           0 :   s << "Error occurred at line " << m_line << ", col " << m_column << " near: ";
     259           0 :   for(const auto& element : m_history)
     260             :   {
     261           0 :     s << element;
     262             :   }
     263           0 :   return s.str();
     264           0 : }
     265             : 
     266     1572718 : int text_aterm_istream::next_char(bool skip_whitespace, bool required)
     267             : {
     268     1572718 :   character = EOF;
     269             : 
     270             :   do
     271             :   {
     272             :     try
     273             :     {
     274             :       // In liblts_lts the exception bit is set, so we need to use exception to handle EOF.
     275     1572718 :       character = m_stream.get();
     276             :     }
     277           0 :     catch (std::ios::failure&)
     278             :     {
     279           0 :       return EOF;
     280           0 :     }
     281             : 
     282     1572718 :     if (character != EOF)
     283             :     {
     284     1562611 :       if (character == '\n')
     285             :       {
     286           0 :         m_line++;
     287           0 :         m_column = 0;
     288             :       }
     289             :       else
     290             :       {
     291     1562611 :         m_column++;
     292             :       }
     293             : 
     294     1562611 :       if (m_history.size() >= m_history_limit)
     295             :       {
     296             :         // If the history is full the first element must be removed.
     297     1068416 :         m_history.erase(m_history.begin());
     298             :       }
     299             : 
     300     1562611 :       m_history.emplace_back(character);
     301             :     }
     302       10107 :     else if (required)
     303             :     {
     304           0 :       throw std::runtime_error("Premature end of file while parsing.");
     305             :     }
     306             :   }
     307     1572718 :   while (isspace(character) && skip_whitespace);
     308             : 
     309             :   // The stream also returns a newline for the last symbol.
     310     1572718 :   return character == '\n' ? EOF : character;
     311             : }
     312             : 
     313        6689 : std::string text_aterm_istream::parse_quoted_string(int& character)
     314             : {
     315             :   // We need a buffer for printing and parsing.
     316        6689 :   std::string string;
     317             : 
     318        6689 :   assert(character == '"');
     319             : 
     320             :   // First obtain the first symbol after ".
     321        6689 :   character = next_char();
     322             : 
     323       20067 :   while (character != '"')
     324             :   {
     325       13378 :     switch (character)
     326             :     {
     327           0 :       case '\\':
     328           0 :         character = next_char(false, true);
     329           0 :         switch (character)
     330             :         {
     331           0 :           case 'n':
     332           0 :             string += '\n';
     333           0 :             break;
     334           0 :           case 'r':
     335           0 :             string += '\r';
     336           0 :             break;
     337           0 :           case 't':
     338           0 :             string += '\t';
     339           0 :             break;
     340           0 :           default:
     341           0 :             string += static_cast<char>(character);
     342           0 :             break;
     343             :         }
     344           0 :         break;
     345       13378 :       default:
     346       13378 :         string += static_cast<char>(character);
     347       13378 :         break;
     348             :     }
     349       13378 :     character = next_char(false, true);
     350             :   }
     351             : 
     352        6689 :   character = next_char(true, false);
     353        6689 :   return string;
     354           0 : }
     355             : 
     356      205335 : std::string text_aterm_istream::parse_unquoted_string(int& character)
     357             : {
     358      205335 :   std::string string;
     359             : 
     360      205335 :   if (character != '(')
     361             :   {
     362             :     // First parse the identifier
     363     1611539 :     while (character != '"' && character != '(' && character != ')' && character != ']'
     364     1246127 :       && character != ']' && character != ',' && character != ' ' && character != '\n'
     365     2607092 :       && character != '\t' && character != '\r' && character != EOF)
     366             :     {
     367     1200869 :       string += static_cast<char>(character);
     368     1200869 :       character = next_char(false);
     369             :     }
     370             :   }
     371             : 
     372      205335 :   return string;
     373           0 : }
     374             : 
     375           7 : void write_term_to_text_stream(const aterm& term, std::ostream& os)
     376             : {
     377           7 :   text_aterm_ostream(os) << term;
     378           7 : }
     379             : 
     380       10101 : aterm read_term_from_string(const std::string& s)
     381             : {
     382       10101 :   std::stringstream ss(s);
     383       10101 :   aterm t;
     384       10101 :   read_term_from_text_stream(ss, t);
     385       20202 :   return t;
     386       10101 : }
     387             : 
     388       10107 : void read_term_from_text_stream(std::istream& is, aterm& t)
     389             : {
     390       10107 :   text_aterm_istream(is).get(t);
     391       10107 : }
     392             : 
     393             : 
     394       12579 : std::ostream& operator<<(std::ostream& os, const aterm& term)
     395             : {
     396       12579 :   text_aterm_ostream(os) << term;
     397       12579 :   return os;
     398             : }
     399             : 
     400             : } // namespace atermpp

Generated by: LCOV version 1.14