LEMON源码分析笔记——状态默认动作

LEMON源码分析笔记——状态默认动作

2011-4-6 陕师大

为压缩分析表,将状态中使用最频的生产式所对应的动作,作为状态默认动作。状态默认动作保存在状态的iDflt域中。

设置状态默认动作

1.    对每个状态,找出使用最频的生产式,然后标识它对应的动作。标识的办法是:将第一个动作的先行符改为{default},而其它的只将其类型由REDUCE改成NOT_USED.CompressTables

2.    遍历每一个状态,先将iDflt域设成lemon::nstate + lemon::nrule(就是ERROR).再将有归约动作(归约意味着可以挑出最频生产式)的状态的iDflt设为默认动作。(ResortStates

3.    将状态动作制成分析表,将默认动作排除在外。从而达到压缩分析表的目的。(ReportTable)

使用状态默认动作

每一个状态的默认动作都打印在yy_default[]表中,可以根据状态编号来索引。接下来看看这个数组在什么情况下使用:

 

第一处调用(yy_find_shift_action)

  if( stateno>YY_SHIFT_MAX || (i = yy_shift_ofst[stateno])==YY_SHIFT_USE_DFLT )

  {

    return yy_default[stateno];

  }

 

没有偏移量的状态。YY_SHIFT_MAXyy_shift_ofst的最大下标。yy_shift_ofst的最大长度不就就是lemon::nstate吗?从理论上说是对的,但lemon为了最大限度地压榨缩小用使用空间,就连yy_shift_ofst的长度也经过精心计算。

 

     while( n>0 && lemp->sorted[n-1]->iTknOfst==NO_OFFSET ) n--;

     fprintf(out, "#define YY_SHIFT_MAX %d/n", n-1);

 

lemp->sorted[n-1]->iTknOfst==NO_OFFSET表示状态没有偏移量。但一个状态只要有一个先行符为终结符,在不考虑ax[i].nAction>0的情况下,就会执行到下面代码中的stp->iTknOfst = acttab_insert(pActtab);。而一个状态的先行符(指动作中的文法符号)中,不可能没有终结符,若没有一个文法符号,系统会状态加上符号”$”垫底。先行符中也不可能出现只有非终结符,而没有终结符,因为如果可以接受非终结符,那么它的First集一定能被状态接受。

 

for(i=0; i<lemp->nstate*2 && ax[i].nAction>0; i++)

{

     stp = ax[i].stp;

     if( ax[i].isTkn )

     {

         for(ap=stp->ap; ap; ap=ap->next)

         {

              int action;

              if( ap->sp->index>=lemp->nterminal ) continue;

              action = compute_action(lemp, ap);

              if( action<0 ) continue;

              acttab_action(pActtab, ap->sp->index, action);

         }

         stp->iTknOfst = acttab_insert(pActtab);

         if( stp->iTknOfst<mnTknOfst ) mnTknOfst = stp->iTknOfst;

         if( stp->iTknOfst>mxTknOfst ) mxTknOfst = stp->iTknOfst;

     }

     else

     {

         ...

     }

}

 

答案就在ax[i].nAction的计算方法中。

 

     for(i=0; i<lemp->nstate; i++)

     {

         stp = lemp->sorted[i];

         ax[i*2].stp = stp;

         ax[i*2].isTkn = 1;

         ax[i*2].nAction = stp->nTknAct;//nTknActResortStates被赋值

         ax[i*2+1].stp = stp;

         ax[i*2+1].isTkn = 0;

         ax[i*2+1].nAction = stp->nNtAct;

     }

 

state::nTknActstate::nNtActResortStates函数中初使化了。

 

     for(i=0; i<lemp->nstate; i++)

     {

         stp = lemp->sorted[i];

         stp->nTknAct = stp->nNtAct = 0;

         stp->iDflt = lemp->nstate + lemp->nrule;//ERROR

         stp->iTknOfst = NO_OFFSET;

         stp->iNtOfst = NO_OFFSET;

         for(ap=stp->ap; ap; ap=ap->next)

         {

              if( compute_action(lemp,ap)>=0 )

              {

                   if( ap->sp->index<lemp->nterminal )

                   {

                       stp->nTknAct++;

                   }

                   else if( ap->sp->index<lemp->nsymbol )

                   {

                       stp->nNtAct++;

                   }

                   else//难道有符号不在三界五行之内!有——{default}

                   {

                       stp->iDflt = compute_action(lemp, ap);

                   }

              }

         }

     }

 

if( compute_action(lemp,ap)>=0 )使得标有NOT_USED的动作得不到统计,而标识{default}的先行符也没有记入state::nTknAct之中(只可能是终结符)。也就是说如果一个状态所有的终结符都与默认动作有关,那么这些终结符将全军覆没。其对应的ax[i].nAction就为零,所有不可能进入for循环并把iTknOfst修改。在计算iTknOfst之前,还有一句:

qsort(ax, lemp->nstate*2, sizeof(ax[0]), axset_compare);//先行符少的垫底

这样使得先行符少的垫底,所以for中只要碰到ax[i].nAction0,那么后面的一定也为零,for没有必要再执行了。但是要明确的是,先行符少的垫底是指在ax数组中,对于lemon::sorted并不一定。也就是说这些空着iTknOfst的状态散落在lemon::sorted的不同位置。对于在尾端的,截去之后,才是yy_shift_ofst的真正大小。这样stateno>YY_SHIFT_MAX就有可能成立了。经过上面的分析(i = yy_shift_ofst[stateno])==YY_SHIFT_USE_DFLT的含义也清楚了。ReportTable中将散落在lemon::sorted中,没有偏移量的状态,赋值为YY_SHIFT_USE_DFLT。而这两个条件的任何一个成立都是与默认动作有关的,返回yy_default[stateno]一定是默认动作。

 

第二处调用(yy_find_shift_action)

 

i += iLookAhead;

if( i<0 || i>=YY_SZ_ACTTAB || yy_lookahead[i]!=iLookAhead )

{

    return yy_default[stateno];

}

 

延迟报错。主要解释yy_lookahead[i]!=iLookAhead的情形。当状态不接受iLookAhead时,会出现这种情况,但如果状态偏移量不为NO_OFFSET,那么它会通过第一个关卡。到了这里i可以是不接受的符号也可能是被丢掉了的归约先行符,但它们统一使用默认动作,要知道此时的默认动作是归约动作。这样做对于丢掉的可归约先行符来说是没有问题的,但对于不可接受的符号,会出现问题吗?答案是否定的。因为默认动作是归约,并没有移进这个符号,它还是在“外面”等待是否接受,当栈里的句柄被归约后,进入了一个新的状态,如果这么巧,新的状态还是这样,那继续等待,直到碰到一个没有归约动作的状态。此时,yy_default[]中的值就不是归约动作了,而是ERROR了,这时就等着报错吧。所以碰到不接受符号时,虽然不会立马检查出来,但报错是迟早的事。

 

另外在yy_find_reduce_action中,还有两处对yy_default的引用,原理与yy_find_shift_action中的类似,不再做分析。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值