文章目录
概述
一个语言中最小的语法单位是符号(单词),如标识符、常数、界限符等。词法分析的任务就是将源程序代码转换为单词符号序列。词法规则一般用3型文法或正规表达式描述。
其产生的集合是语言基本字符集 ∑ \sum ∑上的字符串的子集,又称为正规集。
词法分析的理论基础是有限自动机理论,它与文法、正规表达式在描述语言方面是一一对应的,因而自动机也是词法分析中的重点。
在了解下面内容前,假定你知道文法与正规表达式的关系,并知道关于文法的基础概念,如果不了解则可以先浏览 文法基础 一文。
正规表达式(RE, Regular Expression)
对于字母表 ∑ \sum ∑,其 正规表达式\正规式(RE, Regular Expression) 可以定义如下:
设A是非空的有限字母表, A = { a i ∣ i = 1 , 2 , … , n } A= \{a_i | i = 1, 2, \dots ,n \} A={ai∣i=1,2,…,n}则:
- ε , ∅ , a i ( i = 1 , 2 , … , n ) \varepsilon, \empty, a_i(i=1,2, \dots, n) ε,∅,ai(i=1,2,…,n)均为RE。
- 若 α , β \alpha, \beta α,β是RE,则 α ∣ β , α ⋅ β , α ∗ , β ∗ \alpha | \beta, \alpha \cdot \beta, \alpha^*, \beta^* α∣β,α⋅β,α∗,β∗均为RE。
- RE只能通过有限次的使用上述两条规则产生。
上述的“|”表示”或“;“·”表示”连接“,书写时可省略;”*“表示”闭包“;这三个符号优先级自高到低为"*"、"·"、"|"。
而RE所能组成的语言即正规集,若两个RE所能组成的正规集相同,则两者等价,如: b ( a b ) ∗ = ( b a ) ∗ b b (ab)^* = (ba)^* b b(ab)∗=(ba)∗b、 ( a ∣ b ) ∗ = ( a ∗ b ∗ ) ∗ (a|b)^* = (a^* b^*)^* (a∣b)∗=(a∗b∗)∗。
从上例可知,正规式也遵循代数性质,设U、V、W均为RE,其性质如下:
- U ∣ V = V ∣ U U|V = V|U U∣V=V∣U
- ( U V ) W = U ( V W ) (UV)W = U(VW) (UV)W=U(VW)
- ( U ∣ V ) ∣ W = U ∣ ( V ∣ W ) (U|V)|W = U|(V|W) (U∣V)∣W=U∣(V∣W)
- ε U = U ε = U \varepsilon U = U \varepsilon = U εU=Uε=U
- U ( V ∣ W ) = U V ∣ U W U(V|W) = UV|UW U(V∣W)=UV∣UW
- V ∗ = ( V + ∣ ε ) V^* = (V^+ | \varepsilon) V∗=(V+∣ε)
- ( U ∣ V ) W = U W ∣ V W (U|V)W = UW | VW (U∣V)W=UW∣VW
- V ∗ ∗ = V ∗ V^{**} = V^* V∗∗=V∗
一些例子
- 设 ∑ = { a , b } \sum = \{ a, b\} ∑={a,b},其正规表达式和对应的正规集如下表:
正规表达式 | 正规集 |
---|---|
a b ( 或 写 作 a ⋅ b ) ab(或写作a \cdot b) ab(或写作a⋅b) | 字符串ab构成的集合 |
$a | b$ |
a ∗ a^* a∗ | 由0或多个a构成的字符串集合 |
$(a | b)^*$ |
$a(a | b)^*$ |
$(a | b)^* aab$ |
-
设 A = { a i ∣ i = 1 , 2 , … , n } A = \{ a_i | i = 1, 2, \dots, n \} A={ai∣i=1,2,…,n},则:
ε , a 1 , a 1 a 2 , a 1 ∣ a 5 a 7 , a 5 ( a 3 ∣ a 2 ) , … … \varepsilon, a_1, a_1 a_2, a_1 | a_5 a_7, a_5(a_3|a_2),\dots \dots ε,a1,a1a2,a1∣a5a7,a5(a3∣a2),……等都是A的正规表达式。
-
设 V = { 0 , 1 } V = \{0, 1\} V={0,1},则:
ε , 0 , 1 , 0011 , ( 01 ) ∗ 10 , ( ( 01 ) ∗ ∣ 1 ) ∗ … … \varepsilon, 0, 1, 0011, (01)^*10, ((01)^* | 1)^*\dots\dots ε,0,1,0011,(01)∗10,((01)∗∣1)∗……等都是V的正规表达式,也就是说,所有二进制字符串(包括空串)都是V上的正规表达式。
有限自动机(Finite Automata)
有限自动机是一种识别装置的抽象概念,它是具有离散输入输出系统的数学模型。其系统内具有有限数目的状态,系统状态就概括了过去输入处理的信息。只需根据当前所处的状态和面临的输入字符就可以决定系统的后继行为。例如电梯控制装置就是有限自动机,乘客只需选择索要到达的楼层作为输入,而电梯所处的楼层和方向作为状态,就能决定电梯的行为,而无需记住以前的行为。
确定有限自动机(DFA, Deterministic Finite Automata)
确定有限自动机定义为五元组: M = ( S , ∑ , f , s 0 , Z ) M = (S, \sum, f, s_0, Z) M=(S,∑,f,s0,Z),它们的定义如下:
-
S S S 是有限集,其中每个元素称为一个状态。
-
∑ \sum ∑ 是有穷字母表,其种每个元素称为一个输入字符。
-
f f f 是 S × ∑ → S S \times \sum \to S S×∑→S 上的单值部分映射。
f ( A , a ) = A ′ f (A,a) = A' f(A,a)=A′ 表示当前状态为 A A A、输入为 a a a时,将转换到状态 A ′ A' A′,又称 A ′ A' A′为 A A A的后继状态。其中, A , A ′ ∈ S A, A' \in S A,A′∈S、 a ∈ ∑ a \in \sum a∈∑。
-
s 0 s_0 s0 是唯一的初始状态, s 0 ∈ S s_0 \in S s0∈S。
-
Z Z Z 是终止状态集, Z ⊆ S Z \subseteq S Z⊆S。但 Z Z Z可以为空集,表示该DFA不接受任何东西。
状态转换图、状态转换矩阵
一个DFA可以用两种方式来表示:状态转换图和状态转换矩阵。
状态转换矩阵主要表示 f ( A , a ) f(A,a) f(A,a)的值,通常约定第一行的状态为初始态,而终态则需特别说明,具体的表示方式见下方例子,这样的矩阵对计算机来说是友好的,但对人来说则不那么直观。因此又有便于人理解的状态转换图。
状态转换图也简称为转换图,是有向图,DFA中每个状态对对应转换图中的一个结点,DFA中的每个映射对应图中的一条有向弧。
假定一个DFA(M)含有m个状态和n个输入字符,那么转换图就包含m个状态结点,每个结点最多有n条弧射出。整个图只有一个用 → \rightarrow →射入的表示的初始状态结点,用双圆圈表示的终止状态结点可以有若干个(也可能一个没有)。若存在一条从初始状态结点到任一终止状态结点的路径,且路径上所有的弧标识符连接成一个字符串等于 ω \omega ω ,则称 ω \omega ω可由该DFA识别。
若DFA的初始状态结点=终止状态结点,则仅有 ε \varepsilon ε可被该DFA识别。DFA所能识别的的语言为: L ( M ) = { ω ∣ ω 是 M 的 初 态 到 终 态 的 路 径 上 的 弧 组 成 的 字 符 串 } L(M) = \{ \omega | \omega是M的初态到终态的路径上的弧组成的字符串 \} L(M)={ω∣ω是M的初态到终态的路径上的弧组成的字符串}
一些例子
-
有DFA M = ( { s 0 , s 1 , s 2 , s 3 } , { a , b } , f , s 0 , s 3 ) M = (\{s_0, s_1, s_2, s_3\}, \{a,b\}, f, s_0, s_3) M=({s0,s1,s2,s3},{a,b},f,s0,s3),其中 f f f为:
- f ( s 0 , a ) = s 1 f(s_0, a) = s_1 f(s0,a)=s1
- f ( s 0 , b ) = s 2 f(s_0, b) = s_2 f(s0,b)=s2
- f ( s 1 , a ) = s 3 f(s_1, a) = s_3 f(s1,a)=s3
- f ( s 1 , b ) = s 2 f(s_1, b) = s_2 f(s1,b)=s2
- f ( s 2 , a ) = s 1 f(s_2, a) = s_1 f(s2,a)=s1
- f ( s 2 , b ) = s 3 f(s_2, b) = s_3 f(s2,b)=s3
- f ( s 3 , a ) = s 3 f(s_3, a) = s_3 f(s3,a)=s3
求该DFA对应的状态转换图及状态转换矩阵。
状态转换矩阵如下:
a b s 0 s_0 s0 s 1 s_1 s1 s 2 s_2 s2 s 1 s_1 s1 s 3 s_3 s3 s 2 s_2 s2 s 2 s_2 s2 s 1 s_1 s1 s 3 s_3 s3 s 3 s_3 s3 s 3 s_3 s3 其状态转换图如下:
-
构造一个DFA,它接受字母表 { a , b , c } \{a,b,c\} {a,b,c}上以a或b开始的字符串,或以c开始但所含的a不多于一个的字符串。
首先根据题意画出其状态转换图如下:
由该图就可以简单的写出该DFA M:
M = ( { s 0 , s 1 , s 2 , s 3 } , { a , b , c } , f , s 0 , { s 1 , s 2 , s 3 } ) M = (\{s_0, s_1, s_2, s_3\}, \{a, b, c\}, f, s_0, \{s_1, s_2, s_3\}) M=({s0,s1,s2,s3},{a,b,c},f,s0,{s1,s2,s3}),其中 f f f等于:
- f ( s 0 , a ) = s 1 f(s_0, a) = s_1 f(s0,a)=s1
- f ( s 0 , b ) = s 1 f(s_0, b) = s_1 f(s0,b)=s1
- f ( s 0 , c ) = s 2 f(s0, c) = s_2 f(s0,c)=s2
- f ( s 1 , a ) = s 1 f(s_1, a) = s_1 f(s1,a)=s1
- f ( s 1 , b ) = s 1 f(s_1, b) = s_1 f(s1,b)=s1
- f ( s 1 , c ) = s 1 f(s_1, c) = s_1 f(s1,c)=s1
- f ( s 2 , a ) = s 3 f(s_2, a) = s_3 f(s2,a)=s3
- f ( s 2 , b ) = s 2 f(s_2, b) = s_2 f(s2,b)=s2
- f ( s 2 , c ) = s 2 f(s_2, c) = s_2 f(s2,c)=s2
- f ( s 3 , b ) = s 3 f(s_3, b) = s_3 f(s3,b)=s3
- f ( s 3 , c ) = s 3 f(s_3, c) = s_3 f(s3,c)=s3
不确定有限自动机(NFA, Nondeterministic Finite Automata)
NFA也是由五元组 M = ( S , ∑ , f , s 0 , Z ) M = (S, \sum, f, s_0, Z) M=(S,∑,f,s0,Z)定义,但与DFA不同的是:
- f f f 是 S × ∑ → 2 S S \times \sum \to 2^S S×∑→2S 上的映射,对于S中给定的状态及输入符号,返回一个状态的集合,即当前状态和后继状态不唯一。
- 有向弧上的标记可以是 ε \varepsilon ε。
一个典型的NFA如下图:
NFA同样有状态转换矩阵,不同的是其对应的是一组状态集。上图对应的状态转换矩阵如下:
A | B | |
---|---|---|
s 0 s_0 s0 | { s 0 , s 1 } \{s_0, s_1\} {s0,s1} | { s 0 } \{s_0\} {s0} |
s 1 s_1 s1 | { s 2 } \{s_2\} {s2} | |
s 2 s_2 s2 | { s 3 } \{s_3\} {s3} | |
s 3 s_3 s3 |
NFA实际上是DFA的特例,每个NFA M都存在DFA N,且 L ( M ) = L ( N ) L(M) = L(N) L(M)=L(N),即存在和NFA等价的DFA。
NFA转换为DFA
上面提到,每个NFA都存在等价的DFA,也就是说,任一NFA都可以被转换为DFA,它由一个构造算法证明。
由NFA M = ( S , ∑ , f , s 0 , Z ) M = (S, \sum, f, s_0, Z) M=(S,∑,f,s0,Z) 构造一个等价的DFA M ′ = ( T , ∑ , f ′ , I 0 , Z ′ ) M' = (T, \sum, f', I_0, Z') M′=(T,∑,f′,I0,Z′)算法如下:
- I 0 = s 0 I_0 = s_0 I0=s0。
- 若状态集 T T T中有状态 I i = { s 0 , s 1 , … , s j } I_i = \{s_0, s_1, \dots, s_j \} Ii={s0,s1,…,sj}, s k ∈ S , 0 ≤ k ≤ j s_k \in S, 0 \leq k \leq j sk∈S,0≤k≤j。并且 M M M中有 f ( { s 0 , s 1 , … , s j } , a ) = ⋃ k = 0 j f ( s k , a ) = { s 0 , s 1 , … , s u } = I u f(\{s_0, s_1, \dots, s_j \}, a) = \bigcup^j_{k=0}f(s_k, a) = \{s_0, s_1, \dots, s_u\} = I_u f({s0,s1,…,sj},a)=⋃k=0jf(sk,a)={s0,s1,…,su}=Iu,若 I u I_u Iu不在 T T T中,则将 I u I_u Iu加入 T T T。
- 重复上述步骤,直至 T T T中没有新的状态加入。
- 取 Z ′ = { I ∣ I ∈ T 且 I ⋂ Z ≠ ∅ } Z' = \{I | I \in T 且I \bigcap Z \neq \empty \} Z′={I∣I∈T且I⋂Z̸=∅}。
这个过程总能在有限时间内完成,因为M的状态幂集是有限的。NFA转换为DFA又称作 子集化\确定化。
一个例子
光看算法可能不太直观,因此实际操作一下更容易理解。
有NFA M = ( { q 0 , q 1 } , { a , b } , f , { q 0 } , { q 1 } ) M = (\{q_0, q_1\}, \{a, b\}, f, \{q_0\}, \{q_1\}) M=({q0,q1},{a,b},f,{q0},{q1}), f f f的映射见下映射矩阵:
a | b | |
---|---|---|
q 0 q_0 q0 | q 0 q_0 q0 | q 1 q_1 q1 |
q 1 q_1 q1 | q 0 , q 1 q_0, q_1 q0,q1 | q 0 q_0 q0 |
这里可以发现当状态处于 q 1 q_1 q1时读入a,其后继状态不唯一。
其确定化的过程如下(T,I含义见上方算法):
T中初态 I 0 = q 0 I_0 = {q_0} I0=q0,当读入字符a, b时,按NFA映射函数有:
- f ( I 0 , a ) = { q 0 } f (I_0, a) = \{q_0\} f(I0,a)={q0}
- f ( I 0 , b ) = { q 1 } f(I_0, b) = \{q_1\} f(I0,b)={q1}
其中发现新状态集 { q 1 } \{q_1\} {q1},将其记为 I 1 I_1 I1并加入到T中。
此时考察 I 1 I_1 I1,当读入字符a, b时,按NFA映射函数有:
- f ( I 1 , a ) = { q 0 , q 1 } f(I_1, a) = \{q_0, q_1\} f(I1,a)={q0,q1}
- f ( I 1 , b ) = { q 0 } f(I_1, b) = \{q_0\} f(I1,b)={q0}
其中发现新状态集 { q 0 , q 1 } \{q_0, q_1 \} {q0,q1},将其记为 I 2 I_2 I2并加入T中。
此时考察 I 2 I_2 I2,当读入字符a, b时,按NFA映射函数有:
- f ( I 2 , a ) = { q 0 , q 1 } f(I_2, a) = \{q_0, q_1\} f(I2,a)={q0,q1}
- f ( I 2 , b ) = { q 0 , q 1 } f(I_2, b) = \{q_0, q_1\} f(I2,b)={q0,q1}
其中没有新的状态集,因此循环部分结束。如果将过程画为状态表则更为直观:
a | b | |
---|---|---|
I 0 = { q 0 } I_0 = \{q_0\} I0={q0} | I 0 = { q 0 } I_0 = \{q_0\} I0={q0} | I 1 = { q 1 } I_1 = \{q_1\} I1={q1} |
I 1 = { q 1 } I_1 = \{q_1\} I1={q1} | I 2 = { q 0 , q 1 } I_2 = \{q_0, q_1\} I2={q0,q1} | I 0 = { q 0 } I_0 = \{q_0\} I0={q0} |
I 2 = { q 0 , q 1 } I_2 = \{q_0, q_1\} I2={q0,q1} | I 2 = { q 0 , q 1 } I_2 = \{q_0, q_1\} I2={q0,q1} | I 2 = { q 0 , q 1 } I_2 = \{q_0, q_1\} I2={q0,q1} |
可以发现,将 I n I_n In看做单独的整体即为确定化的状态转换表。最后一步就是找出 Z ′ Z' Z′,按照定义 Z ′ = { I ∣ I ∈ T 且 I ⋂ Z ≠ ∅ } Z' = \{I | I \in T 且I \bigcap Z \neq \empty \} Z′={I∣I∈T且I⋂Z̸=∅},其计算如下:
- I 0 ∈ T 且 I 0 ⋂ ( Z = { q 1 } ) ≠ ∅ I_0 \in T 且 I_0 \bigcap (Z = \{q_1\}) \neq \empty I0∈T且I0⋂(Z={q1})̸=∅,后一条件不满足 。
- I 1 ∈ T 且 I 1 ⋂ ( Z = { q 1 } ) ≠ ∅ I_1 \in T 且 I_1 \bigcap ( Z = \{q_1\}) \neq \empty I1∈T且I1⋂(Z={q1})̸=∅,条件满足。
- I 2 ∈ T 且 I 2 ⋂ ( Z = { q 1 } ) ≠ ∅ I_2 \in T 且 I_2 \bigcap ( Z = \{q_1\}) \neq \empty I2∈T且I2⋂(Z={q1})̸=∅,条件满足。
最终 Z ′ = { I 1 , I 2 } Z' = \{I_1, I_2\} Z′={I1,I2},最终DFA N = ( { I 0 , I 1 , I 2 } , { a , b } , f ′ , I 0 , { I 1 , I 2 } ) N = (\{I_0, I_1, I_2\}, \{a, b\}, f', I_0, \{I_1, I_2\}) N=({I0,I1,I2},{a,b},f′,I0,{I1,I2}),画成状态转换图如下:
要理解算法的关键在于,它将状态以集为单位,当遇到新状态集时,结果状态集为各自NFA映射的并集。
确定有限自动机(DFA)的化简
通常,对NFA确定化后产生的DFA状态数都会增加,而且可能出现一些状态之间是等价的,这时就需要化简,对确定有限自动机的化简又称作最小化。化简采用最小化算法,也称作划分法,它的原则是将DFA M中状态划分为不相交的子集,在每个子集内部其状态都等价,而在不同子集间状态均不等价(可区分的)。最后从每个子集中选一状态最为代表,消去等价状态。
其定义:设DFA M中有两个状态 s , t s, t s,t,若 ( s , ω ) ⊢ ∗ ( s 1 , ε ) , ( t , ω ) ⊢ ∗ ( t 1 , ε ) (s, \omega) \vdash^* (s_1, \varepsilon), (t, \omega) \vdash^*(t_1, \varepsilon) (s,ω)⊢∗(s1,ε),(t,ω)⊢∗(t1,ε),且 s 1 , t 1 s_1, t_1 s1,t1均属于终态, ω ∈ V T ∗ \omega \in V^*_T ω∈VT∗,则称s,t是等价的,否则则是可区分的。
这里要解释一个新符号,状态前进至下一状态,我们称作自动机作了1步动作,其中" ⊢ n , ⊢ + , ⊢ ∗ \vdash^n, \vdash^+, \vdash^* ⊢n,⊢+,⊢∗"分别代表自动机作了n步、1步及以上、0步及以上的灯座。
简单直白即,对DFA中的任两状态 s , t s, t s,t,若从其中一个状态触发接受字符串 ω \omega ω,而另一状态出发不能接受字符串 ω \omega ω,或从 s , t s, t s,t出发均接受字符串 ω \omega ω到达的是不同的状态,则称它们是可区分的,否则就是不可区分。对任俩不可区分的状态都可以合并为等价状态。
按这个原则算法步骤如下:
- 把状态集S分为终态集和非终态集 Π 0 = { I 0 1 , I 0 2 } \Pi_0 = \{I^1_0, I^2_0\} Π0={I01,I02},其中 I 0 1 I^1_0 I01属于非终态集,另一个则是终态集。终态集可接受 ε \varepsilon ε而后者不行,因此是可以区分的。
- 假定经过k次划分后,含有m个子集,记为: Π k = { I k 0 , I k 1 , … , I k m } \Pi_k = \{I^0_k, I^1_k, \dots, I^m_k\} Πk={Ik0,Ik1,…,Ikm},这些子集到现在为止仍可区分,然后继续划分,设取任一子集 I k i = { s 1 , s 2 , … , s t } I_k^i = \{s_1, s_2, \dots, s_t\} Iki={s1,s2,…,st}若存在一个输入字符a,使得 f ( I k i , a ) f(I_k^i, a) f(Iki,a)不全包含在现 Π k \Pi_k Πk的某个子集,则说明 I k i I^i_k Iki中仍存在不等价状态,应该还可被划分。如: f ( s 1 , a ) = t 1 , f ( s 2 , a ) = t 2 f(s_1,a) = t_1, f(s_2, a) = t_2 f(s1,a)=t1,f(s2,a)=t2, t 1 , t 2 t1, t2 t1,t2分别属于 Π k \Pi_k Πk的不同子集。对所有的子集 I k m I^m_k Ikm读入字符a做一遍才完成此次划分。
- 重复上述步骤,直至子集数不再增加位置。也就是不可再划分了。
- 对每个子集任取一状态作为代表,若该子集包含原初态,则此状态就是最小化后的初态;若此子集包含原有的终态,则此状态就是最小化后的终态。
一个例子
设有一DFA状态转换图如图:
按步骤将其划分为非终态集与终态集: Π 0 = { { s 0 , s 1 , s 2 } , { s 3 , s 4 , s 5 , s 6 } } \Pi_0 = \{ \{s_0, s_1, s_2\}, \{s_3, s_4, s_5, s_6\} \} Π0={{s0,s1,s2},{s3,s4,s5,s6}}
来看看如何划分 Π 0 \Pi_0 Π0
- 先解决 I 0 1 = { s 0 , s 1 , s 2 } I^1_0 = \{s_0, s_1, s_2\} I01={s0,s1,s2}集划分,有 f ( { s 0 , s 1 , s 2 } , a ) = { s 1 , s 3 } f( \{s_0, s_1, s_2\}, a) = \{s_1, s_3\} f({s0,s1,s2},a)={s1,s3},不在现有 Π \Pi Π中,其中 s 0 , s 2 s_0, s_2 s0,s2均经由a抵达状态 s 1 s_1 s1,而 s 1 s_1 s1仅能从a抵达状态 s 3 s_3 s3。因此分为 { s 0 , s 2 } , { s 1 } \{s_0, s_2\}, \{s_1\} {s0,s2},{s1}两个子集。
- 然后看 I 0 2 = { s 3 , s 4 , s 5 , s 6 } I^2_0 = \{s_3, s_4, s_5, s_6\} I02={s3,s4,s5,s6},有 f ( { s 3 , s 4 , s 5 , s 6 } , a ) = { s 3 , s 4 , s 5 , s 6 } , f ( { s 3 , s 4 , s 5 , s 6 } , b ) = { s 3 , s 4 , s 5 , s 6 } f(\{s_3, s_4, s_5, s_6\}, a) = \{s_3, s_4, s_5, s_6\}, f(\{s_3, s_4, s_5, s_6\}, b) = \{s_3, s_4, s_5, s_6\} f({s3,s4,s5,s6},a)={s3,s4,s5,s6},f({s3,s4,s5,s6},b)={s3,s4,s5,s6},因此它们无法再被划分。这里要注意的,对于终态集,是存在接受 ε \varepsilon ε的情况的,这也是为什么 f ( s 3 , a ) = s 4 f( s_3, a) = s_4 f(s3,a)=s4,因为这里可以假定接受了一个 ε \varepsilon ε的情况。
这轮完成后,现结果为: Π 1 = { { s 0 , s 2 } , { s 1 } , { s 3 , s 4 , s 5 , s 6 } } \Pi_1 = \{ \{s_0, s_2 \}, \{s_1\} ,\{s_3, s_4, s_5, s_6\} \} Π1={{s0,s2},{s1},{s3,s4,s5,s6}}
然后继续划分 Π 1 \Pi_1 Π1
- 看仍存在 I 1 1 = { s 0 , s 2 } I^1_1 = \{s_0, s_2\} I11={s0,s2},有 f ( { s 0 , s 2 } , a ) = { s 1 } f(\{s_0, s_2\}, a) = \{s_1\} f({s0,s2},a)={s1},存在于 Π 1 \Pi_1 Π1中;但 f ( { s 0 , s 2 } , b ) = { s 2 , s 5 } f(\{s_0, s_2\}, b) = \{s_2, s_5\} f({s0,s2},b)={s2,s5}出现了新子集,说明可划分,因为只有两个状态了,因此最终分为 { s 0 } , { s 2 } \{s_0\}, \{s_2\} {s0},{s2}。
这轮完成后,现结果为: Π 2 = { { s 0 } , { s 2 } , { s 1 } , { s 3 , s 4 , s 5 , s 6 } } \Pi_2 = \{ \{s_0\}, \{s_2 \}, \{s_1\} ,\{s_3, s_4, s_5, s_6\} \} Π2={{s0},{s2},{s1},{s3,s4,s5,s6}}
这样一来,就全部分完了,发现仅有一个状态集存在多个状态,只需取其一,因为它们是等价的,设取 s 3 = { s 3 , s 4 , s 5 , s 6 } s_3 = \{s_3, s_4, s_5, s_6 \} s3={s3,s4,s5,s6},最后使指向其他等价状态的弧指向 s 3 s_3 s3即可。最终简化的DFA如下图:
正规式与有限自动机的转换
正规式与文法均是用来描述语言的,而自动机是用来识别的,它们之间自然也存在关系。事实上,它们是可以互相转换的,可以用以下定理证明:
- ∑ \sum ∑ 上的NFA M所能识别的语言 L ( M ) L(M) L(M)可以用 ∑ \sum ∑上的正规表达式(RE)表示。
- 对于 ∑ \sum ∑上的任一RE α \alpha α ,都存在一个DFA M使得 L ( M ) = L ( α ) L(M) = L(\alpha) L(M)=L(α)。
下面分别了解它们之间是如何转换的,同时也能知道定理是可实践的。
有限自动机转换为正规式
令状态图中每一条弧都拓广,这样一来,每条弧就可以用一个正规式表达,有如下3条非常简单的转换规则:
其中,前两条都很好理解,第三条好像也很容易理解,但证明一下:
- L ( s 1 ) = L ( s 0 ) a + L ( s 1 ) b L(s_1) = L(s_0)a + L(s_1)b L(s1)=L(s0)a+L(s1)b
- L ( s 0 ) = ε L(s_0) = \varepsilon L(s0)=ε
- 设 L ( s 1 ) = β L(s_1) = \beta L(s1)=β,代入后得 β = a + β b \beta = a+\beta b β=a+βb,左递归的等价式即 β = a b ∗ \beta = ab^* β=ab∗。
转换过程比较简单,其实就是将一个FA逐渐利用转换规则以减少状态(结点),使最终仅剩一对(一起点,一终点)。也有说用虚设x在初态之前,y在所有终态之后的。但实际原理是一样的。
看个例子
消除的过程清晰的描述在图片中,由最后步骤的状态图可得出该FA接受的正规式为$(ac | b)(dc)^* $。
正规式转换为有限自动机
本质上,这是FA转为RE的逆过程,它同样用一样的3条规则,只不过不一样的是,需要分裂出新的结点,详细的规则见下图:
主要不同的就在于RE转FA需要体现出新分裂的结点,只需按规则将每根弧上标记是 ∑ \sum ∑上的字符或 ε \varepsilon ε为止,最后将该NFA(因为含有 ε \varepsilon ε弧)转为DFA并化简即可。
一个例子
可以看到最终就成功生成了NFA,但需要进一步确定化(存在 ε \varepsilon ε弧)。
词法分析器构造
上面的内容和前一篇 文法基础 简要的介绍了语法、正规式及有限自动机,在有了这些基础理论和工具之后,就可以构造出编译所需要的词法分析模块。其一般步骤如下:
- 用RE描述语言的构成规则。
- 为每一RE构造NFA,用以识别RE所表示的正规集。
- 对构造出的NFA转为DFA,并简化。
- 用该DFA来实现词法分析。
以后有时间继续深入来看看具体都是怎么实现的。