📜  为什么在编译器设计中要先行后继?

📅  最后修改于: 2021-09-27 14:51:11             🧑  作者: Mango

为什么是第一?
我们在上一篇关于语法分析的介绍中看到了回溯的需要,这确实是一个实现起来很复杂的过程。可以有更简单的方法来解决这个问题:

如果编译器事先知道“应用产生式规则时产生的字符串的第一个字符”是什么,并将其与它看到的输入字符串的当前字符或标记进行比较,则可以明智地采用决定应用哪个生产规则。

让我们采用上一篇文章中相同的语法:

S -> cAd
A -> bc|a 
And the input string is “cad”. 

因此,在上面的例子中,如果它知道在读取输入字符串中的字符’c’ 并应用 S->cAd 后,输入字符串中的下一个字符是 ‘a’,那么它就会忽略产生式规则 A-> bc(因为’b’是这个产生式规则产生的字符串的第一个字符,而不是’a’),直接使用产生式规则A->a(因为’a’是这个产生式产生的字符串的第一个字符产生式规则,并且与输入字符串的当前字符相同,也是 ‘a’ )。
因此可以验证,如果编译器/解析器知道可以通过应用产生式规则获得的字符串的第一个字符,那么它可以明智地应用正确的产生式规则来为给定的输入字符串获得正确的语法树。

为什么要关注?
解析器面临另一个问题。让我们考虑以下语法来理解这个问题。

A -> aBb
 B -> c | ε
 And suppose the input string is “ab” to parse. 

由于输入中的第一个字符是 a,解析器应用规则 A->aBb。

A
        / |  \
      a   B   b

现在解析器检查输入字符串的第二个字符b,要派生的非终端是 B,但解析器无法从包含 b 作为第一个字符的B 派生任何字符串。
但是语法确实包含产生式规则 B -> ε,如果应用它,那么 B 将消失,解析器得到输入“ab”,如下所示。但只有当它知道后面乙方在生产规则中的字符是一样的,在输入当前字符的解析器可以应用它。

在A -> aBb的RHS中,b跟在Non-Terminal B之后,即FOLLOW(B) = {b},当前读取的输入字符也是b。因此解析器应用此规则。它能够从给定的语法中获取字符串“ab”。

A                    A
        /  |  \              /    \                                                
      a    B    b    =>     a      b       
           |
           ε 

因此,如果需要从解析树生成字符串,FOLLOW 可以使非终结符消失。

结论是,我们需要为给定的语法找到 FIRST 和 FOLLOW 集,以便解析器可以在正确的位置正确应用所需的规则。

在下一篇文章中,我们将讨论 FIRST 和 FOLLOW 的正式定义,以及计算这些集合的一些简单规则。

语法分析测验