📄 fu.htm.bak
字号:
<br>
“\n”;<br>
<br>
注意\t和\n中的双引号可以去掉。<br>
<br>
外部字符数组yytext的内容是当前被某规则匹配的字符串,例如正规式[a-z]+与所有由小写字母组成的字符串匹配,要想知道与它匹配的具体字符串是什么,可用下述规则:<br>
<br>
[a-z]+ printf(“% s”, yytext);<br>
<br>
动作printf(“%s”,yytext)就是将字符数组yytext的内容打印出来,这个动作用得很频繁,Lex提供了一个宏ECHO来表示它,因此上述识别规则可以写成:<br>
<br>
[a-z]+ECHO;<br>
<br>
请注意,上面说过缺省的动作就是将输入串原样抄到输出文件中,那么上述规则起什么作用呢?这一点将在“规则的二义性”一节中解释。<br>
<br>
有时有必要知道被匹配的字符串中的字符个数,外部变量yyleng就表示当前yytext中字符的个数。例如要对输入串中单词的个数和字符的个数进行计数(单词假定是由大写或小写字母组成的字符串),可用下述规则:<br>
[a-zA-Z]+ {words++;<br>
<br>
Chars+=yyleng;}<br>
<br>
注意被匹配的字符串的第一个字符和最后一个字符分别是<br>
<br>
yytext[0]和yytext[yyleng-1]<br>
<br>
下面介绍三个Lex提供的在写动作时可能用到的C函数<br>
<br>
l.yymore()<br>
<br>
当需下一次被匹配的字符串被添加在当前识别出的字符串后面,即不使下一次的输入替换yytext中已有的内容而是接在它的内容之后,必须在当前的动作中调用yymore( )<br>
<br>
例:假设一个语言规定它的字符串括在两个双引号之间,如果某字符串中含有双引号,则在它前面加上反斜线\。用一个正规式来表达该字符串的定义很不容易,不如用下面较简明的正规式与yymore()配合来识别:<br>
<br>
\" [∧"]*{ <br>
if(yytext[yyleng-1]<br>
= =’\\’yymore( );<br>
else<br>
…normal user processing<br>
}<br>
<br>
当输入串为”abc\"def”时,上述规则首先与前五个字符”abc\匹配,然后调用yymore( )使余下部分”def被添加在前一部分之后,注意作为字符串结尾标志的那个双引号由”normal user proessing”部分负责处理<br>
<br>
2.yyless(n)<br>
<br>
如果当前匹配的字符串的末尾部分需要重新处理,那么可以调用 yyless(n)将这部分子串“退回”给输入串,下次再匹配处理。yyless(n)中的n是不退回的字符个数,即退回的字符个数是yyleng-n。<br>
<br>
例;在C语言中串“=-a”具有二义性,假定要把它解释为“=-a”同时给出信息,可用下面的识别规则:<br>
<br>
=-[a-zA-Z]{<br>
printf(“Operator(=-)<br>
ambiguous\n”);<br>
yyless(yyleng-1);<br>
…action for=-…<br>
}<br>
<br>
上面的规则先打印出一条说明出现二义性的信息,将运算符后面的字母返回给输入串,最后将运算符按“=-”处理.另外,如果希望把“=- a”解释为”=- a”,这只需要把负号与字母一起退回给输入串等候下次处理,用下面的规则即可:<br>
<br>
=-[a-zA-Z]{<br>
printf(“Operator(=-)<br>
ambiguous\n”);<br>
yyless (yyleng-1);<br>
…action for = …<br>
}
<br>
3. yywrap ( )<br>
<br>
当Lex处理到输入串的文件尾时,自动地调用yywrap(),如果
yywrap()返回值是 1,那么Lex就认为对输入的处理完全结束,如果yywrap()返回的值是0,Lex就认为有新的输入串等待处理。<br>
<br>
Lex自动提供一个yywrap(),它总是返回1,如果用户希望有一个返回0的yywrap( ),那么就可以在”用户子程序部分”自己写一个 yywrap(),它将取代Lex自动提供的那个yywrap(),在用户自己写的ywrap()中,用户还可以作其他的一些希望在输入文件结束处要作的动作,如打印表格、输出统计结果等,使用yywrap()的例子在后面举出。<br>
<br>
1. 5识别规则的二义性<br>
<br>
有时Lex的程序中可能有多于一条规则与同一个字符串匹配,这就是规则的二义性,在这种情况下,Lex有两个处理原则:<br>
<br>
1)能匹配最多字符的规则优先<br>
<br>
2)在能匹配相同数目的字符的规则中,先给出的规则优先<br>
<br>
例:设有两规则按下面次序给出:<br>
<br>
integer kegword action…<br>
[a-z]+ identifier action…<br>
<br>
如果输入是integers,则它将被当成标识符处理,因为规则integer只能匹配7个字符,而[a-z]+能匹配8个字符;如果输入串是integer,那么它将被当作关键字处理,因为两条规则都能与之匹配,但规则integer先给出。</font></p>
</p>
<p><font size="2">1.6 lex源程序中的辅助定义部分<br>
<br>
Lex源程序的第一部分是辅助定义,到目前为止我们只涉及到怎样写第二部分,即识别规则部分的写法,现在来看第一部分的写法。在Lex源程序中,用户为方便起见,需要一些辅助定义,如用一个名字代表一个复杂的正规式。辅助定义必须在第一个%%之前结出,并且必须从第一列开始写,辅助定义的语法是:<br>
<br>
name translation<br>
<br>
例如用名字IDENT来代表标识符的正规式的辅助定义为<br>
<br>
IDENT [a-zA-Z][a-zA-Z0-9]*<br>
<br>
辅助定义在识别规则中的使用方式是用运算符{ }将 name括起来,Lex自动地用 translation去替换它,例如上述标识符的辅助定义的使用为:<br>
<br>
{IDENT}action for identifer…<br>
<br>
下面我们用辅助定义的手段来写一段识别FORTRAN语言中整数和实数的Lex源程序:<br>
<br>
D [0一9]<br>
E [DEde][-+]?{ D}+<br>
%%<br>
{D}+ printf(“integer”);<br>
{D}+"."{D}*({E})? |<br>
{D}*"."{D}+({E})? |<br>
{D}+{E} printf( "real" );<br>
<br>
请注意在辅助定义部分中可以使用前面的辅助定义。例如:定义E时使用了D,但所用的辅助定义必须是事先已定义过的,不能出现循环定义。上面的规则只是说明辅助定义的用法,并不是识别FORTRAN中数的全部规则,因为它不能处理类似35.EQ.I这样的问题,即会把35.EQ.I中的35.E当作实数,怎么解决这种问题请读者思考。<br>
<br>
除了上面介绍的辅助定义之外,用户还需要在Lex源程序中使用变量,还需要有一些自己写的子程序。前面已经见过两个常用的变量即yytext和yylong,也介绍过几个Lex提供的子程序yymore,yyless和yywrap,现在介绍用户如何自己定义变量和写子程序。<br>
<br>
Lex是把用户写的Lex源程序转换成一个C语言的程序yylex,在转换过程中,Lex是把用户自己的变量定义和子程序照抄到yylex中去,lex规定属于下面三种情况之一的内容就照抄过去;<br>
<br>
1)以一个空格或tab起头的行,若不是 Lex的识别规则的一部分,则被照抄到 Lex产生的程序中去。如果这样的行出现在第一个%%之前,它所含的定义就是全局的,即Lex产生的程序中的所有函数都可使用它。如果这样的行紧接在第一个%%之后但在所有识别规则之前,它们就是局部的,将被抄到涉及它的动作相应的代码中去。注意这些行必须符合C语言的语法,并且必须出现在所有识别规则之前。<br>
<br>
这一规定的一个附带的作用是使用户可以为Lex源程序或Lex产生的词法分析器提供住解,当然注解必须符合C语言文法。<br>
<br>
2)所有被界于两行%{和%}之间的行,无论出现在哪里也无论是什么内容都被照抄过去,要注意 %{和%}必须分别单独占据一行.例如;<br>
<br>
%{<br>
<br>
# defineENDOFFILE 0<br>
#include “head.h”<br>
int flag<br>
%}<br>
<br>
提供上面的措施主要因为在C语言中有一些行如上例中的宏定义或文件蕴含行必须从第一列开始写。<br>
<br>
3)出现在第二个%%之后的任何内容,不论其格式如何,均被照抄过去。<br>
<br>
1.7 怎样在Unix系统中使用Lex假定已经写好了一个Lex源程序。怎样在Unix系统中从它得到一个词法分析器呢?<br>
<br>
Lex自动地把Lex源程序转换成一个C语言的可运行的程序,这个可运行的程序放在叫Lex.yy.c的文件中,这个C语言程序再经过C编译,即可运行。<br>
<br>
例,有一名叫source的Lex源程序,第一步用下面的命令将它转换成lex.yy.c:<br>
<br>
$ lex source<br>
<br>
($是 Unix的提示符)。Lex.yy.c再用下面的命令编译即得到可运行的目标代码 a.<br>
<br>
out:<br>
<br>
$cc lex.yy.c-ll<br>
<br>
上面的命令行中的一11是调用Lex的库,是必须使用的,请参看[1]。<br>
<br>
这一节内容请读者参看[4]中的lex(1)<br>
<br>
Lex可以很方便地与Yacc配合使用,这将在下一章中介绍。<br>
<br>
$1.8例子<br>
<br>
这一节举两个例子看看Lex源程序的写法<br>
<br>
1.将输入串中所有能被7整除的整数加3,其余部分照原样输出,先看下面的Lex源程序:<br>
<br>
%%<br>
int k;<br>
[0-9]+{<br>
scanf(-1, yytext,“%d”,&k);<br>
if(k % 7 = =0)<br>
printf(“%d”,k+3);<br>
else<br>
printf(“ % d”, k);<br>
}<br>
<br>
上面的程序还有不足的地方,如对负整数,只是将其绝对值加上3,而且象X7,49.63这样<br>
<br>
的项也做了修改,下面对上面的源程序稍作修改就避免了这些问题。<br>
<br>
%%<br>
int k; <br>
-?[0-9]+{<br>
scanf(-1,yytext,“%d”,&k);<br>
printf(“%d”,k%7= =0?k+3;k);<br>
}<br>
-?[0-9]+ ECHO;<br>
[A-Za-z][A-Za-z0-9]+ ECHO;<br>
<br>
2.下一个例子统计输人串中各种不同长度的单词的个数,统计结果在数组lengs中,单词定义为由字母组成的串,源程序如下;<br>
<br>
int lengs [100];<br>
%%<br>
[a-z]+ lengs[yyleng]++;<br>
•|<br>
\n ;<br>
%%<br>
yywrap ( )<br>
{<br>
int i ;<br>
printf(“Length No.words \n”) ;<br>
for(i=0;i<100;i++)<br>
if(lengs[i]>0)<br>
ptintf(“%5d % 10d\n”,i,
lengs[i];<br>
return (1);<br>
}<br>
<br>
在上面的流程序中,当Lex读入输入串时,它只统计而不输出,到输入串读入完毕后,才在调用 yywrap()时输出统计结果,为此用户自己提供了yywrap(),注意yywrap()的最后一个语句是返回值1。<br>
<br>
1.9 再谈上下文相关性的处理<br>
<br>
在$3中介绍Lex用的正规式时提到了上下文相关性的表示,这里再详细介绍Lex提供的处理上下文相关的措施。要处理的问题是某些规则在不同的上下文中要采取不同的动作,或者说同样的字符串在不同的上下文中有不同的解释。例如在程序设计语言中,同一个等号“=”,在说明部分表示为变量赋初值,这时的动作应是修改符号表内容;而在语句部分等号就是赋值语句的赋值号,这时又应该产生相应于赋值语句的代码。因此要依据等号所处的上下文来判断它的含义。Lex提供了两种主要的方法,<br>
<br>
1)使用标志来区分不同的上下文。<br>
<br>
标志是用户定义的变量,用户在不同的上下文中为它置不同的值,以区分它在哪个上下文中,这样识别规则就可以根据标志当前值决定在哪个上下文中并采取相应的动作。<br>
<br>
例:将输入串照原样输出,但对magic这个词,当它出现在以字母a开头的行中,将其改为first,出现在以b开头的行中将其改为second,出现在以c开头的行中则改为third。<br>
<br>
使用标志flag的Lex源程序如下;<br>
<br>
int flag ;<br>
<br>
%%<br>
∧a {flag='a';ECHO;}<br>
∧b {flag='b'; ECHO;}<br>
∧c {flag='c'; ECHO;}<br>
\n {flag=o; ECHO;}<br>
magic{<br>
switch (flag)<br>
{
<br>
case 'a' : printf (“first”); break;<br>
case 'b': printf (“second”); break;<br>
case 'c' : printf (“third”); break;<br>
default; ECHO; break;<br>
}
<br>
}<br>
<br>
2)使用开始条件来区分不同上下文<br>
<br>
在Lex源程序中用户可以用名字定义不同的开始条件。当把某个开始条件立置于某条识别规则之前时,只有在Lex处于这个开始条件下这条规则才起使用,否则等于没有这条规则。Lex当前所处的开始条件可以随时由用户程序(即Lex动作)改变。<br>
<br>
开始条件由用户在Lex源程序的“辅助定义部分”定义,语法是<br>
<br>
%Start name1 name2 name3…<br>
<br>
其中Start可以缩写成S或s。开始条件名字的顺序可以任意给出,有很多开始条件时也可以由多个%Start行来定义它们。<br>
<br>
开始条件在识别规则中的使用方法是把它用尖括号括起来放在识别规则的正规式左边:<br>
<name1>expression<br>
<br>
要进入开始条件如Name1,在动作中用语句<br>
<br>
BEGIN name1<br>
<br>
它将Lex所处的当前开始条件改成 name1<br>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -