Как именно flex поддерживает bison-location?
-
19-08-2019 - |
Вопрос
Я пытаюсь использовать 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
вариант.
Итак, я заставил это "сработать", но с помощью пары дополнительных шагов (возможно, я их здесь упустил...в таком случае приношу свои извинения):
В синтаксический анализатор.y, Я должен был сказать:
#define YYLEX_PARAM &yylval, &yylloc
даже с
%locations
иbison --locations
, чтобы заставить его передавать данные.В lexer.l Я должен был использовать
->
вместо того , чтобы.
дляyylloc
Также в 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