将 C 野牛解析器移植到 C++

Porting C bison parser to C++

本文关键字:C++      更新时间:2023-10-16

我使用基于可重入的FLEX C扫描仪和基于可重入野牛C的解析器。它工作正常。

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

为什么?当输入太复杂(if-else-if 链太长)时,关于野牛堆栈大小的限制是有问题的。另外,我不喜欢未记录的解决方案和作者说某些东西将被更改等的解决方案。这就是为什么我希望保留基于C的FLEX扫描仪。

此外,我需要使用特定的前缀或后缀或命名空间来使用多个基于 FLEX C 的可重入扫描程序和基于可重入野牛C++的解析器。

感谢

假设你想用 Flex 和 Bison 编写C++解析器,这就是答案。

本文回答了您的问题。作者以OO方式自由地使用Flex和Bison C++。在 Flex 的情况下,您可以使用%{ ... %}代码块。另一方面,在Bison的情况下,根据作者的说法,这就是真正的通用解析器在C++中的样子:

%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);
}

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

您没有义务在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来报告内存分配失败,而且我所知道的标准库实现都没有尝试预测未来乐观分配内存不可用。

相关文章:
  • 没有找到相关文章