词法分析器flex中文手册.docx
《词法分析器flex中文手册.docx》由会员分享,可在线阅读,更多相关《词法分析器flex中文手册.docx(21页珍藏版)》请在冰豆网上搜索。
词法分析器flex中文手册
FLEX中文手册
这是flex手册的部分中文翻译,仅供参考
∙一些简单的例子
∙输入文件的格式
∙模式
∙如何匹配输入
∙动作
∙生成的扫描器
∙开始条件
∙文件结尾规则
∙与yacc一起使用
一些简单的例子
首先给出一些简单的例子,来了解一下如何使用flex。
下面的flex输入所定义的扫描器,用来将所有的“
username”字符串替换为用户的登陆名字:
%%usernameprintf("%s",getlogin());
默认情况下,flex扫描器无法匹配的所有文本将被复制到输出,所以该扫描器的实际效果是将输入文件
复制到输出,并对每一个“username”进行展开。
在这个例子中,只有一个规则。
“username”是模式
(pattern),“printf”是动作(action)。
“%%”标志着规则的开始。
这里是另一个简单的例子:
intnum_lines=0,num_chars=0;
%%\n++num_lines;++num_chars;.++num_chars;
%%intmain(void)
{
yylex();
printf("#oflines=%d,#ofchars=%d\n",num_lines,num_chars);
}
该扫描器计算输入的字符个数和行数(除了最后的计数报告,并未产生其它输出)。
第一行声明了两
个全局变量,“num_lines”和“num_chars”,可以在yylex()函数中和第二个“%%”后面声明的main()函数中
使用。
有两个规则,一个是匹配换行符(“\n”)并增加行数和字符数,另一个是匹配所有不是换行符的
其它字符(由正规表达式“.”表示)。
一个稍微复杂点的例子:
/*scannerforatoyPascal-likelanguage*/
%{
/*needthisforthecalltoatof()below*/
#include
%}
DIGIT[0-9]ID[a-z][a-z0-9]*
%%
{DIGIT}+{
printf("Aninteger:
%s(%d)\n",yytext,
atoi(yytext));
}
{DIGIT}+"."{DIGIT}*{
printf("Afloat:
%s(%g)\n",yytext,
atof(yytext));
}
if|then|begin|end|procedure|function{
printf("Akeyword:
%s\n",yytext);
}
{ID}printf("Anidentifier:
%s\n",yytext);
"+"|"-"|"*"|"/"printf("Anoperator:
%s\n",yytext);
"{"[^}\n]*"}"/*eatupone-linecomments*/
[\t\n]+/*eatupwhitespace*/
.printf("Unrecognizedcharacter:
%s\n",yytext);
%%
intmain(intargc,char**argv)
{
++argv,--argc;/*skipoverprogramname*/
if(argc>0)
yyin=fopen(argv[0],"r");
else
yyin=stdin;
yylex();
}
这是一个类似Pascal语言的简单扫描器的初始部分,用来识别不同类型的标志(tokens)并给出报告。
这个例子的详细介绍将在后面的章节中给出。
输入文件的格式
flex输入文件包括三个部分,通过“%%”行来分开:
definitions(定义)%%rules(规则)%%usercode(用户代码)
定义部分,包含一些简单的名字定义(namedefinitions),用来简化扫描器的规范,还有一些开始状态
(startconditions)的声明,将会在后面的章节中说明。
名字定义的形式如下:
namedefinition
“name”由字母或者下划线(“_”)起始,后面跟字母,数字,“_”或者“-”(破折号)组成。
定义由名字
后面的一个非空白(non-white-space)字符开始,直到一行的结束。
可以在后面通过“{name}”来引用定
义,并展开为“(definition)”。
例如,
DIGIT[0-9]ID[a-z][a-z0-9]*
定义了“DIGIT”为一个正规表达式用来匹配单个数字,“ID”为一个正规表达式用来匹配一个字母,后面
跟零个或多个字母和数字。
后面的引用如下,
{DIGIT}+"."{DIGIT}*
等同于
([0-9])+"."([0-9])*
用来匹配一个或多个数字,后面跟一个“.”,然后是零个或者多个数字。
flex输入的规则部分包括一系列的规则,形式如下:
patternaction
模式(pattern)不能有缩进,动作(action)必须在同一行。
参见后面对模式和动作的进一步描述。
最后,用户代码部分将被简单的逐字复制到“lex.yy.c”中,作为随同程序用来调用扫描器或者被扫描器
调用。
该部分是可选的,如果没有,输入文件中第二个“%%”也可以省略掉。
在定义部分和规则部分,任何缩进的文本或者包含在“%{”和“%}”中的文本,都会被逐字的复制到输出中(并去掉“%{}”)。
“%{}”本身不能有缩进。
在规则部分,在第一个规则之前的任何缩进的或者%{}中的文本,可以用来声明扫描程序的局部变量。
其它在规则部分的缩进或者%{}中的文本也会被复制到输出,但是它的含义却不好定义,而且可能会产生编译时错误(这一特点是为了与POSIX相同;参见后面的其它特点)。
在定义部分(但不是在规则部分),一条未缩进的注释(即,由“/*”起始的行)也会被逐字的拷贝到输出,直到下一个“*/”。
模式
输入中的模式,使用的是扩展的正规表达式集。
它们是:
'x'
匹配字符'x'
'.'
除了换行符以外的任意字符(字节)
'[xyz]'
一个字符类别(characterclass);在这个例子中,该模式匹配一个'x',或者一个'y',或者一
个'z''[abj-oZ]'
一个带有范围的字符类别;匹配一个'a',或者一个'b',或者从'j'到'o'的任意字母,或者一个'Z'
'[^A-Z]'
一个反选的字符类别(negatedcharacterclass),即任意不属于这些的类别。
在这个例子中,
表示任意一个非大写字母的字符。
'[^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'(
用来转义操作符,例如'*')。
'\0'
一个NUL字符(ASCII代码0)
'\123'
八进制值为123的字符
'\x2a'
十六进制值为2a的字符
'(r)'
匹配一个r;括号用来改变优先级(参见后面)
'rs'
正规表达式r,后面跟随正规表达式s;称作“concatenation”
'r|s'
或者r,或者s
'r/s'
一个r,但是后面要跟随一个s。
在文本匹配时,s会被包含进来,以判断该规则是否是最长的匹配,但是在动作执行前会被返回给输入。
因此,动作只会看到匹配r的文本。
这种模式称作trailingcontext。
(有些'r/s'组合,flex会匹配错误;参见后面的不足和缺陷章节中,关于“危险的尾部相关”的注解)
'^r'
一个r,但是只在一行的开始(即,刚开始扫描,或者一个换行符刚被扫描之后)
'r$'
一个r,但是只在一行的结尾(即,正好在换行符之前)。
等同于“r/\n”。
注意,flex中对换行符的概念跟用来编译flex的C编译器中对'\n'的解释是一模一样的。
特别的是,在一些DOS系统上,必须在输入中自己过滤出'\r',或者显示的使用r/\r\n来表示r$。
'r'
一个r,但是只在起始条件(startcondition)s(参见下面关于起始条件的讨论)下匹配。
相同,但是在任意起始条件s1,s2,s3下都可以。
'<*>r'
一个r,在任意的起始条件下,甚至是互斥的(exclusive)起始条件。
'<>'
文件结尾
'<>'
文件结尾,当在起始条件s1或s2下匹配。
注意,在字符类别里面,除了转义符(‘\’),字符类别操作符‘-’,‘]’和类别开始处的‘^’,所有其它的
正规表达式操作符不再具有特殊的含义。
上面列出的正规表达式,是按照优先级由高到低排列的。
同一级别的具有相同的优先级。
例如,
foo|bar*
等同于
(foo)|(ba(r*))
因为,‘*’操作符的优先级比串联高,串联的优先级比间隔符高(‘|’),所以,该模式匹配字符串“foo”
或者字符串“ba”后面跟随零个或多个r。
如果要匹配“foo”或者零个或多个“bar”,可以使用:
foo|(bar)*
如果要匹配零个或者多个“foo”,或者零个或多个“bar”:
(foo|bar)*
除了字符和序列字符,字符类别也可以包含字符类别表达式。
这些表达式由‘[:
’和‘:
]’分隔符封装(并且
必须在字符类别的分隔符‘[’和‘]’之中)。
有效的表达式包括:
[:
alnum:
][:
alpha:
][:
blank:
][:
cntrl:
][:
digit:
][:
graph:
][:
lower:
][:
print:
][:
punct:
][:
space:
][:
upper:
][:
xdigit:
]
这些表达式都指定了与标准C中‘isXXX’函数相对应的字符类别。
例如,‘[:
alnum:
]’指定了‘isalnum()’返
回值为真的字符集,即,任意的字母或者数字。
一些系统没有提供‘isblank()’,则flex定义‘[:
bland:
]’为
一个空格符(blank)或者一个制表符(tab)。
例如,下面的字符类别是等同的:
alnum:
[[:
alpha:
][:
digit:
][[:
alpha:
]0-9][a-zA-Z0-9]
如果你的扫描器是大小写无关的(使用‘-i’命令行选项),则‘[:
upper:
]’和‘[:
lower:
]’等同于‘[:
alpha:
]’。
关于模式的一些注意事项:
一个反选的字符类别例如上面的“[^A-Z]”将会匹配一个换行符,除非“\n”(或者等同