lex 和 yacc 学习…

来源:互联网 发布:淘宝被保护自助开通 编辑:程序博客网 时间:2024/06/03 07:21
笔记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 }
其中% defineapi.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); returnTITLE;}
[/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
--------------------------------------------------------------------------------------------------------
% defineapi.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;
}
--------------------------------------------------------------------------------------------

编译命令同上一篇博客

转载请注明出自高孝鑫的博客


0 0
原创粉丝点击