也许可以补充一下d11wtq的优秀答案:
在解析器构建过程中,可空规则(派生ϵ的规则)是在函数FOLLOW(X)
和FIRST(X)
下考虑的。例如,如果你有A -> B x
,并且B可以推导出ϵ,则我们必须在FIRST(A)
计算的集合中包含x
。同时也要包括在FOLLOW(B)
的集合中。
此外,在规范的LR(1)
项目集中很容易表示空规则。
一个有用的技巧是想象有一个额外的非终结符号$
,它代表文件的结束。
让我们来看一下语法:
S -> X | ϵ
X -> id
对于第一个规范的LR(1)
项集,我们可以取第一个LR(0)
项集,并在符号“$”上添加展望:
S -> . X , '$'
S -> . , '$'
X -> . id , '$'
然后我们有一个针对前瞻是
id
的例子:
S -> . X , 'id'
S -> . , 'id
X -> . id , 'id'
现在让我们来看一下
FIRST
和
FOLLOW
集合:
S -> . X , '$'
这不是一个“点结束”的项目,所以在这里需要进行移位操作,但仅当集合 FIRST(X)
包含我们的展望符号 $
时才进行。这是错误的,因此我们不会填写表格条目。
接下来:
S -> . , '$'
这是一个"点最终"项,所以它希望进行归约。为了验证归约的上下文,我们查看
FOLLOW(S)
:我们希望归约的语法符号后面是否跟着前瞻中的内容?确实如此。因为起始符号定义为跟随输入结束符
$
,所以它总是在
FOLLOW(S)
中。因此,我们可以进行归约。由于我们正在减少符号
S
,所以这个归约实际上就是一个
accept
操作:解析结束了。我们用
accept
操作填充表格条目。
同样的,我们可以重复查看下一个具有前瞻
id
的项目集。让我们跳到 S-推导出空规则:
S -> . , 'id'
能否将 S
后跟 id
? 几乎不可能。因此这种约简是不适当的。我们不会填充分析器表格项。
可以看出,空规则不会带来问题。它立即转换为一个点终态的 LR(0)
或 LR(1)
项目(取决于解析器构造方法),并且在考虑前瞻和填充表格方面与任何其他点终态项目一样被处理。