将C bison解析器移植到C ++

时间:2019-07-17 09:48:27

标签: c++ c bison

我使用基于折返FLEX C的扫描器和基于折返野牛C的解析器。很好。

我希望保留基于可重入C的FLEX扫描器,并获得基于可重入Bison C ++的解析器。

为什么?当输入太复杂(if-else-if链太长)时,就存在关于bison堆栈大小的限制的问题。另外,我不喜欢那些未公开的解决方案和那些作者说有些事情会改变的解决方案等等。这就是为什么我希望保留基于C的FLEX扫描仪。

此外,我需要使用特定的前缀或后缀或名称空间,才能使用多个基于可重入FLEX C的扫描器和基于可重入bison C ++的解析器。

感谢

2 个答案:

答案 0 :(得分:1)

假设您想使用Flex和Bison来用C ++编写解析器,那么答案就来了。

This article为您的问题提供了答案。作者以OO方式免费使用C ++中的Flex和Bison。如果使用Flex,则可以使用%{ ... %}代码块。另一方面,根据作者的说法,就Bison而言,这就是真正的通用解析器的样子:

%skeleton "lalr1.cc"
%require  "3.0"
%debug 
%defines 
%define api.namespace {MC}
%define parser_class_name {MC_Parser}

%code requires{
   namespace MC {
      class MC_Driver;
      class MC_Scanner;
   }

// The following definitions is missing when %locations isn't used
# ifndef YY_NULLPTR
#  if defined __cplusplus && 201103L <= __cplusplus
#   define YY_NULLPTR nullptr
#  else
#   define YY_NULLPTR 0
#  endif
# endif

}

%parse-param { MC_Scanner  &scanner  }
%parse-param { MC_Driver  &driver  }

%code{
   #include <iostream>
   #include <cstdlib>
   #include <fstream>

   /* include for all driver functions */
   #include "mc_driver.hpp"

#undef yylex
#define yylex scanner.yylex
}

%define api.value.type variant
%define parse.assert

%token               END    0     "end of file"
%token               UPPER
%token               LOWER
%token <std::string> WORD
%token               NEWLINE
%token               CHAR

%locations

%%

list_option : END | list END;

list
  : item
  | list item
  ;

item
  : UPPER   { driver.add_upper(); }
  | LOWER   { driver.add_lower(); }
  | WORD    { driver.add_word( $1 ); }
  | NEWLINE { driver.add_newline(); }
  | CHAR    { driver.add_char(); }
  ;

%%


void 
MC::MC_Parser::error( const location_type &l, const std::string &err_message )
{
   std::cerr << "Error: " << err_message << " at " << l << "\n";
} 

...和驱动程序代码:

#include <cctype>
#include <fstream>
#include <cassert>

#include "mc_driver.hpp"

MC::MC_Driver::~MC_Driver()
{
   delete(scanner);
   scanner = nullptr;
   delete(parser);
   parser = nullptr;
}

void 
MC::MC_Driver::parse( const char * const filename )
{
   /**
    * Remember, if you want to have checks in release mode
    * then this needs to be an if statement 
    */
   assert( filename != nullptr );
   std::ifstream in_file( filename );
   if( ! in_file.good() )
   {
       exit( EXIT_FAILURE );
   }
   parse_helper( in_file );
   return;
}

void
MC::MC_Driver::parse( std::istream &stream )
{
   if( ! stream.good()  && stream.eof() )
   {
       return;
   }
   //else
   parse_helper( stream ); 
   return;
}


void 
MC::MC_Driver::parse_helper( std::istream &stream )
{

   delete(scanner);
   try
   {
      scanner = new MC::MC_Scanner( &stream );
   }
   catch( std::bad_alloc &ba )
   {
      std::cerr << "Failed to allocate scanner: (" <<
         ba.what() << "), exiting!!\n";
      exit( EXIT_FAILURE );
   }

   delete(parser); 
   try
   {
      parser = new MC::MC_Parser( (*scanner) /* scanner */, 
                                  (*this) /* driver */ );
   }
   catch( std::bad_alloc &ba )
   {
      std::cerr << "Failed to allocate parser: (" << 
         ba.what() << "), exiting!!\n";
      exit( EXIT_FAILURE );
   }
   const int accept( 0 );
   if( parser->parse() != accept )
   {
      std::cerr << "Parse failed!!\n";
   }
   return;
}

void 
MC::MC_Driver::add_upper()
{ 
   uppercase++; 
   chars++; 
   words++; 
}

void 
MC::MC_Driver::add_lower()
{ 
   lowercase++; 
   chars++; 
   words++; 
}

void 
MC::MC_Driver::add_word( const std::string &word )
{
   words++; 
   chars += word.length();
   for(const char &c : word ){
      if( islower( c ) )
      { 
         lowercase++; 
      }
      else if ( isupper( c ) ) 
      { 
         uppercase++; 
      }
   }
}

void 
MC::MC_Driver::add_newline()
{ 
   lines++; 
   chars++; 
}

void 
MC::MC_Driver::add_char()
{ 
   chars++; 
}


std::ostream& 
MC::MC_Driver::print( std::ostream &stream )
{
   /** NOTE: Colors are defined as class variables w/in MC_Driver **/
   stream << red  << "Results: " << norm << "\n";
   stream << blue << "Uppercase: " << norm << uppercase << "\n";
   stream << blue << "Lowercase: " << norm << lowercase << "\n";
   stream << blue << "Lines: " << norm << lines << "\n";
   stream << blue << "Words: " << norm << words << "\n";
   stream << blue << "Characters: " << norm << chars << "\n";
   return(stream);
}

此代码的更好解释(与上面的注释相比)可以在上面两个段落链接的作者网站上找到。我真的建议您检查一下。

答案 1 :(得分:0)

您没有义务在C ++程序中使用flex的C ++模板。 flex生成的C代码应在C ++中正确编译并执行。 (至少,我还没有看到问题。)如flex文档中所述,所生成的C代码中的任何C ++不兼容都将被视为一个错误,应这样报告。

已经说过,如果您对bison的C模板的唯一关注是对解析器堆栈大小的限制,则可以通过定义YYMAXDEPTH轻松地增加该限制。特别是,您可以使用以下方法基本上避免任何限制:

#define YYMAXDEPTH (YYSTACK_ALLOC_MAXIMUM / YYSTACK_BYTES (1))

(该公式来自骨架data/yacc.c中的注释:

/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
   if the built-in stack extension method is used).

   Do not make this value too large; the results are undefined if
   YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH)
   evaluated with infinite-precision integer arithmetic.  */

我不知道该评论是否真正构成正式文档,但似乎是针对那些可能考虑更改YYMAXDEPTH值的人的,因此我认为可以依靠它。 :-))另一方面,您可能会考虑施加较小的限制,因为仅将其留给malloc()来报告分配失败-这是上述#define的结果-众所周知在使用乐观内存分配的平台上不可靠。

切换到C ++不能避免乐观的内存分配问题,因为bison的C ++模板依靠std::vector报告内存分配失败,而且我所知道的标准库实现都没有尝试预测将来的不可用的方法。乐观的分配内存。