怎么消除呢?首先需要明确的是,语法表示并不是神圣不可侵犯的,而是可以进行等价变换的。那么,上面这个语法该怎么变换呢?让我们来找找规律:
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”是语法的顶层,那么我们甚至有可能直到看到最后一个记号,才发现选错了,然后一切推翻重来。
刨根问底,会不会还是问不出个所以然?
不会。因为...
...接连便是难懂的话,什么“乔姆斯基体系”,什么“上下文无关文法”之类...
至此,我们就完成了所有准备工作,可以开始实现语法分析器了。请看下一章:《实现语法分析器》。