lex 和 yacc 学习…

笔记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;
}
--------------------------------------------------------------------------------------------

编译命令同上一篇博客

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


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值