yyllocp-> first_line возвращает неинициализированное значение во второй итерации парсера BIS с повторным входом - PullRequest
0 голосов
/ 15 января 2020

У меня есть парсер reEntrant, который принимает входные данные из строки и имеет структуру для поддержки контекста. Функция вызывается с различными входными строками для анализа. Соответствующий код этой функции:

void parseMyString(inputToBeParsed) {

 //LEXICAL COMPONENT - INITIATE LEX PROCESSING
   yyscan_t scanner;    
   YY_BUFFER_STATE  buffer;
   yylex_init_extra(&parseSupportStruct, &scanner );
   //yylex_init(&scanner);

   buffer = yy_scan_buffer(inputToBeParsed, i+2, scanner);

   if (buffer == NULL) {
       strcpy(errorStrings,"YY_BUFFER_STATE returned NULL pointer\n");
       return (-1);
   }


//BISON PART - THE ACTUAL PARSER
yyparse(scanner, &parseSupportStruct);

...

yylex_destroy(scanner);
...
}

Мои параметры .l:

 %option noinput nounput noyywrap 8bit nodefault                                 
 %option yylineno
 %option reentrant bison-bridge bison-locations                                  
 %option extra-type="parseSupportStructType *"

Соответствующие строки из .y:

  %define api.pure full
  %locations
  %param { yyscan_t scanner }
  %parse-param { parseSupportStructType* parseSupportStruct}
  %code {
    int yylex(YYSTYPE* yylvalp, YYLTYPE* yyllocp, yyscan_t scanner);
    void yyerror(YYLTYPE* yyllocp, yyscan_t unused, parseSupportStructType* parseSupportStruct,  const char* msg);
    char *yyget_text (yyscan_t);
    char *strcpy(char *, const char *);
  }
  %union {
     int numval;
     char *strval;
     double floatval; 
  }

В моем парсере в некоторых правилах я пытаюсь получить доступ к yyllocp-> first_line. При первом вызове parseMyString (...) я получаю правильное значение. Во второй раз я получаю неинициализированное значение. Нужно ли инициализировать yyllocp-> first_line при каждом вызове parseMyString? Как и где? Я знаю, что дал частичный отредактированный код, чтобы объяснить ситуацию. Будем рады предоставить более подробную информацию.

Используя valgrind, я устранил утечки памяти в меру своих способностей, но некоторые проблемы со сторонними библиотеками находятся вне моего контроля.

1 Ответ

1 голос
/ 15 января 2020

Ничто в flex или bison не будет поддерживать значение yylloc.

Парсеры Bison (кроме парсеров pu sh) будут инициализировать эту переменную. (Если вы примете тип местоположения по умолчанию - то есть вы не #define YYLTYPE - yylloc будут инициализированы как {1, 1, 1, 1}. В противном случае он будет инициализирован нулем, что бы это ни значило для любого типа .) Bison также создает код, который вычисляет местоположение нетерминала на основе местоположения первого и последнего дочерних элементов нетерминала. Сгенерированный код Flex вообще не касается объекта местоположения.

Flex-сканер автоматически поддерживает yylineno, если вы попросите включить эту функцию с помощью

%option yylineno

. Обычно Flex может сделать это более эффективно, чем вы, и обрабатывает все угловые случаи ( yyless, yymore, input(), REJECT). Поэтому, если вы хотите отслеживать номера строк, я настоятельно рекомендую позволить flex сделать это.

Но есть одна важная проблема с поддержкой flex * yylineno. В реентерабельном сканере номер строки сохраняется в каждом буфере Flex, а не в объекте состояния сканера. Это почти наверняка правильное место для его хранения, ИМХО, потому что, если вы используете несколько буферов, они, вероятно, представляют несколько входных потоков, и обычно вы захотите указать номер строки в своем файле. Но yy_scan_buffer не инициализирует это поле. (И поэтому не делают yy_scan_string и yy_scan_bytes, которые являются просто обертками вокруг yy_scan_buffer.)

Так что, если вы используете один из yy_scan_* интерфейсов, вы должны сбросить yylineno, вызвав yyset_lineno сразу после yy_scan_*. В вашем случае это будет:

buffer = yy_scan_buffer(inputToBeParsed, i+2, scanner);
yyset_lineno(1, scanner);

Как только вы получите yylineno, объект yylloc будет легко поддерживать. У Flex есть ловушка, которая позволяет вводить код непосредственно перед выполнением какого-либо действия для шаблона (даже если оно пустое), и эта ловушка может использоваться для автоматического сохранения yylloc. В этом ответе я привожу простой пример этого метода (который зависит от того, поддерживается ли yylineno сканером, сгенерированным сгибанием):

#define YY_USER_ACTION                                             \
  yylloc->first_line = yylloc->last_line;                          \
  yylloc->first_column = yylloc->last_column;                      \
  if (yylloc->last_line == yylineno)                               \
    yylloc->last_column += yyleng;                                 \
  else {                                                           \
    yylloc->last_line = yylineno;                                  \
    yylloc->last_column = yytext + yyleng - strrchr(yytext, '\n'); \
  }

Как примечания в этом ответе Отметим, что приведенное выше не является полностью общим, но оно будет работать во многих случаях:

Этот макрос YY_USER_ACTION должен работать для любого сканера, который не использует yyless(), yymore(), input() или REJECT. Правильно справиться с этими функциями не так уж сложно, но здесь это, похоже, выходит за рамки.

Вы не можете обработать yyless(), yymore() или REJECT до действия (так как до действия это не можно узнать, будут ли они выполнены), поэтому более надежный трекер местоположения в приложении, использующем эти функции, должен будет включать код для исправления yylloc():

  • Для yyless() приведенный выше код для установки last_line и last_column может быть повторно выполнен после вызова yyless(), поскольку гибкий сканер исправит yyleng и yylineno.

  • Для REJECT невозможно вставить код после REJECT. Единственный способ справиться с этим - сохранить резервную копию yylloc и восстановить ее непосредственно перед макросом REJECT. (Я настоятельно не рекомендую использовать REJECT. Он крайне неэффективен и почти всегда может быть заменен комбинацией вызова yyless() и начального условия.)

  • Для yymore(), yylloc по-прежнему правильно, но действие next не должно перезаписывать начальную позицию токена. Чтобы получить это право, вероятно, потребуется поддерживать флаг, указывающий, был ли вызван yymore().

  • Для input(), если вы хотите, чтобы прочитанные символы считались частью текущий токен, вы можете продвинуть конечное местоположение в yylloc после вызова input() (что требует различения между input(), возвращающими символ новой строки, индикатор конца файла или обычный символ). В качестве альтернативы, если вы хотите, чтобы символы, прочитанные с input(), не считались частью какого-либо токена, вам необходимо отказаться от идеи использования конечной позиции предыдущего токена в качестве начальной позиции текущего токена, что потребует сохранения значение позиции разделения, которое будет использоваться в качестве начальной позиции следующего токена.

...