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

让词法分析形式化,自动化!


一 为什么要谈形式语言

    前边说过我们用模式(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:
                程序错误;
        }
    }
}

    这段程序是不是很美呢!简易。


                                                           转载请注明出处哈喽易

                                                           注:文中有不当之处,还请指出,谢谢!

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值