Използване на lex/flex извън yacc/bison

В момента използвам bison и flex, за да анализирам линейни темпорални логически формули и да генерирам автомати от тях. Използвам flex по начина "по подразбиране", т.е. записвам токена на yylval, ако е необходимо, и връщам идентификатора на токена bison.

Използвам отделна рутина за сканиране на входния файл. Входът се състои от имена на идентификатори и цели/реални числа, с които flex вече може да работи. Пример:

x    y
3.20 78.3
3.31 76.2
3.32 77.4
//etc

Въпросът ми е следният: ако е възможно, как да използвам flex за сканиране на входния файл? Знам как да превключвам входния буфер на flex, но как да получа стойностите на токена след това? Ако решението е по-сложно от това просто да имаш отделна рутина, тогава няма да си правя труда да го правя.

Между другото използвам C, но в крайна сметка ще трябва да пренеса това към C++ в бъдеще.

Благодаря.


person merkman    schedule 28.07.2015    source източник


Отговори (1)


Ако проблемът е толкова прост, колкото показва вашият пример, можете просто да използвате fscanf. Проблемът с fscanf вероятно няма да се различава от проблема с използването на вашия генериран от flex скенер, който е, че новите редове просто се третират като интервал. Ако вашият скенер връща специален знак за нови редове (или може да бъде убеден да го направи, вижте по-долу), тогава прочетете нататък.

Ако разбирам какво искате да направите правилно, просто трябва да настроите входа правилно и след това многократно да извиквате yylex. Ако използвате приложния програмен интерфейс (API) без повторно влизане по подразбиране, тогава може да изглежда по следния начин:

// Returns a count, and sets the out parameter to a
// malloc'd array of malloc'd strings.
// The free function is left as an exercise. 
int read_headers(char*** out) {
   int count = 0;
   char** id_array = NULL;
   *out = id_array;
   int token;
   while ((token = yylex()) == TOKEN_IDENTIFIER) {
     id_array = realloc(id_array, (count + 1) * sizeof *id_array);
     if (id_array) *out = id_array;
     else
       // Handle error
     id_array[count ++] = strdup(yytext);
   }
   if (token != '\n')
     // Handle error
   return count;
}

// Reads exactly n numbers and stores them in the out parameter,
// which must point at an array of size n.
// Returns: n on success. 0 if EOF reached. -1 on error
int read_n_numbers(double* out, int n) {
  for (int i = 0; i < n; ++i, ++out) {
    int token = yylex();
    if (token != TOKEN_NUMBER) {
      if (i == 0 && token == 0) return 0;
      // Error: non-number or too few numbers
      return -1;
    }
    *out = yylval.number;
  }
  if (yylex() != '\n') {
    // Error: too many numbers
    return -1;
  }
  return 0;
}

Вероятно вашият скенер всъщност не връща \n за нов ред. Това рядко би било полезно в граматиката на израза (въпреки че понякога е полезно). Но е лесно да модифицирате скенера да обработва нови редове при поискване; просто трябва да използвате начално условие. Ние го правим включително условие за начало, защото то трябва да обработва само нови редове, но имайте предвид, че това означава, че всички немаркирани правила също са активни, така че трябва да се уверите, че немаркираното правило, което обработва също само нови редове обработва един нов ред.

%s RETURN_NEWLINES
%%
<RETURN_NEWLINES>\n { return '\n'; }
\n                  ; // Ignore a single newline
[ \t]+              ; // Ignore any number of horizontal whitespace

С това място можете да активирате нови редове, като просто извикате BEGIN(RETURN_NEWLINES) преди сканиране (и BEGIN(INITIAL), за да се върнете към игнорирането им.) Ще трябва да поставите функции, които активират и деактивират сканирането за нов ред във вашия файл с дефиниции на flex, тъй като изискваните макроси не се изнасят.

person rici    schedule 28.07.2015
comment
Готино, благодаря ти! Нямам нужда от нови редове, тъй като по същество отчитам броя на идентификаторите в горната част и го използвам, за да настроя условията на цикъла и да насоча следните данни към правилните им местоположения в масива. Използвам fscanf в рутината, която използвам в момента, просто си мислех дали би било по-чисто/по-бързо/по-малко податливо на грешки вместо това да използвам flex, тъй като той вече е настроен да сканира типовете данни във входния файл. - person merkman; 28.07.2015