(1) 编译原理-词法分析

基本术语: 记号模式词素

术语含义
记号词素的规则名称,譬如所有变量其实都可以用记号:标识符代替,标识符就是所有变量的记号
模式表示该名词所属的规则的描述信息,譬如标识符的命名规则
词素记号对应符号的特定实例,譬如记号标识符的某个词素就是一个变量名

下面是一个实际例子:

记号词素模式(非形式描述)
constconstconst
relation<, <=, ==, >, >=< 或 <= 或 == 或 > 或 >=
idpi, a, count, d2字符开头的字母数字串

在多数程序设计语言中, 下面这些结构被当作记号: 关键字、操作符、标识符、常量、字符串、标点符号

1. 记号属性:

当一个记号的模式描述可以匹配多个词素时,需要词法分析器为这个记号提供附加的关于匹配的特殊词素的信息。

一个记号和其属性值可以用一个二元组来表示:

<id, 指向符号表中变量A相关表项>

<assign_op, >

<num, 整数2>

<exp_op, >

2. 输入缓冲:双缓冲输入方案

1. 词法分析器实现的三种方案:

  1. 使用词法分析器生成器,从基于正则表达式的说明,自动产生一个词法分析器。(此时由生成器提供输入流的读取和缓冲)
  2. 使用传统的程序设计语言编写词法分析器,并使用该语言提供的I/O功能对输入进行读取
  3. 使用汇编语言编写词法分析器,并显式地控制输入流的读取

2. 双缓冲区:(龙书: P61)

在一个词素被一个模式匹配之前,通常需要向前扫描该词素后面的若干字符

在缓冲区中,存在两个指针,一个指针指向匹配的开始词素,一个指针指向匹配结束位置的词素。两个指针的移动可以抽象地表示读取和回退,效率更高。在后续地学习中,需要关注匹配结束位置对应地指针。

输入的源程序读取结束时,还会读入一个EOF符号,表示整个源程序读取完毕。

3. *记号描述: (龙书: P62)

术语描述
字母表(字符类)表示有限符号集合
串(字符串, 句子, 字)字母表上的串时该字母表符号的有穷序列, 串s的长度是s中符号的个数,记为|s|, 空串记为 ϵ \epsilon ϵ
语言给定字母表上的串的集合
串的相关术语描述
s的前缀去掉s尾部0个或者多个符号后得到的串,如:ban是banana的前缀
s的后缀去掉s头部的0个或者多个符号后得到的符号串,如:nana是banana的后缀
s的子串去掉s个一个前缀和一个后缀后得到的串
s的真前缀、真后缀、真子串如果非空串x是s的前缀、后缀、子串,则称x为s的真前缀、真后缀、真子串
s的子序列从串s中删除0个或多个符号后得到的串
语言的运算描述
L和M的并:记为L ∪ \cup ML ∪ \cup M={s|s属于L或s属于M}
L和M的连接:记为LMLM={st|s属于L且t属于M}
L的克林(Kleene)闭包:记作L*L*= ∪ i = 0 ∞ L i \cup_{i=0}^{\infin} L^i i=0Li , 表示0或多个 L的连接
L的正闭包:记作 L + L^+ L+L*= ∪ i = 1 ∞ L i \cup_{i=1}^{\infin} L^i i=1Li , 表示1或多个 L的连接

1. 正规表达式与正规定义

正规表达式 和 我们常用的正则表达式并无二致,且此处只有基本的正则表达式语法, | + *

1. 正规定义:

d 1 → r 1 d 2 → r 2 d 3 → r 3 … d n → r n d_1 \rightarrow r_1 \\ d_2 \rightarrow r_2 \\ d_3 \rightarrow r_3 \\ \dots \\ d_n \rightarrow r_n d1r1d2r2d3r3dnrn

上面是正规定义的一般形式, r i r_i ri 中的字符为 d j d_j dj或者符号表中的字符。

如果 r i r_i ri用到了 d j d_j dj, 且 j ≥ i j\ge i ji, 则 r i r_i ri 是递归定义的。

为了区分名字和符号,通常用黑体表示正规定义中的名字。

例:

letter → \rightarrow A | B | ⋯ \cdots | Z | a | b | ⋯ \cdots | z

digit → \rightarrow 0 | 1 | ⋯ \cdots | 9

id → \rightarrow letter (letter | digit) *

2. 缩写表示法:

  • 一个或多个实例: ( r ) + (r)^+ (r)+
  • 零个或一个实例: ( r ) ? (r)? (r)?
  • 字符类: [ a b c ] [abc] [abc] 表示 a | b | c

3. 非正规集:

有些语言不可以用正规表达式描述,譬如带有括号匹配的编程语言, 但是在后面我们可以使用上下文无关文法 来描述

2. *状态转换图 (龙书 P68)

transition diagram

术语描述
状态用圆圈表示
状态间由箭头连接, 称之为边. 边上标识的字符(也称为标记字符)表示使状态s转换到状态r的输入字符

从一个状态出发, 如果每条边上的标记字符都不相同, 则称这个状态转换图是 确定的

如果输入一个字符, 可以让状态s转移到状态t, 也可以转移到状态r, 则称这个状态转换图是不确定的

符号 ϵ \epsilon ϵ 表示空串(即不读入字符)

4. *有穷自动机: 词法分析章节的核心内容

确定的有穷自动机和不确定的又穷自动机, 都只能识别正规集: 即它们只能识别正规表达式所表示的语言

1. 不确定有穷自动机(NFA): (龙书 P77)

不确定的有穷自动机是由以下几部分组成的数学模型:

  • 一个状态的又穷集合 S S S
  • 一个输入符号集合 Σ \Sigma Σ , 即输入符号表
  • 一个转换函数 m o v e move move, 把由状态和符号组成的二元组映射到状态集合
  • 状态 s 0 s_0 s0 是唯一的开始或初始状态
  • 状态集合 F F F 是接受(或终止)状态集合

2. 确定有穷自动机(DFA)

确定的又穷自动机(简称DFA)是不确定的有穷自动机的特例, 满足以下条件:

  • 没有一个状态具有 ϵ \epsilon ϵ 转换
  • 对于每个状态 s s s 和输入符号 a a a , 最多只有一条标记为 a a a 的边离开 s s s

3. 从NFA到DFA的转换: 子集构造法

子集构造法: 从NFA 构造 DFA

从NFA上构造所有 ϵ \epsilon ϵ- c l o s u r e ( s ) closure(s) closure(s) , 每个闭包集合将成为输出的DFA中的一个状态

NFA上构造的闭包集合之间的转化将成为DFA中的边

算法元素描述
输入一个NFA N
输出一个接受同样语言的DFA D
方法为D构造转换表(Tran), DFA的每个状态是NFA的状态集,D将并行地模拟N对输入串的所有可能的移动
操作描述
ϵ \epsilon ϵ- c l o s u r e ( s ) closure(s) closure(s)从NFA中的状态s出发, 只经过 ϵ \epsilon ϵ 转换可以到达的NFA状态集合
ϵ \epsilon ϵ- c l o s u r e ( T ) closure(T) closure(T)从T中的状态只经过 ϵ \epsilon ϵ 转换可以到达的NFA状态集合
m o v e ( T , a ) move(T, a) move(T,a)从T中的状态s经过输入符号a上的转换可以到达的NFA状态集

4. 从正规表达式到NFA

算法元素描述
输入字母表 Σ \Sigma Σ上的一个表达式r
输出接受 L ( r ) L(r) L(r)的NFA N N N
  • 对于 ϵ \epsilon ϵ , 构造出NFA:

1

  • 对于 Σ \Sigma Σ中每个符号a, 构造NFA:

2

  • 对于正规表达式 s ∣ t s|t st ,可构造复合的NFA N ( s ∣ t ) N(s|t) N(st)如下:

3

  • 对于正规表达式 s t st st, 可构造复合的NFA N ( s t ) N(st) N(st)如下:

4

  • 对于正规表达式 s ∗ s^* s, 可构造复合的NFA N ( s ∗ ) N(s^*) N(s)如下:

5

5. 从正规表达式到DFA

  1. 构造扩展的正规表达式 ( r ) # (r)\# (r)# 的语法树 T T T, 其中 # \# # 是附加在 ( r ) (r) (r) 后面的唯一结束标志
  2. 通过对 T T T进行深度优先遍历计算函数 n u l l a b l e nullable nullable f i r s t p o s firstpos firstpos l a s t p o s lastpos lastpos f o l l o w p o s followpos followpos的值
  3. 根据下面的伪代码构造 D s t a t e s Dstates Dstates (DFA D的状态集)、生成D的状态转换表Dtrans。D的开始状态是 f i r s t p o s ( r o o t ) firstpos(root) firstpos(root),接受状态是包含与结束符#相关位置的状态
初始时, Dstates中唯一未标记的节点是firstpos(root). root是(r)#语法树的根节点;
while Dstates 中存在一个未标记的状态T do begin
	标记T;
	for 每个输入符号a do begin
		令U是followpos(p)中的位置的集合,p是T中的某个位置,位置p的符号为a
		if U非空而且不在Dstates中 then
			将U作为一个未标记的状态加入到Dstates中
		Dtran[T, a] := U
	end
end
节点nnullable(n)firstpos(n)
n是一个标记为 ϵ \epsilon ϵ 的叶节点true ϕ \phi ϕ
n是一个标记为位置i的节点false{i}
1nullable( c 1 c_1 c1) or nullable( c 2 c_2 c2)firstpos( c 1 c_1 c1) ∪ \cup firstpos( c 2 c_2 c2)
2nullable( c 1 c_1 c1) and nullable( c 2 c_2 c2)if nullable( c 1 c_1 c1) then firstpos( c 1 c_1 c1) ∪ \cup firstpos( c 2 c_2 c2) else firstpos( c 1 c_1 c1)
3truefirstpos( c 1 c_1 c1)

followpos函数描述了紧跟在 i i i后面的字符集合,对于 followpos函数, 计算规则如下:

  1. 如果 n 是cat ( $\cdot ) 节 点 , 它 具 有 左 子 节 点 ) 节点, 它具有左子节点 ),c_1$ 和右子节点 c 2 c_2 c2 , 并且i是lastpos( c 1 c_1 c1)中的一个位置, 则firstpos( c 2 c_2 c2)中所有位置都在followpos(i)中
  2. 如果n是star( ∗ * )节点, 并且i是lastpos(n)中的一个位置, 则所有firstpos(n) 中的位置都在followpos(i) 中

当每个节点的firstpos和lastpos都计算完毕时, 通过对语法树进行深度优先遍历就可以计算出每一个位置的followpos

6. 最小化DFA的状态数

算法元素描述
输入DFA M M M(其状态集合为 S S S),输入符号集为 Σ \Sigma Σ, 转换函数为 f f f: S × Σ → S S \times \Sigma \rightarrow S S×ΣS , 开始状态为 s 0 s_0 s0, 接受状态集为 F 0 F_0 F0
输出一个DFA M ′ M' M ,它接收和 M M M相同的语言, 且状态数最少

简化方法是:

  1. 构造具有两个组的状态集合初始划分 Π \Pi Π: 接受状态组 F F F, 非接受状态组 S − F S-F SF
  2. Π \Pi Π 执行下述伪代码的流程, 构造新的划分 Π n e w \Pi_{new} Πnew
  3. 如果 Π n e w \Pi_{new} Πnew= Π \Pi Π, 令 Π f i n a l \Pi_{final} Πfinal = Π \Pi Π, 执行步骤4. 否则, 令 Π \Pi Π:= Π n e w \Pi_{new} Πnew, 重复步骤2
  4. 在划分 Π f i n a l \Pi_{final} Πfinal的每个状态组中选一个状态作为该组的代表,这些代表构成了简化后的DFA M ′ M' M的状态。令 s s s是一个代表状态,而且假设:在DFA M M M中,所有输入a上有从s到t的转换。令t所在的组的代表是r(r可以是t),在 M ′ M' M中有一个从 s s s r r r a a a上的转换。令包含 s 0 s_0 s0的状态组的代表是 M ′ M' M的开始状态,并令 M ′ M' M的接受状态是那些属于 F F F集的状态所在组的代表。 Π f i n a l \Pi_{final} Πfinal的每个组中或者仅包含F中的状态或者不含F中的状态
  5. 如果 M ′ M' M中有死状态(即一个对所有输入符号都有到自身的转换的非接受状态d),则从 M ′ M' M中删掉它;删除从开始状态不可到达的状态;取消从任何其他状态到死状态的转换定义。

for Π \Pi Π中的每个组G do begin

  当且仅当对任意输入符号a,状态s和t在a上的转换到达\Pi的同一组状态时, 才把G划分称小组,以便G的两个状态s和t在同一个小组中 

  /* 最坏情况下,一个状态就可能成为一个组 */ 

  用所有新形成的小组集代替\Pi_{new}中的G; 

end

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

fanqiliang630

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值