Почему последнее соответствие правилу в моем файле lex, когда у меня есть правила получше?

У меня есть файл lex с моими правилами, такими как:

PROGRAM           return Parser::PROGRAM;
PROGRAM_END       return Parser::PROGRAM_END;
VARIABLES:        return Parser::VARIABLES;
INSTRUCTIONS:     return Parser::INSTRUCTIONS; 
SKIP              return Parser::SKIP;
.           {
                std::cerr << lineno() << ": ERROR." << std::endl;
                exit(1);
            }

и когда я пытаюсь использовать полностью скомпилированную (с файлом yacc и т. д.) версию, то в тестовом файле используется только это, последнее правило, даже если тестовый файл правильный.

Например, это тестовый файл для этих правил:

PROGRAM fst
INSTRUCTIONS:
    SKIP
PROGRAM_END

Для этого файла я получил только: 1: ERROR.

Почему это так и как я могу это решить?


person Farm Éva    schedule 09.11.2019    source источник
comment
Либо ваш ввод не содержит <, <= или >, вы ошибаетесь в том, что используется только последнее правило, либо в вашем реальном коде происходит что-то еще, что не видно в опубликованных вами выдержках. Невозможно сказать, не видя минимально воспроизводимый пример.   -  person sepp2k    schedule 09.11.2019
comment
@ sepp2k Я обновил свой вопрос примером и необходимыми правилами для этого примера.   -  person Farm Éva    schedule 09.11.2019
comment
Откуда вы знаете, что правило PROGRAM не работает в этом примере? Если вы добавите оператор печати к этому правилу, я уверен, что вы увидите его вывод. В любом случае, вам, вероятно, не следует выходить из программы на неизвестных символах, если вы хотите, чтобы она все еще что-то делала (кроме выхода с сообщением об ошибке) для ввода, содержащего неизвестные символы. Я также предлагаю напечатать оскорбительный ввод в правиле ..   -  person sepp2k    schedule 09.11.2019
comment
@sepp2k sepp2k Я знаю это, потому что файл yacc должен печатать каждое перехваченное правило. Если я печатаю с YYText(), то тоже ничего: 1: ERROR:   -  person Farm Éva    schedule 09.11.2019
comment
@ sepp2k, а также, если я удалю это правило ., то yacc напечатает все, но это правило не будет правильным и также позволит использовать плохие файлы.   -  person Farm Éva    schedule 09.11.2019
comment
Когда я делаю ваш код компилируемым, а затем добавляю оператор печати в строку PROGRAM, я вижу вывод оператора печати (за которым следует сообщение об ошибке и программа завершается). Мне трудно поверить, что ты этого не сделаешь.   -  person sepp2k    schedule 09.11.2019
comment
И относительно того, что yacc ничего не делает: неудивительно, если лексер выходит из программы, пока синтаксический анализатор все еще выполняет синтаксический анализ. Даже если у вас есть действие с оператором печати непосредственно после токена программы, синтаксический анализатор вполне может запросить другой токен перед выполнением этого действия. Поэтому, если лексер завершает работу в этот момент, синтаксический анализатор ничего не сможет сделать.   -  person sepp2k    schedule 09.11.2019
comment
@ sepp2k Да, я тоже это понял, но если я пойду дальше, то fst или INSTRUCTIONS и так далее не появятся в качестве вывода. И еще, файл yacc должен это поймать и распечатать, но он этого не делает.   -  person Farm Éva    schedule 09.11.2019
comment
Вы вызываете exit в правиле .. Таким образом, после выполнения правила . больше ничего не произойдет. Если вы этого не хотите, не звоните exit.   -  person sepp2k    schedule 09.11.2019
comment
@sepp2k sepp2k Хорошо, я понял, но почему выход из лексера, когда у него есть правила, которые соответствуют другим вещам? У меня есть правило для каждой части этого тестового файла. fst должен быть перехвачен правилом ({CHAR}|{UNDERSCORE}) и макросами CHAR [a-zA-Z] UNDERSCORE "_".   -  person Farm Éva    schedule 09.11.2019
comment
Вы, вероятно, узнаете, если напечатаете оскорбительный символ в правиле .. Я не могу сказать вам, какие правила отсутствуют в вашем реальном коде, потому что я не видел вашего реального кода, но я могу сказать вам, что в вашем примере первым неизвестным символом будет пробел между PRORGAM и fst.   -  person sepp2k    schedule 09.11.2019


Ответы (1)


Как указано в комментариях, почти наверняка PROGRAM начинает распознаваться как токен и передается синтаксическому анализатору. Однако почти во всех случаях синтаксический анализатор немедленно запросит другой токен, а следующим символом во входной последовательности будет пробел, который соответствует последнему правилу. Это правило выводит сообщение об ошибке и вызывает exit(), завершая работу приложения. (Как правило, это не очень хорошая идея, но я полагаю, что это всего лишь тестовая программа.) Вот и все, что вы получите на выходе.

Если вы укажете аргумент командной строки -d при вызове (f)lex, будет сгенерирован сканер отладки, который сообщает о ходе работы сканера. Это очень простой способ увидеть, что происходит в вашем сканере. Bison также имеет режим отладки, как описано в руководстве по bison. Эти инструменты очень просты в использовании и очень рекомендуются.

Вот, например, быстрая тестовая установка:

%{
#include <iostream>
#include <cstdlib>
class Parser {
  public:
    enum Token {
      PROGRAM = 257,
      PROGRAM_END, VARIABLES, INSTRUCTIONS, SKIP
    };
};
%}
%option batch noyywrap yylineno c++
%%
PROGRAM           return Parser::PROGRAM;
PROGRAM_END       return Parser::PROGRAM_END;
VARIABLES:        return Parser::VARIABLES;
INSTRUCTIONS:     return Parser::INSTRUCTIONS; 
SKIP              return Parser::SKIP;
.                 {
                    std::cerr << lineno() << ": ERROR." << std::endl;
                    exit(1);
                  }
%%
int main() {
  yyFlexLexer lexer{};
  lexer.set_debug(1);
  while(lexer.yylex() != 0) { }
  return 0;
}

И пример запуска:

$ g++ lex.yy.cc && ./a.out<<<"PROGRAM fst"
--(end of buffer or a NUL)
--accepting rule at line 14("PROGRAM")
--accepting rule at line 19(" ")
1: ERROR.

что дает понять, что сканер сначала создал токен PROGRAM, прежде чем выйти на символ пробела.

person rici    schedule 09.11.2019