移进/规约冲突

文章讨论了一个编程中的问题,涉及不完全if语句导致的移进/规约冲突,以及在编译过程中tmpnametab清理引发的变量名混淆bug。通过分析和解决方法,解释了如何避免这种错误并优化内存管理。
摘要由CSDN通过智能技术生成

前面的函数goto中给出一个费波纳挈例子。有个不寻常的if语句。是故意这么写的。

func fib(n)
{
	print ":","\b";
goo:
	print "fib(", n, ")", "\b";
	if(n==1||n==0) return 1; else ;
	j= fib(n-1).goto goo;
	k= fib(n-2).goto goo;
	print j+k;
	return j+k;
}

正常来说最后的空else分句是不用写的。当然遵循习惯,一开始也没这样写,这样就是个不完全if语句。但是这里有个坑。分析器运行到这里时,不知道后面会不会有else,需要再读进一个token来确认这个if语句是不是结束了。这就是移进/规约冲突。冲突的解决办法是移进。在函数外面解释执行的if语句也会遇到,遇到时多输入一个空分号“;”就可以了。这样一切都ok了。

实际上说一切都ok了还差了一点。只是分析器部分ok了。编译函数的时候,不同于外面,外面的语句接受一条执行一条,不会积累。函数要接收很多token,这样会占用很多内存空间。而且其中很多有关联的yylval值。

清理这些内存最合适的时机,是每条语句接收完成。而不是等到整个函数编译完成。

应用这些规则都是合理的。…(坑来了)…

因为这是个不完全的if语句,分析器读到 return 1;时还要再读一个token,读到j,缓冲j,然后宣布if语句规约。这是LALR(1)分析器的规范动作。记号j的token是VAR,关联的名称是字符串"j"。一句话中所有的名称字符串都保存在一个临时字符串表tmpnametab中。相关的VAR都通过一个索引引用它。好了,或者应该说“坏了”,当分析器执行规约时状态回到语句头,于是进行一个tmpnametab清理。

注意,下一条语句的第一个token在上一条语句结束时,已经提前读入了。当tmpnametab清理后,记号j的关联的名称字符串的空间已经被回收了!它指向一个将要跟人家冲突的一个过时的内存!

接着,分析器继续读入。fib是第一个,n是第二个,goo是第三个。这时候j呢,j引用的是goo的位置。它被覆盖了,它的名称变成了"goo"。当这个语句被接受时,灾难发生了。这个

	j= fib(n-1).goto goo;

被分析器不声不响的当作

	goo= fib(n-1).goto goo;

处理了! j没有被赋值, k=1, j+k = 1, return j+k也=1。

fib(n)的执行看起来永远不会增加,结果总是1。这是个令人十分困惑的问题。直到花费大量的时间来调试,最终发现是这个bug的原因之前,一直是很困惑。不知道要怀疑谁。然而加上else;就正常,立竿见影,很神奇。

找到原因之后当然就没什么神秘了。问题也迎刃而解了。只要清理tmpnametab时检查分析器有没有缓冲着提前读入的记号,如果有的话暂缓清理。这样就行了。

funcss:                 { $$=0;}
        | funcss ls     {
                        struct node *p;
                        p= getnode();
                        p->node_type= NT_CSS;
                        p->left = (struct node *)$1;
                        p->medium = (struct node *)$2;
                        p->right= NULL;
                        $$=p;
                        if(yychar ==YYEMPTY)  tmpnamecount=0;
                        }
        ;

或者,仍然坚持清理。但妥善保存前读入的记号。清理过后,相关的值重新登记到tab中。这样也没问题。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值