lemon源码分析

基本概念见上篇
lemon源码基本概念整理

1. follow集

对于如下4条产生式

          program ::= expr TK_SEM
          expr ::=  expr TK_IMPL expr
          expr ::=  TK_LPAREN expr TK_RPAREN
          expr ::=  TK_NEG expr

现在要求expr ::= TK_LPAREN expr TK_RPAREN*的follow集方法如下

      expr ::=  TK_LPAREN expr TK_RPAREN*
 =  expr ::=  TK_LPAREN expr* TK_RPAREN
  =  expr ::=  TK_LPAREN *expr TK_RPAREN
  =  expr ::=  *TK_LPAREN expr TK_RPAREN

上面这4条,后面是前面的bplp,这个见上篇文章的最后。同时前面也就是后面的fplp,代码如下
在这里插入图片描述
expr ::= *TK_LPAREN expr TK_RPAREN的follow集由下面这些项目决定

          program ::= *expr TK_SEM
          expr ::= * expr TK_IMPL expr
          expr ::=  expr TK_IMPL *expr
          expr ::=  TK_LPAREN *expr TK_RPAREN
          expr ::=  TK_NEG *expr

代码在Configlist_closure函数里
在这里插入图片描述
反正经过上面的步骤最终可以求得expr ::= TK_LPAREN expr TK_RPAREN的follow集是下面这3个
在这里插入图片描述
这个也符号我们对follow集的直观感受,那么follow集在哪里会用到呢?这个在规约时会用到,见下面代码
在这里插入图片描述
也就是
在产生式最右边时,会把这个项目的follow集加到归约动作里

2. 压缩动作链表

这个就是把本来这样的状态输出

State 9:
          expr ::= expr * TK_IMPL expr
      (1) expr ::= expr TK_IMPL expr *

                       TK_IMPL reduce 1
                        TK_SEM reduce 1
                     TK_RPAREN reduce 1

压缩成

State 12:
          expr ::= expr * TK_IMPL expr
      (1) expr ::= expr TK_IMPL expr *

                     {default} reduce 1

代码如下,在CompressTables函数里
在这里插入图片描述
这里之所以if( rp2==rbest ) continue;的原因是,绿框这个循环在rbest赋值之前就已经把rp的数量统计好了,当rbest赋值后已经是下一个大循环了,所以不必再对rbest做处理

3.类型联合

所有终结符和非终结符类型组成一个联合体,定义成一个YYMINORTYPE类型,代码在print_stack_union里,
在这里插入图片描述

4.动作数组

4.1 框架

首先定义一个ax数组,这个数组有2*nstate个元素,每2个元素分为终结符和非终结符关联绑定一个stp,代码如下
在这里插入图片描述
整体流程代码如下
在这里插入图片描述
首先肯定是遍历所有ax数组,获得状态stp,然后分为终结符和非终结符两个分支进行处理。这里值得注意的是,对于每个stp,先遍历所有的动作链表ap,同时执行acttab_action把动作数据放在pActtab->aLookahead里。执行完for循环后调用acttab_insert把该状态里的所有动作数据放在p->aAction里,并把pActtab->aLookahead清零
在这里插入图片描述

4.2 动作的数值

原先动作由struct action结构体表示,生成语法解析文件后会转换成一个整数。对于移进动作,动作取对应的状态编号。对于归约动作,动作数值取状态总数加对应的产生式编号,相关代码如下
在这里插入图片描述

4.3 每个状态的所有动作

每个状态的所有动作会放在pActtab->aLookahead里,每增加一个动作nLookahead加1,同时会记录该状态中动作关联的先行符的最小值和最大值,代码实现在acttab_action函数里
在这里插入图片描述
可以看到每个动作和先行符绑定在一起放在aLookahead里,aLookahead的每个元素是紧挨在一起的

4.4 所有状态的动作插入

每遍历完一个状态的所有动作后,会把aLookahead的动作数据转移到aAction数组里,因为aAction的数据会导入到yy_action里,只要传入当前状态和先行符就可以通过yy_action求得当前的动作,公式是

yy_action[ yy_shift_ofst[S] + X ]

在这里插入图片描述

代码实现在acttab_insert里,aAction数组的下标是先行符,一般来说数组里会有很多空闲,后一个状态的数据插入时会先查看能不能往空隙里插数据,如果能的话就像两把梳子对接起来,不行的话只能往数组的最后添加了
在这里插入图片描述
最后返回i - p->mnLookahead,这个值存放在stp->iTknOfst里,将来会导入yy_shift_ofst[S]数组,即每个状态的最小先行符在aAction数组里的起始位置
在这里插入图片描述
再来分析一下前面蓝框里的代码,这个代码有点莫名奇妙的

        for(j=0; j<p->nLookahead; j++){
          k = p->aLookahead[j].lookahead - p->mnLookahead + i;
          if( k<0 ) break;
          if( p->aAction[k].lookahead>=0 ) break;
        }
        if( j<p->nLookahead ) continue;
        for(j=0; j<p->nAction; j++){
          if( p->aAction[j].lookahead==j+p->mnLookahead-i ) break;
        }
        if( j==p->nAction ){
          break;  /* Fits in empty slots */
        }

因为前面的for循环已经出来了,到这里说明已经找到空隙。 p->aAction[j].lookahead== j+p->mnLookahead-i这里的j绝对不可能是上面的k,因为
p->aAction[k].lookahead<0
k+p->mnLookahead-i>0
所以不可能满足p->aAction[j].lookahead==j+p->mnLookahead-i这个条件,那么这个j就不是新加入的动作,而是原来状态的动作,为什么原来的状态和现在的状态的最小先行符mnLookahead不能相同呢,这个还不知道原因

5.语法分析

lemon文件运行后会输出一个语法分析的.c文件,再来看看这个.c文件是怎么工作的
首先会根据先行符和当前的状态,找到对应的动作,代码如下
在这里插入图片描述
获得动作的值后,就可以判断是移进还是规约,进而到相应的函数里处理
在这里插入图片描述
yy_shift时会更新当前的状态
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值