首页 > 解决方案 > 在 rhs 生产中匹配错误时在 Yacc/Bison 中删除令牌

问题描述

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

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

下面的代码匹配error ';'并将其简化为stmt_list. 然后它将尝试stmt_list使用有效的表达式来减少它,但是由于从未调用过第一个产生式,这将触发内存异常。我的目标是让 Bison 在匹配错误时不做任何事情,这样以后的有效表达式可以首先减少为stmt_list.

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)
        ;

标签: error-handlingcompiler-errorscompiler-constructionbisonyacc

解决方案


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

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

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

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

/* The list header structure, which contain all the information
 * necessary to use the list. The forward declaration makes it
 * possible to use pointers to ExprList objects without having to
 * expose its implementation details.
 */
typedef struct ExprList ExprList;

/* Creates a new empty expression-list and returns a pointer to its header. */
ExprList* expr_list_create(void);

/* Resizes the expression list to the supplied size. If the list
 * currently has fewer elements, new elements with default values are
 * added at the end. If it currently has more elements, the excess
 * ones are discarded. Calling with size 0 empties the list (but
 * doesn't delete it).
 */
int expr_list_resize(ExprList* list, int new_length);

/* Frees all storage associated with the expression list. The
 * argument must have been created with expr_list_create, and its
 * value must not be used again after this function returns.
 */
void expr_list_free(ExprList* list);

/* Adds one element to the end of the expression-list.
 * I kept the float datatype for expression values, although I
 * strongly believe that its not ideal. But an advantage of using an
 * API like this is that it is easier to change.
 */
void expr_list_push(ExprList* list, float value);

/* Returns the number of elements in the expression-list. */
int expr_list_len(ExprList* list);

/* Returns the address of the element in the expression list
 * with the given index. If the index is out of range, behaviour
 * is undefined; a debugging implementation will report an error.
 */
float* expr_list_at(ExprList* list, int index);

使用该 API,我们可以重写有效表达式的产生式:

stmt_list: expr ';'            { $$ = expr_list_create();
                                 expr_list_push($$, $1);
                               }
         | stmt_list expr ';'  { $$ = $1;
                                 expr_list_push($$, $1);
                               }

现在针对错误情况。你有两个错误规则;一个在错误位于列表开头时触发,另一个在处理一个或多个(可能是错误的)表达式后遇到错误时触发。这两个都是产生式,因此它们必须具有与( )stmt_list相同的值类型。因此,当产生语法错误时,他们必须做任何你认为合适的事情。stmt_listExprList*

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

stmt_list: error ';'           { $$ = expr_list_create(); }

在我看来,当列表具有至少一个成功计算的值之后检测到错误时,其他错误操作至少有两种选择。一种可能性是丢弃错误的项目,而列表的其余部分保持不变。这仅需要默认操作:

stmt_list: stmt_list error ';'

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

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

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

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


推荐阅读