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: 136 174 78.2 %
Date: 2019-09-14 00:54:39 Functions: 19 20 95.0 %
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_appl.h"
      11             : #include "mcrl2/atermpp/aterm_list.h"
      12             : #include "mcrl2/atermpp/aterm_int.h"
      13             : #include "mcrl2/atermpp/aterm_io.h"
      14             : 
      15             : #include <cstdio>
      16             : #include <cstdlib>
      17             : #include <cctype>
      18             : #include <string>
      19             : #include <cassert>
      20             : #include <stdexcept>
      21             : #include <iostream>
      22             : #include <fstream>
      23             : #include <sstream>
      24             : 
      25             : namespace atermpp
      26             : {
      27             : 
      28             : using namespace std;
      29             : 
      30             : // utility functions
      31             : 
      32      211147 : static void write_string_with_escape_symbols(const std::string& s, std::ostream& os)
      33             : {
      34             :   // Check whether the string starts with a - or a number, or contains the symbols
      35             :   // \, ", (, ), [, ], comma, space, \t, \n or \r. If yes, the string will be
      36             :   // surrounded by quotes, and the symbols \, ", \t, \n, \r
      37             :   // will be preceded by an escape symbol.
      38             : 
      39      211147 :   char c = s[0];
      40      211147 :   bool contains_special_symbols = ((c =='-') || isdigit(c));
      41             : 
      42     1413040 :   for(std::string::const_iterator i=s.begin(); !contains_special_symbols && i!=s.end(); ++i)
      43             :   {
      44     1201893 :     if (*i=='\\' || *i=='"' || *i=='(' || *i==')' || *i=='[' || *i==']' || *i==',' || *i==' ' || *i=='\n' || *i=='\t' || *i=='\r')
      45             :     {
      46        6682 :       contains_special_symbols=true;
      47             :     }
      48             :   }
      49             : 
      50      211147 :   if (contains_special_symbols)
      51             :   {
      52             :     // This function symbol needs quotes.
      53        6682 :     os << "\"";
      54       20046 :     for(std::string::const_iterator i=s.begin(); i!=s.end(); ++i)
      55             :     {
      56             :       // We need to escape special characters.
      57       13364 :       switch (*i)
      58             :       {
      59             :         case '\\':
      60             :         case '"':
      61           0 :           os << "\\" << *i;
      62           0 :           break;
      63             :         case '\n':
      64           0 :           os << "\\n";
      65           0 :           break;
      66             :         case '\t':
      67           0 :           os << "\\t";
      68           0 :           break;
      69             :         case '\r':
      70           0 :           os << "\\r";
      71           0 :           break;
      72             :         default:
      73       13364 :           os << *i;
      74       13364 :           break;
      75             :       }
      76             :     }
      77        6682 :     os << "\"";
      78             :   }
      79             :   else
      80             :   {
      81      204465 :     os << s;
      82             :   }
      83      211147 : }
      84             : 
      85             : // Public functions
      86             : 
      87       12510 : text_aterm_output::text_aterm_output(std::ostream& os, std::function<aterm_transformer> transformer, bool newline)
      88             :   : m_stream(os),
      89             :     m_transformer(transformer),
      90       12510 :     m_newline(newline)
      91       12510 : {}
      92             : 
      93       12510 : void text_aterm_output::write_term(const aterm& term)
      94             : {
      95       12510 :   write_term_line(term);
      96             : 
      97       12510 :   if (m_newline)
      98             :   {
      99           0 :     m_stream << "\n";
     100             :   }
     101       12510 : }
     102             : 
     103       10103 : text_aterm_input::text_aterm_input(std::istream& is, std::function<aterm_transformer> transformer)
     104             :   : m_stream(is),
     105       10103 :     m_transformer(transformer)
     106             : {
     107       10103 :   character = next_char();
     108       10103 : }
     109             : 
     110       10103 : aterm text_aterm_input::read_term()
     111             : {
     112       10103 :   aterm term;
     113             : 
     114             :   try
     115             :   {
     116       10103 :     if (character != EOF)
     117             :     {
     118       10103 :       term = parse_aterm(character);
     119             :     }
     120             :   }
     121           0 :   catch (atermpp::runtime_error& e)
     122             :   {
     123           0 :     throw atermpp::runtime_error(e.what() + string("\n") + print_parse_error_position());
     124             :   }
     125             : 
     126             :   // Reset the parsing error buffers.
     127       10103 :   m_column = 0;
     128       10103 :   m_history.clear();
     129             : 
     130       10103 :   return term;
     131             : }
     132             : 
     133             : // Private functions
     134             : 
     135      222148 : void text_aterm_output::write_term_line(const aterm& t)
     136             : {
     137      222148 :   if (t.type_is_int())
     138             :   {
     139             :     // Write a single integer as is.
     140        2225 :     m_stream << atermpp::down_cast<aterm_int>(t).value();
     141             :   }
     142      219923 :   else if (t.type_is_list())
     143             :   {
     144             :     // A list l0...ln is formatted as [l0, ..., ln].
     145        8776 :     m_stream << "[";
     146        8776 :     const aterm_list& list = down_cast<aterm_list>(t);
     147       26299 :     for (aterm_list::const_iterator it = list.begin(); it != list.end(); ++it)
     148             :     {
     149       17523 :       if (it!=list.begin())
     150             :       {
     151        8750 :         m_stream << ",";
     152             :       }
     153       17523 :       write_term_line(*it);
     154             :     }
     155             : 
     156        8776 :     m_stream << "]";
     157             :   }
     158             :   else
     159             :   {
     160             :     // An aterm_appl f(t0, ..., tn) is written as f(t0, ..., tn)
     161      211147 :     assert(t.type_is_appl());
     162             : 
     163      422294 :     aterm_appl appl = m_transformer(down_cast<aterm_appl>(t));
     164             : 
     165      211147 :     write_string_with_escape_symbols(appl.function().name(), m_stream);
     166             : 
     167      211147 :     if (appl.function().arity() > 0)
     168             :     {
     169      114186 :       m_stream << "(";
     170      114186 :       write_term_line(appl[0]);
     171      192115 :       for (std::size_t i = 1; i < appl.function().arity(); i++)
     172             :       {
     173       77929 :         m_stream << ",";
     174       77929 :         write_term_line(appl[i]);
     175             :       }
     176      114186 :       m_stream << ")";
     177             :     }
     178             :   }
     179      222148 : }
     180             : 
     181      221061 : aterm text_aterm_input::parse_aterm(int& character)
     182             : {
     183             :   // Parse the term.
     184      221061 :   switch (character)
     185             :   {
     186             :     case '"':
     187             :     {
     188       13378 :       std::string function_name = parse_quoted_string(character);
     189        6689 :       return parse_aterm_appl(function_name, character);
     190             :     }
     191             :     case '[':
     192             :     {
     193        8851 :       return parse_aterm_list(character, '[', ']');
     194             :     }
     195             :     default:
     196             :     {
     197      205521 :       if (isdigit(character) || character == '-')
     198             :       {
     199         202 :         return parse_aterm_int(character);
     200             :       }
     201             : 
     202      410638 :       std::string function_symbol = parse_unquoted_string(character);
     203      205319 :       return parse_aterm_appl(function_symbol, character);
     204             :     }
     205             :   }
     206             : }
     207             : 
     208      212008 : aterm_appl text_aterm_input::parse_aterm_appl(const std::string& function_name, int& character)
     209             : {
     210             :   // Parse the arguments.
     211      424016 :   aterm_list arguments = parse_aterm_list(character, '(', ')');
     212             : 
     213             :   // Wrap up this function application.
     214      424016 :   function_symbol symbol(function_name, arguments.size());
     215      424016 :   return m_transformer(aterm_appl(symbol, arguments.begin(), arguments.end()));
     216             : }
     217             : 
     218         202 : aterm_int text_aterm_input::parse_aterm_int(int& character)
     219             : {
     220             :   std::array<char, 32> number;
     221         202 :   auto it = number.begin();
     222             : 
     223         202 :   if (character == '-')
     224             :   {
     225           0 :     *it = static_cast<char>(character);
     226           0 :     ++it;
     227           0 :     character = next_char(true, true);
     228             :   }
     229             : 
     230         862 :   while (isdigit(character) && it != number.end())
     231             :   {
     232         330 :     *it = static_cast<char>(character);
     233         330 :     ++it;
     234         330 :     character = next_char();
     235             :   }
     236             : 
     237         202 :   *it = '\0';
     238         202 :   return aterm_int(static_cast<std::size_t>(atol(number.data())));
     239             : }
     240             : 
     241      220859 : aterm_list text_aterm_input::parse_aterm_list(int& character, char begin, char end)
     242             : {
     243      441718 :   aterm_list list;
     244             : 
     245             :   // A list is [t0, ..., tn] or surrounded by ().
     246      220859 :   if (character == begin)
     247             :   {
     248      123655 :     character = next_char(true, true);
     249      123655 :     if (character != end)
     250             :     {
     251      123646 :       list.push_front(parse_aterm(character));
     252             : 
     253      298270 :       while (character == ',')
     254             :       {
     255       87312 :         character = next_char(true, true);
     256       87312 :         list.push_front(parse_aterm(character));
     257             :       }
     258             : 
     259      123646 :       if (character != end)
     260             :       {
     261           0 :         throw atermpp::runtime_error(std::string("Missing ") + end + " while parsing a list term");
     262             :       }
     263             :     }
     264             : 
     265      123655 :     character = next_char(true);
     266             :   }
     267             : 
     268      441718 :   return reverse(list);
     269             : }
     270             : 
     271             : 
     272           0 : std::string text_aterm_input::print_parse_error_position()
     273             : {
     274           0 :   std::stringstream s;
     275           0 :   s << "Error occurred at line " << m_line << ", col " << m_column << " near: ";
     276           0 :   for(const auto& element : m_history)
     277             :   {
     278           0 :     s << element;
     279             :   }
     280           0 :   return s.str();
     281             : }
     282             : 
     283     1572524 : int text_aterm_input::next_char(bool skip_whitespace, bool required)
     284             : {
     285     1572524 :   character = EOF;
     286             : 
     287           0 :   do
     288             :   {
     289             :     try
     290             :     {
     291             :       // In liblts_lts the exception bit is set, so we need to use exception to handle EOF.
     292     1572524 :       character = m_stream.get();
     293             :     }
     294           0 :     catch (std::ios::failure&)
     295             :     {
     296           0 :       return EOF;
     297             :     }
     298             : 
     299     1572524 :     if (character != EOF)
     300             :     {
     301     1562421 :       if (character == '\n')
     302             :       {
     303           0 :         m_line++;
     304           0 :         m_column = 0;
     305             :       }
     306             :       else
     307             :       {
     308     1562421 :         m_column++;
     309             :       }
     310             : 
     311     1562421 :       if (m_history.size() >= m_history_limit)
     312             :       {
     313             :         // If the history is full the first element must be removed.
     314     1068236 :         m_history.erase(m_history.begin());
     315             :       }
     316             : 
     317     1562421 :       m_history.emplace_back(character);
     318             :     }
     319       10103 :     else if (required)
     320             :     {
     321           0 :       throw atermpp::runtime_error("Premature end of file while parsing.");
     322             :     }
     323             :   }
     324     1572524 :   while (isspace(character) && skip_whitespace);
     325             : 
     326             :   // The stream also returns a newline for the last symbol.
     327     1572524 :   return character == '\n' ? EOF : character;
     328             : }
     329             : 
     330        6689 : std::string text_aterm_input::parse_quoted_string(int& character)
     331             : {
     332             :   // We need a buffer for printing and parsing.
     333        6689 :   std::string string;
     334             : 
     335        6689 :   assert(character == '"');
     336             : 
     337             :   // First obtain the first symbol after ".
     338        6689 :   character = next_char();
     339             : 
     340       33445 :   while (character != '"')
     341             :   {
     342       13378 :     switch (character)
     343             :     {
     344             :       case '\\':
     345           0 :         character = next_char(false, true);
     346           0 :         switch (character)
     347             :         {
     348             :           case 'n':
     349           0 :             string += '\n';
     350           0 :             break;
     351             :           case 'r':
     352           0 :             string += '\r';
     353           0 :             break;
     354             :           case 't':
     355           0 :             string += '\t';
     356           0 :             break;
     357             :           default:
     358           0 :             string += static_cast<char>(character);
     359           0 :             break;
     360             :         }
     361           0 :         break;
     362             :       default:
     363       13378 :         string += static_cast<char>(character);
     364       13378 :         break;
     365             :     }
     366       13378 :     character = next_char(false, true);
     367             :   }
     368             : 
     369        6689 :   character = next_char(true, false);
     370        6689 :   return string;
     371             : }
     372             : 
     373      205319 : std::string text_aterm_input::parse_unquoted_string(int& character)
     374             : {
     375      205319 :   std::string string;
     376             : 
     377      205319 :   if (character != '(')
     378             :   {
     379             :     // First parse the identifier
     380     4012777 :     while (character != '"' && character != '(' && character != ')' && character != ']'
     381     1245967 :       && character != ']' && character != ',' && character != ' ' && character != '\n'
     382     2606764 :       && character != '\t' && character != '\r' && character != EOF)
     383             :     {
     384     1200713 :       string += static_cast<char>(character);
     385     1200713 :       character = next_char(false);
     386             :     }
     387             :   }
     388             : 
     389      205319 :   return string;
     390             : }
     391             : 
     392           7 : void write_term_to_text_stream(const aterm& term, std::ostream& os)
     393             : {
     394          14 :   text_aterm_output stream(os);
     395           7 :   stream.write_term(term);
     396           7 : }
     397             : 
     398       10097 : aterm read_term_from_string(const std::string& s)
     399             : {
     400       20194 :   stringstream ss(s);
     401       20194 :   return  read_term_from_text_stream(ss);
     402             : }
     403             : 
     404       10103 : aterm read_term_from_text_stream(istream& is)
     405             : {
     406       20206 :   text_aterm_input stream(is);
     407       20206 :   return stream.read_term();
     408             : }
     409             : 
     410             : 
     411       12503 : std::ostream& operator<<(std::ostream& os, const aterm& term)
     412             : {
     413       25006 :   text_aterm_output stream(os);
     414       12503 :   stream.write_term(term);
     415       25006 :   return os;
     416             : }
     417             : 
     418         420 : } // namespace atermpp

Generated by: LCOV version 1.12