最近想自己写个轻量级的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
转载请注明出自高孝鑫的博客
首先需要解决的lex和yacc的基本使用的问题,跑通一个最简单的例子。
网上有很多lex和yacc的入门例子,但基本都一样,看完后还是不知道在程序里怎么调用它们来做解析。
主要不清楚的问题有:
1. 如何在lex和yacc间传递数据
通过lex中可以的yylval 和yacc中的$n 来相互传递数据。它们的类型可以通过宏
typedef struct Mail
{
} MAIL;
例如
test.l中一个匹配规制如下:
title
test.y中一个匹配规则如下:
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;
...
}
这个函数是完成一次解析的入口函数,除了传入要解析的字符串与长度外,还传入了用于保存解析结果的数据结构。 整个解析过程就是往这个传入的结构体进行填充和修改的过程。
3. 如何在自己的程序里调用yacc和lex来解析一个给定的字符串
输入主要是指给lex模块输入, 默认的lex输入buffer为文件buffer,即可以导入一个文件进行解析。但lex本身是支持多种输入方式的。 以下是一段示例代码,让lex和yacc来解析一个输入的字符串:
int parser_do(const char *str, int len, MAIL *m)
{
}
关于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
[/n]
[0-9]+
[a-zA-Z][a-zA-Z0-9]*
[/+/-/*///%]
.
%%
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
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)
{
}
int parser_do(const char *str, int len, MAIL *m)
{
}
---------------------------------------------------------------------------------------------------------
main.h
-----------------------------------------------------------------------------------------------------------
typedef struct Mail
{
} MAIL;
#define YYSTYPE MAIL
-------------------------------------------------------------------------------------------------------------
main.c
--------------------------------------------------------------------------------------------------------------
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "test.tab.h"
#include "main.h"
int main()
{
}
------------------------------------------------------------------------
编译命令
-------------------------------------------------------------------------------
bison -d test.y
flex -t test.l >test.c
gcc -g -o test3 main.c test.c test.tab.c
转载请注明出自高孝鑫的博客