Подтвердить что ты не робот

Можно ли установить приоритеты для правил, чтобы избежать "самой длинной самой ранней" модели соответствия?

Еще один простой вопрос: есть ли способ сказать flex, чтобы предпочесть правило, которое соответствует короткой вещи над правилом, которое соответствует более длинной вещи? Я не могу найти хорошую документацию об этом.

Вот почему мне это нужно: я разбираю файл для псевдоязыка, который содержит некоторые ключевые слова, соответствующие инструкциям управления. Я бы хотел, чтобы они были абсолютным приоритетом, так что они не анализировались как часть выражения. Мне действительно нужна эта приоритетная задача, потому что мне не нужно писать полную грамматику для моего проекта (это было бы полностью перехитрить в моем случае, так как я выполняю структурный анализ в анализируемой программе, мне не нужно знать подробности..), поэтому я не могу использовать тонкую настройку грамматики, чтобы убедиться, что эти блоки не будут проанализированы в выражении.

Любая помощь будет оценена.

Вот пример проанализированного файла:

If a > 0 Then read(b); Endif
c := "If I were...";
While d > 5 Do d := d + 1 Endwhile

Я просто хочу собрать информацию о Ifs, Thens, Endifs и т.д. Остальное не имеет значения для меня. Вот почему я хотел бы, чтобы правила Ifs, Thens и т.д. Были приоритетными, чтобы не писать грамматику.

4b9b3361

Ответ 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.

Я также использовал этот подход в своем личном проекте, и до сих пор я не обнаружил никаких проблем.