lex 和 yacc 学习笔记1

来源:互联网 发布:淘宝被保护自助开通 编辑:程序博客网 时间:2024/04/28 20:21
最近想自己写个轻量级的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


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

0 0