LCOV - code coverage report
Current view: top level - core/source - dparser.cpp (source / functions) Hit Total Coverage
Test: mcrl2_coverage.info.cleaned Lines: 108 255 42.4 %
Date: 2024-04-19 03:43:27 Functions: 20 36 55.6 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // Author(s): Wieger Wesselink
       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 mcrl2/core/dparser.cpp
      10             : /// \brief add your file description here.
      11             : 
      12             : #include <d.h>
      13             : #include "mcrl2/core/detail/dparser_functions.h"
      14             : #include "mcrl2/utilities/logger.h"
      15             : #include <iomanip>
      16             : #include <locale>
      17             : 
      18             : extern "C"
      19             : {
      20             :   extern D_ParserTables parser_tables_mcrl2;
      21             : }
      22             : 
      23             : namespace mcrl2 {
      24             : 
      25             : namespace core {
      26             : 
      27           4 : std::string parse_node::add_context(const std::string& message) const
      28             : {
      29           4 :   return detail::add_context(&node->start_loc, message);
      30             : }
      31             : 
      32      713001 : int parse_node::symbol() const
      33             : {
      34      713001 :   return node->symbol;
      35             : }
      36             : 
      37      722837 : int parse_node::child_count() const
      38             : {
      39      722837 :   return d_get_number_of_children(node);
      40             : }
      41             : 
      42             : // 0 <= i < child_count()
      43      564335 : parse_node parse_node::child(int i) const
      44             : {
      45      564335 :   return parse_node(d_get_child(node, i));
      46             : }
      47             : 
      48           0 : parse_node parse_node::find_in_tree(int symbol) const
      49             : {
      50           0 :   return parse_node(d_find_in_tree(node, symbol));
      51             : }
      52             : 
      53       99833 : std::string parse_node::string() const
      54             : {
      55       99833 :   return std::string(node->start_loc.s, node->end - node->start_loc.s);
      56             : }
      57             : 
      58           0 : std::string parse_node::tree() const
      59             : {
      60           0 :   if (child_count() < 2)
      61           0 :     return this->string();
      62           0 :   std::stringstream result;
      63           0 :   result << "(" << child(0).tree();
      64           0 :   for (int i = 1; i < child_count(); ++i)
      65           0 :     result << " " << child(i).tree();
      66           0 :   result << ")";
      67           0 :   return result.str();
      68           0 : }
      69             : 
      70           0 : int parse_node::column() const
      71             : {
      72           0 :   return node->start_loc.col;
      73             : }
      74             : 
      75           0 : int parse_node::line() const
      76             : {
      77           0 :   return node->start_loc.line;
      78             : }
      79             : 
      80           0 : std::string parse_node::pathname() const
      81             : {
      82           0 :   return std::string(node->start_loc.pathname);
      83             : }
      84             : 
      85      567489 : parse_node::~parse_node()
      86             : {
      87      567489 :   if (parser)
      88             :   {
      89        3153 :     free_D_ParseNode(parser, node);
      90             :   }
      91      567489 : }
      92             : 
      93             : // Prints a tree of
      94           0 : std::string parser_table::tree(const core::parse_node& node) const
      95             : {
      96           0 :   std::stringstream result;
      97           0 :   result << symbol_name(node) << "(";
      98           0 :   if (node.child_count() == 0)
      99           0 :     result << '"' << node.string() << '"';
     100             :   else
     101           0 :     result << tree(node.child(0));
     102           0 :   for (int i = 1; i < node.child_count(); ++i)
     103           0 :     result << " " << tree(node.child(i));
     104           0 :   result << ")";
     105           0 :   return result.str();
     106           0 : }
     107             : 
     108             : // Returns the number of symbols in the table
     109      301047 : unsigned int parser_table::symbol_count() const
     110             : {
     111      301047 :   return m_table.nsymbols;
     112             : }
     113             : 
     114             : // Returns the name of the i-th symbol
     115      834251 : std::string parser_table::symbol_name(unsigned int i) const
     116             : {
     117      834251 :   if (i >= m_table.nsymbols)
     118             :   {
     119           0 :     print();
     120           0 :     std::ostringstream out;
     121           0 :     out << "parser_table::symbol_name: index " << i << " out of bounds!";
     122           0 :     throw std::runtime_error(out.str());
     123           0 :   }
     124      834251 :   const char* name = m_table.symbols[i].name;
     125      834251 :   if (!name)
     126             :   {
     127           0 :     return "";
     128             :   }
     129      834251 :   return std::string(name);
     130             : }
     131             : 
     132       99827 : std::string parser_table::symbol_name(const parse_node& node) const
     133             : {
     134       99827 :   return symbol_name(node.symbol());
     135             : }
     136             : 
     137             : // Returns the 'start symbol' of the i-th symbol
     138        3154 : int parser_table::start_symbol(unsigned int i) const
     139             : {
     140        3154 :   return m_table.symbols[i].start_symbol;
     141             : }
     142             : 
     143             : // Returns true if the i-th symbol is of type D_SYMBOL_NTERM
     144      301047 : bool parser_table::is_term_symbol(unsigned int i) const
     145             : {
     146      301047 :   return m_table.symbols[i].kind == D_SYMBOL_NTERM;
     147             : }
     148             : 
     149        3154 : unsigned int parser_table::start_symbol_index(const std::string& name) const
     150             : {
     151      301047 :   for (unsigned int i = 0; i < symbol_count(); i++)
     152             :   {
     153      301047 :     if (is_term_symbol(i) && symbol_name(i) == name)
     154             :     {
     155        3154 :       return start_symbol(i);
     156             :     }
     157             :   }
     158           0 :   throw mcrl2::runtime_error("unknown start symbol '" + name + "'");
     159             :   return 0;
     160             : }
     161             : 
     162           0 : void parser_table::print() const
     163             : {
     164           0 :   std::clog << "--------------------" << std::endl;
     165           0 :   std::clog << "-   symbol table   -" << std::endl;
     166           0 :   std::clog << "--------------------" << std::endl;
     167           0 :   for (unsigned int i = 0; i < symbol_count(); i++)
     168             :   {
     169           0 :     std::clog << std::setw(3) << i << " " << symbol_name(i) << std::endl;
     170             :   }
     171           0 :   std::clog << "--------------------" << std::endl;
     172           0 : }
     173             : 
     174        3154 : parser::parser(D_ParserTables& tables, D_AmbiguityFn ambiguity_fn, D_SyntaxErrorFn syntax_error_fn, std::size_t max_error_message_count)
     175        3154 :   : m_table(tables)
     176             : {
     177        3154 :   detail::set_dparser_max_error_message_count(max_error_message_count);
     178        3154 :   m_parser = new_D_Parser(&tables, 0);
     179        3154 :   m_parser->initial_globals = this;
     180        3154 :   m_parser->save_parse_tree = 1;
     181        3154 :   m_parser->initial_scope = nullptr;
     182        3154 :   m_parser->dont_use_greediness_for_disambiguation = 1;
     183        3154 :   m_parser->dont_use_height_for_disambiguation = 1;
     184        3154 :   if (ambiguity_fn)
     185             :   {
     186        3150 :     m_parser->ambiguity_fn = ambiguity_fn;
     187             :   }
     188        3154 :   if (syntax_error_fn)
     189             :   {
     190        3150 :     m_parser->syntax_error_fn = syntax_error_fn;
     191             :   }
     192        3154 : }
     193             : 
     194        3154 : parser::~parser()
     195             : {
     196        3154 :   free_D_Parser(m_parser);
     197        3154 : }
     198             : 
     199      636692 : const parser_table& parser::symbol_table() const
     200             : {
     201      636692 :   return m_table;
     202             : }
     203             : 
     204        3154 : unsigned int parser::start_symbol_index(const std::string& name) const
     205             : {
     206        3154 :   return m_table.start_symbol_index(name);
     207             : }
     208             : 
     209        3154 : parse_node parser::parse(const std::string& text, unsigned int start_symbol_index, bool partial_parses)
     210             : {
     211        3154 :   detail::reset_dparser_error_message_count();
     212        3154 :   m_parser->start_state = start_symbol_index;
     213        3154 :   m_parser->partial_parses = partial_parses ? 1 : 0;
     214        3154 :   D_ParseNode* result = dparse(m_parser, const_cast<char*>(text.c_str()), static_cast<int>(text.size()));
     215        3154 :   if (!result || m_parser->syntax_errors)
     216             :   {    
     217           1 :     if (result != nullptr) {
     218           1 :       free_D_ParseNode(m_parser, result);
     219             :     }
     220           1 :     throw mcrl2::runtime_error("syntax error");
     221             :   }
     222        3153 :   return parse_node(result, m_parser);
     223             : }
     224             : 
     225           0 : void parser::print_symbol_table() const
     226             : {
     227           0 :   m_table.print();
     228           0 : }
     229             : 
     230           0 : std::string parser::indent(unsigned int count) const
     231             : {
     232           0 :   return std::string(count, ' ');
     233             : }
     234             : 
     235           0 : std::string parser::truncate(const std::string& s, unsigned int max_size) const
     236             : {
     237           0 :   std::string result = s.substr(0, max_size);
     238             : 
     239             :   // truncate at newline
     240           0 :   std::string::size_type pos = result.find('\n');
     241           0 :   if (pos != std::string::npos)
     242             :   {
     243           0 :     result = result.substr(0, pos);
     244             :   }
     245             : 
     246           0 :   return result;
     247           0 : }
     248             : 
     249           0 : void parser::print_tree(const parse_node& node, unsigned int level) const
     250             : {
     251           0 :   if (node)
     252             :   {
     253           0 :     std::string symbol = m_table.symbol_name(node.symbol());
     254           0 :     std::string prefix = indent(2 * level);
     255           0 :     std::cout << prefix << "--- " << symbol << " \"" << truncate(node.string()) << "\"" << std::endl;
     256           0 :     for (int i = 0; i <= node.child_count(); i++)
     257             :     {
     258           0 :       print_tree(node.child(i), level + 1);
     259             :     }
     260           0 :   }
     261           0 : }
     262             : 
     263           0 : void parser::destroy_parse_node(const parse_node& node)
     264             : {
     265           0 :   free_D_ParseNode(m_parser, node.node);
     266           0 : }
     267             : 
     268             : /// \brief Callback function for nodes in the parse tree
     269           0 : void parser::announce(D_ParseNode& node_ref)
     270             : {
     271           0 :   parse_node node(&node_ref);
     272           0 :   std::cout << "parsed " << m_table.symbol_name(node.symbol()) << " " << node.string() << std::endl;
     273           0 : }
     274             : 
     275             : namespace detail {
     276             : 
     277           5 : std::string add_context(const d_loc_t* loc, const std::string& message)
     278             : {
     279           5 :   std::stringstream s;
     280           5 :   s << "Line " << loc->line << ", column " << loc->col << ": "
     281           5 :     << message << std::endl;
     282           5 :   char* beg = loc->s - loc->col;
     283           5 :   char* end = loc->s;
     284          43 :   while (*end != '\0' && *end != '\n' && *end != '\r')
     285             :   {
     286          38 :     ++end;
     287             :   }
     288           5 :   std::string line(beg, end);
     289           5 :   s << "  " << line << std::endl;
     290          43 :   for (int i = 0; i < loc->col + 2; ++i)
     291             :   {
     292          38 :     s << ' ';
     293             :   }
     294           5 :   s << '^';
     295          10 :   return s.str();
     296           5 : }
     297             : 
     298             : inline
     299           0 : bool is_all_of_type(D_ParseNode* nodes[], int n, const char* type, const core::parser_table& table)
     300             : {
     301           0 :   for (int i = 0; i < n; i++)
     302             :   {
     303           0 :     core::parse_node node(nodes[i]);
     304           0 :     if (table.symbol_name(node) != type)
     305             :     {
     306           0 :       return false;
     307             :     }
     308           0 :   }
     309           0 :   return true;
     310             : }
     311             : 
     312             : inline
     313             : void print_ambiguous_nodes(D_ParseNode* nodes[], int n, const char* type, const core::parser_table& table)
     314             : {
     315             :   mCRL2log(log::verbose) << "--- " << type << " ambiguity" << std::endl;
     316             :   for (int i = 0; i < n; ++i)
     317             :   {
     318             :     core::parse_node vi(nodes[i]);
     319             :     // mCRL2log(log::verbose) << vi.tree() << " " << table.tree(vi) << std::endl;
     320             :     mCRL2log(log::verbose) << "ALT " << table.tree(vi) << std::endl;
     321             :   }
     322             : }
     323             : 
     324             : inline
     325             : void print_chosen_node(D_ParseNode* node, const core::parser_table& table)
     326             : {
     327             :   core::parse_node vi(node);
     328             :   mCRL2log(log::verbose) << "CHOOSE " << table.tree(vi) << std::endl;
     329             : }
     330             : 
     331             : /// \brief Function for resolving parser ambiguities.
     332           0 : D_ParseNode* ambiguity_fn(struct D_Parser * /*p*/, int n, struct D_ParseNode **v)
     333             : {
     334           0 :   core::parser_table table(parser_tables_mcrl2);
     335             : 
     336             :   // resolve PbesExpr ambiguities
     337           0 :   if (is_all_of_type(v, n, "PbesExpr", table))
     338             :   {
     339           0 :     D_ParseNode* result = nullptr;
     340           0 :     for (int i = 0; i < n; i++)
     341             :     {
     342           0 :       core::parse_node node(v[i]);
     343           0 :       if (table.symbol_name(node.child(0)) == "Id")
     344             :       {
     345           0 :         return v[i];
     346             :       }
     347           0 :       else if (table.symbol_name(node.child(0)) != "DataExpr")
     348             :       {
     349           0 :         result = v[i];
     350             :       }
     351           0 :     }
     352           0 :     if (result)
     353             :     {
     354           0 :       return result;
     355             :     }
     356           0 :     return v[0];
     357             :   }
     358             : 
     359             :   // resolve ActFrm ambiguities
     360           0 :   if (is_all_of_type(v, n, "ActFrm", table))
     361             :   {
     362             : //print_ambiguous_nodes(v, n, "ActFrm", table);
     363           0 :     D_ParseNode* result = nullptr;
     364           0 :     for (int i = 0; i < n; i++)
     365             :     {
     366           0 :       core::parse_node node(v[i]);
     367           0 :       if (table.symbol_name(node.child(0)) == "MultAct")
     368             :       {
     369             : //print_chosen_node(v[i], table);
     370           0 :         return v[i];
     371             :       }
     372           0 :       else if (table.symbol_name(node.child(0)) != "DataExpr")
     373             :       {
     374           0 :         result = v[i];
     375             :       }
     376           0 :     }
     377           0 :     if (result)
     378             :     {
     379             : //print_chosen_node(result, table);
     380           0 :       return result;
     381             :     }
     382             : //print_chosen_node(v[0], table);
     383           0 :     return v[0];
     384             :   }
     385             : 
     386             :   // resolve StateFrm ambiguities
     387           0 :   if (is_all_of_type(v, n, "StateFrm", table))
     388             :   {
     389             : //print_ambiguous_nodes(v, n, "StateFrm", table);
     390           0 :     D_ParseNode* result = nullptr;
     391           0 :     for (int i = 0; i < n; i++)
     392             :     {
     393           0 :       core::parse_node node(v[i]);
     394           0 :       if (table.symbol_name(node.child(0)) == "Id")
     395             :       {
     396             : //print_chosen_node(v[i], table);
     397           0 :         return v[i];
     398             :       }
     399           0 :       else if (table.symbol_name(node.child(0)) != "DataExpr")
     400             :       {
     401           0 :         result = v[i];
     402             :       }
     403           0 :     }
     404           0 :     if (result)
     405             :     {
     406             : //print_chosen_node(result, table);
     407           0 :       return result;
     408             :     }
     409             : //print_chosen_node(v[0], table);
     410           0 :     return v[0];
     411             :   }
     412             : 
     413             :   // resolve RegFrm ambiguities
     414           0 :   if (is_all_of_type(v, n, "RegFrm", table))
     415             :   {
     416             : //print_ambiguous_nodes(v, n, "RegFrm", table);
     417           0 :     for (int i = 0; i < n; i++)
     418             :     {
     419           0 :       core::parse_node node(v[i]);
     420           0 :       if (table.symbol_name(node.child(0)) == "RegFrm" || table.symbol_name(node.child(0)) == "(")
     421             :       {
     422             : //print_chosen_node(v[i], table);
     423           0 :         return v[i];
     424             :       }
     425           0 :     }
     426             :   }
     427             : 
     428             :   // If we reach this point, then the ambiguity is unresolved. We print all
     429             :   // ambiguities on the debug output, then throw an exception.
     430           0 :   for (int i = 0; i < n; ++i)
     431             :   {
     432           0 :     core::parse_node vi(v[i]);
     433           0 :     mCRL2log(log::verbose) << "Ambiguity: " << vi.tree() << std::endl;
     434           0 :     mCRL2log(log::debug) << "Ambiguity: " << table.tree(vi) << std::endl;
     435           0 :   }
     436           0 :   throw mcrl2::runtime_error("Unresolved ambiguity.");
     437             : }
     438             : 
     439           1 : static void log_location(struct D_Parser *ap)
     440             : {
     441             :   // We recover information about the last parsed node by casting D_Parser to Parser, which
     442             :   // is the structure that the dparser library internally uses to keep its administration in.
     443           1 :   std::string after;
     444           1 :   SNode *s = ((Parser*)ap)->snode_hash.last_all;
     445           1 :   ZNode *z = s != nullptr ? s->zns.v[0] : nullptr;
     446           1 :   while (z != nullptr && z->pn->parse_node.start_loc.s == z->pn->parse_node.end)
     447             :   {
     448           0 :     z = (z->sns.v && z->sns.v[0]->zns.v) ? z->sns.v[0]->zns.v[0] : nullptr;
     449             :   }
     450           1 :   if (z && z->pn->parse_node.start_loc.s != z->pn->parse_node.end)
     451             :   {
     452           1 :     after = std::string(z->pn->parse_node.start_loc.s, z->pn->parse_node.end);
     453             :   }
     454             : 
     455           1 :   std::string message = "syntax error";
     456           1 :   if (!after.empty())
     457             :   {
     458           1 :     message = message + " after '" + after + "'";
     459             :   }
     460           1 :   mCRL2log(log::error) << add_context(&ap->loc, message) << std::endl;
     461           1 : }
     462             : 
     463           1 : void syntax_error_fn(struct D_Parser *ap)
     464             : {
     465           1 :   core::detail::increment_dparser_error_message_count();
     466           1 :   if (core::detail::get_dparser_error_message_count() > core::detail::get_dparser_max_error_message_count())
     467             :   {
     468           0 :     return;
     469             :   }
     470           1 :   log_location(ap);
     471           1 :   if (ap->loc.s == nullptr)
     472             :   {
     473           0 :     mCRL2log(log::error) << "Unexpected end of input." << std::endl;
     474             :   }
     475             :   else
     476             :   {
     477             :     // Dive into the internals of dparser to recover some extra diagnostics.
     478           1 :     Parser* p = (Parser*)ap;
     479           1 :     if (p->pnode_hash.all && p->pnode_hash.all->latest)
     480             :     {
     481           1 :       core::parse_node n(&p->pnode_hash.all->latest->parse_node);
     482           1 :       D_Symbol &s = p->t->symbols[n.symbol()];
     483           1 :       if (s.kind == D_SYMBOL_INTERNAL)
     484             :       {
     485             :         /* DParser stores production rules in order: search for the corresponding nonterminal. */
     486           0 :         int parentsym = n.symbol() - 1;
     487           0 :         while (p->t->symbols[parentsym].kind == D_SYMBOL_INTERNAL)
     488           0 :           --parentsym;
     489           0 :         s = p->t->symbols[parentsym];
     490             :       }
     491             : 
     492           1 :       switch (s.kind)
     493             :       {
     494           1 :       case D_SYMBOL_STRING:
     495             :       case D_SYMBOL_TOKEN:
     496             :         {
     497           1 :           std::locale loc;
     498           2 :           mCRL2log(log::error) << "Unexpected "
     499           2 :                                          << (std::isalpha(n.string()[0], loc) ? "keyword " : "")
     500           2 :                                          << "'" << n.string() << "'" << std::endl;
     501           1 :         }
     502           1 :         break;
     503           0 :       case D_SYMBOL_NTERM:
     504           0 :         mCRL2log(log::error) << "Unexpected " << s.name << " '" << n.string() << "'" << std::endl;
     505           0 :         break;
     506           0 :       default:
     507             :         // TODO: check if we can give more sensible output in the remaining cases.
     508           0 :         break;
     509             :       }
     510           1 :     }
     511             :   }
     512             : }
     513             : 
     514             : } // namespace detail
     515             : 
     516           0 : void parser::custom_parse_error(const std::string& message) const
     517             : {
     518           0 :   core::detail::increment_dparser_error_message_count();
     519           0 :   if (core::detail::get_dparser_error_message_count() > core::detail::get_dparser_max_error_message_count())
     520             :   {
     521           0 :     return;
     522             :   }
     523           0 :   detail::log_location(m_parser);
     524           0 :   mCRL2log(log::error) << message << std::endl;
     525             : }
     526             : 
     527             : } // namespace core
     528             : 
     529             : } // namespace mcrl2
     530             : 

Generated by: LCOV version 1.14