在 LALR(1) 解析器中,语法中的规则被转换为一个解析表,该表有效地表示“如果到目前为止你有这个输入,并且前瞻标记是 X,那么转移到状态 Y,或者通过规则 R 减少” .
我已经成功地用解释语言(ruby)构建了一个 LALR(1) 解析器,而不是使用生成器,而是在运行时计算解析表并使用该解析表评估输入。这工作得非常好,表生成非常简单(这让我有些惊讶),支持自我引用规则和左/右关联。
然而,我难以理解的一件事是 yacc/bison 在概念上如何处理空规则定义。我的解析器无法处理它们,因为在生成表时,它会递归地查看每个规则中的每个符号,并且“空”不是来自词法分析器的东西,也不会被规则减少。那么,LALR(1) 解析器如何处理空规则呢?他们是否特别对待它,或者它是一个有效算法应该使用的“自然”概念,甚至不需要对这样一个概念有特别的认识?
假设一个规则可以匹配任意数量的成对括号,中间没有任何内容:
expr: /* empty */
| '(' expr ')'
;
如下输入将匹配此规则:
((((()))))
这意味着在前瞻标记中读取 '(' 并看到 ')' 时,解析器会选择:
- 移动“)”(不可能)
- 根据其他规则减少输入(不可能)
- 还有什么...
不太适合“移位”或“减少”的核心算法。解析器实际上需要什么都不移动到堆栈上,将“无”归expr
约为 ,然后移动下一个标记')'
,给'(' expr ')'
,当然归约为expr
,依此类推。
让我困惑的是“什么都不做”。解析表是如何传达这样一个概念的?还要考虑到应该可以调用一些语义动作来返回一个值来$$
减少空值,所以一个相当简单的视图只是从解析表中跳过它并说'('
在堆栈和')'
前瞻中应该简单地转换为一个转变,不会真正产生序列'(' expr ')'
,而只会产生序列'(' ')'
。