Flex 删除预定义的宏
Flex dropping predefined macro
我有以下 flex 源:
%{
#if !defined(__linux__) && !defined(__unix__)
/* Maybe on windows */
#endif
int num_chars = 0;
%}
%%
. ++num_chars;
%%
int main()
{
yylex();
printf("%d chars\n", num_chars);
return 0;
}
int yywrap()
{
return 1;
}
我通过命令flex flextest.l
生成一个C文件,然后用gcc -o fltest lex.yy.c
编译结果
令我惊讶的是,我得到以下输出:
flextest.l:2:37: error: operator "defined" requires an identifier
#if !defined(__linux__) && !defined(__unix__)
进一步检查,问题似乎是flex实际上已经将__unix__
替换为空字符串,如下所示:
$ grep __linux_ lex.yy.c
#if !defined(__linux__) && !defined()
为什么会发生这种情况,是否可以避免?
实际上是m4
(当前版本flex
使用的宏处理器)将__unix__
扩展为空字符串。 m4
的 Gnu 实现将某些符号定义为空宏,以便它们可以用 ifdef
.
进行测试
当然,这是(更好的说法是)flex 中的一个错误。 Flex 不应允许 m4
在从扫描仪定义文件复制的用户内容中扩展宏,并且当前版本的 flex 正确安排了要引用的扫描仪描述文件中包含的文本,以便它通过 m4
未修改,即使它恰好包含一个可以被 m4
解释为宏扩展的字符串。
这个bug肯定存在于flex的v2.5.39和v2.6.1中。我没有测试所有以前的版本,但我想它是在将 flex 修改为使用 m4
时引入的,根据 NEWS 文件,它是 v2.5.30。
这个特殊的引用问题已在 v2.6.2 中修复,但当前版本的 flex (2.6.4) 包含各种其他错误修复,因此我建议您升级到最新版本。
如果您真的需要一个既能与有问题的版本又能与 flex 的最新版本一起使用的版本,您可以使用以下两种 hack 之一:
换一种写法__unix__
。一种可能是下面
#define C(x,y) x##y
#define UNIX_ C(__un,ix__)
#if !defined(__linux__) && !UNIX_
该 hack 不适用于 defined
,因为 defined(UNIX_)
测试是否定义了 UNIX_
,而不是它扩展到的内容是否定义。但通常像 __unix__
这样的内置符号实际上被定义为 1,如果它们被定义的话,并且 #if
指令将任何不是 #define 的标识符视为 0,这意味着你通常可以离开使用 x
而不是 defined(x)
。 (但是,如果 #define x 0
有效,它会产生不同的结果,因此它不是一个完美的替代品。)
Flex 和很多m4 应用程序一样,将m4 的引号重新定义为[[
和]]
。有问题的 flex 和更正后的版本都用一个相当复杂的序列替换了这些引号,该序列有效地引用了引号。但是,有缺陷的版本不会以其他方式引用用户定义的文本,因此将在用户文本中执行宏替换。 (如前所述,这就是 __unix__
变成空字符串的原因。
在不引用用户定义文本的 flex 版本中,可以调用重新定义引号的 m4 宏。然后可以使用这些新引号来引用 #if
行,防止 __unix__
的宏替换。但是,必须恢复引用定义,否则它将完全破坏文件其余部分的宏处理。这有点棘手,因为不可能写 [[
。 (Flex 将用不同的字符串替换它。)
以下似乎可以解决问题。请注意,宏调用位于 C 注释中。如果扩展了 changequote 宏,它们将扩展为空字符串。但是在 v2.6.2 之后的 flex 版本中,用户提供的文本被引用,所以 changequote 宏不会被扩展。将它们放在注释中会对 C 编译器隐藏它们。
%{
/*m4_changequote(<<,>>)<<*/
#if !defined(__linux__) && !defined(__unix__)
/*>>m4_changequote(<<[>><<[>>,<<]>><<]>>)*/
/* Maybe on windows */
#endif
(更改引号的 m4 宏是 changequote
但 flex 使用 -P
标志调用 m4
将 changequote
等内置函数更改为 m4_changequote
. 在对 changequote 的第二次调用中,组成 [[
符号的两个 [
分别用临时 <<
引号引起来,这将它们隐藏在修改 use 的 flex 代码中[[
.)
我不知道这个 hack 的可靠性如何,但它适用于我在我的机器上使用过的 flex 版本,包括 2.5.4(M4 之前)2.5.39(错误),2.6。 1(错误)、2.6.2(有些调试)和 2.6.4(更多调试)。
我有以下 flex 源:
%{
#if !defined(__linux__) && !defined(__unix__)
/* Maybe on windows */
#endif
int num_chars = 0;
%}
%%
. ++num_chars;
%%
int main()
{
yylex();
printf("%d chars\n", num_chars);
return 0;
}
int yywrap()
{
return 1;
}
我通过命令flex flextest.l
生成一个C文件,然后用gcc -o fltest lex.yy.c
令我惊讶的是,我得到以下输出:
flextest.l:2:37: error: operator "defined" requires an identifier
#if !defined(__linux__) && !defined(__unix__)
进一步检查,问题似乎是flex实际上已经将__unix__
替换为空字符串,如下所示:
$ grep __linux_ lex.yy.c
#if !defined(__linux__) && !defined()
为什么会发生这种情况,是否可以避免?
实际上是m4
(当前版本flex
使用的宏处理器)将__unix__
扩展为空字符串。 m4
的 Gnu 实现将某些符号定义为空宏,以便它们可以用 ifdef
.
当然,这是(更好的说法是)flex 中的一个错误。 Flex 不应允许 m4
在从扫描仪定义文件复制的用户内容中扩展宏,并且当前版本的 flex 正确安排了要引用的扫描仪描述文件中包含的文本,以便它通过 m4
未修改,即使它恰好包含一个可以被 m4
解释为宏扩展的字符串。
这个bug肯定存在于flex的v2.5.39和v2.6.1中。我没有测试所有以前的版本,但我想它是在将 flex 修改为使用 m4
时引入的,根据 NEWS 文件,它是 v2.5.30。
这个特殊的引用问题已在 v2.6.2 中修复,但当前版本的 flex (2.6.4) 包含各种其他错误修复,因此我建议您升级到最新版本。
如果您真的需要一个既能与有问题的版本又能与 flex 的最新版本一起使用的版本,您可以使用以下两种 hack 之一:
换一种写法
__unix__
。一种可能是下面#define C(x,y) x##y #define UNIX_ C(__un,ix__) #if !defined(__linux__) && !UNIX_
该 hack 不适用于
defined
,因为defined(UNIX_)
测试是否定义了UNIX_
,而不是它扩展到的内容是否定义。但通常像__unix__
这样的内置符号实际上被定义为 1,如果它们被定义的话,并且#if
指令将任何不是 #define 的标识符视为 0,这意味着你通常可以离开使用x
而不是defined(x)
。 (但是,如果#define x 0
有效,它会产生不同的结果,因此它不是一个完美的替代品。)Flex 和很多m4 应用程序一样,将m4 的引号重新定义为
[[
和]]
。有问题的 flex 和更正后的版本都用一个相当复杂的序列替换了这些引号,该序列有效地引用了引号。但是,有缺陷的版本不会以其他方式引用用户定义的文本,因此将在用户文本中执行宏替换。 (如前所述,这就是__unix__
变成空字符串的原因。在不引用用户定义文本的 flex 版本中,可以调用重新定义引号的 m4 宏。然后可以使用这些新引号来引用
#if
行,防止__unix__
的宏替换。但是,必须恢复引用定义,否则它将完全破坏文件其余部分的宏处理。这有点棘手,因为不可能写[[
。 (Flex 将用不同的字符串替换它。)以下似乎可以解决问题。请注意,宏调用位于 C 注释中。如果扩展了 changequote 宏,它们将扩展为空字符串。但是在 v2.6.2 之后的 flex 版本中,用户提供的文本被引用,所以 changequote 宏不会被扩展。将它们放在注释中会对 C 编译器隐藏它们。
%{ /*m4_changequote(<<,>>)<<*/ #if !defined(__linux__) && !defined(__unix__) /*>>m4_changequote(<<[>><<[>>,<<]>><<]>>)*/ /* Maybe on windows */ #endif
(更改引号的 m4 宏是
changequote
但 flex 使用-P
标志调用m4
将changequote
等内置函数更改为m4_changequote
. 在对 changequote 的第二次调用中,组成[[
符号的两个[
分别用临时<<
引号引起来,这将它们隐藏在修改 use 的 flex 代码中[[
.)我不知道这个 hack 的可靠性如何,但它适用于我在我的机器上使用过的 flex 版本,包括 2.5.4(M4 之前)2.5.39(错误),2.6。 1(错误)、2.6.2(有些调试)和 2.6.4(更多调试)。