lex 和 yacc 学习笔记1

最近想自己写个轻量级的sql的解析器,所以开始学习lex和yacc,并记录其中碰到的主要问题。

首先需要解决的lex和yacc的基本使用的问题,跑通一个最简单的例子。
网上有很多lex和yacc的入门例子,但基本都一样,看完后还是不知道在程序里怎么调用它们来做解析。
主要不清楚的问题有:
          a. 如何在lex,yacc和自己的程序间传递数据
          b. 如何在自己的程序里调用lex和yacc来解析一个指定的字符串

1. 如何在lex和yacc间传递数据

通过lex中可以的yylval 和yacc中的$n 来相互传递数据。它们的类型可以通过宏  #define YYSTYPE MAIL 来指定。 MAIL是我自己定义的一个结构体
typedef struct Mail
{
  char title[10];
  int num;
  char body[50];
} MAIL;

例如

test.l中一个匹配规制如下:
title                                {showtitle(); strcpy(yylval.title, yytext); return TITLE;}

test.y中一个匹配规则如下:
  head: TITLE UNKNOWN NUM { strcpy(mail->title, $1.title); mail->num=$3.num;};

yylval和 $1都是MAIL类型, 首先在lex的token解析中把解析到的token值赋给了yylval,然后在yacc的语法分析中可以通过$n来使用这个匹配到的值。

2. 自己的程序如何和yacc还有lex交互

其实主要是和yacc交互。
可以在yacc中定义一个函数,例如:
MAIL *mail;
int parser_do(const char *str, int len, MAIL *m)
{
...
mail = m;  //这个mail在上面的例子中用于保存解析结果。
...
}

这个函数是完成一次解析的入口函数,除了传入要解析的字符串与长度外,还传入了用于保存解析结果的数据结构。 整个解析过程就是往这个传入的结构体进行填充和修改的过程。

3. 如何在自己的程序里调用yacc和lex来解析一个给定的字符串

输入主要是指给lex模块输入, 默认的lex输入buffer为文件buffer,即可以导入一个文件进行解析。但lex本身是支持多种输入方式的。 以下是一段示例代码,让lex和yacc来解析一个输入的字符串:

int parser_do(const char *str, int len, MAIL *m)
{
  mail = m;
  linenum=0;
  YY_BUFFER_STATE state = yy_scan_bytes(str, len);  //创建了一个给定字符串初始化的输入缓冲
  yy_switch_to_buffer(state);    //切换到这个输入缓冲
  yyparse();                                                      //开始yacc解析(yacc会调用lex做词法分析)
  yy_delete_buffer(state);          //释放这个新建的输入缓冲
  return 0;
}
关于yy_scan_bytes 和其他的输入方式可以查看 http://flex.sourceforge.net/manual/Multiple-Input-Buffers.html

这样我们就可以在自己写的程序里面调用这个parser_do方法来解析我们想解析的字符串了。


附录:
本文的代码如下:
test.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);

%}
%%
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");
}
int yywrap()
{
return 1;
}
------------------------------------------------------------------------------------
test.y
----------------------------------------------------------------------------------------
%{
#include
#include
#include
#include "main.h"

MAIL *mail;
typedef YY_BUFFER_STATE;
extern int linenum;
%}

%token SPACE LOF TITLE NUM STRING OP UNKNOWN

%%

mail: head UNKNOWN body
          | no;

head: TITLE UNKNOWN NUM { strcpy(mail->title, $1.title); mail->num=$3.num;};

body: STRING {strcpy(mail->body, $1.body);};

no : SPACE|UNKNOWN|OP;

%%

int yyerror(char *msg)
{
  printf("error: %s", msg);
  return 0;
}
int parser_do(const char *str, int len, MAIL *m)
{
  mail = m;
  linenum=0;
  YY_BUFFER_STATE state = yy_scan_bytes(str, len);
  yy_switch_to_buffer(state);
  yyparse();
  yy_delete_buffer(state);
  return 0;
}
---------------------------------------------------------------------------------------------------------
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;
}
------------------------------------------------------------------------
编译命令
-------------------------------------------------------------------------------
bison -d test.y
flex -t test.l >test.c
gcc -g -o test3 main.c test.c test.tab.c


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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值