让词法分析形式化,自动化!
一 为什么要谈形式语言
前边说过我们用模式(pattern)描述词法单元,而我们口头的描述方法似乎不够精确,
那怎样才足够精确呢?似乎数学总让我们感到如此,总让我们信服!
1.形式语言
chomsky是个语言学家,他定义了形式语言描述文法,对计算机技术的影响很大
先看下语言的一般运算,其实就是集合的一些运算
《图中closure的 r,e重叠,抱歉 》
这里 M^i 表示 i 个 M 的连接,MN的意思就如两组人,分别从M和N组选一个人,所有的选择便构成了MN集合。
M^0 被定义为{ ε}, ε表示空元素。
正闭包(positive closure)与 kleene 闭包唯一区别是除非 ε ∈ M,否则 ε∉ M+。闭包就是个无敌运算啊!
下图中的 -> 是推导的意思,意思 -> 左边的东西可以被 -> 右边的东西替换,就好像是等价的,但他们是有序的。
终结符号:就是这个语言的基本符号(元符号),对元是否有印象。就如我们说的话的每一个字
非终结符号:就是去归纳,推导这个语言所用到的符号。就是每一个字(终结符)组成了词语(非终结符),
词语有组成了句子(非终结符)等等。
开始符号:如汉语,这就是个开始符号。它是这个语言最大的概念。
就像这样:
汉语 -> 句子+ | 字
句子 -> 词语+ | 字
词语 -> 字+
语言都是由终结符号组成的串,推导的最终结果就是终结符串。
是否有些感觉,那继续。
大家现在先有个印象,后边慢慢会了解这些文法的意思。
我们现在只关心3型文法,因为它对应正则语法,定义了正则语言。
而我们用正则表达式去描述模式(pattern),正则表达式表示的语言属于正则语言(这里的语言很广义),
“虽然正则表达式不能表达出所有可能的模式,但是它们可以高效地描述模式”,龙书上这样说,
但我目前还没有见到不能用正则表达式描述的模式。
2.那么什么是正则表达式
首先 ε 空元素是正则表达式,L(ε)= {ε} 表示该语言L包含空串。
0个人可以组成一个组(语言),就表示这个组可以没有人。
其次a 属于字母表(3型文法的终结符号集),那么a 就是正则表达式,
L(a)= {a} 表示这个语言仅包含一个长度为 1 的串a。
随便在拉出来一个人(所以人就是一个字母表),让他组成一个组,他本身就能表示(表示就是正则表达式的工作)这个组的某个人,那就是他自己。
那么现在就让这些小的(小到只能表示自己)正则表达式构造(这势必就是运算)大的正则表达式。
a,b 都是正则表达式,分别表示语言L(a),L(b)
(a)是正则表达式,表示语言L(a),括号并不影响表达式所表示的语言
(a) | (b)是正则表达式,表示语言L(a) U L(b)
(a)(b)是正则表达式,表示语言L(a)L(b) (这是连接,别忘了)
(a)*是正则表达式,表示语言(L(a))*
有点语言的感觉了吗?正则表达式定义的语言叫做正则集合(regular set)
既然是运算那就看下它们的定律
r | s = s | r
r | (s | t) = (r | s) | t
r(st) = (rs)t
r(s | t) = rs | rt
εr = rε = r 学过离散的应该都知道,ε 是幺元,就是乘法中的 1 ,加法中的 0 一样。不管怎么搞和没搞一样
r* = (r |ε) * 这意思闭包运算一定包含空串
r** = r* 这就更好理解了,* 都无敌了,你再怎么搞都一样
为了便于正则表达式书写,或是为了让它更强大,就有了所谓的正则定义(regular definition)
d1 ->r1
d2 ->r2
...
dn ->rn //这里n表示下标
di 是一个新符号,它们都不在中,并且个不相同
ri 是字母表 U {d1,d2,...di-1}上的正则表达式
就是构造出一个新符号,下次构造新符号时,就可以用以前定义的新符号了
就是一个大组里边在分小组,一个人可以参加多个小组(如果你足够有力气)。下次如果一个小组1里的所有人都参加另一个小组2,
那么就用小组2的名单就不必写上小组1成员的所以名字,用小组1代替就行了。
你有没有发现组(组合)是越来越多了,但还是那些人啊(字母表,终结符)。
这些组(0个人可以是一组,1个人也行,不明白看前边)就构成了这个大组(语言)。
让它更强大 正则表达式的扩展
正闭包运算 (r)+,kleene闭包 (r)* 这个就不多说了
零个或一个实例。?运算符号,r? =r |ε。好理解,问一下是 r 吗?是,一个 r; 不是,什么都没 ε
a1 | a2 | ... | an = [a1a2..an] 这些正则表达式都是一人一组。如果a1...an逻辑连续可以这样[a1-an],例子:letter ->[A-Za-z]
就是letter这个符号表示 A 数到 Z 或 a 数到 z 中的一个字母。
我们举几个例子便于理解:
数字:digit -> [0-9] 数字就是0或1或...或9
整数:integer -> [1-9](digit)* 看这里就用到了正则定义,意思是以非0开头的数字串。
差不多就行了,慢慢会理解的。
3.有限自动机
怎样识别这些正则表达式表示的语言呢?
就是识别出一堆小组(语言串,就是一句话)是参加比赛项目名单上的那个条目,每个条目(正则表达式)表示从事这个项目的的小组(这个正则表达式表示的语言)
前面说过正则表达式表示的语言就是正则语言(3型文法),而它对应有限自动机,这里的对应就是被识别的意思,
正则语言一定可以被有限自动机(FA)识别,
就如现在有几个小组(一句话),现在有一个房子(有限自动机),一个入口,多个出口(多个模式,多个正则表达式),
出口的门牌都写着一个体育项目名,你就让它们一个一个(字母)从这个入口进去,从那个出口出来,那他们就是参加那个体育项目的。
这里一个人(字母)可以参加多个项目(模式,语言,正则表达式)
下面看一下内部
有穷自动机的内部其实就是状态转换图,完了会系统的介绍(精确的定义),这里只简单理解
这就是一个识别我们所定义语言的状态转换图
图中的圆表示状态,0状态表示初态是入口,直线上的表示输入字母,当前的字母是什么它就让它走那条路,到达另一个状态,
直到有字母走到两个圆圈(终态,出口)处,这次识别就完成了。状态机一次只能识别一个词素,也就是一次只有一个出口输出。
比如现在有三个人,叫 i,f ,(
我们要知道它们是参加那个项目的,好进这个房间(0状态),
进去看到好多路,但不用管,你报自己名字 i ,状态机就把你送到 i 这条路上,i 到了1状态,
这时候 f 进了(1状态),不过入口好像换成了 1,f 一进入就是1状态,同样状态机把他送到了2状态。
接下来 ( 进了(2状态),状态机把他送到了3状态,好这时候这个机器知道了 if 这两个参加了 ”关键字if“ 这个组,
谢谢 ( 啦,让我知道了 if 是关键字。
那 ( 怎么办,哎,麻烦你在进一次,这时候这个房子就将入口换为了0状态(初态),去识别 ( 。
进去后他就被送到了 11状态。哦!你没参加任何比赛。
大家是否略知一二了?好!继续。
二 轻松实现一个词法分析器
明白了状态机,这就变的简单了。
这次我们基于状态图实现一个词法分析器
看下面伪代码,结合上边的故事(算故事吧..)基于上面的状态转换图
这里,缓冲区的处理在nextChar()内完成,上篇已经有叙述,这里不在讲解
Token getToken() {
while (ture) { //机器运转直到到达一个终态
switch (state) { //入口状态 state
case 0:
//下一个人进来
c = nextChar();
//看把这个人送到哪
if (c == 'i') state = 1;
else if (c == digit) state = 6;
else if (c == '*') state = 8;
else state = 11;
break;
case 1:
...
break;
...
case 8:
//下一个人进来
c = nextChar();
//看把这个人送到哪
if (c == '*') state = 10;
else state = 9;
break;
case 9:
//撤回的意思,让他在进一次
retract();
//到达终态,识别出一组人
return 运算符*;
case 10:
retract():
return 运算符**;
case 11:
return 不能识别;
default:
程序错误;
}
}
}
这段程序是不是很美呢!简易。
转载请注明出处哈喽易
注:文中有不当之处,还请指出,谢谢!