570 likes | 809 Views
第三章 词法分析 词法分析是编译的第一个阶段,它的主要任务是从左至右逐个字符地对源程序进行扫描,产生一个个单词序列,用以语法分析。执行词法分析的程序称为词法分析程序或扫描程序。 3.1 词法分析的基本概念 3.1.1 词法分析的意义 词法分析程序完成的是编译第一阶段的工作。词法分析工作可以是独立的一遍,把字符流的源程序变为单同序列,输出在一个中间文件上,这个文件做为语法分析程序的输入而继续编译过程、为减少与外存储器交换数据,将词法分析程序设计成一个子程序,每当语法分析程序需要一个单词时,则调用该子程序。词法分析程序每得到一次调用,便从.
E N D
第三章 词法分析 词法分析是编译的第一个阶段,它的主要任务是从左至右逐个字符地对源程序进行扫描,产生一个个单词序列,用以语法分析。执行词法分析的程序称为词法分析程序或扫描程序。 3.1 词法分析的基本概念 3.1.1 词法分析的意义 词法分析程序完成的是编译第一阶段的工作。词法分析工作可以是独立的一遍,把字符流的源程序变为单同序列,输出在一个中间文件上,这个文件做为语法分析程序的输入而继续编译过程、为减少与外存储器交换数据,将词法分析程序设计成一个子程序,每当语法分析程序需要一个单词时,则调用该子程序。词法分析程序每得到一次调用,便从
源程序文件中读入一些字符,直到识别出一个单词。停留在空格、回车、制表符或下一单词的第一个字符为止。这样把词法分析程序和语法分析程序是放在同一遍里,而省掉了中间文件,节省了运行时间。源程序文件中读入一些字符,直到识别出一个单词。停留在空格、回车、制表符或下一单词的第一个字符为止。这样把词法分析程序和语法分析程序是放在同一遍里,而省掉了中间文件,节省了运行时间。 正如前面所说,正则文法是上下文无关文法的特例,也就是说,词法分析可以看作语法分析的的一部分,词法描述完全可以归并到语法描述中去,。那么为什么将词法分析做为一个独立的阶段?为什么把编译过程的分析上作划分成词法分析和语法分析两个阶段?主要的考虑因素为: 1.使整个编译程序的结构更简洁、清晰和条理化。词法分析比语法分析简单的多,由于源程序结构上的一些细节,常使得识别单词的工作交给语法分析处理比较曲折和费时。例如,空白和注释的处理;早期的FORTRAN受书写格式限制,需在识别单词时进行特殊处理等等。如果这些工作都在语法分析时一并考虑,显然会使得分析程序的结构变得十分复杂。
2.编译程序的工作效率也是要考虑的。正则文法和上下文无关文法采用的识别器是不同的,把词法分析从语法分析独立出来,采用专门的读字符和分离单词的技术可大大加快编译速度。由于单词的结构可采用与正则文法对应的有限自动机进行识别,进而可建立词法分析程序的自动构造工具。 3.可增强编译程序的可移植性。在同一个语言的不同实现中,或多或少地会涉及到与设备有关的特征,比如采用 ASCII还是 EBCDIC字符编码。另外语言的字符集的特殊性的处理,一些专用符号,如 PASCAL中的“↑”的表示等等,都可置于词法分析程序中解决而不影响编译程序其它成分的设计。
3.1.2词法分析的输入输出 词法分析程序的功能是读入源程序,输出单词符号。词法分析是读入文本文件的源程序,读入文本文件可采用从文件中逐个读入符号,或者建立一个缓冲区,先将字符读入缓冲区,然后从缓冲区中读入字符,使用缓冲区也可以采用双区域方式,即将缓冲区分成二部分一部分在分析时,更换另一部数据,从而提高词法分析的效率。 单词符号是一个程序设计语言的基本语法符号。程序设计语言的单同符号大致可分成5种: 1.基本字,也称关键字,如 PASCAL语言中的 begin,end,if;while和 var等。 2.标识符,用来表示各种名字,如常量名、变量名、类型名、函数名和过程名等。 3.常数,各种类型的常数,如 25,3.1415,TRUE和“ABC”等。
4.运算符,如+,-,*,/,<=等。 5.界符,如逗点,分号,括号,冒号等。 词法分析程序所输出的单词符号常常采用以下二元式表示(单词种别,单词自身的值)。单词的种别是语法分析需要的信息,而单词自身的值则是编译其它阶段需要的信息。比如在 PASCAL的语句 const i=25, yes=1;中的单词 25和 1的种别都是常数,常数的值25和1;对于代码生成来说,是必不可少的、有时,对某些单词来说,不仅仅需要它的值,还需要其它一些信息以便编译的进行。比如,对于标识符来说,还需要记载它的类别。层次还有其它属性,如果这些属性统统收集在符号表中,那么可以将单词的二元式表示设计成如下形式(标识符,指向该标识符所在符号表中位置的指什),如上述语句中的单词i和yes的表示为:
(标识符,指向i的表项的指针) (标识符,指向yes的表项的指针) 单词的种别可以用整数编码表示,设标识符编码为1,常数为2,保留字为3,运算符为4,界符为5. 这种码称为机内码。 例:写出程序段if i=5 then x:=y; 在经词法分析器扫描后输出的单词符号和它们的表示
答: 保留字if (3,‘if’) 标识符i (1,指向i的符号表入口) 等号= (4,‘=’) 常数5 (2.‘5’) 保留字then (3,‘then’) 标识符x (1,指向x的符号表入口) 赋值号:= (4‘:=’) 标识符y (1,指向y的符号表入口) 分号; (5,‘;’) 综上所述可以把单词表示为:(内部码,属性)的二元式。
3.1.3 词法分析的实现方法 词法分析的实现主要有二种方法,一是将词法分析单独写成一个独立的程序,其工作作为独立的一遍,把字符流的源程序变为单同序列,输出在一个某个中间文件上(也可直接存放在内存中),另一种方法是将词法分析程序设计成一个子程序,每当语法分析程序需要一个单词时,则调用该子程序。词法分析程序每得到一次调用,便从源程序文件中读入一些字符,直到识别出一个单词。停留在空格、回车、制表符或下一单词的第一个字符为止。这样把词法分析程序和语法分析程序是放在同一遍里,而省掉了中间文件,节省了运行时间。
3.2 正规式自动机和状态图 3.2.1正规式的表示 在编译程序设计时,应尽量减少语法规则,从而提高编译程序的效率,那么如何使设计的正规规则最少,或者当一个语言较复杂时,怎样能够判定它是否能用正规文法表示,以及如何自动生词法分析程序等到,都需要用一种新的表示方法,为些引进了正规式和正则集。在这里可以用正规式来描述单词符号语言中的基本语法符号,井且基于正规式这类描述工具,可以建立词法分析技术,进而可以建立词法分析程序的自动构造方法。
正规式也称正则表达式,也是表示正现集的工具。正规式也称正则表达式,也是表示正现集的工具。 对于字母表Σ,Σ上的正规式和它所表示的正现集是递归定义的: 1.ε和φ是Σ上的正规式,它们所表示的正规集分别为{ε}和φ; 2.任何a∈Σ,a是Σ上的一个正规式,它所表示的正规集为{a}; 3.假定e1和e2都是Σ上的正规式,它们所表示的正现集分别为L(e1)和L(e2),那么,(e1)、e1|e2、e1·e2和e1*也都是正规式,它们所表示的正规集分别为L(e1),L(e1)∪L(e2),L(e1)L(e2)和L(e1)* 由有限次使用上述三步骤而定义而构成的表达式就是Σ上的正规式,由这些正规式所表示的字符串集就是Σ上的正规集。
例:令Σ={a,b},则 ba* a(a|b)* (a|b)*(aa|bb)(a|b)*都是正规式,它们的正规集为: {ban|n≥0} {以a为首的字} {含有两个相继的a或两个相继的b的字} 例:设Σ1={0,1},则(0|1)(0|1)* 是Σ1上的正规式 设Σ2={A,B,0,1}*,则(A|B)(A|B|0|1)*是Σ2上的正规式 定义 3.1若两个正规式e1和e2所表示的正规集相同,则说e1和e2是等价,记作e1=e2。 例: a|b=b|a b(ab)*=(ba)*b , (a|b)*=(a*|b*)*
设r,s,t为正规式,则: (1)r|s=s|r (2)r|(s|t)=(r|s)|t (3)(rs)t=r(st) (4)r(s|t)=rs|rt (r|s)t=rt|st (5)εr=rε=r (6)(r*)*=r* 下面以(1)为例,证明之 证:∵L(r|s)= L(r) ∪L(s)= L(s) ∪L(r)= L(s|r) ∴ r|s=s|r 证毕。
1.将Σ的一个正规式转换成文法G=(Vn, Vt,P,S),令其中的Vt =Σ,确定产生式和Vn的元素用如下办法。 对任何正规式r,选择一个非终结符S生成产生式S→r,并将S定为G的识别符号。 若x和y都是正规式,对形如A→xy的产生式,重写:A→xB,B→y两产生式,其中B是新选择的非终结符,即B∈Vn 。 对已转换的文法中的形如 A→x*y的产生式,重写为 A→xB A→y B→xB B→y 其中B为一新非终结符。 或 A→xA A→y 对形如A→x|y的产生式,重写为; A→x A→y 不断利用上述规则做变换,直到每个产生式最多含有一个终结符为止。
例:将 R=a(a|d)*转换成相应的正规文法,令 S是文法的开始符号、首先形成S→a(a|d)*,然后形成 S→aA和 A→(a|d)*.再重写第二条产生式形成: S→aA A→(a|d)A A→ε 即:S→aA A→aA A→dA A→ε 严格来说,正规文法不含ε规则,故消去之。改为: S→aA|a A→aA|dA|a|d 2.将正视文法转换成正规式。基本上是上述过程的逆过程,最后只剩下一个开始符 号定义的产生式,并且该产生式的右部不含非终结符。其转换规则下: (1)产生式 A→xB,B→y 有正规式 A=xy (2)产生式 A→xA|y 有正规式A=x*y (3)产生式 A→x, A→y 有正规式A=x|y
例:文法 G[S] S→aA S→a A→aA A→dA A→a A→d 先有: S=aA|a A=(aA|dA)|(a|d) 再将A的正规式变换为A=(a|d)A|(a|d) 据表中规则2变换为:A=(a|d)*(a|d) 再将A右端代入S的正规式得: S=a((a|d )*(a|d))|a 再利用正规式的代数变换可依次得到 S=a((a|d)* (a|d ) |ε) S=a(a|d)* 即 a(a|d)*为所求。
3.2.2有限自动机 形式语言的识别在编译理论中起着重要的作用。语言识别器是一个程序,它以串x作为输入,x是语言的句子是回答“是”,否则回答“不是”。有限自动机(也称有穷自动机)它也是一种识别程序,它能准确地识别正现集,即识别正规文法所定义的语言和正规式所表示的集合,引人有穷自动机这个理论,正是为了词法分析程序的自动构造寻找的特殊方法和工具。 有限自动机分为两类;确定的有限自动机(Deterministic Finite Automata)和不确定的有限自动机(Nondeterministic Finite Automata),虽然确定有限自动机和非确定有限自动机面都能识别正规集,前者任一状态对于某输入符号只存在一种状态的转换,后者有些状态对于某输入符号可能存在多种状态的转换。前者执行效率高,占存储空间大,后者反之。下面我们给出确定的有穷自动机和不确定的有穷自动机的定义,有关概念及不确定的有穷自动机的确定化算法。
1.确定的有限自动机(DFA) 定义 3.2 一个确定的有限自动机(DFA)M是一个五元组:M=(K,Σ,f,S,Z)其中 (1) K是一个有穷集,它的每个元素称为一个状态 (2) Σ是一个有穷字母表,它的每个元素称为一个输入字符 (3) f是一个从K×Σ→K的单值映象 (4) S∈K,是唯一的初始状态(开始状态) (5) Z K,是非空的终止状态集,终止状态也称接受状态或结束状态。
f(S,a)=S’意味着,当现行状态为S,输入字符为a是时,将转换到下一个状态S’ 。这里把S’ 称为S的一个后继状态。 一个 DFA可以也可以表示成一个状态图(或称状态转换图简称转换图)。假定DFA M含有m个状态,n个输入字符,那么这个状态图含有m个结点,每个结点最多有n个弧射出,整个图含有唯—的一个初态结点和若干个终态结点,初态结点以“=>”,终态结点用双圈表示。若f(S,a)=S’则从状态结点S到状态结点S’画标记为a的弧;
例:接受语言(a|b)*abb的DFA状态图 例:上述状态图的五元式的DFA为: DFA M=({S0,S1,S2 , S3},{a,b},f,S0 ,{S3,}) 其中: f (S0,a) = S1 f (S0,b) = S0 f (S1,a) = S1 f (S1,b) = S2 f (S2,a) = S1 f (S2,b) = S3 f (S3,a) = S1 f (S3,a) = S0
另外一个DFA还可以用矩阵表示,该矩阵的行表示状态,列表示输入字符,矩阵元素表示相应状态行和输入字符列下的新状态,即k行a列为f(k,a)的值。其中用“=>”标明初态;否则第一行即是初态,相应终态行在表的右端标以 1,非终态标以 0。则上例中可表示成:
定义 3.3 扩充了映象f定义如下: f(R,ε)=R,其中R为任意状态, f(R,tα)=f(f(R,t),α) 其中α∈∑*,t∈∑ 定义 3.4 对于某个DFA M=(K,Σ,f,S,Z),如有f (S,α)=P,P∈Z ,则称字符串α可被DFA所接受 若M是又初始状态,又是终止状态,显然ε也是可以被接受的。DFA M所能接受的全体记为L(M)
例:试证 abaaba为上述的 DFA所接受。 f(S,abaaba)=f(U,baaba)=f(V,aaba)=f(U,aba)=f(Q,ba)=f(Q,a)=Q ∵Q∈Z ∴得证。 结论:∑上的一个字符串集V ∑*是正规的,当且仅当存在一个∑上的确定有穷自动机M,使得V=L(M)。
2.不确定的有穷自动机(NFA) 定义 3.5 一个不确定的有穷自动机(NFA)M是一个五元组M=(K,Σ,f,S,Z)其中 1.K是一个有穷状态集 2.Σ是一个有穷输入字符字母表 3. f是个从K×Σ到K的子集的映象。 4 S K,是一个非空初态 (开始状态)集; 5. Z K,是一个非空终态(终止状态)集。 一个NFA可用带标记的有向图来表示,这张有向图又称状态转换图,简称转换图。它的结点是状态,有标记的边代表转换函数,这种转换图,从一个状态出发的不同边可以有相同的标记,该转换图的边可以用ε标记。
例:试根据如下状态图构造相应的NFA M=({S,A,B,Z},{a,b},f,{S},{Z}) 其中f: f(S,a)={A} f(S,b)={B} f(A,a)={Z} f(A,b)={B} f(B,a)={A,B} f(B,b)={Z} f(Z,a)={A,Z} f(Z,b)=Φ
显然该自动机能接受字符串 baaaaba abaaaa abababa 定义 3.6 扩充了映象f定义如下: f(R,ε)={R},其中R为任意状态, f(R,tα)是所有集合f(Qi,α)日并集,即f(R,tα)= f(Q1,α)∪f(Q2,α) ∪ f(Q3,α),···∪f(Qn,α) 其中α∈∑*,t∈∑ 而f(R,t)={Q1,Q2,Q3,···Qn}
直观起见 f({Q1,Q2,Q3,···Qn},α)= f(Qi,t) 因而,假如f(R,t)={Q1,Q2,Q3,···Qn},则 f(R,tα) =f({Q1,Q2,Q3,···Qn},α) 定义 3.7对于某个NFA M=(K,Σ,f,S,Z),如对于某个NFA存在状态P,P∈Z,且P∈f(S0, α),S0∈S ,则称字符串α可被该NFA所接受 定义 3.8对于任何两个有限自动机M和M’,如果L(M)=L(M’),则称M和M’是等价的。
3. NFA到DFA的转换 对于一个NFA总是存在与之对应的DFA。新的DFA的每个状态对应NFA的一个状态集,其中NFA的状态集是输入各种符号串所能到达的状态集,用DFA的状态来保存NFA在读入符号后能到达的所有状态踪迹。这样NFA转换表里,每个条目是一个状态集,在DFA的转换表中,每个条目只有一个状态。若NFA有n个状态,所有的状态集个数为2n-1(不能出现空集), DFA到NFA转换的思想是让DFA的每个状态代表NFA的状态集,这个DFA也就是用它的状态去保持NFA在读入符号后能到达的所有状态踪迹。也就是说,在读入输入串a1a2a3···an后,DFA到达一个氏表NFA状态子集T的状态,这个子集T是从NFA的开始状态沿着某些标有a1a2a3···an的路径的状态数集合,一般来说,当某个NFA的状态集合K包含n个状态时,相应的DFA的状态集合K’将包含2n-1个状态。但事实上其中很多状态是达不到的。
从NFA构造相应的DFA的算法: (1) 将全部开始状态,以及从这些状态用ε弧能到达的状态集作为DFA的开始状态。 (2) 对于每个新添的DFA状态作: 对每个输入符号a,作移动集合({f(T,a)|T∈Si})以及移动集合用ε弧能到达的状态集作为DFA的状态。 (3) 重复2直至没有新的状态 添入为止 (4) 包含原NFA的终止状态的状态为新的终止状态。
例:试构造上例中的相应的DFA。 解:列出DFA的状态:
令{S},{A},{B},{Z},{A,B},{A,Z},{A,B,Z},{B,Z}分别为S0, S1, S2, S3, S4, S5, S6, S7。则相应的DFA M‘为: M’=({S0, S1, S2, S3, S4, S5, S6, S7},{a,b},f’,S0,{S3, S5, S6, S7}) 其中f’见图:
令状态{X,5,1}、{5,3,1}、{5,4,1}、{5,3,1,2,6,Y}、{5,4,1,2,6,Y }、{5,4,1,6,Y }、{5,3,1,6,Y}分别为0,1,2,3,4,5,6则
4. 确定自动机的化简 为提高语言的识别效率,应该尽量压缩DFA的状态,这里需要研究DFA状态所谓最小化问题,也就是能不能找到一个状态数最少的DFA,可以证明在不考虑同构的前提下,最小化的DFA是唯一的。所谓确定自动机M的最小化实际上是找一个状态数最少的M’使L(M)=L(M’)。 定义 3.9如果DFA M从状态S出发,输入是ω,它可以停在某个终止状态,但是从T 出发同样的输入,它停在一个非终止状态,或者相反,称为串ω可区分(可区别)状态S和T。 例:ε可区分任何终止和非终止状态。 最小化DFA状态数的算法就是把DFA的状态数分成一些不相交的子集,每一子集的状态都是不可区别的。每个子集合并成一个状态。终止状态和非终止状态是可区分的,因此最初应划分成两个组,终止状态组和非终止状态组。
极小化DFA的状态数算法: (1) 构造状态集合的初始划分Π,分成两组,终止状态F和非终止状态K-F。 (2)应用下面的过程对Π构造新的划分Πnew Πnew=Π; do {Π=Πnew; for(Π中的每个组G ) {把G划分成小组,G的两个状态S和T在同一小组中,当且 仅当对所有的输入符号a,S和T的a的转换是到Π的同一组中 } while(Π!=Πnew);
(3) 在最终划分中的每个状态组中选一个状态代表它。 (4) 如果等价的DFA中有对任何字符a 都转换到 本身,而不能从开始状态到的那些状态,以及不可能从开始状态到达的那些状态删除。从任何其它到上述状态的转换都成为无定义。 例:对图表示的DFA最小化
解:为了方便起见将图代表的DFA列一张表(如下)解:为了方便起见将图代表的DFA列一张表(如下) {0,1,2,3} {4} 对于状态3输入b时不在同一组,故 {0,1,2} {3} {4} 对于状态1输入b时不在同一组,故 {0,2} {1} {3} {4} 而{0,2}对于任何输入都 在同一组中,故不能再分了。
5. 正规式和有限自动机的转换 可以证明对于正规式都存在与之等价的有穷自动机,反之亦成立。 从正规式到有限自动机 利用转换系统构造NFA,把具有唯一开始状态和唯一终止状态允许标记有ε弧的NFA称为转换系统 转换系统的构造: Φ,ε,a的转换系统分别是:
令SABZ , ABZ , BZ 分别为S0、S1和S2,则相应的DFA为: DFA A0=({S0 , S1 , S2},{a,b},δ, S0,{S0 , S1 , S2}) 其中:δ(S0 ,a)= S1 δ(S0 ,b)= S2 δ(S1 ,a)= S1 δ(S1 ,b)= S2 δ(S2 ,b)= S2 即:
(2) (a|b)*ab(a|b)* (2)解
令 SA,AB,A,ACZ,ABCZ分别为 S0 , S1 , S2, S3 , S4则最小化为: { S0 , S1 , S2,},{ S3 , S4} S1的输入b不在同一组中,故 { S0 , S2,},{S1} ,{ S3 , S4} { S0 , S2},{S1} ,{ S3 , S4}分别为S’ ,A’ ,Z’则相应的DFA为: DFA A0=({ S’,A’,Z’},{a,b},δ, S’,{ Z’}) 其中:δ(S’,a)= A’, δ(S’ ,b)= S’ (A’,a)= A’ δ(A’,b)= Z’ δ(Z’,a)= Z’ δ(Z‘,b)= Z’ 注意:据题意不必确定化和最小化,但不能含ε弧
从有限自动机到正规式 把M的所有状态转换图上加上两个结点X、Y,从X结点用所有的初始结点,从M的所有终态结点用ε弧连接到Y结点,形成只有一个开始状态和一个终止状态的转换系统。然后用下列规则逐步替换(适当可用等价的状态替换)直至只剩结点X、Y为止。
例:试写出下图的正规式 解:
故FA的正规式为(a|bb*a(bb*a)*a)* 化简为: ( ( ε |bb*a(bb*a)*)a)* 即:((bb*a )* a)*
正规文法和有穷自动机间的转换 正规文法也可描述正规集,正规文法与有穷自动机有特殊关系,采用下面的规则可从正规文法G直接构造一个有穷自动机NFA M;使得L(M)=L(G) (1)右线性文法 ·字母表与G的终结符集相同; ·为G中的每个非终结符生成M的一个状态,(不妨取成相同的名字)G的开始符 号S是开始状态S; ·增加一个新状态Z,做为NFA的终态; ·对G中的形如A→tB 其中t为终结符,A和B为非终结符的产生式,构造M的一个转换函数 f(A,t)=B;即:从A到B画一条弧标记为t。 ·对G中形如A→t的产生式,构造M的一个转换函数f(A,t)=Z。从A到Z画一条弧标记为t 例:与文法 G[S]等价的 NFA M 如图
G[S]: S→aA|bB|b A→aB|bA|a B→aS|bA|a 则:其NFA如图:
(2)左线性文法 ·字母表与G的终结符集相同; ·为G中的每个非终结符生成M的一个状态, G的识别符号Z是终止状态Z; ·增加一个新状态S,做为NFA的开始状态; ·对G中的形如A→Bt和t 其中t为终结符,A和B为非终结符的产生式,构造M的一个转换函数f(B,t)=A;即: ·对G中形如A→t的产生式,构造M的一个转换函数f(S,t)=A,即:从B到A画一条弧标记为t。