将函数作为参数传递给 yyparse 时存在 Bison 错误?

Bison bug when passing functions as arguments to yyparse?

我正在重写解析器以使其可重入。本着这种精神,我有兴趣将几个函数传递给 bison,然后让 bison 也将它们传递给 lex。 其中一个函数是我在操作中使用的回调,另一个是 flex 调用以获取输入数据的函数。

为此,我已将其放入我的 .y 文件中:

%lex-param {void (*my_input)(void *, char*, int *, int)}
%parse-param {void (*my_input)(void *, char*, int *, int)}
%parse-param {void *(*my_callback)(void *, char *, int, struct YYLTYPE *, int, ...)}

然后,在我的 .l 文件中声明:

#define YY_DECL int yylex (YYSTYPE *yylval_param, YYLTYPE *yylloc_param, yyscan_t yyscanner, void (*my_input)(void *, char*, int *, int))

问题是我认为 bison 在生成它的代码时可能有一个错误。当我尝试构建 bo 时,出现以下错误:

tmp.tab.c: In function ‘yy_symbol_value_print’:
tmp.tab.c:4043: error: expected expression before ‘)’ token

如果我们访问那一行,我们会得到这个函数:

static void
yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, yyscan_t scanner, void (*my_input)(void *, char*, int *, int), void *(*my_callback)(void *, char *, int, struct YYLTYPE *, int, ...))
{
    FILE *yyo = yyoutput;
    YYUSE (yyo);
    YYUSE (yylocationp);
    YYUSE (scanner);
    YYUSE (int);
    YYUSE (int);
    if (!yyvaluep)
        return;
    YYUSE (yytype);
}

带有 YYUSE(int) 的第一行是抛出错误的行。如您所见,此函数以某种方式接收与 yyparse 相同的参数,然后它使用接收到的参数调用名为 YYUSE() 的宏。我认为,由于我的两个参数是函数(带有它们的参数列表,因为如果我理解正确的话,它们必须被声明)野牛调用 YYUSE() 并使用每个函数原型的最后一个参数......据我所知应该是 USE(my_input) 和 USE(my_callback)...

我很难相信这真的是一个错误,我的意思是,真的,直到现在还没有人尝试过吗?我很难相信...

YYUSE() 调用遍及生成的文件,尽管我真的不知道它们的用途...所以手动更改不是真正的选择... 过去有人成功地做到过吗?我做错了什么吗?

Bison 的参数解析器有点原始。它希望看到类似 %param {type name} 的内容,如果发现更复杂的内容,它可能会做错事。 (type 可以相当复杂;它可以包括 const* 和其他此类修饰符。但是 name 需要是规范中的最后一件事。)

这记录在 Bison manual 中:(强调)

Directive: %parse-param {argument-declaration} …

Declare that one or more argument-declaration are additional yyparse arguments. The argument-declaration is used when declaring functions or prototypes. The last identifier in argument-declaration must be the argument name.

类似的限制适用于 %union 指令中的标记名。

您可以使用 typedef 使您的程序对野牛和人类读者都更具可读性:

将其放入通用头文件中:

typedef void (*InputFunction)(void *, char*, int *, int);
typedef void *(*CallbackFunction)(void *, char *, int, struct YYLTYPE *, int, ...);

野牛文件:

%lex-param {InputFunction my_input}
%parse-param {InputFunction my_input} {CallbackFunction my_callback}

弹性文件:

#define YY_DECL int yylex (YYSTYPE *yylval_param, YYLTYPE *yylloc_param, yyscan_t yyscanner, InputFunction my_input)

顺便说一下,YY_USE 宏的目的是将参数标记为 "used",即使它们不是;这避免了碰巧不使用参数的函数中的编译器警告。有人可能会争辩说,确保参数被使用或标记为未使用是程序员的责任,但这并不是 bison 碰巧采取的方法。无论如何,即使没有 YY_USE.

,non-conforming 参数声明(例如您提供的那个)也会以其他有趣的方式失败