Flex(快速词法分析器生成器)是lex的免费开源软件替代品。它是生成词法分析器(也称为“扫描器”或“词法分析器”)的计算机程序。扫描仪是识别文本中的词汇模式的程序。该flex 程序读取给定的输入文件,或它的标准输入,如果没有指定文件名,为扫描仪产生的描述。描述采用成对的正则表达式和C代码(称为rules)形式。flex生成一个C源文件作为输出, 文字库默认情况下,它定义了一个例程yylex()。可以编译该文件并将其与Flex运行时库链接以生成可执行文件。运行可执行文件时,它将分析其输入以查找正则表达式的出现。只要找到一个,它就会执行相应的C代码。经常连同Yacc或GNU Bison一起使用。
Flex由三部分组成
- 定义部分
- %%
- 规则部分
- %%
- 用户附加的C语言部分
下面将以PostgreSQL中的scan.l为例,解释Flex的部分规则:
定义部分
代码部分
%top{ ... },%{ ... %},内容都是C代码,并将代码照搬到生成的C文件中。
- %top{ ... }则是将其中的代码拷贝到C文件中的顶部。这里我们可以加入一些注释或者#inlcude。
- %{ ... %}中可以定义一些函数声明,结构体,类型等,还可以重定义一些Flex的宏,从而改变程序的规则。
选项%option
flex 提供一个机制用来在扫描器的说明中,而不是在flex 命令中控制选项。
以下是san.l中定义的option:
- %option reentrant: flex能够生成一个可重入的扫描器。通过定义%option reentrant(与-R选项等价)来实现可重入。所生成的扫描器在一个或多个控制线程中不仅可移植,而且安全性好。可重入扫描器通常应用于多线程应用程序。任何一个线程都可以在不考虑与其他线程同步的情况下创建并执行一个可重入的flex扫描器。默认情况下,flex生成一个不可重入的扫描器。本项目为了实现多线程,因而在定义段指定%option reentrant.
- %option bison-bridge: flex和bison联合使用的选项,由于flex和bison部分内容声明不同,使用此选项后可以相互调用。
- %option bison-locations: 此模式同上面参数同时使用,如果做了此声明,yylex 将被声明为int yylex (YYSTYPE* lvalp, YYLTYPE* llocp, yyscan_t scaninfo);加入了location参数.
- %option 8bit: 识别其输入中8位字符的扫描器。
- %option never-interactive: flex 生成的扫描器从不认为输入是交互的(不会调用isatty()
- %option nodefault: 所有没有被匹配的输入都拷贝到yyoutflex允许在添加%option nodefault,使它不要添加默认的规则这样输入无法被给定的规则完全匹配时,词法分析器可以报告一个错误。
- %option noinput:不使用默认的input函数
- %option nounput: 添加Flex默认的C函数,比如yy_scan_buffer,yy_scan_bytes,yy_scan_string
- %option noyywrap: 不使用yywrap(),当lex读取到文件末尾时,会调用yywrap(), 目的是,当有另外一个输入文件时,yywrap可以调整yyin的值并且返回0来重新开始词法分析。如果是真正的文件末尾,那么就返回1来完成分析。
- %option noyyalloc,%option noyyrealloc,%option noyyfree:不使用flex自带的函数yyalloc,yyrealloc,yyfree(内存的申请,重申请以及释放)
- %option warn: 开启所有警告
- %option prefix="core_yy": 可以将原来的yylex等函数 变成core_yylex.这样可以在一个程序中建立多个词法分析器。
状态定义
PostgreSQL对引号字符串,扩展注释使用开始状态,并消除了数字字符串的解析麻烦。开始状态代表进入一个特定的状态,在规则段只有定义了特定状态的规则才会匹配,这种规则通过<start stat>来标识。
状态定义:
- <xb>位字符串文字
- <xc>扩展的C样式注释
- <xd>分隔标识符(双引号标识符)
- <xh>十六进制数字字符串
- <xq>标准带引号的字符串
- <xe>扩展的带引号的字符串(支持反斜杠转义序列)
- <xdolq> $ foo $用引号引起来的字符串
- <xui>带Unicode转义的带引号的标识符
- <xuiend>用Unicode转义引起的带引号的标识符结尾,UESCAPE可以跟随
- <xus>带Unicode转义的带引号的字符串
- <xusend>带Unicode转义的带引号的字符串结尾,UESCAPE可以跟随
- <xeu>扩展引号字符串中的Unicode代理对
PostgreSQL代码如下:
%x xb
%x xc
%x xd
%x xh
%x xq
%x xe
%x xdolq
%x xui
%x xuiend
%x xus
%x xusend
%x xeu
模式定义
模式可以为正在表达式命名,例如:
space [ \t\n\r\f]
digit [0-9]
规则部分
规则分为模式/行为行和C代码行。
模式/行为
模式一般为正则表达式,或可以使用有定义部分定义的模式名替代;行为一般是C代码,可以为空。
例如:
whitespace ({space}+|{comment}) //定义部分
{whitespace} {
/* ignore */ //行为定义,遇到whitespace不做任何操作
}
- BEGIN, Flex 初始状态为 INITIAL, BEGIN (start stat) 意思是开始进入一个新的状态. 进入这个状态后,只有定义了对应状态的规则才会被匹配。例如:上面调用BEGIN(xc)之后,即匹配xcstart之后,进入xc状态,所以只有<xc>{xcstart} { ... }等开头带<xc>的代理器才会被匹配。其他的任何规则都不会被匹配。
- yyextra, 此变量是用户传入的参数数据,其类型为YY_EXTRA_TYPE定义,其值默认为void*, 但是可以根据需要自定义。Postgresql 中这里定义为了core_yy_extra_type *。此参数是通过两个途径传入:
- 通过yylex_init_extra( yyext, &scanner)定义
- 用yylex_init(&scanner)之后,调用yyset_extra(yyext, scanner);设置进去
- yylval,yylex(在Bison yyparse 函数中调用)返回值可以理解分为两部分,一个是在规则中的return 值,此为返回的token, 另一个是与之一一对应的yylval。 yylval 的类型为YYSTYPE, 可以根据用户需要重新定义
- 一般定义为一个union类型,在Bison中将token和这个union中的一个变量绑定。然后在scan.l中直接将对应值设置到yylval->(union 的成员变量)。在yyparse中即可获取到, token和 其对应的值。
- yytext,其为一个字符数组,里面存放的是匹配的字符串。
- yylloc,其为位置信息,即匹配的token在整个输入流中的位置。通过这个值反馈给Bison中的yyparse.
- yyalloc,默认是malloc,这里通过%opiton noyylloc屏蔽掉了,并定义了自己的版本为palloc。
- yyrealloc,默认realloc, 通过%option noyyrealloc屏蔽,并定义了自己的版本 repalloc。
- yyfree,默认为free, 通过%option noyyfree屏蔽,并定义了自己的版本pfree。
- yyleng,是匹配token对应字符串的长度。
代码段
此段落,postgresql, 定义了一些在规则段中使用的工具函数。例如scanner_yyerror等函数。
二义性处理
只要是对于语言的解析,往往都会伴随二义性的问题。而Flex大多数程序都具有二义性,相同的输入可能被多种不同的模式匹配。flex是通过两个简单的规则来解决的:
- 词法分析器匹配输入时匹配尽可能多的字符串。
- 如果两个模式都可以匹配的话,匹配在程序中早出现的模式。
比如,flex程序为以下模式时:
"+" { return ADD; }
"=" { return ASSIGN }
"+=" { return ASSIGNADD }
当我们输入“+=”时,优先返回的是“ASSIGNADD”。