词法分析器生成器flex中文手册Word下载.docx

上传人:b****6 文档编号:20202067 上传时间:2023-01-18 格式:DOCX 页数:22 大小:95.94KB
下载 相关 举报
词法分析器生成器flex中文手册Word下载.docx_第1页
第1页 / 共22页
词法分析器生成器flex中文手册Word下载.docx_第2页
第2页 / 共22页
词法分析器生成器flex中文手册Word下载.docx_第3页
第3页 / 共22页
词法分析器生成器flex中文手册Word下载.docx_第4页
第4页 / 共22页
词法分析器生成器flex中文手册Word下载.docx_第5页
第5页 / 共22页
点击查看更多>>
下载资源
资源描述

词法分析器生成器flex中文手册Word下载.docx

《词法分析器生成器flex中文手册Word下载.docx》由会员分享,可在线阅读,更多相关《词法分析器生成器flex中文手册Word下载.docx(22页珍藏版)》请在冰豆网上搜索。

词法分析器生成器flex中文手册Word下载.docx

An*/*eatupone-linecomments*/tn+/*eatupwhitespace*/.printf(Unrecognizedcharacter:

%intmain(intargc,char*argv)+argv,-argc;

/*skipoverprogramname*/if(argc0)yyin=fopen(argv0,r);

elseyyin=stdin;

yylex();

这是一个类似Pascal语言的简单扫描器的初始部分,用来识别不同类型的标志(tokens)并给出报告。

这个例子的详细介绍将在后面的章节中给出。

输入文件的格式flex输入文件包括三个部分,通过“%”行来分开:

definitions(定义)%rules(规则)%usercode(用户代码)定义部分,包含一些简单的名字定义(namedefinitions),用来简化扫描器的规范,还有一些开始状态(startconditions)的声明,将会在后面的章节中说明。

名字定义的形式如下namedefinition“name由字母或者下划线(“_”)起始,后面跟字母,数字,“或者“-”(破折号)组成。

定义由名字后面的一个非空白(non-white-space)字符开始,直到一行的结束。

可以在后面通过“name”来引用定DIGIT0-9IDa-za-z0-9*定义了“DIGIT”为一个正规表达式用来匹义,并展开为“(definition)”。

例如,配单个数字,“ID”为一个正规表达式用来匹配一个字母,后面跟零个或多个字母和数字。

后面的引用如下,DIGIT+.DIGIT*等同于(0-9)+.(0-9)*用来匹配一个或多个数字,后面跟一个“.”,然后是零个或者多个数字。

flex输入的规则部分包括一系列的规则,形式如下patternaction模式(pattern)不能有缩进,动作(action)必须在同一行。

参见后面对模式和动作的进一步描述。

最后,用户代码部分将被简单的逐字复制到“lex.yy.c”中,作为随同程序用来调用扫描器或者被扫描器调用。

该部分是可选的,如果没有,输入文件中第二个“%”也可以省略掉。

在定义部分和规则部分,任何缩进的文本或者包含在“%”和“%”中的文本,都会被逐字的复制到输出中(并去掉“%”)。

“%”本身不能有缩进。

在规则部分,在第一个规则之前的任何缩进的或者%中的文本,可以用来声明扫描程序的局部变量。

其它在规则部分的缩进或者%中的文本也会被复制到输出,但是它的含义却不好定义,而且可能会产生编译时错误(这一特点是为了与P0SIX相同;

参见后面的其它特点)。

在定义部分(但不是在规则部分),一条未缩进的注释(即,由“/*”起始的行)也会被逐字的拷贝到输出,直到下一个“*/”。

模式输入中的模式,使用的是扩展的正规表达式集。

它们是匹配字符x除了换行符以外的任意字符(字节)xyz一个字符类别(characterclass);

在这个例子中,该模式匹配一个x或者一个y,或者个zabj-oZ一个带有范围的字符类别;

匹配一个a,或者一个b,或者从j到o的任意字母,或者一个ZFA-Z,即任意不属于这些的类一个反选的字符类别(negatedcharacterclass)别。

在这个例子中,表示任意一个非大写字母的字符。

FA-Zn一个或者多个r?

两个到五个rr2,两个或者更多个rr4确切的4个rname“name定义的展开(参见前面)用来转义操作符,例如*)。

0文字串:

xyzfoox一个NUL字符(ASCII代码0)123八进制值为123的字符x2a十六进制值为2a的字符(r)匹配一个r;

括号用来改变优先级(参见后面)rs正规表达式r,后面跟随正规表达式s;

称作“concatenation”r|s或者r,或者sr/s一个r,但是后面要跟随一个s。

在文本匹配时,s会被包含进来,以判断该规则是否是最长的匹配,但是在动作执行前会被返回给输入。

因此,动作只会看。

(有些r/s组合,flex到匹配r的文本。

这种模式称作trailingcontext会匹配错误;

参见后面的不足和缺陷章节中,关于“危险的尾部相关”的注解)Ar一个r,但是只在一行的开始(即,刚开始扫描,或者一个换行符刚被扫描之后)r$一个r,但是只在一行的结尾(即,正好在换行符之前)。

等同于“r/n”。

注意,flex中对换行符的概念跟用来编译flex的C编译器中对n的解释是一模一样的。

特别的是,在一些DOS系统上,必须在输入中自己过滤出r,或者显示的使用r/rn来表示r$。

r一个r,但是只在起始条件(startcondition)s(参见下面关于起始条件s2,S3下都可的讨论)下匹配。

s1,s2,s3相同,但是在任意起始条件s1,以。

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函数相对应的字符类别。

例如,:

指定了isalnum()返为回值为真的字符集,即,任意的字母或者数字。

一些系统没有提供isblank(),则flex定义:

bland:

一个空格符(blank)或者一个制表符(tab)。

例如,下面的字符类别是等同的:

0-9a-zA-Z0-9如果你的扫描器是大小写无关的(使用-i命令行选项),则:

等同于:

关于模式的一些注意事项:

一个反选的字符类别例如上面的“FA-Z”将会匹配一个换行符,除非n”(或者等同的转义序)。

这一点不像许多其它正列)在反选字符类别中显示的指出(如“AAn”规表达式工具,但不幸的配整个的输出直到遇到另一条规则中只能最多有一个尾部相关的情况(/操作符或者$操作符)。

一个引号。

起始条件,A和”只能出现在模式的开始处,并且和/,$一样,都不能包含在圆括号中。

A如果不出现在规则的开始处,或者$不出现在规则的结尾,将会失去它的特殊属性,并且被作为普通字符。

下面的例子是非法的:

foo/bar$foobar作为普通字符:

foo|(bar$)foofbar用下面的方式(特殊动作|,foo|bar$/*actiongoeshere*/类似的技巧可以用来匹配一个foo,或将在下面介绍):

者一个bar并且在一行的起始处。

如何匹配输入当生成的扫描器运行时,它会分析它的输入,来查找匹配任意模式的字符串。

如果找到多个匹配,则采取最长文本的匹配方式(对于尾部相关规则,也包括尾部的长度,虽然尾部还要返回给输入)。

如果找到两个或者更多的相同长度的匹配,则选择列在flex输入文件中的最前面的规则。

旦匹配确定,则所匹配的文本(称作标识,token)可以通过全局字符指针yytext来访问,文本的长度存放在全局整形yyleng中。

与匹配规则相应的动作(action)将被执行(后面会有关于动作的详细描述),然后剩余的输入再被继续扫描匹配。

如果没有找到匹配,则执行默认的规则:

紧接着的输入字符被认为是匹配的,并且复制到标准输出。

因此,最简单合法的flex输入是:

%生成的扫描器只是简单的将它的输入(一次一个字符的)复制到它的输出。

注意,yytext可以通过两种方式来定义:

作为一个字符指针,或者作为一个字符数组。

可以在flex输入的第一部分(定义部分)通过专门的指令%pointer或者%array来控制flex使用哪种定义。

缺省的为pointer,除非使用-llex兼容选项,使得yytext为一个数组。

使用%pointer的优点是能够进行足够快的扫描,并且当匹配非常大的标识时不会有缓冲溢出(除非是动态内存耗尽)。

缺点是在动作中对yytext的修改方式将会有所限制(参见下一章节),并且调用unput()函数将会破坏yytext的现有内容,在不同lex版本中移植时,这将是一个非常头痛的事情。

使用%array的优点是,可以按照自己的意愿来修改yytext,并且调用unput()也不会破坏yytext(参见下面)。

而且,已有的lex程序有时可以通过如下的声明方式,在外部访问yytext:

externcharyytext;

这种定义在使用pointer时是错误的,但是对于%array却可以。

%array定义了yytext为一个YYLMA个字符的数组,缺省情况下,YYLMA是一个相当大的值。

可以在flex输入的第一部分简单的通过#defineYYLMAX来改变大小。

正如上面提到的,%pointer指定的yytext是通过动态增长来适应大的标识的。

这也意味着%pointer的扫描器能够接受非常大的标识(例如匹配整个的注释块),不过要记住,每次扫描器都要重新设定yytext的长度,而且必须从头扫描整个标识,所以匹配这样的标识时速度会很慢。

目前,如果调用unput()并且返回太多的文本,yytext将不会动态增长,而只是产生一个运行时错误。

而且要注意,不能在C+m描器类(C+选项,参见下面)中使用array。

动作规则中的每一个模式都有一个相应的动作,它可以是任意的C语句。

模式结束于第一个非转义的空白字符;

该行的剩余部分便是它的动作。

如果动作是空的,则当模式匹配时,输入的标识将被简单的丢弃。

例如,下面所描述的程序,是用来删除输入中的所有“zapme”:

%zapme(输入中的所有其它字符,会被缺省规则匹配,并被复制到输出。

)下面的程序用来将多个空格和制表符压缩为单个空格,并且丢弃在一行尾部的所有空格%t+putchar();

t+$/*ignorethistoken*/如果动作包括,则动作的范围直到对称的,并且可以跨过多行。

flex可以识别C字符串和注释,因此字符串和注释中的大括号不会起作用。

但(包括在动作里面出现的普通大括号)。

是,也允许动作由%开始,并且将直到下一个%之间的动作看作是文本只包含一个垂直分割线(|)的动作意味着“与下一个规则相同”。

参见下面的例子。

动作能够包含任意的C代码,包括return语句来返回一个值给调用yylex()的程序。

每次调用yylex(),它将持续不断的处理标识,直到文件的结尾或者执行了return。

动作可以自由的修改yytext,除了增加它的长度(在尾端增加字符的,将会覆盖输入流中后面的字符)。

不过这种情形不会发生在使用%array时(参见前面);

在那种情况下,yytext可以任意修改。

动作可以自由修改yyleng,除非他们不应该这么做,比如动作中也使用了yymore()(参见下面)。

在动作中可以包含许多特殊指令:

*ECHO将yytext复制到扫描器的输出。

*BEGIN后面跟随起始状态的名字,使扫描器处于相应的起始状态下(参见下*REJECT指示扫描器继续采用“次优”的规则匹配输入(或者输入的前面一部分)。

规则根据前面在“如何匹配输入”一节中描述的方式来选择,并且yytext和yyleng也被设为适当的值。

它可能是和最初选择的规则匹配相同多的文本,但是在flex输入文件中排在后面的规则,也可能是匹配较少的文本的规则。

例如,下面的将会计算输入中的单词,并且还在“frob”出现时调用程序special():

intword_count=0;

%frobspecial();

REJECT;

Ftn+word_count;

如果没有REJEC,T输入中任何“frob”都不会被计入单词个数,因为扫描器在通常情况下只是对每一个标识执行一个动作。

可以使用多个REJEC,T对于每个,都将查找当前活动规则的下一个最优选择。

例如,当下面的扫描器扫描标识“abed”时,它将往输出写入“abcdabcaba”:

%ab|abc|abcdECHO;

.|n/*eatupanyunmatchedcharacter*/(前三个规则都执行第四个的动作,因为他们使用了特殊的动作|。

)对于扫描器执行效率来说,REJECT是一种相当昂贵的特征;

如果在扫描器的任意动作中使用了它,它将使得扫描器的所有匹配速度降低。

而且,REJECTS能和-Cf或-CF选项一起使用(参见下面)。

还要注意的是,不像其它特有动作,REJECTS一个分支跳转;

在动作中紧接其后面的代码将不会被执行。

*yymore()告诉扫描器在下一次匹配规则时,相应的标识应该被追加到现在的yytext的值中,而不是替代它。

例如,假设有输入“mega-kludge”,下面的将会向输出写入“mega-mega-kludge”:

%mega-ECHO;

yymore();

kludgeECHO;

首先是“mega”被匹配,并且回显到输出。

然后是“kludge”被匹配,但是kludge”规则中的ECHO实先前的“mega-”还保留在yytext的起始处,因此“际将会写出“mega-kludge”yymore依赖于yyleng的值能够正确地反映当前标识的长度,所以在使用yymore()时,一定不要修改使用yymore()时,有两点需要注意的。

首先,,会给扫描器的匹配速度稍微yyleng。

其次,在扫描器的动作中使用yymore()有些影响。

*yyless(n)将当前标识的除了前n个字符之外的都回送给输入流,扫描器在查找接下来的匹配时,会重新扫描它们。

yytext和yyleng会相应做适当的调整(例如,yyleng将会等于n)。

例如,在输入“foobar”时,下面的规则将会输出foobarbar”%foobarECHO;

yyless(3);

a-z+ECHO;

如果yyless的参数为0,则会使当前整个输入字符串被重新扫描。

除非已经改变扫描器接下来如何处理它的输入(例如,使用BEGIN),否则将会产生一个无限循环。

注意,yyless是一个宏,并且只能用在flex输入文件中,其它源文件则不可以。

*unput(c)将字符c回放到输入流,并将其作为下一次扫描的字符。

下面的动作将会接受当前的标识,并使它封装在括号中而被从新扫描。

inti;

/*Copyyytextbecauseunput()trashesyytext*/char*yycopy=strdup(yytext);

unput();

for(i=yyleng-1;

i=0;

-i)unput(yycopyi);

free(yycopy);

注意,由于unput()每次都将字符放回到输入流的起始处,因此如果要回放字符串,则必须从后往前操作。

在使用unput()时,一个重要的潜在问题是如果使用%pointer(缺省情况),调用unput()会破坏yytext的内容,将会从最右面的字符开始,每次向左吞并一个。

如果需要保留yytext的值直到调用unput()之后(如上面的例子),必须将其复制到别处,或者使用array来构建扫描器(参见如何匹配输入)。

最后,注意不能将EOF放回来标识输入流出去文件结尾。

*input()从输入流中读取下一个字符。

例如,下面的方法可以去掉C注释:

%/*registerintc;

for(;

)while(c=input()!

=*&

c!

=EOF);

/*eatuptextofcomment*/if(c=*)while(c=input()=*)if(c=/)break;

/*foundtheend*/if(c=EOF)error(EOFincomment);

break;

(注意,如果扫描器是用C+编译的,则input()由yyinput()代替,为了避免与C+流input的名字冲突。

)*YY_FLUSH_BUFFE刷新扫描器内部的缓存,以至于扫描器下次匹配标识时,将会首先使用YY_INPUT重新填充缓存(参见下面的生成的扫描器)。

这个动作是较为常用的函数yy_flush_buffer()的特殊情况,将在下面的多输入缓存章节介绍。

*yyterminate()可以在动作中的用来代替return语句。

它将终止扫描,并返回0给扫描器的调用者,用来表示“全部完成”。

默认情况下,yyterminate()也会在遇到文件结尾时被调用。

它是一个宏,可以被重定义。

生成的扫描器lex.yy.c是flex的输出文件,其中包含了扫描程序yylex(),许多的数据表,用来匹配标识,还有许多的辅助程序和宏。

默认情况下,yylex()按面的方式被声明:

intyylex().variousdefinitionsandtheactionsinhere.(如果环境支持函数原形,则为“intyylex(void)”。

)可以通过定义“YY_DEC”宏来改变该定义。

例如,可以使用1.defineYY_DECLfloatlexscan(a,b)floata,b;

来给出扫描程序的名字lexscan,返回一个浮点值,并且接受两个浮点参数。

注意,如果是使用K&

F风格/无原形的函数声明,在给出扫描程序参数时,必须用分号(;

)来结束定义。

只要调用yylex(),它便从全局输入文件yyin(缺省值为stdin)中扫描标)或者其中的一个动作执行了识,并且直到文件的结尾(这种情况下将返回0return语句。

如果扫描器到达文件的结尾,接下来的调用则是未定义的,除非yyin被指向一个新的输入文件(这种情况下,将继续扫描那个文件),或者调用了yyrestart()。

yyrestart()接受一个参数,一个FILE*指针(可为空,如果已经设置了YY_INPU来指定输入源,而不是yyin),并且初始化yyin来扫描那个文件。

基本上,直接将新的输入文件赋值给yyin和使用yyrestart()没有区别;

后者可以用来与之前的flex版本相兼容,因为它可以用来在扫描过程的中间来改变输入文件。

它也可以用来丢弃当前的输入缓存,通过将yyin作为参数进行调用;

不过更好的方式是用YY_FLUSH_BUFFER见上面)。

注意,yyrestart()不会重设开始状态为INITIAL(参见下面的开始状态)。

如果yylex()是由于动作中执行了一个return语句而停止扫描的,则扫描器可以被再次调用,并且它将会从上次离开的地方继续扫描。

默认情况下(出于效率目的),扫描器使用块读取方式从yyin中读入字符,而不是简单的调用getc()。

可以通过定义YY_INPU眩来控制扫描器获得输入的方式。

YY_INPUT勺调用方式为“YY_INPUT(buf,result,max_size)”。

它执行的操作为在字符数组中放入max_size个字符,并且通过整数变量result返回值,或者是读入字符的个数或者是常量YY_NULL在Unix系统下为0)来表示EOF缺省的YY_INPUT从全局文件指针“yyin”中读取字符。

一个定义YY_INPUT勺例子(在输入文件的定义部分部分):

%#defineYY_INPUT(buf,result,max_size)intc=getchar();

result=(c=EOF)?

YY_NULL:

(buf0=c,1);

%该定义将会改变输入的处理方式为一次处理一个字符。

当扫描器从YY_INPUT获得文件结束标识时,它将检查yywrap()函数。

如果yywrap()返回假(零),则认为调用函数已经运行并将yyin设为指向另一个输入文件,并开始继续扫描。

如果返回真(非零),则扫描器终止,并返回0给调用者。

注意,对于每一种情况,开始状态都会保持不变,并不转变为INITIAL。

如果不提供自己的yywrap()版本,则必须使用%optionnoyywrap(这种情况,跟从yywrap()返回1一样),或者必须与-lfl连接来获得

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 求职职场 > 笔试

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1