移进/规约冲突

移进/规约冲突

前面的函数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中。这样也没问题。

相关推荐

如何在没有 Office 365 订阅的情况下下载和安装 Office 免费版?
Windows 11键盘锁定怎么操作?如何锁住键盘?
365备用主页器

Windows 11键盘锁定怎么操作?如何锁住键盘?

📅 10-29 👁️ 6441
理性看球 别让世界杯变成“世界悲”
365真正的官网

理性看球 别让世界杯变成“世界悲”

📅 09-03 👁️ 6885