http://flex.sourceforge.net/manual/ html
说明:发现这个manual太罗嗦了,要是按照它的思路下去,个人笔记又成为了翻译。果断跳过,仍是从例子开始来学习比较好,先弄出例子,而后看须要什么功能,再翻这个文档,再理解就是了。linux
1. hello,worldgit
先用一个最简单的例子看看flex究竟是什么玩意。正则表达式
根据前面的内容,写一个flex的输入文件以下:ide
/* filename: input.lex */ /* Definitions Section */ %% /* Rules Section */ %% /* User Code Section */
很显然,所有都是注释。而后运行一下“flex input.lex"会提示错误。缘由是注释的问题,rules部分的第一行要求为正则表达式,直接写注释有问题。参考前面的内容知道,为了简化,注释开头最好都是另起一行,同时最好/*前面有一个以上空格或tab。既然如此,之后就follow这个规则,防止出错(第一行我就不follow了,看起来太丑了)。函数
/* filename: input.lex */ /* Definitions Section */ %% /* Rules Section */ %% /* User Code Section */ /* main */
而后运行flex input.lex,没有错误,获得一个输出为:lex.yy.c的C语言文件。打开生成的文件看看,内容还挺多,可是能够找到上面几个注释都被生成在了输出的文件中。而后用gcc编译lex.yy.c,发现有错误,没有定义main函数。因此,修改输入的lex文件以下:
/* filename: input.lex */ /* Definitions Section */ %% /* Rules Section */ %% /* User Code Section */ /* main */ int main() { printf("Hello, world\n"); return 0; }
一样生成lex.yy.c而后编译,仍是出错,提示yywrap没有定义。关于yywrap函数,Google一下就知道缘由了。具体就不解释了。总之,就目前而言,将其实现一下就能够了。有几种方法能够避免这个问题,好比使用gcc lex.yy.c -lfl编译(即连接到flex的库,其中有yywrap的默认实现)还能够本身实现如下,目前返回1便可。后面再来理解这个函数的做用。最终的hello,world的输入文件以下:
/* filename: input.lex */ /* Definitions Section */ %% /* Rules Section */ %% /* User Code Section */ int yywrap() { return 1;} /* main */ int main() { printf("Hello, world\n"); return 0; }
运行以下:工具
#ls input.lex #flex input.lex #ls input.lex lex.yy.c #gcc lex.yy.c #./a.out Hello, world #
经过hello,world大概就更容易了解flex是什么了,其输入有一些什么,输出又是什么,涉及到哪些工具等。
2. 理解definition section&user code section学习
/* filename: input.lex */ /* Definitions Section */ /* usage of %top */ %top{ /* This code goes at the "top" of the generated file. */ #include <stdint.h> #include <inttypes.h> } /* usage of %{ %} */ %{ // This part will also be copied to the output int a = 1; %} /* usage of definitions section, "name definition" */ digit [0-9] number {digit}+ letter [a-zA-Z] identifier {letter}+ newline n whitespace [ t]+ %% /* Rules Section */ %% /* User Code Section */ int yywrap() { return 1;} /* main */ int main() { printf("Hello, world, %d\n", a); return 0; }
使用flex和gcc运行上面的input.lex文件,分析来理解definition section&user code section。PS:main函数自己就属于user code section里面了。这里的definition section中digit等定义在生成的文件中是没有对应内容的,这些内容须要结合其它内容一块儿使用才有意义,这里主要了解%top和%{%}的做用和效果。
3. 理解rules sectionflex
到这里,已经大概知道flex是怎么回事了,其实就是根据咱们的输入.lex文件定义的一些内容,生成一个扫描器,用这个扫描器进行词法分析。spa
那么第一个问题是:扫描器如何启动?即如何开始扫描?如何指定要扫描哪个文件?答案是,flex的输出lex.yy.c中其实就实现了一些函数,还有一些变量也是能够被使用的,好比extern FILE *yyin, *yyout;就是用于指定扫描器输入输出文件的,因此咱们只须要在咱们的user code section里面对这些变量赋值就能够了。(其实也不必定非要在user code section了,在definition section里面用%top或%{%}也是能够的啦,应该很容易理解了,固然rules section里面也可使用%{%}的内容了)
下一个问题是:rules section是干吗的?rules section就是指定扫描器工做的过程了,里面定义一些pattern,当扫描器扫描的时候知足的时候就执行一些action,这样就能完成整个扫描了。
其实,definition sections部分的定义就是为了给rules section的pattern用的,定义一些正则表达式,而后用在这里,使得其看起来简洁。前面说过,用{name}就能够引用definition sections里面的定义了。
下面的问题就是lex中pattern即模式是如何定义的,以及如何匹配的,还有actions能够为一些什么内容。这也是lex的核心内容。
4. 模式(正则表达式)
可参考:
https://www.ibm.com/developerworks/cn/linux/sdk/lex/#2
http://flex.sourceforge.net/manual/Patterns.html#Patterns
其正则表达式规则应该是GNU的正则表达式规则,对于lex,经常使用的一些正则表达式其实就那么一些,查看一下手册就知道怎么写了。
5. 匹配规则
http://flex.sourceforge.net/manual/Matching.html#Matching
当生成的扫描器运行的时候,它会分析输入并查找字符串来进行模式的匹配。若是它找到超过一个匹配,它就会选择匹配最长的内容。若是它找到两个或多个一样长度的匹配,那么rules section中列举在前面的被选择。
一旦匹配了,知足该匹配的文字(即token)就会保存在lex的全局变量yytext中(char*指针类型),它的长度保存在yyleng中。而后,其对应的action会被执行,而后为下一个匹配继续扫描剩下的输入。若是没有找到匹配,那么default的rule被执行:即输入的下一个字符来考虑进行匹配并复制到标准输出中。因此,最简单的做为flex的输入的是:
%%
这样会生成一个扫描器,会将全部的输入复制到输出中。
6. 匹配的action
每个rule中匹配的模式都有一个对应的action。action能够为任意的C语句。若是action为空,那么匹配的token就直接被删除。
若是一个action只有一个"|"符号,那么表示“与下一个rule的action相同"。
a |
ab |
abc {return 0;}
即表示对于token为a或ab或abc,都执行return 0.
须要说明的是,action里面能够是任意的C代码,包括return语句,那么return语句是返回给谁呢?是返回一个值到yylex()函数。每一次yylex()被调用的时候,它从上次扫描离开的地方继续处理,知道达到文件结束或者执行到return。另外,action也能够修改yytext和yyleng等lex的变量,可是有一些相关的规则。
有一些特殊的指令,能够被用于action中:
ECHO:复制yytext到扫描器的输出
BEGIN:后面紧接一个start condition(开始条件)的名字,将扫描器放置到对应的开始条件中(后面继续理解开始条件)。
REJECT:告诉扫描器继续执行能匹配该模式的”第二好“的规则。简单理解,拒绝嘛。就是忽略这个匹配,继续考虑是否有其余的rules能匹配的,执行其它的操做。好比:
%%
special special();REJECT;
spec dosth();
这样,首先,当输入有special时候,根据前面的匹配,因为special是最长的匹配,因此匹配到这一个,这时候会执行special(),而后遇到了REJECT,就会让扫描器对这个"special"的匹配拒绝,就会从新匹配一个“第二好"的,那么就能匹配到"specia"知足匹配,就会执行dosth()的action。固然,这里两行交换顺序并不会影响这个流程。若是没有REJECT,只会匹配到special的,不会匹配spec。
7. Lex的函数、宏、变量、数据类型、钩子、选项等等
http://flex.sourceforge.net/manual/Indices.html#Indices
这里能够查到这些信息,经常使用的一些函数和变量要了解。
可参考此文(https://www.ibm.com/developerworks/cn/linux/sdk/lex/#8)了解几个最经常使用的变量和函数。
经常使用的正则表达式模式: http://flex.sourceforge.net/manual/Common-Patterns.html#Common-Patterns