Ответ 1
Из Dragon Book 2nd edition, раздел 3.5.3 "Разрешение конфликтов в Lex":
We have alluded to the two rules that Lex uses to decide on the proper lexeme
to select, when several prefixes of the input match one or more patterns:
1. Always prefer a longer prefix to a shorter prefix.
2. If the longest possible prefix matches two or more patterns, prefer the
pattern listed first in the Lex program.
Правило выше также относится к Flex. Вот что написано в руководстве Flex (Глава 7: Как подобрать вход.)
When the generated scanner is run, it analyzes its input looking for strings
which match any of its patterns. If it finds more than one match, it takes the
one matching the most text (for trailing context rules, this includes the length
of the trailing part, even though it will then be returned to the input). If it
finds two or more matches of the same length, the rule listed first in the flex
input file is chosen.
Если я правильно понял, ваш lexer рассматривает ключевые слова, такие как Endif
как идентификатор, поэтому впоследствии он будет рассматриваться как часть выражения. Если это ваша проблема, просто поместите правила ключевых слов поверх спецификации, например: (предположим, что каждое слово в верхнем регистре является предопределенным перечислением, соответствующим токену)
"If" { return IF; }
"Then" { return THEN; }
"Endif" { return ENDIF; }
"While" { return WHILE; }
"Do" { return DO; }
"EndWhile" { return ENDWHILE; }
\"(\\.|[^\\"])*\" { return STRING; }
[a-zA-Z_][a-zA-Z0-9_]* { return IDENTIFIER; }
Затем ключевые слова будут всегда совпадать перед идентификатором из-за правила № 2.
EDIT:
Спасибо за ваш комментарий, кол. Я забыл добавить правило для строки. Но я не думаю, что мое решение неверно., например, если идентификатор с именем If_this_is_an_identifier
, будет применяться правило 1, поэтому правило идентификатора вступит в силу (так как он соответствует самой длинной строке). Я написал простой тестовый пример и не видел проблем в своем решении. Вот мой файл lex.l:
%{
#include <iostream>
using namespace std;
%}
ID [a-zA-Z_][a-zA-Z0-9_]*
%option noyywrap
%%
"If" { cout << "IF: " << yytext << endl; }
"Then" { cout << "THEN: " << yytext << endl; }
"Endif" { cout << "ENDIF: " << yytext << endl; }
"While" { cout << "WHILE: " << yytext << endl; }
"Do" { cout << "DO: " << yytext << endl; }
"EndWhile" { cout << "ENDWHILE: " << yytext << endl; }
\"(\\.|[^\\"])*\" { cout << "STRING: " << yytext << endl; }
{ID} { cout << "IDENTIFIER: " << yytext << endl; }
. { cout << "Ignore token: " << yytext << endl; }
%%
int main(int argc, char* argv[]) {
++argv, --argc; /* skip over program name */
if ( argc > 0 )
yyin = fopen( argv[0], "r" );
else
yyin = stdin;
yylex();
}
Я проверил свое решение со следующим тестовым примером:
If If_this_is_an_identifier > 0 Then read(b); Endif
c := "If I were...";
While While_this_is_also_an_identifier > 5 Do d := d + 1 Endwhile
и он дает мне следующий результат (другой вывод, не относящийся к проблеме, о которой вы упомянули, игнорируется.)
IF: If
IDENTIFIER: If_this_is_an_identifier
......
STRING: "If I were..."
......
WHILE: While
IDENTIFIER: While_this_is_also_an_identifier
Программа lex.l модифицирует базу в примере из flex manual: (которые используют тот же метод для сопоставления ключевого слова из идентификаторы)
Также посмотрите грамматика ANSI C, спецификация Lex.
Я также использовал этот подход в своем личном проекте, и до сих пор я не обнаружил никаких проблем.