Подтвердить что ты не робот

Как Flex точно поддерживает бизон-местоположение?

Я пытаюсь использовать flex и bison для создания фильтра, потому что хочу получить определенные грамматические элементы из сложного языка. Мой план состоит в том, чтобы использовать flex + bison для распознавания грамматики и сбрасывать местоположение элементов, представляющих интерес. (Затем используйте script для захвата текста в соответствии с местами, сброшенными.)

Я обнаружил, что flex может поддерживать функцию бизона, называемую bison-location, но как она работает точно. Я попробовал этот пример в гибком документе, кажется, что yylloc не устанавливается автоматически flex, я всегда получаю (1,0)-(1,0). Можно ли автоматически вычислить местоположение каждого токена? Если нет, какая функция интерфейса определена для меня? Есть ли какой-нибудь пример?

Любое лучшее решение относительно инструментов?

С наилучшими пожеланиями, Кевин

Edit:

Теперь интерфейс для yylex превратится в:

int yylex(YYSTYPE * yylval_param,YYLTYPE * yylloc_param );

Руководство bison не указывает, как lexer должен реализовать, чтобы правильно установить yylloc_param. Для меня трудно вручную отслеживать номер столбца каждого токена.

4b9b3361

Ответ 1

Взгляните на раздел 3.6 руководства Bison - это, по-видимому, охватывает местоположения в деталях. В сочетании с тем, что вы нашли в руководстве Flex, этого может быть достаточно.

Ответ 2

Объявление yylex, вероятно, изменилось, потому что вы использовали реентерабельный или чисто-парсер. Похоже, что многие документы по всему сайту предлагают, чтобы вы захотели, чтобы места бизонов работали, но это не требовалось.

Мне тоже нужны номера строк, и я нашел документацию Bison запутанной в этом отношении. Простое решение (с использованием глобального var yylloc): В вашем файле Bison просто добавьте директиву% location:

%{
...
%}
%locations
...
%%
...

в вашем лексере:

%{
...
#include "yourprser.tab.h"  /* This is where it gets the definition for yylloc from */
#define YY_USER_ACTION yylloc.first_line = yylloc.last_line = yylineno;
%}
%option yylineno
...
%%
...

Макрос YY_USER_ACTION "вызывается" перед каждым действием вашего токена и обновляет yylloc. Теперь вы можете использовать правила @N/@$следующим образом:

statement : error ';'   { fprintf(stderr, "Line %d: Bad statement.\n", @1.first_line); }

или используйте yylloc global var:

void yyerror(char *s)
{
  fprintf(stderr, "ERROR line %d: %s\n", yylloc.first_line, s);
}

Ответ 3

Мне нравится ответ Шломи.

Кроме того, я искал обновление расположения столбцов. Найдено http://oreilly.com/linux/excerpts/9780596155971/error-reporting-recovery.html, что имело больше смысла после прочтения ответа Шломи.

К сожалению, на этой странице есть опечатка для yylloc. Я немного упростил его.

В вашем парсере добавьте:

%locations

в вашем лексере:

%{

#include "parser.tab.h"

int yycolumn = 1;

#define YY_USER_ACTION yylloc.first_line = yylloc.last_line = yylineno; \
    yylloc.first_column = yycolumn; yylloc.last_column = yycolumn + yyleng - 1; \
    yycolumn += yyleng; \
    yylval.str = strdup(yytext);

%}

%option yylineno

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

Надеюсь, что это поможет.

Ответ 4

Ни bison, ни flex не обновляет yylloc автоматически, но на самом деле это не сложно сделать самому - если вы знаете трюк.

Трюк к реализации поддержки yylloc заключается в том, что, хотя yyparse() объявляет yylloc, он никогда не меняет его. Это означает, что если вы измените yylloc в одном вызове lexer, вы найдете в нем те же значения при следующем вызове. Таким образом, yylloc будет содержать позицию последнего токена. Поскольку последний токен совпадает с началом текущего токена, вы можете использовать старое значение yylloc, чтобы помочь вам определить новое значение.

Другими словами, yylex() не должен вычислять yylloc; он должен обновить yylloc.

Чтобы обновить yylloc, мы должны сначала скопировать значения last_ в first_, а затем обновить значения last_, чтобы отразить длину только что сопоставленного токена. (Это не тег strlen(), это длина строк и столбцов.) Мы можем сделать это в макросе YY_USER_ACTION, который вызывается непосредственно перед выполнением любого действия lexer; который гарантирует, что если правило совпадает, но оно не возвращает значение (например, правило, пропускающее пробелы или комментарии), местоположение этого не-токена пропускается, а не включается в начале фактического токена, или потерянный таким образом, что делает отслеживание местоположения неточным.

Здесь версия, предназначенная для повторного анализатора; вы можете изменить его для не-реентерабельного анализатора, заменив операторы -> на .:

#define YY_USER_ACTION \
    yylloc->first_line = yylloc->last_line; \
    yylloc->first_column = yylloc->last_column; \
    for(int i = 0; yytext[i] != '\0'; i++) { \
        if(yytext[i] == '\n') { \
            yylloc->last_line++; \
            yylloc->last_column = 0; \
        } \
        else { \
            yylloc->last_column++; \
        } \
    }

Если вы предпочитаете, вместо этого вы можете поместить этот код в функцию и сделать макрокоманду функцией, но эти два метода эквивалентны.

Ответ 5

Ответ Shomi - это самое простое решение, если вы только заботитесь о сохранении номера строки. Однако, если вам также нужны номера столбцов, вам необходимо их отслеживать.

Один из способов сделать это - добавить правила yycolumn = 1 везде, где появляется новая строка (как предложено в ответе Дэвида Элсона), но если вы не хотите отслеживать все места, которые может появиться в новой строке (пробелы, комментарии, и т.д.), альтернатива - проверка буфера yytext в начале каждого действия:

static void update_loc(){
  static int curr_line = 1;
  static int curr_col  = 1;

  yylloc.first_line   = curr_line;
  yylloc.first_column = curr_col;

  {char * s; for(s = yytext; *s != '\0'; s++){
    if(*s == '\n'){
      curr_line++;
      curr_col = 1;
    }else{
      curr_col++;
    }
  }}

  yylloc.last_line   = curr_line;
  yylloc.last_column = curr_col-1;
}

#define YY_USER_ACTION update_loc();

Наконец, стоит отметить, что как только вы начнете отслеживать номера столбцов вручную, вы также можете отслеживать номера строк в одном месте и не беспокоиться об использовании опции Flex yylineno.

Ответ 6

Итак, я получил это, чтобы "работать", но с несколькими дополнительными шагами (я мог упустить их здесь... извинения в этом случае):

  • В parser.y я должен был сказать:

    #define YYLEX_PARAM &yylval, &yylloc
    

    даже с %locations и bison --locations, чтобы заставить его передать данные.

  • В lexer.l мне пришлось использовать -> вместо . для yylloc

  • Также в lexer.l, я reset столбец в действии:

    [\n] { yycolumn = 1; }
    

Очевидно, немного сложнее, для \r и т.д., но по крайней мере я получил его для работы.

Ответ 7

Я думаю, мне удалось заставить его работать (кредит принадлежит писателю справочника bison Лексический анализатор ltcalc). По умолчанию бизон создает yylloc, который содержит

{ first_line, first_column , last_line , last_column }

Нам нужно только обновить эти значения в нашем лексическом анализаторе. Пример:

[ \t]     { ++yylloc.last_column; }
[\n]      { yyloc.last_column = 0; return EOL; }
[a-zA-Z]+ { 
            yylloc.last_column += strlen(yytext);
            return IDENTIFIER;
          }

Теперь в бизоне, чтобы получить эти поля:

statement : IDENTIFIER '=' expression 
            { printf("%d - %d\n", @1.last_line, @1.last_column); }

По умолчанию эти поля инициализируются одним, мы должны инициализировать поля столбца до нуля, иначе они сообщают о некорректном столбце.

Ответ 8

В дополнение к ответу Шломи:

Если вы используете% define api.pure в bison для создания реентераторного парсера, вам также нужно указать% bison-locations в опции flex. Это связано с тем, что в реентерабельном парсере yylloc не является глобальной переменной и должен быть передан в lexer.

Итак, в синтаксическом анализаторе:

%define api.pure
%locations

в лексере:

#include "yourprser.tab.h"
#define YY_USER_ACTION yylloc.first_line = yylloc.last_line = yylineno;
%option bison-locations
%option yylineno