FLEX 中文手册来自 ChinaUnix Wiki
一些简单的例子
输入文件的格式
模式
如何匹配输入
动作
生成的扫描器
开始条件
文件结尾规则
与yacc一起使用
一、一些简单的例子
首先给出一些简单的例子,来了解一下如何使用flex。下面的flex输入所定义的扫描器,用来将所有的“
username”字符串替换为用户的登陆名字:
%% username printf("%s", getlogin());
默认情况下,flex扫描器无法匹配的所有文本将被复制到输出,所以该扫描器的实际效果是将输入文件
复制到输出,并对每一个“username”进行展开。在这个例子中,只有一个规则。“username”是模式
(pattern),“printf”是动作(action)。“%%”标志着规则的开始。
这里是另一个简单的例子:
int num_lines = 0, num_chars = 0;
%% \n ++num_lines; ++num_chars; . ++num_chars;
%% int main(void)
{
yylex();
printf("# of lines = %d, # of chars = %d\n", num_lines, num_chars);
}
该扫描器计算输入的字符个数和行数(除了最后的计数报告,并未产生其它输出)。第一行声明了两
个全局变量,“num_lines”和“num_chars”,可以在yylex()函数中和第二个“%%”后面声明的main()函数中
使用。有两个规则,一个是匹配换行符(“\n”)并增加行数和字符数,另一个是匹配所有不是换行符的
其它字符(由正规表达式“.”表示)。
一个稍微复杂点的例子:
/* scanner for a toy Pascal-like language */
%{
/* need this for the call to atof() below */
#include <math.h>
%}
DIGIT [0-9] ID [a-z][a-z0-9]*
%%
{DIGIT}+ {
printf( "An integer: %s (%d)\n", yytext,
atoi( yytext ) );
}
{DIGIT}+"."{DIGIT}* {
printf( "A float: %s (%g)\n", yytext,
atof( yytext ) );
}
if|then|begin|end|procedure|function {
printf( "A keyword: %s\n", yytext );
}
{ID} printf( "An identifier: %s\n", yytext );
"+"|"-"|"*"|"/" printf( "An operator: %s\n", yytext );
"{"[^}\n]*"}" /* eat up one-line comments */
[ \t\n]+ /* eat up whitespace */
. printf( "Unrecognized character: %s\n", yytext );
%%
int main(int argc, char **argv)
{
++argv, --argc; /* skip over program name */
if ( argc > 0 )
yyin = fopen( argv[0], "r" );
else
yyin = stdin;
yylex();
}
这是一个类似Pascal语言的简单扫描器的初始部分,用来识别不同类型的标志(tokens)并给出报告。
这个例子的详细介绍将在后面的章节中给出。
二、输入文件的格式
flex输入文件包括三个部分,通过“%%”行来分开:
definitions(定义) %% rules(规则) %% user code(用户代码)
定义部分,包含一些简单的名字定义(name definitions),用来简化扫描器的规范,还有一些开始状态
(start conditions)的声明,将会在后面的章节中说明。名字定义的形式如下:
name definition
“name”由字母或者下划线(“_”)起始,后面跟字母,数字,“_”或者“-”(破折号)组成。定义由名字
后面的一个非空白(non-white-space)字符开始,直到一行的结束。可以在后面通过“{name}”来引用定
义,并展开为“(definition)”。例如,
DIGIT [0-9] ID [a-z][a-z0-9]*
定义了“DIGIT”为一个正规表达式用来匹配单个数字,“ID”为一个正规表达式用来匹配一个字母,后面
跟零个或多个字母和数字。后面的引用如下,
{DIGIT}+"."{DIGIT}*
等同于
([0-9])+"."([0-9])*
用来匹配一个或多个数字,后面跟一个“.”,然后是零个或者多个数字。
flex输入的规则部分包括一系列的规则,形式如下:
pattern action
模式(pattern)不能有缩进,动作(action)必须在同一行。
参见后面对模式和动作的进一步描述。
最后,用户代码部分将被简单的逐字复制到“”中,作为随同程序用来调用扫描器或者被扫描器
调用。该部分是可选的,如果没有,输入文件中第二个“%%”也可以省略掉。
在定义部分和规则部分,任何缩进的文本或者包含在“%{”和“%}”中的文本,都会被逐字的复制到输出中(并去掉“%{}”)。“%{}”本身不能有缩进。
在规则部分,在第一个规则之前的任何缩进的或者%{}中的文本,可以用来声明扫描程序的局部变量。其它在规则部分的缩进或者%{}中的文本也会被复制到输出,但是它的含义却不好定义,而且可能会产生编译时错误(这一特点是为了与POSIX相同;参见后面的其它特点)。
在定义部分(但不是在规则部分),一条未缩进的注释(即,由“/*”起始的行)也会被逐字的拷贝到输出,直到下一个“*/”。
三、模式
输入中的模式,使用的是扩展的正规表达式集。它们是:
'x'
匹配字符'x'
'.'
除了换行符以外的任意字符(字节)
'[xyz]'
一个字符类别(character class);在这个例子中,该模式匹配一个'x',或者一个'y',或者一
个'z' '[abj-oZ]'
一个带有范围的字符类别;匹配一个'a',或者一个'b',或者从'j'到'o'的任意字母,或者一个'Z'
'[^A-Z]'
一个反选的字符类别(negated character class),即任意不属于这些的类别。在这个例子中,
表示任意一个非大写字母的字符。 '[^A-Z\n]'
任意一个非大写字母的字符,或者一个换行符
'r*'
零个或者多个r,其中r是任意的正规表达式
'r+'
一个或者多个r
'r?'
零个或者一个r(也就是说,一个可选的r)
'r{2,5}'
两个到五个r
'r{2,}'
两个或者更多个r
'r{4}'
确切的4个r
'{name}'
“name”定义的展开(参见前面)
' "[xyz]\"foo" ' (这里单引号和双引号之间没有空格)
文字串:'[xyz]"foo'
'\x'
如果x是一个'a','b','f','n','r','t'或者'v',则为ANSI-C所解释的\x。否则,为一个文字'x'(
include中文用来转义操作符,例如'*')。 '\0'
一个NUL字符(ASCII代码0)
'\123'
八进制值为123的字符
'\x2a'
十六进制值为2a的字符
'(r)'
匹配一个r;括号用来改变优先级(参见后面)
'rs'
正规表达式r,后面跟随正规表达式s;称作“concatenation”
'r|s'
或者r,或者s
'r/s'
一个r,但是后面要跟随一个s。在文本匹配时,s会被包含进来,以判断该规则是否是最长的匹配,但是在动作执行前会被返回给输入。因此,动作只会看到匹配r的文本。这种模式称作trailing context。(有些'r/s'组合,flex会匹配错误;参见后面的不足和缺陷章节中,关于“危险的尾部相关”的注解)
'^r'
一个r,但是只在一行的开始(即,刚开始扫描,或者一个换行符刚被扫描之后)
'r$'
一个r,但是只在一行的结尾(即,正好在换行符之前)。等同于“r/\n”。注意,flex中对换行符的概念跟用来编译flex的C编译器中对'\n'的解释是一模一样的。特别的是,在一些DOS系统上,必须在输入中自己过滤出'\r',或者显示的使用r/\r\n来表示r$。
'<s>r'
一个r,但是只在起始条件(start condition)s(参见下面关于起始条件的讨论)下匹配。<s1, s2, s3>相同,但是在任意起始条件s1,s2,s3下都可以。
'<*>r'
一个r,在任意的起始条件下,甚至是互斥的(exclusive)起始条件。
'<<EOF>>'
文件结尾
'<s1, s2><<EOF>>'
文件结尾,当在起始条件s1或s2下匹配。
注意,在字符类别里面,除了转义符(‘\’),字符类别操作符‘-’,‘]’和类别开始处的‘^’,所有其它的
正规表达式操作符不再具有特殊的含义。
上面列出的正规表达式,是按照优先级由高到低排列的。同一级别的具有相同的优先级。例如,
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论