Sunday 24 June 2007

Lex和Yacc从入门到精通(3)--一个极其简单的lex和yacc程序

http://blog.csdn.net/pandaxcl/archive/2006/08/27/1127739.aspx


一个极其简单的lex和yacc程序




本文版权归熊春雷所有 ,我的邮箱:,欢迎大家和我讨论计算机方面 的问题,在我的博客上 面还写了很多其他的文档,有空来看看哦。如果转载,请保留此版权信息,并注 明出处。谢谢:)






摘要


在本章中,将会首先给出一个最基本的lex和yacc联合使用的框架,这个基本框架最主要的特点就是能够正确的被编译。在我学习lex和yacc的过程中经历了无数次的痛苦折磨,我发现一个一开始足够简单而且能够被正确编译的例子往往能够使 学习者增加学习的兴趣和信心。因此我的所有的文章都尽可能的采用这种方式进行描述。我写这些文档的最大的愿望就是希望能够减少新手学习的痛苦。希望自 己能够做到这一点!









1. 基本的lex文件






例 3.1. frame.l



%{
int yywrap(void);
%}
%%
%%
int yywrap(void)
{
        return 1;
}

lex文件和yacc文件都是被%%分成了上中下三个部分,在这个程序中的yywrap函数 需要说明一下:


yywrap

lex源文件中的yywrap函数是必须的!具体的原因就是因为给了这个函数实现之后就可以不需要依赖flex库了。具体yywrap的作用会在后面的章节应 用的时候进行解释。通常的做法就是直接返回1,表示输入已经结束了。









2. 基本的yacc文件






例 3.2. frame.y



%{
void yyerror(const char *s);
%}
%%
program:
        ;
%%
void yyerror(const char *s)
{
}
int main()
{
         yyparse();
        return 0;
}

如前所述,yacc文件被%%分成了上中下三个部分,在这个程序中有几个需要说明 的地方:


program

这是语法规则里面的第一个非终结符,注意上面的格式哦:“program”后 面紧跟着一个冒号“:”,然后换行之后有一个分号“;”,这表明这个 program是由空串组成的。至于什么是非终结符以及什么是终结符,还有什 么是语法规则都会在后面的章节中进行详细介 绍。


yyerror

从字面上就可以看出是一个处理错误的函数,在这里为空的原因是为了保 证代码尽可能的简洁! 实际上这个函数里面的代码通常只有一句输出语句 ,当然如果你喜欢还可以加入纠错代码,使你的解析器具备纠错能力:)


yyparse

其实这个函数是yacc生成的,所以你在代码里面可以直接使用。这个时候你可能会问:“yacc生成了yyparse函数,那么lex是不是也生成了什么函数呢?”,是的,lex生成的函数为yylex函数。实际上yyparse还间接调用 了yylex函数,可以在生成的C源文件中去核实。


main

每一个C/C++程序都必须的装备啊,少了怎么能行呢:)所以这个main函数你 可以放到任何的地方,当然要保证能够调用yyparse就可以了。但是通常的 做法就是将main函数放到yacc文件中。



从上面的yacc文件中还可以看出被%%分割成为的三个部分,第一部分中要写入 C/C++代码必须用%{和%}括起来;但是第三个部分就可以直接写入C/C++代码了,不需要任何的修饰;中间的那一部分就是yacc语法规则了。为了能够让这个最最简单的yacc源程序能够通过bison的编译必须要提供一个语法规则,这里给出了一个最简单的规则:一个program就是由空字符串构成的。实际上等于什么也没有做。呵呵,对啊,本章的目的就是为了能够编译通过lex和yacc源程序,并且也能够被C/C++编译器编译通过啊。现在是不是已经真的编译通过了呢 ,可以按照下面的编译步骤一步一步的来编译核实。



提示


对yacc的描述同样也适用于lex。


lex就是词法扫描器,yacc就是语法分析器,这是通用的说法;具体的实现有所不同GNU的lex就是flex,GNU的yacc就是bison。为了统一,所以在后面的文章中就只会用lex来表达词法扫描器,用yacc来表达语法分析器啦!









3. 用C语言编译器编译





下面是编译全过程记录,采用了我在第一章中所制作的lex和yacc转换环境:



D:\work\lex_yacc\chapter03>dir
驱动器 D 中的卷是 工作区
卷的序列号是 54D0-5FC0

D:\work\lex_yacc\chapter03 的目录

2006-09-25   20:27     <DIR>           .
2006-09-25   20:27     <DIR>           ..
2006-09-25   20:07                 71 frame.l
2006-09-25   20:20                144 frame.y
                2 个文件             215 字节
                2 个目录   7,785,578,496 可用字节

D:\work\lex_yacc\chapter03>flex frame.l

D:\work\lex_yacc\chapter03>dir
驱动器 D 中的卷是 工作区
卷的序列号是 54D0-5FC0

D:\work\lex_yacc\chapter03 的目录

2006-09-25   20:28     <DIR>           .
2006-09-25   20:28     <DIR>           ..
2006-09-25   20:07                 71 frame.l
2006-09-25   20:20                144 frame.y
2006-09-25   20:28             36,997 lex.yy.c
                3 个文件          37,212 字节
                2 个目录   7,785,537,536 可用字节

D:\work\lex_yacc\chapter03>bison -d frame.y

D:\work\lex_yacc\chapter03>dir
驱动器 D 中的卷是 工作区
卷的序列号是 54D0-5FC0

D:\work\lex_yacc\chapter03 的目录

2006-09-25   20:28     <DIR>           .
2006-09-25   20:28     <DIR>           ..
2006-09-25   20:07                 71 frame.l
2006-09-25   20:28             19,416 frame.tab.c
2006-09-25   20:28                 74 frame.tab.h
2006-09-25   20:20                144 frame.y
2006-09-25   20:28             36,997 lex.yy.c
                5 个文件          56,702 字节
                2 个目录   7,785,517,056 可用字节

D:\work\lex_yacc\chapter03>


过程 3.1. 总的来说就是如下的几个步骤:




  1. 将前面的例子frame.lframe.y保存成为相应的文件




  2. flex frame.l




  3. bison frame.y




  4. gcc frame.tab.c lex.yy.c






提示


实际上经过flex和bison的转换之后的C/C++源程序是可以直接在VC里面使用的!


上面的frame.tab.c是由bison编译frame.y产生的,而lex.yy.c则是由flex编译 frame.l产生的。




好了,一个最简单的lex和yacc程序已经完备了,因此这一章的目的也就已经达到了。在下一章里面将会对这里的框架例子进行扩充以适应自己特殊的需要,逐步逐步的实现一个分析C/C++源代码的工具程序,但是每一章的结尾都会尽可能的给出一个可以编译通过的lex和yacc源程序。本来也想给出一个计算器的源程序作为例子的,但是这样的资料已经很多了。这些资料往往不能够让自己说清楚问题,在自己的开发中还是会遇到千奇百怪的问题,因此为了让自己能够有机会解决一个新手在开发新程序中可能出现的问题,我也就找了一个我没有开发过的程序来让自己一步一步的解决这些问题 。我想这种方式也许是比较好的学习方式吧:)


本章完!

No comments:

Post a Comment