编译器实现之旅——第五章 实现语法分析器前的准备 (2)

怎么消除呢?首先需要明确的是,语法表示并不是神圣不可侵犯的,而是可以进行等价变换的。那么,上面这个语法该怎么变换呢?让我们来找找规律:

1. Six ::= '6' 2. Six ::= Six '6' ::= '6' '6' 3. Six ::= Six '6' ::= Six '6' '6' ::= '6' '6' '6' ...

找到规律了!我们发现:

每一次使用“Six '6'”替换“Six”,就会在“Six”的右边多出一个“6”

“Six”终将被“6”而不是“Six '6'”替换

也就是说,“Six”推导出的应该是:1至有限多个“6”。写成语法就是:

Six ::= '6' { '6' }

可见,我们成功的消除了左递归。现在,语法的推导就可以正常开始并进行下去,而不会出现无限循环的情况了。

6. First集合

解决了左递归问题后,让我们再来看看接下来的这个问题。

假设有以下语法:

1. A ::= B | C 2. B ::= 'b' 3. C ::= D 4. D ::= 'd'

“A ::= B | C”?选B还是C?我们无从获知。因为“B”和“C”并不代表某个具体的字符,而是另外的两条语法。这时候怎么办呢?这时候呀,我们可就要刨根问底了。

我们先来看看“B”又是什么,我们发现:“B ::= 'b'”,也就是说,如果我们看到了“b”,我们就选“B”。

那么,“C”又是什么?我们发现:“C ::= D”,不行,我们需要继续看。“D”又是什么?我们发现:“D ::= 'd'”。也就是说:

如果我们看到了“d”,我们就选“D”

由于“C ::= D”,所以如果我们看到了“d”,我们也可以选“C”

经过一番刨根问底,现在我们知道了:对于“A ::= B | C”这条语法而言,如果我们看到了“b”,我们就选“B”;如果我们看到了“d”,我们就选“C”。当然了,如果我们看到的既不是“b”也不是“d”,这就是语法错误了。

上面我们得到的结果,其实就是First集合了。First集合可以帮助我们在“多选一”类语法中立即做出正确的选择。

我们的旅程到这里就快要暂告一段落了,最后,我们可以考虑两个问题。请看:

我就不用First集合,行不行?

行,当我们看到“A ::= B | C”时,管他呢,我们就选“B”。如果选“B”是错的,那么我们就想办法把这个“选错了”的信息传回来,然后我们再换成“C”试试看。

这样做的缺点显而易见:计算量可能会非常大,且毫无意义。如果“A ::= B | C”是语法的顶层,那么我们甚至有可能直到看到最后一个记号,才发现选错了,然后一切推翻重来。

刨根问底,会不会还是问不出个所以然?

不会。因为...

...接连便是难懂的话,什么“乔姆斯基体系”,什么“上下文无关文法”之类...

至此,我们就完成了所有准备工作,可以开始实现语法分析器了。请看下一章:《实现语法分析器》。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zwgsff.html