第二章 词法分析
2.1词法分析器的设计方法
1.词法分析器的处理结构(2种):
第一种:
词法分析器和语法分析器完全分开
词法分析器的输出(单词符号流)作为语法分析器的输入
将词法分析工作作为独立的一遍来完成,在这个过程中不断查询和完善符号表
第二种:
词法分析器作为语法分析器调用的子程序
每当语法分析器需要一个单词时便调用词法分析器
词法分析和语法分析交替进行
2.单词符号的分类与输出形式
(1)分类:
单词符号是程序语言的基本语法单位,具有确定的语法意义。程序语言的单词符号通常可分为下面五种:
保留字:如C语言中的if、else、while和do等
几乎所有的程序语言都禁止用户使用保留字作为标识符
标识符:用户自己定义的常量名、变量名、方法名等
常数:布尔常数(true/false)和其它常数
运算符:“+”、“-”、“*”、“/ ”、“>”、“<”等
界符:在语言中是作为语法上的分界符号使用的,如“,”、“;”、“(”、“)”、“=等
(2)输出形式:
单词符号通常表示成如下的二元式:(单词种别,单词自身的值/内码值)
① 单词种别:
即单词的种类。为了处理方便,通常让每种单词对应一个整数码,可以最大限度地将每个单词区别开来
保留字:可以统一视为一种,也可一字一种(后一种较常用)
标识符:统一归为一种
常数:可统一归为一种或按照整型、实型、布尔型等分为几种
运算符和界符可统一归为一种或采用一符一种
② 单词自身的值
如果一个种别只含一个单词符号,对于这个单词符号,种别编码就完全代表它自身的值
如保留字、运算符、界符
如果一个种别含有多个单词符号,除了给出种别编码之外,还应给出单词符号自身的值,以便区分同一种类的单词
标识符自身的值就是标识符自身的字符串
常数自身的值是常数本身的二进制数值或数值
3.状态转换图
(1)概念
在词法分析中,可以用状态转换图来识别单词。状态转换图是状态有限的有向图,结点代表状态,用圆圈表示;结点之间可由有向边连接,代表状态转换关系,有向边上可标记字符,表示前一状态接受某一个字符之后的状态转移
例如,上图表示在状态i下的状态转换:
若输入字符为x,则读入x并转换到状态j;
若输入字符为y,则读入y并转换到状态k。
(2) 表示
①状态转换图的要求:
·状态(即结点)个数有限
·至少一个初始状态,若干终止状态
·每条边上标有字符(也可以是空字符)-
②状态转换图的表示习惯:
·初始状态用“ → \rightarrow →O”表示
·非终止状态用“O”表示
·状态之间的跳转用“ ⟶ x \stackrel{x}{\longrightarrow} ⟶x”(有向边)表示
·终止状态用“◎*”表示
特别说明:终止状态用“◎*”表示
某些终止状态是在读入了一个其它不属于该单词的符号后才得到相应的单词编码的,这表明在识别单词的过程中多读入了一个符号,所以识别出单词后应将最后多读入的这个符号予以回退;我们对此类情况的处理是在终态上以**“*”作为标识**。
例如:想要识别数字,输入“234a”
读入‘2’:状态0->1
读入‘3’:状态1
读入‘4’:状态1
读入‘a’:状态1->2
回退,识别“234”
(3)举例
(4)编程
①含分支的状态
对应一个switch()语句
或对应一组if-else语句
②含回路的状态
·对应一个while语句
③终态
· 对应一个return语句
·意味着从词法分析器返回到调用段,一般指返回到语法分析器
2.2一个简单的词法分析器
大多数程序语言的单词符号都可以用状态转换图予以识别
构造一个C语言子集的词法分析器:
-定义C语言子集的单词符号及内码值
-C语言子集对应的状态转换图
-状态转换图的代码实现
1.C语言子集的单词符号表示
-使用种别编码不利于记忆,故使用助记符和种别编码对应
2.C语言子集对应的状态转换图
对输入程序串预处理
在设计的状态转换图中,首先对输入串做预处理,即剔除多余的空白符(在实际的词法分析中,预处理还包括剔除注释和制表换行符等编辑性字符的工作),使词法分析工作既简单又清晰。
. “ ”(空格)、“\t”、“\n”、“\r”、注释
将保留字作为一类特殊的标识符来处理
·即对保留字不专设对应的状态转换图,当转换图识别出一个标识符时就去查对表2.1的前五项,确定它是否为一个保留字。当然,也可以专设一个保留字表来进行处理。
3.状态转换图的实现
实现方法:让每个状态对应一小段程序
含分支多的状态对应switch()语句,分支少的对应if-else
含回路的状态对应一个while语句
4.词法分析器的设计
输入:源程序文件(字符流)
输出:单词符号流
(1)预处理(2)识别单词对象(3)词法错误处理(4)符号表
2.3正规表达式与有限自动机简介
1.正规表达式与正规集
(1)定义和运算
状态转换图可以构造词法分析程序,但属于非形式化描述
正规表达式(简称正规式)是词法分析的形式化表示方法
所谓形式化的方法,是指用一整套带有严格规定的符号体系来描述问题的方法,优点:更加清晰和准确
-例如:形式化表示标识符,即标识符的正规式:
letter(letter | digit)* 能够表示的单词集合称为正规集
这里,字母字符用letter表示,数字字符用digit表示
·letter与(letter | digit)*的并置表示连接
·括号中的“|”表示letter或digit两者选一
·“*”表示零次或多次引用,长度为0,1,2,……·
(2)相关基础概念
① 字母表:
语言元素的非空有穷集合,通常用‘ ∑ \sum ∑’表示
·例如: ∑ = { a , b , c } \sum=\{a, b, c\} ∑={a,b,c}
· ∑ \sum ∑是字母表,它由a, b, c三个元素组成
·注意:字母表中至少包含一个元素,任何语言的字母表规定了该语言中允许出现的一切符号。
② 符号:
字母表中的每一个元素,也叫字符
③ 符号串:
由符号组成的有穷序列(可以是0个),也叫字
·如 ∑ = { a , b } \sum =\{a, b\} ∑={a,b},则ε , a, b, aa, ab, aaa, bbb,……都是字
④ 空字:
长度为0的字,用‘ε’表示
⑤ 字的全体:
所有字组成的集合,用“ ∑ ∗ \sum^{*} ∑∗”表示
·例如:如果 ∑ \sum ∑={a, b}
·则 ∑ ∗ \sum^{*} ∑∗= { e , a,b,aa,ab,ba,bb,aaa,…}
⑥ 空语言:
不含任何字的语言{ },用‘Φ’表示
注意区分ε、{ }和{ε } :
ε是空字,是语言字的集合中的一个元素,不是 ∑ \sum ∑的元素
{ }不包含任何字,属于集合的概念
{ε }由空字组成的集合,这个集合比{ }多一个元素
(3)递归定义
对于给定的字母表,正规式和正规集定义为:
1) :ε和 ϕ \phi ϕ都是 ∑ \sum ∑上的正规式,它们所表示的正规集分别为{epsilon}和Φ。
2)对于任一个符号a∈ ∑ \sum ∑,a是 ∑ \sum ∑上的一个正规式,它所表示的正规集为{a}。
这两条规则称为基础规则
第二条:从普通的符号产生对应的正规式和正规集
3)如果R和S是 ∑ \sum ∑上的正规式,它们所表示的正规集分别为L®和L(S),则:
① R│ s是上的正规式,它所表示的正规集为L®UL(S);
②RS是 ∑ \sum ∑上的正规式,它所表示的正规集为L®L(S);
③(R)*是 ∑ \sum ∑上的正规式,它所表示的正规集为(L®)*;
第三条规则称为归纳规则
根据已有的正规式和正规集生成其它正规式和正规集可以看出,归纳规则中有三种运算:
“|”为或运算
“’·”为连接运算,通常可省略
“*”为闭包运算
4)仅由有限次使用规则(1)~(3)得到的表达式是上的正规式,它所表示的集合是 ∑ \sum ∑上的正规集
第四条规则称为界限规则或者终止规则
根据已有的正规式和正规集生成其它正规式和正规集
注意:
在后面的正规式中,约定:不做特殊说明情况下,大写字母(如R、S等)对应字的集合
(4)正规式的运算
①连接运算:
· 字的连接:
设“ab”和“aab”是两个字,则
ab · aab = abaab
· 正规式的连接:
RS = {a β |a∈R&β∈S}
例:A={ab,bc} ,B={ac, cb },则:
AB ={abac, abcb, bcac, bccb} AB≠BA
② 正规式的幂运算:
正规式自身的n次连接
③ 闭包运算:
·集合R的闭包表示为R*,具体定义为:*
R ∗ = R 0 ∪ R 1 ∪ R 2 ∪ R 3 ∪ … \mathbf{R}^{*}=\mathbf{R}^{0} \cup \mathbf{R}^{1} \cup \mathbf{R}^{2} \cup \mathbf{R}^{3} \cup \ldots R∗=R0∪R1∪R2∪R3∪…
集合R的正闭包表示为R+,具体定义为:
R + = R 1 ∪ R 2 ∪ R 3 ∪ … = R R ∗ \mathbf{R}^{+}=\mathbf{R}^{1} \cup \mathbf{R}^{2} \cup \mathbf{R}^{3} \cup \ldots=\mathbf{R} \mathbf{R}^{*} R+=R1∪R2∪R3∪…=RR∗
显然: R ∗ = R 0 ∪ R + \mathbf{R}^{*}=\mathbf{R}^{0} \cup \mathbf{R}^{+} R∗=R0∪R+
(5)正规式的运算性质
-对于 ∑ \sum ∑上的正规式R和S,如果它们的正规集满足L®=L(S),则称R和S等价,记为R=S。正规式的性质有:
2.有限自动机:可以自动识别单词的机器
(1)有限自动机(Finite Automation) :
·FA是一个状态转换图,“有限”指的是状态有限。当前状态读入一个字符后,和后继状态的转换有以下三种情形:
1)后继状态为自身
2)后继状态只有一个
3)后继状态有多个
·如果每次转换的后继状态是唯一的,则称它为确定有限自动机(Deterministic FA)
·如果每次转换的后继状态不是唯一的,则称它为非确定有限自动机(Nondeterministic FA)
① 确定有限自动机(DFA) :
·DFA是一个五元组, M d = ( S , ∑ , f , s 0 , Z ) M_d=(S, \sum, f, s_0,Z) Md=(S,∑,f,s0,Z),其中:
1)s是一个有限状态集合,它的每个元素称为一个状态
2) ∑ \sum ∑是一个有穷字母表,它的每个元素称为一个输入字符
3)f是一个从S× ∑ ∗ \sum^* ∑∗至S的单值映射,也叫状态转移函数
4) s 0 ∈ S s_0∈S s0∈S是唯一的初态
5) Z ⊂ S \mathbf{Z} \subset S Z⊂S是一个终态集
② 非确定有限自动机(NFA) :
·NFA是一个五元组, M d = ( S , ∑ , f , Q , z ) M_d=(S,\sum,f,Q,z) Md=(S,∑,f,Q,z),其中:
1)S是一个有限状态集合,它的每个元素称为一个状态
2) ∑ \sum ∑是一个有穷字母表,它的每个元素称为一个输入字符
3)f是一个从S× ∑ ∗ \sum^* ∑∗至S的多值映射,也叫状态转移函数
4) Q ⊂ S \mathbf{Q} \subset S Q⊂S是非空初态集
5) Z ⊂ S \mathbf{Z} \subset S Z⊂S是一个终态集
·NFA相比于DFA的特征:
(1)可以有若干个初始状态
(2)f多值映射
(3)允许接收字和空字符ε
(2)有限自动机识别的语言
① FA所识别的语言
-对于一个自动机FA而言,如果存在一条从初始状态到终止状态的通路,通路上有向边所识别的字符依次连接所得到的字符串为a,则称α可以为FA所接受或者α为FA所识别(FA所识别的字)
-FA所能识别的字符串集为FA所识别的语言,记为L(M)
② FA的等价
-对于任意两个FA M和 FA M’,如果L(M)=L(M’),则称M和M’等价
-对于任意一个NFA M,一定存在一个DFA M’与其等价
2.4正规表达式到有限自动机的构造
从正规式开始构造DFA的过程有以下几个步骤:
-1.由正规式构造NFA;
-2.由NFA构造与之等价的DFA(确定化)
-3.DFA的化简
1.由正规式构造等价的NFA
-1、对于给定的正规式R,将其表示成“拓广转换图”其中X为初始状态,Y为终止状态
-2、对正规式中的三种运算,分别采用如下的对应转换规则
2.NFA的确定化
(1)相关概念
-NFA的确定化:构造一个和NFA等价的DFA
-状态集合I的:ε_ 闭包
·设I是FA:M的状态子集,则以下状态属于:ε_CLOSURE(I) :
1)若 s i s_i si∈I,则 s i s_i si∈ ε_CLOSURE(I) ;
2)若 s i s_i si∈I,则对从 s i s_i si出发经过任意条ε通路所能到达的状态 s j s_j sj,都有 s j s_j sj∈ε_CLOSURE(I)。
-定义 I = ε _ C L O S U R E ( J ) I= ε\_CLOSURE(J) I=ε_CLOSURE(J),其中:
I = { s 1 , s 2 , … , s n } , J = f ( I , a ) = f ( s 1 , a ) ∪ f ( s 2 , a ) ∪ … ∪ f ( s n , a ) \mathbf{I}=\left\{\mathbf{s}_{1}, \mathbf{s}_{2}, \ldots, \mathbf{s}_{n}\right\}, \quad \mathbf{J}=\mathbf{f}(\mathbf{I}, \mathbf{a})=\mathbf{f}\left(\mathbf{s}_{1}, \mathbf{a}\right) \cup \mathbf{f}\left(\mathbf{s}_{2}, \mathbf{a}\right) \cup \ldots \cup \mathbf{f}\left(\mathbf{s}_{\mathbf{n}}, \mathbf{a}\right) I={s1,s2,…,sn},J=f(I,a)=f(s1,a)∪f(s2,a)∪…∪f(sn,a)
(2)子集法
1)构造一张转换表,第一列记为状态子集I,对于不同的符号(a∈ ∑ \sum ∑),在表中单设一列 I a I_a Ia;
2)表的首行首列置为ε_CLOSURE(s0),其中s0为初始状态;
3)根据首行首列的I,为每个a求其Ia并记入对应的Ia列中,如果此la不同于第一列中已存在的所有状态子集I,则将其顺序列入空行中的第一列;
4)重复3)直至对每个I及a均已求得Ia,并且无新的状态子集Ia加入第一列时为止;
5)重新命名第一列的每一个状态子集,形成新的状态转换矩阵,即为与NFA等价的DFA
3.DFA的化简
(1) 基本概念
①状态的等价:
· 假设s1和s2是M的两个不同的状态,如果从s1出发能识别字符串α而停于终态,从s2出发也能识别α而停于终态。反之也是成立的。称s1和s2等价,否则称它们可区分
②确定有限自动机M的化简:
·寻找一个状态数比M少的DFA M’,使得L(M)=L(M’)
③化简后的DFA满足两个条件:
· 没有多余状态
· 状态集中不存在等价状态
(2)方法
1)首先将DFA的状态集按照终态与非终态分为两个子集,形成初始划分H
2)对每个子集G进行如下变换:
① 把G划分为新的子集,使得G的两个状态s1和s2属于同一子集,当且仅当对任何输入符号a,状态s1和s2的后继状态都属于同一子集
②用G划分出的所有子集替换G,形成新的划分 H n e w H_{new} Hnew
3)如果 H n e w H_{new} Hnew,执行(4);否则令 H n e w H_{new} Hnew,重复执行(2)
4)划分结束后,一个子集只对应一个状态,作为代表状态,删去其它一切等价状态,并将对应的弧射向这个代表状态
划分原则:两个状态s1和s2属于同一子集,当且仅当对任何输入符号a,状态s1和s2的后继状态都属于同一子集
任意取一个字符a ∈ ∑ \sum ∑,考察当前的划分:1,2,3…
2.5词法分析器的自动生成
设计一种程序,使得其满足:
-输入:编程语言的词法正规式(标识符、关键字、常量…)
-输出:自动产生词法分析程序
LEX就是这种程序,可以生成高级语言(例如C语言)的词法分析器
-可单独使用
-和YACC(根据输入生成语法分析程序)结合使用
JavaCC也是一种可以自动生成词法分析程序和语法分析程序的软件