Ничто в flex или bison не будет поддерживать значение yylloc
.
Парсеры Bison (кроме парсеров pu sh) будут инициализировать эту переменную. (Если вы примете тип местоположения по умолчанию - то есть вы не #define YYLTYPE
- yylloc
будут инициализированы как {1, 1, 1, 1}
. В противном случае он будет инициализирован нулем, что бы это ни значило для любого типа .) Bison также создает код, который вычисляет местоположение нетерминала на основе местоположения первого и последнего дочерних элементов нетерминала. Сгенерированный код Flex вообще не касается объекта местоположения.
Flex-сканер автоматически поддерживает yylineno
, если вы попросите включить эту функцию с помощью
%option yylineno
. Обычно Flex может сделать это более эффективно, чем вы, и обрабатывает все угловые случаи ( yyless
, yymore
, input()
, REJECT
). Поэтому, если вы хотите отслеживать номера строк, я настоятельно рекомендую позволить flex сделать это.
Но есть одна важная проблема с поддержкой flex * yylineno
. В реентерабельном сканере номер строки сохраняется в каждом буфере Flex, а не в объекте состояния сканера. Это почти наверняка правильное место для его хранения, ИМХО, потому что, если вы используете несколько буферов, они, вероятно, представляют несколько входных потоков, и обычно вы захотите указать номер строки в своем файле. Но yy_scan_buffer
не инициализирует это поле. (И поэтому не делают yy_scan_string
и yy_scan_bytes
, которые являются просто обертками вокруг yy_scan_buffer
.)
Так что, если вы используете один из yy_scan_*
интерфейсов, вы должны сбросить yylineno
, вызвав yyset_lineno
сразу после yy_scan_*
. В вашем случае это будет:
buffer = yy_scan_buffer(inputToBeParsed, i+2, scanner);
yyset_lineno(1, scanner);
Как только вы получите yylineno
, объект yylloc
будет легко поддерживать. У Flex есть ловушка, которая позволяет вводить код непосредственно перед выполнением какого-либо действия для шаблона (даже если оно пустое), и эта ловушка может использоваться для автоматического сохранения yylloc
. В этом ответе я привожу простой пример этого метода (который зависит от того, поддерживается ли yylineno
сканером, сгенерированным сгибанием):
#define YY_USER_ACTION \
yylloc->first_line = yylloc->last_line; \
yylloc->first_column = yylloc->last_column; \
if (yylloc->last_line == yylineno) \
yylloc->last_column += yyleng; \
else { \
yylloc->last_line = yylineno; \
yylloc->last_column = yytext + yyleng - strrchr(yytext, '\n'); \
}
Как примечания в этом ответе Отметим, что приведенное выше не является полностью общим, но оно будет работать во многих случаях:
Этот макрос YY_USER_ACTION
должен работать для любого сканера, который не использует yyless()
, yymore()
, input()
или REJECT
. Правильно справиться с этими функциями не так уж сложно, но здесь это, похоже, выходит за рамки.
Вы не можете обработать yyless()
, yymore()
или REJECT
до действия (так как до действия это не можно узнать, будут ли они выполнены), поэтому более надежный трекер местоположения в приложении, использующем эти функции, должен будет включать код для исправления yylloc()
:
Для yyless()
приведенный выше код для установки last_line
и last_column
может быть повторно выполнен после вызова yyless()
, поскольку гибкий сканер исправит yyleng
и yylineno
.
Для REJECT
невозможно вставить код после REJECT
. Единственный способ справиться с этим - сохранить резервную копию yylloc
и восстановить ее непосредственно перед макросом REJECT
. (Я настоятельно не рекомендую использовать REJECT
. Он крайне неэффективен и почти всегда может быть заменен комбинацией вызова yyless()
и начального условия.)
Для yymore()
, yylloc
по-прежнему правильно, но действие next не должно перезаписывать начальную позицию токена. Чтобы получить это право, вероятно, потребуется поддерживать флаг, указывающий, был ли вызван yymore()
.
Для input()
, если вы хотите, чтобы прочитанные символы считались частью текущий токен, вы можете продвинуть конечное местоположение в yylloc
после вызова input()
(что требует различения между input()
, возвращающими символ новой строки, индикатор конца файла или обычный символ). В качестве альтернативы, если вы хотите, чтобы символы, прочитанные с input()
, не считались частью какого-либо токена, вам необходимо отказаться от идеи использования конечной позиции предыдущего токена в качестве начальной позиции текущего токена, что потребует сохранения значение позиции разделения, которое будет использоваться в качестве начальной позиции следующего токена.