可重入 Flex 和 Bison 的问题
Problems with reentrant Flex and Bison
我正在学习如何结合使用可重入的 Bison 和 Flex。我已经有了一个没有重入功能的简单计算器。然而,当我激活可重入功能并进行必要的修改时,我无法让它工作。
代码如下:
scanner.l
%{
#include <stdio.h>
#include "parser.tab.h"
%}
%option 8bit reentrant bison-bridge
%option warn noyywrap nodefault
%option header-file="lex.yy.h"
DIGIT [0-9]
%%
"+" { return ADD; }
"-" { return SUB; }
"*" { return MUL; }
"/" { return DIV; }
{DIGIT}+ { *yylval = atof(yytext); return NUM; }
\n { return EOL; }
[ \t] { }
. { printf("What is this: %s.\n", yytext); }
%%
parser.y
%{
#include <stdio.h>
#include "lex.yy.h"
void yyerror(yyscan_t scanner, char const *msg);
%}
%define api.value.type {double}
%define parse.error verbose
%define api.pure
%lex-param {yyscan_t scanner}
%parse-param {yyscan_t scanner}
%token NUM EOL
%left ADD SUB
%left MUL DIV
%%
input: %empty
| input line
;
line: EOL { printf("|> ");}
| exp EOL { printf("|R> %.4lf\n", $exp); }
;
exp: NUM { $$ = ; }
| exp ADD exp { $$ = + ; }
| exp SUB exp { $$ = - ; }
| exp MUL exp { $$ = * ; }
| exp DIV exp { $$ = / ; }
;
%%
void yyerror(yyscan_t scanner, char const *msg) {
fprintf(stderr, "Error: %s\n", msg);
}
main.c
#include <stdio.h>
#include "parser.tab.h"
#include "lex.yy.h"
int main(void) {
yyscan_t scanner;
yylex_init(&scanner);
yyset_in(stdin, scanner);
yyparse(scanner);
yylex_destroy(scanner);
return 0;
}
这是我正在使用的Makefile
:
all: calc.x
parser.tab.c parser.tab.h: parser.y
bison -d parser.y
lex.yy.c lex.yy.h: scanner.l parser.tab.h
flex scanner.l
calc.x: lex.yy.c lex.yy.h parser.tab.c parser.tab.h
gcc main.c parser.tab.c lex.yy.c -o calc.x
clean:
rm calc.x lex.yy.c lex.yy.h parser.tab.c parser.tab.h *.o
运行 make
, 我得到以下错误:
In file included from main.c:2:0:
parser.tab.h:66:14: error: unknown type name ‘yyscan_t’
int yyparse (yyscan_t scanner);
^
main.c: In function ‘main’:
main.c:12:3: warning: implicit declaration of function ‘yyparse’ [-Wimplicit-function-declaration]
yyparse(scanner);
^
In file included from parser.y:5:0:
lex.yy.h:282:1: error: unknown type name ‘YYSTYPE’
YYSTYPE * yyget_lval (yyscan_t yyscanner );
^
lex.yy.h:284:18: error: unknown type name ‘YYSTYPE’
void yyset_lval (YYSTYPE * yylval_param ,yyscan_t yyscanner );
^
lex.yy.h:332:17: error: unknown type name ‘YYSTYPE’
(YYSTYPE * yylval_param ,yyscan_t yyscanner);
^
parser.tab.c: In function ‘yyparse’:
parser.tab.c:1130:16: warning: implicit declaration of function ‘yylex’ [-Wimplicit-function-declaration]
yychar = yylex (&yylval, scanner);
^
Makefile:10: recipe for target 'calc.x' failed
make: *** [calc.x] Error 1
但是我不明白这个错误和警告消息的来源,例如:
main.c:12:3: warning: implicit declaration of function ‘yyparse’
但是 yyparse
已经在 parser.tab.h
中定义并且包含在 main.c
中。另一个例子:
parser.tab.h:66:14: error: unknown type name ‘yyscan_t’
在 parser.y
里面,我包括了扫描仪 header lex.yy.h
。
我在网上找到了这些解决方案:
- Configuring Bison and Flex without global or static variable
- Reentrant parsers with Flex and Bison
- Make a reentrant parser with Flex and Bison
- Writing re-entrant lexer with Flex
- Implementing re-entrant parsers in Bison and Flex
但其中 none 有效,导致类似的错误。如果有人能在这个任务中指导我,我将不胜感激。
软件版本
OS:Debian(测试),Bison:3.0.4,Flex:2.5.39,GCC:5.2.1,Make:4.0.
经过一番修修补补,我找到了解决办法。所以问题出在 flex 和 bison 之间的循环依赖。
解析器以这种方式生成调用 flex 例程:
yychar = yylex (&yylval, scanner);
所以在 bison 输入中我们必须包含扫描仪 header 文件 lex.yy.h
它定义为:
int yylex (YYSTYPE * yylval_param ,yyscan_t yyscanner);
但是 YYSTYPE
是在解析器 header parser.tab.h
内部定义的,在我的例子中,我对 bison 说我的类型是 double
:
typedef double YYSTYPE;
现在是解决方案。在 scanner.l
中,您必须包含解析器 headers 以便 flex 可以 return 更正标记(未更改)。
但是在 parser.y
中你必须包含两个 headers 文件,如果你只包含 lex.yy.h
它会抱怨:
lex.yy.h:282:1: error: unknown type name ‘YYSTYPE‘
因为YYSTYPE
定义在parser.tab.h
里面。最后,出于某种原因,野牛解析器不知道 yyscan_t
甚至包括词法分析器 header:
error: unknown type name ‘yyscan_t’
一种解决方法是将其定义为无效:
%lex-param {void *scanner}
%parse-param {void *scanner}
参见yyscan_t
定义:flex yyscan_t
所以这是最终结果:
scanner.l
%{
#include <stdio.h>
#include "parser.tab.h"
%}
%option 8bit reentrant bison-bridge
%option warn noyywrap nodefault
%option header-file="lex.yy.h"
//rest of the scanner
parser.y
%{
#include <stdio.h>
#include "parser.tab.h"
#include "lex.yy.h"
void yyerror(yyscan_t scanner, char const *msg);
%}
%define api.value.type {double}
%define parse.error verbose
%define api.pure
%lex-param {void *scanner}
%parse-param {void *scanner}
//rest of the input
main.c
#include <stdio.h>
#include "parser.tab.h"
#include "lex.yy.h"
int main(void) {
yyscan_t scanner;
yylex_init(&scanner);
yyset_in(stdin, scanner);
yyparse(scanner);
yylex_destroy(scanner);
return 0;
}
Fabricio Sanches 接受的回答帮助我解决了两个问题:
error: unknown type name ‘yyscan_t’
确实改成void *
就解决了。
与循环依赖相关的冲突决定了非常严格的导入顺序:
您的代码,其中 Flex/Bison 的 yyparse 被调用:
#import "parser.h"
#import "lexer.h"
弹性(Lexer.lm):
%{
#import "parser.h"
%}
野牛(Parser.ym):
%{
#import "parser.h"
#import "lexer.h"
%}
我写了博客 post 关于在 Mac OS 上使用 Flex 和 Bison 创建可重入解析器的过程,并举例说明了它与 Xcode 项目的集成: Reentrant parser using Flex and Bison。
我正在学习如何结合使用可重入的 Bison 和 Flex。我已经有了一个没有重入功能的简单计算器。然而,当我激活可重入功能并进行必要的修改时,我无法让它工作。
代码如下:
scanner.l
%{
#include <stdio.h>
#include "parser.tab.h"
%}
%option 8bit reentrant bison-bridge
%option warn noyywrap nodefault
%option header-file="lex.yy.h"
DIGIT [0-9]
%%
"+" { return ADD; }
"-" { return SUB; }
"*" { return MUL; }
"/" { return DIV; }
{DIGIT}+ { *yylval = atof(yytext); return NUM; }
\n { return EOL; }
[ \t] { }
. { printf("What is this: %s.\n", yytext); }
%%
parser.y
%{
#include <stdio.h>
#include "lex.yy.h"
void yyerror(yyscan_t scanner, char const *msg);
%}
%define api.value.type {double}
%define parse.error verbose
%define api.pure
%lex-param {yyscan_t scanner}
%parse-param {yyscan_t scanner}
%token NUM EOL
%left ADD SUB
%left MUL DIV
%%
input: %empty
| input line
;
line: EOL { printf("|> ");}
| exp EOL { printf("|R> %.4lf\n", $exp); }
;
exp: NUM { $$ = ; }
| exp ADD exp { $$ = + ; }
| exp SUB exp { $$ = - ; }
| exp MUL exp { $$ = * ; }
| exp DIV exp { $$ = / ; }
;
%%
void yyerror(yyscan_t scanner, char const *msg) {
fprintf(stderr, "Error: %s\n", msg);
}
main.c
#include <stdio.h>
#include "parser.tab.h"
#include "lex.yy.h"
int main(void) {
yyscan_t scanner;
yylex_init(&scanner);
yyset_in(stdin, scanner);
yyparse(scanner);
yylex_destroy(scanner);
return 0;
}
这是我正在使用的Makefile
:
all: calc.x
parser.tab.c parser.tab.h: parser.y
bison -d parser.y
lex.yy.c lex.yy.h: scanner.l parser.tab.h
flex scanner.l
calc.x: lex.yy.c lex.yy.h parser.tab.c parser.tab.h
gcc main.c parser.tab.c lex.yy.c -o calc.x
clean:
rm calc.x lex.yy.c lex.yy.h parser.tab.c parser.tab.h *.o
运行 make
, 我得到以下错误:
In file included from main.c:2:0:
parser.tab.h:66:14: error: unknown type name ‘yyscan_t’
int yyparse (yyscan_t scanner);
^
main.c: In function ‘main’:
main.c:12:3: warning: implicit declaration of function ‘yyparse’ [-Wimplicit-function-declaration]
yyparse(scanner);
^
In file included from parser.y:5:0:
lex.yy.h:282:1: error: unknown type name ‘YYSTYPE’
YYSTYPE * yyget_lval (yyscan_t yyscanner );
^
lex.yy.h:284:18: error: unknown type name ‘YYSTYPE’
void yyset_lval (YYSTYPE * yylval_param ,yyscan_t yyscanner );
^
lex.yy.h:332:17: error: unknown type name ‘YYSTYPE’
(YYSTYPE * yylval_param ,yyscan_t yyscanner);
^
parser.tab.c: In function ‘yyparse’:
parser.tab.c:1130:16: warning: implicit declaration of function ‘yylex’ [-Wimplicit-function-declaration]
yychar = yylex (&yylval, scanner);
^
Makefile:10: recipe for target 'calc.x' failed
make: *** [calc.x] Error 1
但是我不明白这个错误和警告消息的来源,例如:
main.c:12:3: warning: implicit declaration of function ‘yyparse’
但是 yyparse
已经在 parser.tab.h
中定义并且包含在 main.c
中。另一个例子:
parser.tab.h:66:14: error: unknown type name ‘yyscan_t’
在 parser.y
里面,我包括了扫描仪 header lex.yy.h
。
我在网上找到了这些解决方案:
- Configuring Bison and Flex without global or static variable
- Reentrant parsers with Flex and Bison
- Make a reentrant parser with Flex and Bison
- Writing re-entrant lexer with Flex
- Implementing re-entrant parsers in Bison and Flex
但其中 none 有效,导致类似的错误。如果有人能在这个任务中指导我,我将不胜感激。
软件版本
OS:Debian(测试),Bison:3.0.4,Flex:2.5.39,GCC:5.2.1,Make:4.0.
经过一番修修补补,我找到了解决办法。所以问题出在 flex 和 bison 之间的循环依赖。
解析器以这种方式生成调用 flex 例程:
yychar = yylex (&yylval, scanner);
所以在 bison 输入中我们必须包含扫描仪 header 文件 lex.yy.h
它定义为:
int yylex (YYSTYPE * yylval_param ,yyscan_t yyscanner);
但是 YYSTYPE
是在解析器 header parser.tab.h
内部定义的,在我的例子中,我对 bison 说我的类型是 double
:
typedef double YYSTYPE;
现在是解决方案。在 scanner.l
中,您必须包含解析器 headers 以便 flex 可以 return 更正标记(未更改)。
但是在 parser.y
中你必须包含两个 headers 文件,如果你只包含 lex.yy.h
它会抱怨:
lex.yy.h:282:1: error: unknown type name ‘YYSTYPE‘
因为YYSTYPE
定义在parser.tab.h
里面。最后,出于某种原因,野牛解析器不知道 yyscan_t
甚至包括词法分析器 header:
error: unknown type name ‘yyscan_t’
一种解决方法是将其定义为无效:
%lex-param {void *scanner}
%parse-param {void *scanner}
参见yyscan_t
定义:flex yyscan_t
所以这是最终结果:
scanner.l
%{
#include <stdio.h>
#include "parser.tab.h"
%}
%option 8bit reentrant bison-bridge
%option warn noyywrap nodefault
%option header-file="lex.yy.h"
//rest of the scanner
parser.y
%{
#include <stdio.h>
#include "parser.tab.h"
#include "lex.yy.h"
void yyerror(yyscan_t scanner, char const *msg);
%}
%define api.value.type {double}
%define parse.error verbose
%define api.pure
%lex-param {void *scanner}
%parse-param {void *scanner}
//rest of the input
main.c
#include <stdio.h>
#include "parser.tab.h"
#include "lex.yy.h"
int main(void) {
yyscan_t scanner;
yylex_init(&scanner);
yyset_in(stdin, scanner);
yyparse(scanner);
yylex_destroy(scanner);
return 0;
}
Fabricio Sanches 接受的回答帮助我解决了两个问题:
error: unknown type name ‘yyscan_t’
确实改成void *
就解决了。与循环依赖相关的冲突决定了非常严格的导入顺序:
您的代码,其中 Flex/Bison 的 yyparse 被调用:
#import "parser.h"
#import "lexer.h"
弹性(Lexer.lm):
%{
#import "parser.h"
%}
野牛(Parser.ym):
%{
#import "parser.h"
#import "lexer.h"
%}
我写了博客 post 关于在 Mac OS 上使用 Flex 和 Bison 创建可重入解析器的过程,并举例说明了它与 Xcode 项目的集成: Reentrant parser using Flex and Bison。