笔记1中的实现是线程不可重入的,通过拜读《flex&bison》这本书学习并实践了线程可重入(reentrant)的实现(还是看正经的教科书来的靠谱)。 这里记录下。
1. flex的可重入
实例程序如下: test.l
---------------------------------------------------------------------------------
新浪博客的bug吧,
这里一贴代码就出显示的问题, 这段代码可以到我baidu的博客上看
http://hi.baidu.com/gao_dennis/item/e583244ee2d84a16896d103c
------------------------------------------------------------------------------------------------------
关键在于 参数yyscan_t scanner; 它可以理解为可重入parser一次解析的用户空间。 它包含了非可重入模式下的全局变量和缓存的本地版本。
我们可以通过yylex_init_extra 给scanner设置传参。 通用可以使用yy_scan_bytes来为输入缓存设置。
另外在.l文件中还需要做些修改:
首先 %option reentrant
告知flex这是要生成一个可重入版本的解析器;
然后 %option extra-type="struct pwc *" 告知flex解析器传参的类型。
最后 struct pwc *pp = yyextra; 取这个传参为本次解析的本地变量。 注意这句话的位置, 它位于规则块中:
%%
%{
struct pwc *pp = yyextra;
%}
title
{showtitle(); strcpy(pp->title, yytext);}
[/n]
{linenum++;}
[0-9]+
{ pp->num= atoi(yytext);}
[a-zA-Z][a-zA-Z0-9]* {printf("Var
: %s/n",yytext); strcpy(pp->body, yytext); }
[/+/-//%]
{printf("Op
: %s/n",yytext); return OP;}
.
{printf("Unknown : %c/n",yytext[0]); return UNKNOWN;}
%%
2. bison的可重入
bison的可重入要简单很多,主要是添上:
% define api.pure
%parse-param { struct pureparse *pp }
其中% define api.pure的作用是告知bison这是个可重入的程序,而%parse-param告知用户传参的类型,例如:
yyparse(&mypwc);
其中“&mypwc”的类型就是由 %parse-param来告知。
此外,可重入bison与非可重入版本调用yylex的方式也不一样。在非可重入版本中bison与yylex通过全局变量yylval来交互信息。在可重入bison中yylval将被作为参数传递给yylex
token = yylex(YYSTYPE *yylvalp);
注,如果有使用yyloc的话 token = yylex(YYSTYPE *yylvalp, YYLTYPE *yylocp);
3. bison + flex
示例代码:
test2.l
--------------------------------------------------------------------------
%{
#include "stdio.h"
#include "string.h"
#include "main.h"
#include "test.tab.h"
int linenum;
extern int parse_worker(const char *str, int len);
%}
%option noyywrap nodefault yylineno reentrant bison-bridge
%option header-file="test.l.h"
%%
%{
struct param *pp = yyextra;
%}
title
{showtitle(); strcpy(yylval->title, yytext); return TITLE;}
[/n]
{linenum++; return SPACE;}
[0-9]+
{ yylval->num= atoi(yytext); return NUM;}
[a-zA-Z][a-zA-Z0-9]* {printf("Var
: %s/n",yytext); strcpy(yylval->body, yytext); return STRING;}
[/+/-/*///%]
{printf("Op
: %s/n",yytext); return OP;}
.
{printf("Unknown : %c/n",yytext[0]); return UNKNOWN;}
%%
showtitle()
{
printf("----- Lex Example -----/n");
}
------------------------------------------------------------------------------------------------------------
区别在于
a. 添加了选项 %option bison-bridge, 用来生成和可重入版本bison调用方式相适应的yylex函数
int yylex (YYSTYPE * yylval_param ,yyscan_t yyscanner);
b. 也生成了一个头文件 test.l.h, 这个文件不能被包含在该.l文件中
c. yylval的类型变成了指针
(bison中的$n的类型由宏YYSTYPE来决定,而bison调用yylex时的传参是YYSTYPE *, 且新的yylex函数的第一个参数的类型也是YYSTYPE * )
然后是test.y
--------------------------------------------------------------------------------------------------------
% define api.pure
(这里 %和 define之间没有空格,因为显示的问题所以加上的)
%parse-param { struct param *pp }
%{
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "main.h"
#include "test.l.h"
#define YYLEX_PARAM pp->scaninfo
extern int linenum;
typedef struct param{
yyscan_t scaninfo;
MAIL *m;
} PARAM;
%}
%token SPACE LOF TITLE NUM STRING OP UNKNOWN
%%
mail: head UNKNOWN body
| no;
head: TITLE UNKNOWN NUM {strcpy(pp->m->title, $1.title); pp->m->num=$3.num;};
body: STRING {strcpy(pp->m->body, $1.body);};
no : SPACE|UNKNOWN|OP;
%%
int yyerror(struct param * pp, char *msg)
{
printf("error: %s", msg);
return 0;
}
int parser_do(const char *str, int len, MAIL *m)
{
PARAM mypwc = {NULL,NULL};
mypwc.m = m;
YY_BUFFER_STATE state;
yylex_init_extra(&mypwc, &mypwc.scaninfo);
linenum=0;
state = yy_scan_bytes(str, len, mypwc.scaninfo);
yy_switch_to_buffer(state, mypwc.scaninfo);
yyparse(&mypwc);
yy_delete_buffer(state, mypwc.scaninfo);
yylex_destroy( mypwc.scaninfo );
printf("\n gaotitle: %s, num: %d, body: %s\n", mypwc.m->title, mypwc.m->num, mypwc.m->body);
return 0;
}
-----------------------------------------------------------------------------------------------------------------
这里需要注意的是
#define YYLEX_PARAM pp->scaninfo
这个是指定bison调用 yylex时所传递的yyscan_t对象,这也是flex可重入的关键。
在这个例子中,我们把yyscan_t对象作为bison传入参数的一个属性, 由结构体
typedef struct param{
yyscan_t scaninfo;
MAIL *m;
} PARAM;
定义。
另外这里我们引用了flex生成的头文件test.l.h 主要是因为定义的结构体PARAM用到了yyscan_t。
然后是实例的剩余部分
main.h
------------------------------------------------------------------------------------
typedef struct Mail
{
char title[10];
int num;
char body[50];
} MAIL;
#define YYSTYPE MAIL
-----------------------------------------------------------------------------------------
main.c
----------------------------------------------------------------------------------------
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "test.tab.h"
#include "main.h"
int main()
{
char *text = "title 3 hello123";
MAIL mail;
//parse_worker((const char *)text, strlen(text));
parser_do((const char *)text, strlen(text), &mail);
printf("\ntitle:%s num:%d body:%s\n", mail.title, mail.num, mail.body);
return 0;
}
--------------------------------------------------------------------------------------------
编译命令同上一篇博客
转载请注明出自高孝鑫的博客
1. flex的可重入
实例程序如下: test.l
---------------------------------------------------------------------------------
新浪博客的bug吧,
这里一贴代码就出显示的问题, 这段代码可以到我baidu的博客上看
http://hi.baidu.com/gao_dennis/item/e583244ee2d84a16896d103c
------------------------------------------------------------------------------------------------------
关键在于 参数yyscan_t scanner; 它可以理解为可重入parser一次解析的用户空间。 它包含了非可重入模式下的全局变量和缓存的本地版本。
我们可以通过yylex_init_extra 给scanner设置传参。 通用可以使用yy_scan_bytes来为输入缓存设置。
另外在.l文件中还需要做些修改:
首先 %option reentrant
然后 %option extra-type="struct pwc *" 告知flex解析器传参的类型。
最后 struct pwc *pp = yyextra; 取这个传参为本次解析的本地变量。 注意这句话的位置, 它位于规则块中:
%%
%{
struct pwc *pp = yyextra;
%}
title
[/n]
[0-9]+
[a-zA-Z][a-zA-Z0-9]* {printf("Var
[/+/-//%]
.
%%
2. bison的可重入
bison的可重入要简单很多,主要是添上:
% define api.pure
%parse-param { struct pureparse *pp }
其中% define api.pure的作用是告知bison这是个可重入的程序,而%parse-param告知用户传参的类型,例如:
yyparse(&mypwc);
此外,可重入bison与非可重入版本调用yylex的方式也不一样。在非可重入版本中bison与yylex通过全局变量yylval来交互信息。在可重入bison中yylval将被作为参数传递给yylex
token = yylex(YYSTYPE *yylvalp);
注,如果有使用yyloc的话
3. bison + flex
示例代码:
test2.l
--------------------------------------------------------------------------
%{
#include "stdio.h"
#include "string.h"
#include "main.h"
#include "test.tab.h"
int linenum;
extern int parse_worker(const char *str, int len);
%}
%option noyywrap nodefault yylineno reentrant bison-bridge
%option header-file="test.l.h"
%%
%{
struct param *pp = yyextra;
%}
title
[/n]
[0-9]+
[a-zA-Z][a-zA-Z0-9]*
[/+/-/*///%]
.
%%
showtitle()
{
printf("----- Lex Example -----/n");
}
------------------------------------------------------------------------------------------------------------
区别在于
然后是test.y
--------------------------------------------------------------------------------------------------------
% define api.pure
%parse-param { struct param *pp }
%{
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "main.h"
#include "test.l.h"
#define YYLEX_PARAM pp->scaninfo
extern int linenum;
typedef struct param{
yyscan_t scaninfo;
MAIL *m;
} PARAM;
%}
%token SPACE LOF TITLE NUM STRING OP UNKNOWN
%%
mail: head UNKNOWN body
head: TITLE UNKNOWN NUM {strcpy(pp->m->title, $1.title); pp->m->num=$3.num;};
body: STRING {strcpy(pp->m->body, $1.body);};
no : SPACE|UNKNOWN|OP;
%%
int yyerror(struct param * pp, char *msg)
{
}
int parser_do(const char *str, int len, MAIL *m)
{
}
-----------------------------------------------------------------------------------------------------------------
这里需要注意的是
#define YYLEX_PARAM pp->scaninfo
这个是指定bison调用 yylex时所传递的yyscan_t对象,这也是flex可重入的关键。
在这个例子中,我们把yyscan_t对象作为bison传入参数的一个属性, 由结构体
typedef struct param{
yyscan_t scaninfo;
MAIL *m;
} PARAM;
定义。
另外这里我们引用了flex生成的头文件test.l.h 主要是因为定义的结构体PARAM用到了yyscan_t。
然后是实例的剩余部分
main.h
------------------------------------------------------------------------------------
typedef struct Mail
{
} MAIL;
#define YYSTYPE MAIL
-----------------------------------------------------------------------------------------
main.c
----------------------------------------------------------------------------------------
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "test.tab.h"
#include "main.h"
int main()
{
}
--------------------------------------------------------------------------------------------
编译命令同上一篇博客
转载请注明出自高孝鑫的博客