Вопрос

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

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

Есть ли лучшее решение относительно инструментов?

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

Редактировать:

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

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

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

Это было полезно?

Решение

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

Другие советы

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

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

%{
...
%}
%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:

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

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

Кроме того, я также искал возможность обновить местоположение столбца.Найденный 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

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

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

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

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

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

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

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

#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++; \
        } \
    }

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

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

Один из способов сделать это - добавить 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's yylineno вариант.

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

  1. В синтаксический анализатор.y, Я должен был сказать:

    #define YYLEX_PARAM &yylval, &yylloc
    

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

  2. В lexer.l Я должен был использовать -> вместо того , чтобы . для yylloc

  3. Также в lexer.l, я сбрасываю столбец в действии:

    [\n] { yycolumn = 1; }
    

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

Я думаю, мне удалось заставить это сработать ( заслуга принадлежит автору руководства по зубрам лексический анализатор ltcalc).По умолчанию bison создает 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;
          }

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

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

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

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

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

Итак, в парсере:

%define api.pure
%locations

в лексере:

#include "yourprser.tab.h"
#define YY_USER_ACTION yylloc.first_line = yylloc.last_line = yylineno;
%option bison-locations
%option yylineno
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top