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: 140 188 74.5 %
Date: 2019-06-26 00:32:26 Functions: 18 21 85.7 %
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             : /// \file atermpp/source/aterm_io_text.cpp
      10             : /// \brief This file contains code to read and write aterms
      11             : ///        in human readable format. 
      12             : 
      13             : 
      14             : 
      15             : #include <cstdio>
      16             : #include <cstdlib>
      17             : #include <cctype>
      18             : #include <string>
      19             : #include <cassert>
      20             : #include <stdexcept>
      21             : #include <deque>
      22             : #include <iostream>
      23             : #include <fstream>
      24             : #include <sstream>
      25             : 
      26             : #include "mcrl2/atermpp/aterm_appl.h"
      27             : #include "mcrl2/atermpp/aterm_list.h"
      28             : #include "mcrl2/atermpp/aterm_int.h"
      29             : #include "mcrl2/atermpp/aterm_io.h"
      30             : 
      31             : 
      32             : namespace atermpp
      33             : {
      34             : 
      35             : using namespace std;
      36             : 
      37             : 
      38             : /* Parse error description */
      39             : static std::size_t line = 0;
      40             : static std::size_t col = 0;
      41         140 : static deque<char> error_buf;
      42             : static const std::size_t MAX_ERROR_SIZE = 64;
      43             : 
      44             : /* Prototypes */
      45             : static aterm fparse_term(int* c, istream& is);
      46             : 
      47       22622 : static void aterm_io_init()
      48             : {
      49             :   static bool initialized = false;
      50       22622 :   if (!initialized)
      51             :   {
      52             :     /* Check for reasonably sized aterm (32 bits, 4 bytes)     */
      53             :     /* This check might break on perfectly valid architectures */
      54             :     /* that have char == 2 bytes, and sizeof(header_type) == 2 */
      55             :     assert(sizeof(std::size_t) == sizeof(aterm*));
      56             :     assert(sizeof(std::size_t) >= 4);
      57          21 :     initialized = true;
      58             :   }
      59       22622 : }
      60             : 
      61      211145 : static void write_string_with_escape_symbols(const std::string& s, std::ostream& os)
      62             : {
      63             :   // Check whether the string starts with a - or a number, or contains the symbols
      64             :   // \, ", (, ), [, ], comma, space, \t, \n or \r. If yes, the string will be
      65             :   // surrounded by quotes, and the symbols \, ", \t, \n, \r
      66             :   // will be preceded by an escape symbol.
      67             : 
      68             :   // assert(s.size()>0);
      69      211145 :   char c=s[0];
      70      211145 :   bool contains_special_symbols= c=='-' || isdigit(c);
      71             : 
      72     1413035 :   for(std::string::const_iterator i=s.begin(); !contains_special_symbols && i!=s.end(); ++i)
      73             :   {
      74     1201890 :     if (*i=='\\' || *i=='"' || *i=='(' || *i==')' || *i=='[' || *i==']' || *i==',' || *i==' ' || *i=='\n' || *i=='\t' || *i=='\r')
      75             :     {
      76        6682 :       contains_special_symbols=true;
      77             :     }
      78             :   }
      79             : 
      80      211145 :   if (contains_special_symbols)
      81             :   {
      82             :     /* This function symbol needs quotes */
      83        6682 :     os << "\"";
      84       20046 :     for(std::string::const_iterator i=s.begin(); i!=s.end(); ++i)
      85             :     {
      86             :       /* We need to escape special characters */
      87       13364 :       switch (*i)
      88             :       {
      89             :         case '\\':
      90             :         case '"':
      91           0 :           os << "\\" << *i;
      92           0 :           break;
      93             :         case '\n':
      94           0 :           os << "\\n";
      95           0 :           break;
      96             :         case '\t':
      97           0 :           os << "\\t";
      98           0 :           break;
      99             :         case '\r':
     100           0 :           os << "\\r";
     101           0 :           break;
     102             :         default:
     103       13364 :           os << *i;
     104       13364 :           break;
     105             :       }
     106             :     }
     107        6682 :     os << "\"";
     108             :   }
     109             :   else
     110             :   {
     111      204463 :     os << s;
     112             :   }
     113      211145 : }
     114             : 
     115      222147 : static void writeToStream(const aterm& t, std::ostream& os)
     116             : {
     117      222147 :   if (t.type_is_int())
     118             :   {
     119        2226 :     os << atermpp::down_cast<aterm_int>(t).value();
     120             :   }
     121      219921 :   else if (t.type_is_list())
     122             :   {
     123        8776 :     os << "[";
     124        8776 :     const aterm_list& list = down_cast<aterm_list>(t);
     125       26299 :     for(aterm_list::const_iterator i=list.begin(); i!=list.end(); ++i)
     126             :     {
     127       17523 :       if (i!=list.begin())
     128             :       {
     129        8750 :         os << ",";
     130             :       }
     131       17523 :       writeToStream(*i, os);
     132             :     }
     133        8776 :     os << "]";
     134             :   }
     135             :   else // t.type_is_appl()
     136             :   {
     137      211145 :     const aterm_appl& appl = down_cast<aterm_appl>(t);
     138      211145 :     const function_symbol& sym = appl.function();
     139      211145 :     write_string_with_escape_symbols(sym.name(),os);
     140      211145 :     if (sym.arity() > 0)
     141             :     {
     142      114186 :       os << "(";
     143      114186 :       writeToStream(appl[0], os);
     144      192115 :       for (std::size_t i = 1; i < sym.arity(); i++)
     145             :       {
     146       77929 :         os << ",";
     147       77929 :         writeToStream(appl[i], os);
     148             :       }
     149      114186 :       os << ")";
     150             :     }
     151             :   }
     152      222147 : }
     153             : 
     154             : /*
     155             :  * Write a term into its text representation.
     156             :  */
     157             : 
     158       12501 : std::ostream& operator<<(std::ostream& out, const aterm& t)
     159             : {
     160       12501 :   aterm_io_init();
     161       12501 :   writeToStream(t, out);
     162       12501 :   return out;
     163             : }
     164             : 
     165           8 : void write_term_to_text_stream(const aterm& t, std::ostream& os)
     166             : {
     167           8 :   aterm_io_init();
     168           8 :   writeToStream(t,os);
     169           8 : }
     170             : 
     171             : /*
     172             :  * Read the next character from file.
     173             :  */
     174             : 
     175     1572546 : static void fnext_char(int* c, istream& is)
     176             : {
     177     1572546 :   *c = is.get();
     178     1572546 :   if (*c != EOF)
     179             :   {
     180     1562433 :     if (*c == '\n')
     181             :     {
     182           0 :       line++;
     183           0 :       col = 0;
     184             :     }
     185             :     else
     186             :     {
     187     1562433 :       col++;
     188             :     }
     189     1562433 :     error_buf.push_back(*c);
     190     1562433 :     if (error_buf.size()>=MAX_ERROR_SIZE)
     191             :     {
     192     1073932 :       error_buf.pop_front();
     193             :     }
     194             :   }
     195     1572546 : }
     196             : 
     197           0 : static std::string print_parse_error_position()
     198             : {
     199           0 :   std::stringstream s;
     200           0 :   s << "Error occurred at line " << line << ", col " << col << " near: ";
     201           0 :   for(deque<char>::const_iterator i=error_buf.begin(); i!=error_buf.end(); ++i)
     202             :   {
     203           0 :     s << *i;
     204             :   }
     205           0 :   return s.str();
     206             : }
     207             : 
     208             : /*
     209             :  * Skip layout from file.
     210             :  */
     211             : 
     212      205327 : static void fskip_layout(int* c, istream& is)
     213             : {
     214      205327 :   while (isspace(*c))
     215             :   {
     216           0 :     fnext_char(c, is);
     217             :   }
     218      205327 : }
     219             : 
     220             : /*
     221             :  * Skip layout from file.
     222             :  */
     223             : 
     224      351424 : static void fnext_skip_layout(int* c, istream& is)
     225             : {
     226      351424 :   do
     227             :   {
     228      351424 :     fnext_char(c, is);
     229             :   }
     230      351424 :   while (isspace(*c));
     231      351424 : }
     232             : 
     233             : /*
     234             :  * Parse a list of arguments.
     235             :  */
     236             : 
     237      123646 : static aterm_list fparse_terms(int* c, istream& is)
     238             : {
     239      123646 :   if (*c==']' || *c==')')  // The termlist must be empty.
     240             :   {
     241           0 :     return aterm_list();
     242             :   }
     243             : 
     244      247292 :   aterm el = fparse_term(c, is);
     245      247292 :   aterm_list list;
     246      123646 :   list.push_front(el);
     247             : 
     248      298270 :   while (*c == ',')
     249             :   {
     250       87312 :     fnext_skip_layout(c, is);
     251       87312 :     el = fparse_term(c, is);
     252       87312 :     list.push_front(el);
     253             :   }
     254      123646 :   return reverse(list);
     255             : }
     256             : 
     257             : /*
     258             :  * Parse a quoted application.
     259             :  */
     260             : 
     261        6689 : static string fparse_quoted_string(int* c, istream& is)
     262             : {
     263             :   /* We need a buffer for printing and parsing */
     264        6689 :   std::string function_string;
     265             : 
     266             :   /* First parse the identifier */
     267        6689 :   fnext_char(c, is);
     268             : 
     269       33445 :   while (*c != '"')
     270             :   {
     271       13378 :     switch (*c)
     272             :     {
     273             :       case EOF:
     274           0 :         throw atermpp::runtime_error("Premature end of file while parsing quoted function symbol.");
     275             :       case '\\':
     276           0 :         fnext_char(c, is);
     277           0 :         if (*c == EOF)
     278             :         {
     279           0 :           throw atermpp::runtime_error("Premature end of file while parsing quoted function symbol.");
     280             :         }
     281           0 :         switch (*c)
     282             :         {
     283             :           case 'n':
     284           0 :             function_string+='\n';
     285           0 :             break;
     286             :           case 'r':
     287           0 :             function_string+='\r';
     288           0 :             break;
     289             :           case 't':
     290           0 :             function_string+= '\t';
     291           0 :             break;
     292             :           default:
     293           0 :             function_string+= *c;
     294           0 :             break;
     295             :         }
     296           0 :         break;
     297             :       default:
     298       13378 :         function_string+= *c;
     299       13378 :         break;
     300             :     }
     301       13378 :     fnext_char(c, is);
     302             :   }
     303        6689 :   fnext_skip_layout(c, is);
     304        6689 :   return function_string;
     305             : }
     306             : 
     307             : /* Parse an unquoted string. */
     308             : 
     309      205327 : static string fparse_unquoted_string(int* c, istream& is)
     310             : {
     311      205327 :   std::string function_string;
     312      205327 :   if (*c != '(')
     313             :   {
     314             :     /* First parse the identifier */
     315     2606769 :     while (*c!='"' && *c!='(' && *c!=')' && *c!=']' && *c!=']' && *c!=',' && *c!=' ' && *c!='\n' && *c!='\t' && *c!='\r' && *c!=EOF)
     316             :     {
     317     1200721 :       function_string+= *c;
     318     1200721 :       fnext_char(c, is);
     319             :     }
     320             : 
     321      205327 :     fskip_layout(c, is);
     322             :   }
     323      205327 :   return function_string;
     324             : }
     325             : 
     326      212016 : static aterm_appl parse_arguments(const string& f, int *c, istream& is)
     327             : {
     328             :   /* Time to parse the arguments */
     329      424032 :   aterm_list args;
     330      212016 :   if (*c == '(')
     331             :   {
     332      114804 :     fnext_skip_layout(c, is);
     333      114804 :     if (*c != ')')
     334             :     {
     335      114804 :       args = fparse_terms(c, is);
     336             :     }
     337             : 
     338      114804 :     if (*c != ')')
     339             :     {
     340           0 :       throw atermpp::runtime_error("Missing ')' while parsing term");
     341             :     }
     342      114804 :     fnext_skip_layout(c, is);
     343             :   }
     344             : 
     345             :   /* Wrap up this function application */
     346      424032 :   const function_symbol sym(f, args.size());
     347      424032 :   return aterm_appl(sym, args.begin(), args.end());
     348             : }
     349             : 
     350             : 
     351             : 
     352             : /*
     353             :  * Parse a number.
     354             :  */
     355             : 
     356         204 : static aterm fparse_num(int* c, istream& is)
     357             : {
     358         204 :   char num[32], *ptr = num, *numend = num + 30;
     359             : 
     360         204 :   if (*c == '-')
     361             :   {
     362           0 :     *ptr++ = *c;
     363           0 :     fnext_char(c, is);
     364             :   }
     365             : 
     366         872 :   while (isdigit(*c) && ptr < numend)
     367             :   {
     368         334 :     *ptr++ = *c;
     369         334 :     fnext_char(c, is);
     370             :   }
     371             : 
     372             :   {
     373         204 :     *ptr = '\0';
     374         204 :     return aterm_int(static_cast<std::size_t>(atol(num)));
     375             :   }
     376             : }
     377             : 
     378             : /*
     379             :  * Parse a term from a stream.
     380             :  */
     381             : 
     382      221071 : static aterm fparse_term(int* c, istream& is)
     383             : {
     384      221071 :   switch (*c)
     385             :   {
     386             :     case '"':
     387             :     {
     388       13378 :       string f=fparse_quoted_string(c, is);
     389        6689 :       return parse_arguments(f,c,is);
     390             :     }
     391             :     case '[':
     392             :     {
     393        8851 :       fnext_skip_layout(c, is);
     394        8851 :       if (*c == ']')
     395             :       {
     396           9 :         fnext_skip_layout(c, is);
     397           9 :         return aterm_list(); // The empty list has just been parsed.
     398             :       }
     399             :       else
     400             :       {
     401       17684 :         const aterm result = fparse_terms(c, is);
     402        8842 :         if (*c != ']')
     403             :         {
     404           0 :           throw atermpp::runtime_error("Expecting ']' while parsing term");
     405             :         }
     406        8842 :         fnext_skip_layout(c, is);
     407        8842 :         return result;
     408             :       }
     409             :     }
     410             :     default:
     411             :     {
     412      205531 :       if (isdigit(*c) || *c == '-')
     413             :       {
     414         204 :         return fparse_num(c, is);
     415             :       }
     416      410654 :       string f=fparse_unquoted_string(c, is);
     417      205327 :       return parse_arguments(f,c,is);
     418             :     }
     419             :   }
     420             : }
     421             : 
     422             : /*
     423             :  * Read from a string.
     424             :  */
     425             : 
     426       10106 : aterm read_term_from_string(const std::string& s)
     427             : {
     428       20212 :   stringstream ss(s);
     429       20212 :   return  read_term_from_text_stream(ss);
     430             : }
     431             : 
     432       10113 : aterm read_term_from_text_stream(istream& is)
     433             : {
     434             :   // Initialise global io (esp. for windows)
     435       10113 :   aterm_io_init();
     436             : 
     437             :   // Initialise error handling.
     438       10113 :   line = 0;
     439       10113 :   col = 0;
     440       10113 :   error_buf.clear();
     441             : 
     442             :   int c;
     443       10113 :   fnext_skip_layout(&c, is);
     444             : 
     445             :   try
     446             :   {
     447       10113 :     return fparse_term(&c, is);
     448             :   }
     449           0 :   catch (atermpp::runtime_error& e)
     450             :   {
     451           0 :     throw atermpp::runtime_error(e.what() + string("\n") + print_parse_error_position());
     452             :   }
     453             : }
     454             : 
     455           0 : bool is_binary_aterm_stream(std::istream& is)
     456             : {
     457           0 :   aterm_io_init();
     458             : 
     459             :   int c;
     460           0 :   fnext_char(&c, is);
     461           0 :   return (c == 0);
     462             : }
     463             : 
     464             : /*
     465             :  * Read an aterm from a file that could be binary or text.
     466             :  */
     467           0 : bool is_binary_aterm_file(const std::string& filename)
     468             : {
     469           0 :   if(filename.empty())
     470             :   {
     471           0 :     return is_binary_aterm_stream(std::cin);
     472             :   }
     473             :   else
     474             :   {
     475           0 :     std::ifstream is;
     476           0 :     is.open(filename.c_str());
     477           0 :     return is_binary_aterm_stream(is);
     478             :   }
     479             : }
     480             : 
     481         420 : } // namespace atermpp

Generated by: LCOV version 1.12