我遇到一个问题,我认为flex
和bison
生成的标头之间存在循环依赖关系。类型yyscan_t
在lex头中定义,并且在yacc头中需要。宏YYSTYPE
在yacc头中定义,并且在lex头中需要。无论我导入两个标题的顺序是哪一个,另一个都不会满意。
reentrant.lex:
%{
#include "reentrant.yacc.h"
%}
%option reentrant bison-bridge
%%
[0-9]+ { yylval->int_value = atoi(yytext); return INT_TERM; }
[a-zA-Z]+ { yylval->str_value = strdup(yytext); return STR_TERM; }
, { return COMMA; }
reentrant.yacc:
%{
// UN-COMMENT THE FOLLOWING LINE TO "FIX" THE CYCLE
//typedef void * yyscan_t
#include "reentrant.yacc.h"
#include "reentrant.lex.h"
void yyerror(yyscan_t scanner, char * msg);
%}
%define api.pure full
%lex-param {yyscan_t scanner}
%parse-param {yyscan_t scanner}
%union {
int int_value;
char * str_value;
}
%token <int_value> INT_TERM
%token <str_value> STR_TERM
%token COMMA
%type <int_value> int_non_term
%type <str_value> str_non_term
%%
complete : int_non_term str_non_term { printf(" === %d === %s === \n", $1, $2); }
int_non_term : INT_TERM COMMA { $$ = $1; }
str_non_term : STR_TERM COMMA { $$ = $1; }
%%
int main(void) {
yyscan_t scanner;
yylex_init(&scanner) ;
yyset_debug(1, scanner);
yydebug=1;
int val = yyparse(scanner);
yylex_destroy (scanner) ;
return val;
}
int yywrap (yyscan_t scanner) {
return 1;
}
void yyerror(yyscan_t scanner, char * msg) {
fprintf(stderr, msg);
}
GCC输出:
In file included from reentrant.yacc:5:0:
reentrant.yacc.h:74:14: error: unknown type name ‘yyscan_t’
int yyparse (yyscan_t scanner);
^~~~~~~~
使用的命令参数:
bison -vt --debug --defines=reentrant.yacc.h -o reentrant.yacc.c reentrant.yacc
flex -8 -d --header-file=reentrant.lex.h -o reentrant.lex.c reentrant.lex
gcc -Wall -Wno-unused-function -g reentrant.lex.c reentrant.yacc.c -o reentrant
软件版本
$ flex --version
flex 2.6.4
$ bison --version
bison (GNU Bison) 3.0.4
Written by Robert Corbett and Richard Stallman.
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
$ gcc --version
gcc (GCC) 6.3.1 20170306
Copyright (C) 2016 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
YYSTYPE:
在这里,您可以看到YYSTYPE
在yacc标头中定义并在lex标头中使用。
$ grep 'YYSTYPE' *.h
reentrant.lex.h:YYSTYPE * yyget_lval ( yyscan_t yyscanner );
reentrant.lex.h:void yyset_lval ( YYSTYPE * yylval_param , yyscan_t yyscanner );
reentrant.lex.h: (YYSTYPE * yylval_param , yyscan_t yyscanner);
reentrant.lex.h: (YYSTYPE * yylval_param , yyscan_t yyscanner)
reentrant.yacc.h:#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
reentrant.yacc.h:union YYSTYPE
reentrant.yacc.h:typedef union YYSTYPE YYSTYPE;
reentrant.yacc.h:# define YYSTYPE_IS_TRIVIAL 1
reentrant.yacc.h:# define YYSTYPE_IS_DECLARED 1
yyscan_t:
在这里,您可以看到yyscan_t
在lex标头中定义并在yacc标头中使用。
$ grep 'yyscan_t' *.h
reentrant.lex.h:typedef void* yyscan_t;
<snip lots of function decls including yyscan_t>
reentrant.yacc.h:int yyparse (yyscan_t scanner);
答案 0 :(得分:1)
这是一个非答案,真的,但我看到这个问题没有得到应有的关注,所以我发布这是一个令人沮丧的提醒,这是野牛设计中的这个严重缺陷。
不可能(至少在当前版本的bison / flex中)干净地包含适当的头文件。原因是bison生成的* .h文件的结构(与%union
声明所代表的完全相同):union YYSTYPE {...} YYSTYPE;
声明后面紧跟int yyparse( yyscanner_t yyscanner);
声明{1}}(除非您将前缀yy
更改为其他内容)。由于没有机制可以在两个声明之间插入flex 生成的词法定义文件,无论lexer定义包含在哪里,冲突都是不可避免的,无论解析器定义是否包括在内。将lexer * .h文件放在解析器之前(或%union
声明之前),gcc会抱怨不知道YYSTYPE
是什么。在---之后包含它,编译器将不知道yyscanner_t
声明中yyparse
的含义。
除非bison分别输出其%defines
文件的不同块,否则不清楚如何解决这个问题。解决此问题的一种实用方法是在将yyscanner_t
定义为void *
之后首先包含解析器定义(无论是宏还是typedef
,无关紧要,两者都有效,两者都是一样的丑陋),然后是flex生成的定义文件。