当在 rhs 生产中匹配错误时,在 Yacc/Bison 中删除一个标记

Dropping a token in Yacc/Bison when an error is matched in the rhs production

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

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

下面的代码匹配 error ';' 并将其缩减为 stmt_list。然后它将尝试使用有效表达式减少 stmt_list,但由于从未调用过第一个产生式,这将触发内存异常。我的 objective 是让 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
                                    *$$ = ; 
                                    // Increment pointer (for next job)
                                    $$++;
                                }
        | stmt_list expr ';'    {
                                    $$ = ;
                                    *$$ = ;
                                    $$++;
                                }
        | error ';'             { } // Do nothing (ignore bad stmt)
        | stmt_list error ';'   { } // Do nothing (ignore bad stmt)
        ;

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

事实上,您并没有提供任何操作。您正在提供一个不执行任何操作的明确操作。碰巧的是,如果您使用 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($$, );
                               }
         | stmt_list expr ';'  { $$ = ;
                                 expr_list_push($$, );
                               }

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

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

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

在我看来,当列表至少有一个成功计算的值后检测到错误时,对于另一个错误操作至少有两种选择。一种可能性是放弃错误的项目,而保留列表的其余部分。这只需要默认操作:

stmt_list: stmt_list error ';'

(当然,如果需要,您可以明确添加操作 { $$ = ; }。)

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

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

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