Разобрать один и тот же шаблон по-разному в зависимости от контекста с помощью lex/yacc
Моя проблема в том, что у меня есть идентичный набор символов, которые я хочу анализировать по-разному, учитывая их контекст. В одной части файла мне нужно проанализировать номер версии вида "#.#". Очевидно, это идентично числу с плавающей запятой. Лексер всегда предпочитает возвращать число с плавающей запятой. Я думаю, что могу изменить последовательность правил, чтобы дать приоритет версии # (?), Но это не принесет мне никакой пользы, когда мне нужно будет позже проанализировать числа с плавающей запятой.
Полагаю, я мог бы забыть попросить парсер возвращать каждый фрагмент версии отдельно и разделить число с плавающей запятой на части, но я надеялся, что это будет сделано для меня.
На самом деле есть немного больше контекста версии #. Его полная форма - "ABC #.# XYZ", где "ABC" и "XYZ" никогда не меняются. Я попробовал несколько вещей, чтобы использовать контекст версии #, но не заставил его работать.
Есть ли способ предоставить некоторый контекст для лексера для анализа компонентов версии? Я застрял с получением числа с плавающей запятой и его разбором сам?
1 ответ
У вас есть несколько возможностей.
Самым простым является преобразование строки в число в анализаторе, а не в сканере. Это требует создания копии числа в виде строки, но накладные расходы не должны быть слишком высокими: malloc
из коротких строк хорошо оптимизирован практически на всех платформах. И преимущество в том, что код очень прост и надежен:
синтаксический анализатор
%union {
char* string;
double number;
// other types, including version
}
%token <string> DOTTED
%token <number> NUMBER
%type <number> number
%type <version> version
%%
number : NUMBER
| DOTTED { $$ = atod($1); free($1); }
version: DOTTED { $$ = make_version($1); free($1); }
сканер
[[:digit:]]+\.[[:digit:]]+ { yylval.string = strdup(yytext); return DOTTED; }
[[:digit:]]+\.?|\.[[:digit:]]+ { yylval.number = atod(yytext); }
Выше предполагается, что номера версий всегда одноточечные, как в OP. В приложениях, где номера версий могут иметь несколько точек или нечисловых символов, вы получите три возможных типа токенов: однозначные числа, однозначные строки версий и одноточечные числовые строки, которые могут быть либо. Помимо добавления типа токена VERSION и шаблона для однозначных строк версий в сканер, единственным изменением является добавление | VERSION
к version
производство в парсере.
Другой возможностью, если вы можете легко определить в сканере, требуется ли номер или версия, является использование условия запуска. Вы также можете изменить условие в парсере, но оно более сложное, и вам необходимо понять алгоритм синтаксического анализа, чтобы убедиться, что вы правильно сообщаете об изменениях состояния.
Наконец, вы можете выполнить оба преобразования в сканере и выбрать правильное при уменьшении токена в анализаторе. Если версия представляет собой небольшую и простую структуру данных, она может оказаться оптимальной.