Использование 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 для сканирования входного файла? Я знаю, как переключить входной буфер гибкого диска, но как мне после этого получить значения токенов? Если решение более сложное, чем просто отдельная процедура, я не собираюсь этим заниматься.

Кстати, я использую C, но в будущем мне нужно будет перенести это на C ++.

Спасибо.


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


Ответы (1)


Если проблема настолько проста, как показывает ваш пример, вы можете просто использовать fscanf. Проблема с fscanf, вероятно, не будет отличаться от проблемы с использованием вашего гибкого сканера, который заключается в том, что новые строки просто обрабатываются как пробелы. Если ваш сканер возвращает специальный токен для новой строки (или вас убедили сделать это, см. Ниже), продолжайте читать.

Если я правильно понимаю, что вы хотите сделать, вам просто нужно правильно настроить ввод, а затем повторно вызывать 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), чтобы вернуться к их игнорированию). Вам нужно будет разместить функции, которые включают и отключают сканирование новой строки в вашем файле определения гибкости, потому что требуются макросы не экспортируются.

person rici    schedule 28.07.2015
comment
Круто, спасибо! Мне не нужны символы новой строки, поскольку я в основном подсчитываю количество идентификаторов вверху и использую это для настройки условий цикла и направления следующих данных в их правильные местоположения в массиве. Я использую fscanf в подпрограмме, которую я использую в настоящее время, я просто подумал, было бы чище / быстрее / меньше подвержено ошибкам, если бы вместо этого использовать flex, поскольку он уже настроен для сканирования типов данных во входном файле. - person merkman; 28.07.2015