Flex删除预定义的宏

时间:2019-02-12 15:25:54

标签: flex-lexer

我有以下弹性来源:

%{
#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()

为什么会发生这种情况,有可能避免吗?

1 个答案:

答案 0 :(得分:1)

实际上是m4(当前版本的flex使用的宏处理器)正在将__unix__扩展为空字符串。 m4的Gnu实现将某些符号定义为空宏,以便可以使用ifdef对其进行测试。

当然,(更好地说是)flex中的错误。 Flex不应允许m4扩展从扫描仪定义文件复制的用户内容中的宏,并且当前版本的flex正确排列了扫描仪描述文件中包含的文本,以使其被引号引起来。 {1}}未经修改,即使它恰好包含可以由m4解释为宏扩展的字符串。

该错误肯定存在于flex的v2.5.39和v2.6.1中。我没有测试所有以前的版本,但是我想它是在将flex修改为使用m4(根据NEWS文件为v2.5.30)时引入的。

此特殊的报价问题已在v2.6.2中修复,但是flex(2.6.4)的当前版本包含其他各种错误修复,因此,我建议您升级到最新版本。


如果您确实需要一个可以与越野车以及最新版本的Flex一起使用的版本,则可以使用以下两种黑客之一:

  1. 找到其他写m4的方法。一种可能性是以下

    __unix__

    该黑客无法与#define C(x,y) x##y #define UNIX_ C(__un,ix__) #if !defined(__linux__) && !UNIX_ 一起使用,因为defined会测试defined(UNIX_)是否已定义,而不是测试是否扩展到了UNIX_。但是,通常将内置__unix__之类的内置符号定义为1,并且#if伪指令会将未#define的任何标识符都视为0,这意味着通常可以使用x代替defined(x)。 (但是,如果有#define x 0有效,它将产生不同的结果,因此它并不是一个完美的替代品。)

  2. 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使用m4标志调用-P,标志将诸如changequote之类的内置内容更改为m4_changequote。第二次调用changequote,组成[的两个[[分别用临时<<引号引起来,这将它们隐藏在修改{ {1}}。)

    我不知道这种破解的可靠性如何,但是它适用于我在计算机上使用过的flex版本,包括2.5.4(M4之前的版本)2.5.39(越野车),2.6.1(越野车),2.6.2(已调试)和2.6.4(已调试)。