错误发生的情况- Bison

What happens on error - Bison

本文关键字:Bison 情况 错误      更新时间:2023-10-16

想象一下这个语法:

declaration
    : declaration_specifiers ';' { /* allocate AST Node and return (1) */}
    | declaration_specifiers init_declarator_list ';' { /* allocate AST Node and return (2)*/}
    ;
init_declarator_list
    : init_declarator { /* alloc AST Node and return (3) */}
    | init_declarator_list ',' init_declarator { /* allocate AST Node and return (4) */}
    ;

现在假设在','标记中有错误。目前我们有:

declaration -> declaration_specifiers init_declarator_list -> init_declarator_list ',' /*error*/

这里发生了什么?

bison执行(4)代码吗?(2) ?如果bison没有执行(4)但是执行了(2)$3的值是多少?如何设置$variables的默认值?

如何正确删除错误生成的AST ?

bison仅在动作产生减少时执行动作,这意味着它必须完全匹配输入,除非它是error产生,在这种情况下使用宽松的匹配形式。(见下文)。因此,您可以确信,如果执行了某个操作,那么与其终端和非终端相关联的各种语义值是词法分析器或它们各自操作的结果。

然而,在错误恢复期间,bison将自动从堆栈中丢弃语义值。对于最近的bison版本,可以使用%destructor声明指定在丢弃值时执行的操作。(详见野牛手册)可以按类型或按符号指定析构函数(或两者都指定,但按符号指定的析构函数优先)

bison 丢弃语义值时,%destructor动作将运行。粗略地说,丢弃一个语义值意味着您的程序永远没有机会处理这个语义值。它不适用于减少时从堆栈中弹出的值,即使没有与减少相关的显式操作。关于"丢弃"的完整定义在前面引用的bison手册部分的末尾。

如果不产生错误,除了丢弃整个堆栈和任何forward符号(bison将自动执行),然后终止解析之外,实际上没有太多可能的错误恢复方式。您可以通过在语法中添加错误结果来做得更好。错误产生包括特殊令牌error;在没有其他可能的匹配的情况下,这个标记精确地匹配一个空序列。与正常的结果不同,错误结果不需要立即可见;bison将从堆栈中丢弃状态(和相应的值),直到它找到具有error转换的状态,或者它到达堆栈的末尾。此外,在错误产生中,error后面的终端不需要是前瞻令牌;bison将丢弃前瞻标记(和相应的值),直到它能够继续产生错误(或者到达输入的末尾)。关于这个过程的更详细的描述,请参阅方便的手册(或者在Dragon的书中阅读,如果你附近有一本的话)。

这里有几个问题

Bison通过处于解析状态来检测错误,在这种状态下,对当前的forward令牌没有操作(shift或reduce)。在您的示例中,将在init_declarator_list中移动' , '后处于状态。在这种状态下,只有FIRST(init_declarator)中的令牌是有效的,因此任何其他令牌都将导致错误。

bison代码中的Actions将在相应的规则缩减时执行,因此action(4)将永远不会被调用——它永远不会执行到缩减该规则的程度。操作(3)将在该规则减少时运行,这发生在它将,转移到检测到错误的状态之前。

出现错误(并调用yerror并给出错误消息)后,解析器将尝试通过弹出堆栈状态来恢复,寻找可以移动特殊error令牌的状态。当它弹出和丢弃状态时,它将调用%destructor动作来处理与这些状态相对应的符号,因此如果需要,您可以使用它来清理东西(释放内存)。

在您的示例中,似乎没有错误规则,因此没有可以转移错误令牌的状态。所以它将弹出所有状态,然后从yyparse返回失败。如果它确实找到了可以转移错误的状态,它将停止弹出并转移错误令牌,并尝试在错误恢复模式下继续解析。在错误恢复模式下,它会计算自上次发生错误以来移动了多少令牌(除了错误令牌)。如果它在遇到另一个错误之前移动了少于3个令牌,它将不会为新错误调用yyerror。此外,如果它移动了0个标记,它将尝试通过读取和丢弃输入标记(而不是弹出状态)来从错误中恢复,直到找到一个可以由当前状态处理的标记。当它丢弃令牌时,它会为这些令牌调用%destructor,因此您可以再次清理任何需要清理的内容。

所以回答你的最后一个问题,你可以使用%destructor声明来删除错误发生时的东西。对于每个被丢弃的项目,%destructor只被调用一次,而不被传递给bison操作。传递给动作的项(如$1, $2,…)在动作中)将永远不会有%destructor调用它们,所以如果你在动作后不需要它们,你应该删除它们。