来自Yacc introduction by Stephen C. Johnson:
使用正确的递归规则,例如
seq : item | item seq ;
解析器会更大一些,并且会看到项目,并且 从右到左减少。更严重的是,内部堆栈中 如果一个很长的序列,解析器将有溢出的危险 读。因此,用户应该在任何合理的地方使用左递归。
我知道Yacc会生成LR解析器,所以我尝试手动解析一些简单的右递归语法。到目前为止我看不出问题。任何人都可以举例说明这些问题吗?
答案 0 :(得分:2)
解析器大小不是一个严重的问题(大多数情况下)。
运行时堆栈大小可能是个问题。麻烦的是,正确的递归规则意味着在解析器到达序列末尾之前不能减少堆栈,而使用左递归规则,每次语法遇到seq item
时,它都可以减少数量堆栈上的物品。
经典地,令牌的堆栈是固定的并且大小有限。因此,一个正确的递归规则,如一个处理:
IF <cond> THEN
<stmt-list>
ELSIF <cond> THEN
<stmt-list>
ELSIF <cond> THEN
<stmt-list>
ELSE
<stmt-list>
ENDIF
会限制语法可以接受的ELIF子句链中的术语数量。
假设语法:
if_stmt: if_clause opt_elif_clause_list opt_else_clause ENDIF
;
if_clause: IF condition THEN stmt_list
;
opt_elif_clause_list: /* Nothing */
| elif_clause opt_elif_clause_list /* RR */
;
elif_clause: ELIF condition THEN stmt_list
;
opt_else_clause: /* Nothing */
| ELSE stmt_list
;
stmt_list: stmt
| stmt_list stmt /* LR */
;
我似乎记得很久以前(十年或更久以前)对此进行了一些测试,而我当时使用的Yacc,结合语言语法,与上面类似,意味着在大约300个ELIF子句之后,解析器停止了(我认为它已经停止在控制之下,意识到它已经耗尽了空间,而不是忘记了空间耗尽)。
答案 1 :(得分:2)
我完全不确定为什么他说正确的递归解析器会更大 - 通常它会在状态机中需要少一个状态(如果有什么东西使其更小),但真正的问题是正确的递归语法需要无限的堆栈空间,而左递归语法需要恒定的空间(O(n)空间与O(1)空间)。
现在O(n)vs O(1)可能听起来很重要,但取决于你在做什么,它可能并不重要。特别是,如果您正在将整个输入读入内存以进行处理,那么O(n)空间完全超过O(n)与O(1)区分右右递归。如果您使用的是仍然具有固定解析器堆栈的特别旧版本的yacc,则可能存在问题,但最新版本的yacc(Berkeley yacc,bison)会根据需要自动扩展其解析堆栈,因此唯一的限制是可用的存储器中。