为什么正确的递归语法需要来自Yacc的更大的解析器?

时间:2013-10-22 18:51:24

标签: parsing compiler-construction yacc

来自Yacc introduction by Stephen C. Johnson

  

使用正确的递归规则,例如

    seq     :       item
            |       item  seq
            ;
     

解析器会更大一些,并且会看到项目,并且   从右到左减少。更严重的是,内部堆栈中   如果一个很长的序列,解析器将有溢出的危险   读。因此,用户应该在任何合理的地方使用左递归。

我知道Yacc会生成LR解析器,所以我尝试手动解析一些简单的右递归语法。到目前为止我看不出问题。任何人都可以举例说明这些问题吗?

2 个答案:

答案 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)会根据需要自动扩展其解析堆栈,因此唯一的限制是可用的存储器中。