Flex отбрасывает предопределенный макрос - PullRequest
0 голосов
/ 12 февраля 2019

У меня есть следующий гибкий источник:

%{
#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;                                                                                                                                                                                                            
}

Я генерирую файл C командой flex flextest.l и компилирую результат с помощью 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 голосов
/ 12 февраля 2019

Это на самом деле m4 (макропроцессор, который используется в текущих версиях flex), который расширяет __unix__ до пустой строки.Реализация Gnu m4 определяет определенные символы для пустых макросов, чтобы их можно было протестировать с помощью ifdef.

Конечно, это (лучше сказать, это) ошибка в flex.Flex не должен позволять m4 расширять макросы в пользовательском контенте, скопированном из файла определения сканера, и текущая версия flex правильно размещает текст, включенный в файл описания сканера, в кавычки, чтобы он проходил через m4не изменяется, даже если в него входит строка, которая может быть интерпретирована m4 как расширение макроса.

Ошибка, безусловно, присутствует в v2.5.39 и v2.6.1 flex.Я не тестировал все предыдущие версии, но я полагаю, что он был введен, когда flex был изменен для использования m4, который был v2.5.30 в соответствии с файлом NEWS.

Эта конкретная проблема с цитатами была исправлена ​​в v2.6.2, но текущая версия flex (2.6.4) содержит различные другие исправления ошибок, поэтому я рекомендую вам обновить ее до последней версии.


Если вам действительно нужна версия, которая может работать скак с ошибками, так и с более свежими версиями flex, вы можете использовать один из двух следующих хаков:

  1. Найти другой способ написания __unix__.Возможна следующая версия:

    #define C(x,y) x##y
    #define UNIX_ C(__un,ix__)
    #if !defined(__linux__) && !UNIX_
    

    Этот хак не будет работать с defined, поскольку defined(UNIX_) проверяет, определено ли UNIX_, а не определено, на что оно распространяется.Но обычно встроенные символы, такие как __unix__, фактически определяются как 1, если они определены, а директива #if обрабатывает любой идентификатор, который не является # define'd, как если бы он был 0, что означает, что вы обычно можетеоставьте использование x вместо defined(x).(Однако, если результат был #define x 0, он даст другие результаты, так что это не совсем идеальная замена.)

  2. Flex, как и многие приложения m4, переопределяет кавычки m4быть [[ и ]].И гибкий багги, и исправленные версии заменяют эти кавычки довольно сложной последовательностью, которая эффективно заключает в кавычки.Тем не менее, версия с ошибками не заключает в кавычки пользовательский текст, поэтому подстановки макросов будут выполняться в пользовательском тексте.(Как уже упоминалось, именно поэтому __unix__ становится пустой строкой.

    В версиях flex, в которых пользовательский текст не заключен в кавычки, можно вызывать макрос m4, который переопределяет кавычки. Эти новые кавычкиЗатем можно использовать метки для заключения в кавычки строки #if, предотвращая подстановку макросов __unix__. Однако определение кавычки должно быть восстановлено, иначе будет полностью нарушена обработка макроса остальной части файла. Это немного сложно, потому что этоневозможно написать [[. (Flex заменит его другой строкой.)

    Кажется, что сработает следующее. Обратите внимание, что вызовы макросов помещаются внутри комментариев C. Макросы changequote будут расширены допустая строка, если они раскрываются. Но в гибких версиях начиная с v2.6.2 предоставленный пользователем текст заключен в кавычки, поэтому макросы changequote не будут расширены. Помещение их в комментарии скрывает их от компилятора C.

    %{
    /*m4_changequote(<<,>>)<<*/
    #if !defined(__linux__) && !defined(__unix__)
    /*>>m4_changequote(<<[>><<[>>,<<]>><<]>>)*/
    
    /* Maybe on windows */
    #endif
    

    (Макрос m4, который изменяет кавычки, changequote, но flex вызывает m4 с -P флаг, который изменяет встроенные функции, такие как changequote на m4_changequote.Во втором вызове changequote два [, составляющие знак [[, по отдельности заключаются в временные кавычки <<, что скрывает их от кода в flex, который изменяет использование [[.)

    Я не знаю, насколько надежен этот хак, но он работал на тех версиях flex, которые у меня были на моей машине, включая 2.5.4 (до M4), 2.5.39 (глючит), 2.6.1 (с ошибками), 2.6.2 (несколько отлажены) и 2.6.4 (более отлажены).

...