带有Flex Reentrant C ++ 14示例的Bison C ++变体? 首先让我们看看makefile 树组件 lexer组件解析器组件错误消息

问题描述

我正在尝试使用c ++ 14中的flex / bison工具链来编写玩具语言。

在将bison c ++变体与flex reentrant一​​起使用时,我感到困惑,yylex找不到参数yylval

我的开发环境是装有最新OS和XCode的macbook,自制安装了最新的flex 2.6.4和bison 3.7.1。

为方便起见,您可以在此处下载错误的项目:https://github.com/linrongbin16/tree

现在,让我介绍一下这个不太简单的tree项目:

首先让我们看看makefile

clean:
    rm *.o *.out *.yy.cc *.yy.hh *.tab.cc *.tab.hh *.output

tree.out: tree.o token.yy.o parser.tab.o
    clang++ -std=c++14 -o tree.out tree.o token.yy.o parser.tab.o

token.yy.cc token.yy.hh: token.l
    flex --debug -o token.yy.cc --header-file=token.yy.hh token.l

parser.tab.cc parser.tab.hh: parser.y
    bison --debug --verbose -Wcounterexamples -o parser.tab.cc --defines=parser.tab.hh parser.y

token.yy.o: token.yy.cc
    clang++ -std=c++14 -g -c token.yy.cc token.yy.hh

parser.tab.o: parser.tab.cc
    clang++ -std=c++14 -g -c parser.tab.cc parser.tab.hh

tree.o: tree.cpp parser.tab.hh token.yy.hh
    clang++ -std=c++14 -g -c tree.cpp

应用程序是tree.out,它取决于3个组件:tree tokenparser

树组件

tree.h定义了一个简单的抽象语法树类,因为我没有实现它,所以它只有一个虚拟析构函数

#pragma once

struct Tree {
  virtual ~Tree() = default;
};

tree.cppmain函数,它从stdin读取文件名并初始化lexer和解析器,然后进行解析:

#include "parser.tab.hh"
#include "token.yy.hh"
#include <cstdio>
#include <cstdlib>

struct Scanner {
  yyscan_t yyscanner;
  FILE *fp;
  YY_BUFFER_STATE yyBufferState;

  Scanner(const char *fileName) {
    yylex_init_extra(this,&yyscanner);
    fp = std::fopen(fileName,"r");
    if (!fp) {
      printf("file %s cannot open!\n",fileName);
      exit(-1);
    }
    yyBufferState = yy_create_buffer(fp,YY_BUF_SIZE,yyscanner);
    yy_switch_to_buffer(yyBufferState,yyscanner);
    yyset_lineno(1,yyscanner);
  }

  virtual ~Scanner() {
    if (yyBufferState) {
      yy_delete_buffer(yyBufferState,yyscanner);
    }
    if (yyscanner) {
      yylex_destroy(yyscanner);
    }
    if (fp) {
      std::fclose(fp);
    }
  }
};

int main(int argc,char **argv) {
  if (argc != 2) {
    printf("missing file name!\n");
    return -1;
  }

  Scanner scanner(argv[1]);
  yy::parser parser(scanner.yyscanner);
  if (parser.parse() != 0) {
    printf("parsing Failed!\n");
    return -1;
  }
  return 0;
}

重要的是,我使用了bison c ++变体和flex reentrant功能,我想使项目现代化(使用c ++ 14)并且可以安全地使用多线程。因此,初始化时有点复杂。但是当项目扩展到一个大项目时,这是值得的。

lexer组件

token.l

%option noyywrap noinput nounput
%option nodefault
%option nounistd
%option reentrant

%{
#include <cstdio>
#include <cstring>
#include "parser.tab.hh"
%}

%%

"+"     { yylval->emplace<int>(yy::parser::token::PLUS); return yy::parser::token::PLUS; }
"-"     { yylval->emplace<int>(yy::parser::token::MINUS); return yy::parser::token::MINUS; }
"*"     { yylval->emplace<int>(yy::parser::token::TIMES); return yy::parser::token::TIMES; }
"/"     { yylval->emplace<int>(yy::parser::token::DIVIDE); return yy::parser::token::DIVIDE; }
"("     { yylval->emplace<int>(yy::parser::token::LPAREN); return yy::parser::token::LPAREN; }
")"     { yylval->emplace<int>(yy::parser::token::RPAREN); return yy::parser::token::RPAREN; }
";"     { yylval->emplace<int>(yy::parser::token::SEMICOLON); return yy::parser::token::SEMICOLON; }
"="     { yylval->emplace<int>(yy::parser::token::EQUAL); return yy::parser::token::EQUAL; }

[a-zA-Z][a-zA-Z0-9]+    { yylval->emplace<std::string>(yytext); return yy::parser::token::ID; }
[0-9]+                  { yylval->emplace<int>(atoi(yytext)); return yy::parser::token::NUM; }

%%

在这里我遵循了bison split symbol manual(注意:这里我们遇到了编译错误,我也尝试了make_XXX API,这也给了我错误)。

它会生成token.yy.cc token.yy.hh,期望编译token.yy.o对象。

解析器组件

parser.y

%require "3.2"
%language "c++"
%define api.value.type variant
%define api.token.constructor
%define parse.assert
%define parse.error verbose
%define parse.lac full
%locations
%param {yyscan_t yyscanner}

%code top {
#include <memory>
}

%code requires {
#include <memory>
#include "token.yy.hh"
#include "tree.h"
#define SP_NULL (std::shared<Tree>(nullptr))
}

%token<int> PLUS '+'
%token<int> MINUS '-'
%token<int> TIMES '*'
%token<int> DIVIDE '/'
%token<int> SEMICOLON ';'
%token<int> EQUAL '='
%token<int> LPAREN '('
%token<int> RPAREN ')'
%token<int> NUM

%token<std::string> ID

%type<std::shared_ptr<Tree>> prog assign expr literal

/* operator precedence */
%right EQUAL
%left PLUS MINUS
%left TIMES DIVIDE


%start prog

%%

prog : assign { $$ = SP_NULL; }
     | prog ';' assign { $$ = SP_NULL }
     ;

assign : ID '=' expr { $$ = SP_NULL; }
       | expr { $$ = $1; }
       ;

expr : literal { $$ = SP_NULL; }
     | expr '+' literal { $$ = SP_NULL; }
     | expr '-' literal { $$ = SP_NULL; }
     | expr '*' literal { $$ = SP_NULL; }
     | expr '/' literal { $$ = SP_NULL; }
     ;

literal : ID { $$ = SP_NULL; }
        | NUM { $$ = SP_NULL; }
        ;

%%

我跟随bison c++ variant manual,它生成parser.tab.cc parser.tab.hh parser.output输出文件仅用于分析。

由于flex是可重入的,所以我需要添加参数%param {yyscan_t yyscanner}

错误消息

以下是使用make tree.out制作时的错误消息:

bison --debug --verbose -Wcounterexamples -o parser.tab.cc --defines=parser.tab.hh parser.y
flex --debug -o token.yy.cc --header-file=token.yy.hh token.l
clang++ -std=c++14 -g -c tree.cpp
clang++ -std=c++14 -g -c token.yy.cc token.yy.hh
token.yy.cc:820:10: error: use of undeclared identifier 'yyin'; did you mean 'yyg'?
                if ( ! yyin )
                       ^~~~
                       yyg
token.yy.cc:807:23: note: 'yyg' declared here
    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
                      ^
token.yy.cc:822:4: error: use of undeclared identifier 'yyin'
                        yyin = stdin;
                        ^
token.yy.cc:827:10: error: use of undeclared identifier 'yyout'
                if ( ! yyout )
                       ^
token.yy.cc:829:4: error: use of undeclared identifier 'yyout'
                        yyout = stdout;
                        ^
token.yy.cc:837:23: error: use of undeclared identifier 'yyin'
                                yy_create_buffer( yyin,yyscanner);
                                                  ^
token.yy.cc:895:3: error: use of undeclared identifier 'YY_DO_BEFORE_ACTION'
                YY_DO_BEFORE_ACTION;
                ^
token.yy.cc:902:8: error: use of undeclared identifier 'yy_flex_debug'; did you mean 'yyget_debug'?
                if ( yy_flex_debug )
                     ^~~~~~~~~~~~~
                     yyget_debug
token.yy.cc:598:5: note: 'yyget_debug' declared here
int yyget_debug ( yyscan_t yyscanner );
    ^
token.yy.cc:908:45: error: use of undeclared identifier 'yytext'
                                         (long)yy_rule_linenum[yy_act],yytext );
                                                                        ^
token.yy.cc:911:14: error: use of undeclared identifier 'yytext'
                                         yytext );
                                         ^
token.l:12:3: error: use of undeclared identifier 'yylval'
{ yylval->emplace<int>(yy::parser::token::PLUS); return yy::parser::token::PLUS; }
  ^
token.l:13:3: error: use of undeclared identifier 'yylval'
{ yylval->emplace<int>(yy::parser::token::MINUS); return yy::parser::token::MINUS; }
  ^
token.l:14:3: error: use of undeclared identifier 'yylval'
{ yylval->emplace<int>(yy::parser::token::TIMES); return yy::parser::token::TIMES; }
  ^
token.l:15:3: error: use of undeclared identifier 'yylval'
{ yylval->emplace<int>(yy::parser::token::DIVIDE); return yy::parser::token::DIVIDE; }
  ^
token.l:16:3: error: use of undeclared identifier 'yylval'
{ yylval->emplace<int>(yy::parser::token::LPAREN); return yy::parser::token::LPAREN; }
  ^
token.l:17:3: error: use of undeclared identifier 'yylval'
{ yylval->emplace<int>(yy::parser::token::RPAREN); return yy::parser::token::RPAREN; }
  ^
token.l:18:3: error: use of undeclared identifier 'yylval'
{ yylval->emplace<int>(yy::parser::token::SEMICOLON); return yy::parser::token::SEMICOLON; }
  ^
token.l:19:3: error: use of undeclared identifier 'yylval'
{ yylval->emplace<int>(yy::parser::token::EQUAL); return yy::parser::token::EQUAL; }
  ^
token.l:21:3: error: use of undeclared identifier 'yylval'
{ yylval->emplace<std::string>(yytext); return yy::parser::token::ID; }
  ^
token.l:21:32: error: use of undeclared identifier 'yytext'
{ yylval->emplace<std::string>(yytext); return yy::parser::token::ID; }
                               ^
Fatal error: too many errors emitted,stopping Now [-ferror-limit=]
20 errors generated.
make: *** [token.yy.o] Error 1

您能帮我解决这些问题吗?

解决方法

好吧,我再次阅读了野牛手册,自己解决了这个问题...

bison c++ example中,我们可以看到yylex声明已重新定义:

// Give Flex the prototype of yylex we want ...
# define YY_DECL \
  yy::parser::symbol_type yylex (driver& drv)
// ... and declare it for the parser's sake.
YY_DECL;

这就是为什么我们可以在flex规则中编写如下内容:

return yy::parser::make_MINUS  (loc);