一、正则表达式
DEF. 正则表达式是一种用来描述正则语言的更紧凑的表示方法。
正则表达式可以由较小的正则表达式按照特定规则递归地构建。
每个正则表达式r定义一个语言,记为L ( r ) 。这个语言也是根据r的子表达式所表示的语言递归定义的。
可以用RE定义的语言叫做正则语言或正则集合
正则文法与正则表达式等价
:
- 对任何正则文法G,存在定义同一语言的正则表达式r
- 对任何正则表达式r,存在生成同一语言的正则文法G
运算优先级:克林闭包 > 连接 > 或
二、正则定义
DEF.
正则定义是具有如下形式的定义序列:
d
1
→
r
1
d_1\rightarrow r_1
d1→r1
d
2
→
r
2
d_2\rightarrow r_2
d2→r2
…
d
n
→
r
n
d_n\rightarrow r_n
dn→rn
其中,
- 每个
d
i
d_i
di都是一个新符号,他们不在字母表
Σ
\Sigma
Σ中,而且
各不相同
- 每个 r i r_i ri是字母表 Σ ∪ { d 1 , d 2 , . . . , d i − 1 } \Sigma\cup\lbrace d_1,d_2,...,d_{i-1}\rbrace Σ∪{d1,d2,...,di−1}上的正则表达式
即:给一些RE命名,并在之后的RE中像使用字母表中的符号一样使用这些名字。
三、有穷自动机
Def. 有穷自动机FA是对一类处理系统建立的数学模型,这类系统具有一系列离散的输入输出信息和有穷数目的内部状态,系统只需要根据当前所处的状态和当前面临的输入信息就可以决定系统的后继行为。每当系统处理了当前的输入后,系统的内部状态也将发生改变。
e.g. 电梯控制装置:
- 输入:顾客的乘梯需求
- 状态:电梯所处的层数 + 运动方向
FA模型:
- 输入带:用来存放输入符号串
- 读头:
从左向右
逐个读取输入符号,不能修改(只读),不能往返移动 - 有穷控制器:具有有穷个状态数,根据当前的状态和当前输入符号控制转入下一状态
FA的表示——转换图
- 结点:FA的状态。
- 初始状态:只有一个,由start箭头指向
- 终止状态,至少有一个,双圈表示
- 带标记的有向边:对于输入a,存在一个从状态p到状态q的转换,就在p、q之间画一条有向边,并标记上a
FA定义(接收)的语言:
给定输入串x,如果存在一个对应于串x的从初始状态到某个终止状态的转换序列,则称串x被该FA接收
。
由一个有穷自动机M接收的所有串构成的集合称为是该FA定义/接收的语言
,记为L(M)。
L(M) = 所有以abb结尾的字母表{a,b}上的串的集合。
最长子串匹配原则:
DEF. 当输入串的多个前缀与一个或多个模式匹配时,总是选择最长的前缀
进行匹配。
即:在到达某个终态之后,只要输入带上还有符号,FA就继续前进,以便寻找尽可能长的匹配。
四、有穷自动机的分类
两类:确定的FA(DFA)和非确定的FA(NFA)
4.1 DFA
M
=
(
S
,
Σ
,
δ
,
s
0
,
F
)
M = ( S , \Sigma , \delta , s_0 , F)
M=(S,Σ,δ,s0,F),
其中:
- S:有穷状态集
-
Σ
\Sigma
Σ:输入字母表,即输入符号集合,假设
空串
不是其中的元素 - δ \delta δ:将 S S Sx Σ \Sigma Σ映射到S的转换函数。 ∀ s ∈ S , a ∈ Σ , δ ( s , a ) \forall s\in S,a\in\Sigma,\delta(s,a) ∀s∈S,a∈Σ,δ(s,a)表示从状态s出发,沿着标记为a的边所能到达的状态
- s 0 s_0 s0:开始状态, s 0 ∈ S s_0\in S s0∈S
- F:接收状态
集合
, F ⊆ S F\subseteq S F⊆S
4.2 NFA
M
=
(
S
,
Σ
,
δ
,
s
0
,
F
)
M = ( S , \Sigma , \delta , s_0 , F)
M=(S,Σ,δ,s0,F),
其中:
- S:有穷状态集
-
Σ
\Sigma
Σ:输入字母表,即输入符号集合,假设
空串
不是其中的元素 -
δ
\delta
δ:将
S
S
Sx
Σ
\Sigma
Σ映射到
2
S
2^S
2S的转换函数。
∀
s
∈
S
,
a
∈
Σ
,
δ
(
s
,
a
)
\forall s\in S,a\in\Sigma,\delta(s,a)
∀s∈S,a∈Σ,δ(s,a)表示从状态s出发,沿着标记为a的边所能到达的状态
集合
- s 0 s_0 s0:开始状态, s 0 ∈ S s_0\in S s0∈S
- F:接收状态集合,
F
⊆
S
F\subseteq S
F⊆S
4.3 DFA和NFA的等价性
- 对任何NFA,存在识别同一语言的DFA
- 对任何DFA,存在识别同一语言的NFA
- 正则文法
⇔
\Leftrightarrow
⇔正则表达式
⇔
\Leftrightarrow
⇔FA
4.4 带有 ε \varepsilon ε边的NFA
M
=
(
S
,
Σ
,
δ
,
s
0
,
F
)
M = ( S , \Sigma , \delta , s_0 , F)
M=(S,Σ,δ,s0,F),
其中:
- S:有穷状态集
-
Σ
\Sigma
Σ:输入字母表,即输入符号集合,假设
空串
不是其中的元素 -
δ
\delta
δ:将
S
S
Sx
(
Σ
∪
{
ε
}
)
(\Sigma\cup\lbrace\varepsilon\rbrace)
(Σ∪{ε})映射到
2
S
2^S
2S的转换函数。
∀
s
∈
S
,
a
∈
Σ
∪
{
ε
}
,
δ
(
s
,
a
)
\forall s\in S,a\in\Sigma\cup\lbrace\varepsilon\rbrace,\delta(s,a)
∀s∈S,a∈Σ∪{ε},δ(s,a)表示从状态s出发,沿着标记为a的边所能到达的状态
集合
- s 0 s_0 s0:开始状态, s 0 ∈ S s_0\in S s0∈S
- F:接收状态集合,
F
⊆
S
F\subseteq S
F⊆S
4.5 DFA的算法实现
五、从正则表达式到有穷自动机
直接转换比较困难,通常先将RE构造成NFA,再构造成等价的DFA。
5.1 根据RE构造NFA
5.2 从NFA到DFA
DFA的每个状态都是由一个NFA中的状态构成的集合
,即NFA状态集合的一个子集。
两者都等价于RE:
r
=
a
a
∗
b
b
∗
c
c
∗
r=aa^*bb^*cc^*
r=aa∗bb∗cc∗
转换时需要注意终态
的表示。
r
=
0
∗
1
∗
2
∗
r=0^*1^*2^*
r=0∗1∗2∗
六、识别单词的DFA
识别标识符
的DFA:
NFA无空边 ====> DFA
识别无符号数
的DFA:
有空边则需从NFA转换成DFA:
识别各进制无符号整数
的DFA:
识别注释
的DFA:
识别Token
的DFA:
七、词法分析阶段的错误处理
可检测错误类型:
- 单词拼写错误
- 非法字符
错误检测:如果当前状态与当前输入符号在转换表对应项中的信息为空,则报错,调用错误处理程序。
错误处理:
查找已扫描字符串中最后一个对应某终态的字符。
- 如果找到了,则将该字符与其前面的字符识别成一个单词,然后将输入指针退回到该字符,扫描器重新回到初始状态,继续识别下一个单词
- 如果没找到,则确定出错,采用错误恢复策略
错误恢复策略:
恐慌模式panic mode:从剩余的输入中不断删除字符,直到词法分析器能够在剩余输入的开头发现一个正确的字符为止