lex 和 yacc 学习…

之前给DBScale写的sql解析器并没有处理token的位置信息,这样在重构sql语句的时候非常的麻烦。
本文将说明如何在可重入的bison和flex中添加token的位置信息,即yylloc.

flex文件修改:
首先需要在flex文件中添加选项:
%bison-locations
开启flex对位置信息的支持。

其次,因为flex或者lex会自动维护行的信息(yylineno), 但不会自动为你维护列的信息, 我们需要自己维护。
参考《flex & bison》这本书第8章中的介绍,我们可以使用 YY_USER_ACTION 这个宏(书中的例子不能直接用,因为那个是单线程模式下的例子)。
flex在匹配到一个token之后,在返回给bison之前,会调用YY_USER_ACTION

在flex文件第一部分中添加 YY_USER_ACTION的定义:
%{
#define YY_USER_ACTION yylloc->first_line = yylloc->last_line = yylineno;\
  yylloc->first_column = sp->column; yylloc->last_column = sp->column+yyleng-1;\
  sp->column += yyleng;
%}

上面的sp->column是我添加进去的,用来记录本次sql语句解析过程中当前token匹配位置的。
它的值在本次sql语句匹配过程中被每次token匹配使用和修改。
而sp是yylex_init_extra 初始化时,设置的yylex外部参数。

bison文件修改:
首先是添加选项:
%locations

其次是修改yyerror的函数定义:
int yyerror(YYLTYPE *l, struct param *pp , const char *s, ...);

这里的YYLTYPE是我们重定义的位置信息结构体:
typedef struct location {
  int first_line;
  int first_column;
  int last_line;
  int last_column;
} location_value;
#define YYLTYPE location_value

修改后的示例:
test3.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);

#define YY_USER_ACTION yylloc->first_line = yylloc->last_line = yylineno;\
  yylloc->first_column = sp->column; yylloc->last_column = sp->column+yyleng-1;\
  sp->column += yyleng;

%}
 

%option noyywrap nodefault yylineno reentrant bison-bridge bison-locations
%option header-file="test.l.h"


%%
%{
scan_param *sp = (scan_param *)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");
}

------------------------------------------------------------------------------------------------------------

test3.y
-------------------------------------------------------------------------------------------------------------
% define api.pure 
                  (这里 %和 define之间没有空格,因为显示的问题所以加上的)
%locations
%parse-param { struct param *pp }

%{
#include
#include
#include
#include "main.h"
#include "test.l.h"

#define YYLEX_PARAM pp->scaninfo

extern int linenum;
typedef struct param{
yyscan_t scaninfo;
MAIL *m;
scan_param *sp;
} 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( YYLTYPE *l, struct param * pp, char *msg)
{
  printf("error: %s", msg);
  return 0;
}

int parser_do(const char *str, int len, MAIL *m)
{
  PARAM mypwc ;
  mypwc.m = m;
sp.column = 1;
YY_BUFFER_STATE state;
scan_param sp;
mypwc.sp = &sp;
 
yylex_init_extra(mypwc.sp, &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;
}
-----------------------------------------------------------------------------------------------------------------
main.h
------------------------------------------------------------------------------------
typedef struct Mail
{
 
char title[10];
  int num;
  char body[50];
} MAIL;

typedef struct scan_param
{
   int column;
} scan_param;


typedef struct location {
  int first_line;
  int first_column;
  int last_line;
  int last_column;
} location_value;


#define YYSTYPE MAIL
#define YYLTYPE location_value
-----------------------------------------------------------------------------------------

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
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值