想象一下这个语法:
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_specifiers init_declarator_list - > init_declarator_list','/*error*/
这里发生了什么?
bison是否执行(4)代码? (2)?如果野牛没有执行(4)但它确实执行(2)什么是$ 3值?如何设置$ variables的默认值?
如何正确删除错误时生成的AST?
答案 0 :(得分:2)
bison
仅在动作的制作减少时才执行动作,这意味着它必须与输入完全匹配,除非它是error
制作,在这种情况下轻松匹配表格被使用。 (见下文。)因此,您可以放心,如果执行某个操作,则与其终端和非终端关联的各种语义值是词法分析器或其各自操作的结果。
在错误恢复期间,bison将自动从堆栈中丢弃语义值。使用合理最近的bison版本,您可以指定在使用%destructor
声明丢弃值时要执行的操作。 (有关详细信息,请参阅bison manual。)您可以按类型或符号(或两者都指定析构函数,但每符号析构函数优先。)
只要%destructor
丢弃语义值,就会运行bison
操作。粗略地说,丢弃语义值意味着您的程序从未有机会处理语义值。当生产减少时,它不适用于从堆栈弹出的值,即使没有与减少相关的明确操作。完全定义"丢弃"是在前面引用的野牛手册部分的末尾。
没有错误产生,除了丢弃整个堆栈和任何先行符号(野牛将自动执行)然后终止解析之外,错误恢复的方式实际上没有多少可能。通过向语法添加错误产生,您可以做得更好。错误生成包括特殊标记error
;在没有其他可能匹配的情况下,此标记精确匹配空序列。与普通制作不同,错误制作不需要立即可见; bison
将从堆栈中丢弃状态(和相应的值),直到找到具有error
转换的状态,或者它到达堆栈的末尾。此外,错误生成中error
之后的终端不需要是先行令牌; bison
将丢弃先行令牌(和相应的值),直到它能够继续生成错误(或者它到达输入的末尾)。有关流程的详细说明,请参阅handy manual(如果附近有副本,请在龙书中阅读相关内容。)
答案 1 :(得分:1)
这里有几个问题。
Bison通过处于解析状态来检测错误,其中当前先行令牌没有动作(shift或reduce)。在您移动,
中的“init_declarator_list
”之后将处于州内的示例中。在该状态下,只有FIRST中的令牌(init_declarator)才有效,因此任何其他令牌都会导致错误。
当相应的规则减少时,将执行野牛代码中的操作,因此永远不会调用action(4) - 它永远不会减少该规则。 Action(3)将在该规则减少时运行,该规则在将,
转移到检测到错误的状态之前发生。
在出现错误(并使用错误消息调用yerror)之后,解析器将尝试通过从堆栈弹出状态来恢复,寻找可以移动特殊error
令牌的状态。当它弹出并丢弃状态时,它将为与这些状态对应的符号调用%destructor
动作,因此如果需要,您可以使用它来清理事物(可用内存)。
在您的情况下,看起来没有错误规则,因此没有可以移动错误令牌的状态。所以它将弹出所有状态,然后从yyparse返回失败。如果它确实找到了可以转移错误的状态,它会停止弹出并转移错误令牌,并尝试在错误恢复模式下继续解析。在错误恢复模式下,它会计算自上次发生错误以来已移位的令牌数(错误令牌除外)。如果在击中另一个错误之前它已经移动了少于3个令牌,它将不会为新错误调用yyerror。此外,如果它已经移动了0个标记,它将尝试通过读取并丢弃输入标记(而不是弹出状态)从错误中恢复,直到找到可以由当前状态处理的标记。因为它会丢弃令牌,所以它会为这些令牌调用%destructor
,所以你可以再次清理任何需要清理的东西。
因此,为了回答您的上一个问题,您可以使用%destructor
声明在发生错误时删除内容。对于每个丢弃的项目,%destructor
只被调用一次,而不会被传递给bison动作。传递给操作的项目(在操作中为$1
,$2
,...)将永远不会为%destructor
调用它们,因此如果您在之后不需要它们动作,你应该删除它们。