Можно ли установить приоритеты для правил, чтобы избежать "самого раннего" шаблона сопоставления?
Другой простой вопрос: есть ли способ сказать флексу, чтобы он предпочел правило, которое соответствует короткой вещи, а не правилу, которое соответствует более длинной вещи? Я не могу найти хорошую документацию по этому поводу.
Вот почему мне это нужно: я анализирую файл для псевдоязыка, который содержит некоторые ключевые слова, соответствующие инструкциям управления. Я бы хотел, чтобы они были абсолютным приоритетом, чтобы они не анализировались как части выражения. Мне действительно нужна эта приоритетная вещь, потому что мне не нужно писать полную грамматику для моего проекта (в моем случае это было бы совершенно излишним, так как я выполняю структурный анализ разбираемой программы, мне не нужно знать детали)., поэтому я не могу использовать точную грамматическую настройку, чтобы быть уверенным, что эти блоки не будут проанализированы в выражении.
Любая помощь будет оценена.
Вот пример разбора файла:
If a > 0 Then read(b); Endif
c := "If I were...";
While d > 5 Do d := d + 1 Endwhile
Я просто хочу собрать информацию о Ifs, Thens, Endifs и т.д... Остальное не имеет значения для меня. Вот почему я бы хотел, чтобы правила, связанные с Ifs, Thens и т. Д., Были приоритетными без необходимости писать грамматику.
1 ответ
Из книги Дракона, 2-е издание, раздел 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.
Если я правильно понял, ваш лексер рассматривает ключевые слова как 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.
РЕДАКТИРОВАТЬ:
Спасибо за ваш комментарий, кол. Я забыл добавить правило для строки. Но я не думаю, что мое решение неверно. например, если идентификатор называется 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: (в котором используется тот же метод для сопоставления ключевых слов с идентификаторами)
Также взгляните на грамматику ANSI C, спецификацию Lex.
Я также использовал этот подход в своем личном проекте, и пока я не нашел никаких проблем.