Ни bison
, ни flex
не обновляют yylloc
автоматически, но на самом деле нетрудно сделать это самостоятельно - если вы знаете хитрость.
Хитрость в реализации поддержки yylloc
заключается в том, что, хотя yyparse()
объявляет yylloc
, она никогда не меняет ее. Это означает, что если вы измените yylloc
в одном вызове лексера, вы найдете те же значения в нем при следующем вызове. Таким образом, yylloc
будет содержать позицию последнего токена. Поскольку конец последнего токена совпадает с началом текущего токена, вы можете использовать старое значение yylloc
, чтобы помочь вам определить новое значение.
Другими словами, yylex()
не должен вычислять yylloc
; должно обновить yylloc
.
Чтобы обновить yylloc
, мы должны сначала скопировать значения last_
в first_
, а затем обновить значения last_
, чтобы отразить длину только что подобранного токена. (Это не strlen()
токена; это длина строк и столбцов.) Мы можем сделать это в макросе YY_USER_ACTION
, который вызывается непосредственно перед выполнением любого действия лексера; это гарантирует, что если правило соответствует, но оно не возвращает значение (например, пропущенное правилом пробел или комментарии), местоположение этого не-токена пропускается, а не включается в начало фактического токена, или потерян таким образом, что делает отслеживание местоположения неточным.
Вот версия, предназначенная для реентерабельного парсера; Вы можете изменить его для парентера, не входящего в систему, поменяв операторы ->
на .
:
#define YY_USER_ACTION \
yylloc->first_line = yylloc->last_line; \
yylloc->first_column = yylloc->last_column; \
for(int i = 0; yytext[i] != '\0'; i++) { \
if(yytext[i] == '\n') { \
yylloc->last_line++; \
yylloc->last_column = 0; \
} \
else { \
yylloc->last_column++; \
} \
}
Если вы предпочитаете, вы можете вместо этого поместить этот код в функцию и заставить макрос вызывать функцию, но эти два метода эквивалентны.