Разобрать один и тот же шаблон по-разному в зависимости от контекста с помощью 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 производство в парсере.

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

Наконец, вы можете выполнить оба преобразования в сканере и выбрать правильное при уменьшении токена в анализаторе. Если версия представляет собой небольшую и простую структуру данных, она может оказаться оптимальной.

Другие вопросы по тегам