我发现了三个教程,其中包括使用Bison在C ++中编写解析器:here,here和here。前两个不介绍如何使用类作为规则的返回类型。第三部分确实涵盖了它,但它似乎没有清楚地解释规定。
这是一个简单的parser.yy文件,它以这种方式使用类。
%{
#include <stdio.h>
extern FILE * yyin;
int yyerror(char *s)
{
fflush(stdout);
printf("error\n");
}
int yywrap(void) {
return 1;
}
int yyparse() { return 1; }
int main() {
yyparse();
return 1;
}
class myclass { int x; };
%}
%union {
int token;
myclass * mc;
}
%token <token> REGISTER
%type <mc> start_sym
%start start_sym
%%
start_sym :
REGISTER '+' { $$ = new myclass(); }
;
使用此输入可以毫无问题地运行Bison。我定义了一个简单的flex输入来使用它。但是,当我尝试编译它时,我收到错误:
$ g++ parser.tab.cc lex.yy.cc
In file included from lex.ll:11:0:
parser.yy:31:5: error: ‘myclass’ does not name a type
myclass * mc;
^
声明myclass的适当位置在哪里?
答案 0 :(得分:3)
错误来自你的lex生成的文件,并且是你没有在lex生成的文件可以看到它的地方放置你的类的定义的结果。将定义放在单独的头文件中并在扫描程序(flex)和解析器(bison)文件中包含该头文件是最简单的解决方案;此外,它是有道理的,因为可能还有其他编译单元 - 解析的使用者 - 也需要这些标题。
如果您使用的是相对现代版本的bison,对于类型定义的情况,实际上只需要扫描程序和解析器可见,而没有其他组件,就可以让bison将定义插入标题它通过将%code requires
块放入您的序言中生成的文件:
%code requires {
class myclass {
// ...
};
}
(您也可以使用%code provides
块,但通常您希望在定义语义类型union时可以使用这些定义,这是%code requires
的位置。)
有关详细信息,请参阅bison manual。
关于extern "C"
问题,如果你在你的野牛输入中包含flex生成的头文件,我认为这是不必要的。您也可能希望在flex输入中指定%option noyywrap
,除非您实际使用的是yywrap
。 yywrap
中包含的-lfl
具有"C"
链接,但据我所知,extern "C" yywrap
声明确实发生在flex生成的头文件中。
说了这么多,我需要承认我不会将C ++ API用于bison或flex。我更喜欢使用C接口;生成的文件可以使用C ++编译器(至少使用最新版本的bison和flex工具)进行干净编译,并且您可以在语义联合中使用指向C ++对象的指针而不会出现问题。
答案 1 :(得分:2)
我发现在没有编译错误的情况下我唯一可以声明类的地方是在一个单独的头文件(“myclass.h”)中。此头文件必须包含在parser.yy和scanner.ll中。在scanner.ll,它必须包含在parser.tab.hh之前。
此外,我没有编译器抱怨yyparse(),yylex()或yywrap()的唯一方法就是在myclass.h文件中包含这些函数的原型,并带有外部C链接,像这样:
#ifndef __MYCLASS_H_
#define __MYCLASS_H_
class myclass { int x; };
extern "C" {
int yyparse(void);
int yylex(void);
int yywrap(void);
}
#endif
如果有人知道更好的解决方案,请告诉我。