Error handling 在rhs生产中匹配错误时,在Yacc/Bison中丢弃令牌

Error handling 在rhs生产中匹配错误时,在Yacc/Bison中丢弃令牌,error-handling,compiler-errors,compiler-construction,bison,yacc,Error Handling,Compiler Errors,Compiler Construction,Bison,Yacc,我正在编写一个简单的计算器,其中一个表达式可以简化为一个语句或语句列表。如果一个错误的表达式触发了语法错误,我会尝试用一个产生式规则捕捉它,然后通过不给规则任何操作来忽略它。然而,我相信这仍然是一个stmt\u列表,尽管它不是一个有效的声明 有没有办法让它简单地忽略与错误匹配的令牌,并防止它们被减少并在以后使用 下面的代码与错误“;”匹配并将其缩减为stmt\u列表。然后,它将尝试使用有效的表达式减少stmt\u列表,但由于从未调用第一个产品,因此这将触发内存异常。我的目标是让Bison在匹配错

我正在编写一个简单的计算器,其中一个表达式可以简化为一个语句或语句列表。如果一个错误的表达式触发了语法错误,我会尝试用一个产生式规则捕捉它,然后通过不给规则任何操作来忽略它。然而,我相信这仍然是一个
stmt\u列表
,尽管它不是一个有效的声明

有没有办法让它简单地忽略与错误匹配的令牌,并防止它们被减少并在以后使用

下面的代码与
错误“;”匹配
并将其缩减为
stmt\u列表
。然后,它将尝试使用有效的表达式减少
stmt\u列表
,但由于从未调用第一个产品,因此这将触发内存异常。我的目标是让Bison在匹配错误时不做任何事情,这样以后的有效表达式就可以第一次简化为
stmt\u列表

stmt_list:
        expr ';'                {
                                    // Allocate memory for statement array
                                    stmts = (float*)malloc(24 * sizeof(float));
                                    // Assign pointer for array
                                    $$ = stmts;
                                    // At pointer, assign statement value
                                    *$$ = $1; 
                                    // Increment pointer (for next job)
                                    $$++;
                                }
        | stmt_list expr ';'    {
                                    $$ = $1;
                                    *$$ = $2;
                                    $$++;
                                }
        | error ';'             { } // Do nothing (ignore bad stmt)
        | stmt_list error ';'   { } // Do nothing (ignore bad stmt)
        ;

如果没有为规则提供任何操作,bison/yacc将提供默认操作
$$=$1

事实上,你没有提供任何行动。您提供的是一个不起任何作用的显式操作。碰巧,如果使用C模板,解析器仍将执行默认操作。在其他模板中,未为
$$
赋值的操作可能会在解析器生成期间引发警告。但它肯定不会修改您的数据结构,从而使操作无效。它不知道那是什么意思。如果你知道,你应该把它写成action:-)

我不完全清楚为什么要将评估结果保存在一个固定大小的动态分配数组中。您没有尝试检测数组何时填满,因此完全有可能最终溢出分配并覆盖随机内存。此外,使用这样的全局列表通常不是一个好主意,因为它会阻止您同时构建多个列表。(例如,如果要实现函数调用,因为函数的参数也是表达式列表。)

总的来说,最好将扩展表达式列表的实现放在其他地方实现的简单API中。在这里,我假设你已经做到了;为了明确起见,我将假设以下API(尽管这只是一个示例):

/*包含所有信息的列表标题结构
*必须使用列表。前进宣言使之成为现实
*可以使用指针来导出对象,而无需
*公开其实现细节。
*/
typedef struct ExprList ExprList;
/*创建新的空表达式列表并返回指向其标头的指针*/
ExprList*expr\u list\u创建(作废);
/*将表达式列表调整为提供的大小。如果列表
*当前元素较少,使用默认值的新元素较少
*在末尾添加。如果当前有更多元素,则多余的
*一个被丢弃。使用大小为0的调用将清空列表(但
*不删除它)。
*/
int expr_list_resize(ExprList*list,int new_length);
/*释放与表达式列表关联的所有存储。这个
*参数必须是使用expr_list_create创建的,并且
*此函数返回后,不得再次使用值。
*/
无效expr_列表(ExprList*list);
/*将一个元素添加到表达式列表的末尾。
*我保留了表达式值的浮点数据类型,尽管
*我坚信这并不理想。但是使用
*这样的API更容易更改。
*/
void expr_list_push(ExprList*list,浮点值);
/*返回表达式列表中的元素数*/
int expr_列表(ExprList*list);
/*返回表达式列表中元素的地址
*使用给定的索引。如果指数超出范围,则行为
*是未定义的;调试实现将报告错误。
*/
float*expr\u list\u at(ExprList*list,int索引);
使用该API,我们可以为有效表达式重写产品:

stmt_list: expr ';'            { $$ = expr_list_create();
                                 expr_list_push($$, $1);
                               }
         | stmt_list expr ';'  { $$ = $1;
                                 expr_list_push($$, $1);
                               }
现在是错误案例。您有两个错误规则;一个在错误位于列表开头时触发,另一个在处理一个或多个(可能是错误的)表达式后遇到错误时触发。这两个都是
stmt\u list
的产品,因此它们必须具有与
stmt\u list
相同的值类型(
ExprList*
)。因此,当产生语法错误时,它们必须执行您认为合适的任何操作

第一个,当错误在列表的开头时,只需要创建一个空列表。很难看出它还能做什么

stmt_list: error ';'           { $$ = expr_list_create(); }
在我看来,当在列表至少有一个成功计算的值之后检测到错误时,另一个错误操作至少有两种选择。一种可能是抛弃错误的项目,保留列表的其余部分不变。这只需要默认操作:

stmt_list: stmt_list error ';'
(当然,如果愿意,您可以显式地添加操作
{$$=$1;}
。)

另一种可能是清空整个列表,以便从头开始使用下一个元素:

stmt_list: stmt_list error ';' { $$ = $1;
                                 expr_list_resize($$, 0);
                               }

毫无疑问,还有其他的可能性。正如我所说,野牛无法理解你的意图(我也不能,真的)。您必须实现您想要的任何行为。

如果您没有为规则提供任何操作,bison/yacc将提供默认操作
$$=$1

事实上,你没有提供任何行动。您提供的是一个不起任何作用的显式操作。碰巧,如果使用C模板,解析器仍将执行默认操作。在其他模板中,未为
$$
赋值的操作可能会在解析器生成期间引发警告。但它肯定不会修改您的数据结构,从而使操作无效。它不知道那是什么意思。如果你知道,你应该把它写成action:-)

它是