flex-bison игнорировать пробелы - PullRequest
0 голосов
/ 12 июля 2020

Я пытаюсь реализовать простой лексер и синтаксический анализатор с использованием flex-bison.

Все, что я хотел, это проанализировать эти:

  • a
  • a, б
  • а, б
  • а, б
  • а, б, c
  • а, б, c
  • ....

Просто последовательность, разделенная запятой, может содержать или не содержать пробел. Итак, вот моя грамматика:

KEY_SET             : KEY
                      {
                        printf("keyset 1");
                      }
                      | KEY COMMA KEY_SET
                      {
                        printf("keyset 2");
                      };

Объявлено KEY, COMMA как token. //% token

Но это дает мне синтаксическую ошибку, всякий раз, когда я нажимаю ввод или любой пробел.

Так что я даже объявил IGNORE [ \t\n] в flex. И в парсере я добавил новое правило:

IGNORE_BLOCK        : IGNORE
                      {
                        printf("\n...ignoring...\n")
                      };

Но это даже не работает.

Это заставляет меня выдавать синтаксическую ошибку.

Как я могу решить эту проблему?

Лексер:

%{
    #include "y.tab.h"
%}
%option noyywrap
COMMA                   [,]
KEY                     [[:alpha:][:alnum:]*]
IGNORE                  [ \t\n]
%%
{COMMA}                 {return COMMA;}
{KEY}                   {return KEY;}
{IGNORE}                {return IGNORE;}
.                       {printf("Exiting...\n");exit(0);}
%%

Парсер:

%{
    #include<stdio.h>
    void yyerror (char const *s);
    int yywrap();
    //int extern yylex();
%}
%token      COMMA
%token      KEY
%token      IGNORE
%%
KEY_SET             : KEY
                      {
                        printf("keyset 1");
                      }
                      | KEY COMMA KEY_SET
                      {
                        printf("keyset 2");
                      };

IGNORE_BLOCK        : IGNORE
                      {
                        printf("\n...ignoring...\n")
                      };

%%
int main(int argc, char **argv)
{
    while(1)
    {
      printf("****************\n");
      yyparse();
      char ign;
      scanf("%c",&ign);
    }
    return 0;
}
int yywrap()
{
   return 1;
}
void yyerror (char const *s) {
   fprintf (stderr, "%s\n", s);
}

Команда, которую я использую для сборки:

flex test.l
bison -dy test.y
gcc lex.yy.c y.tab.c -o test.exe

1 Ответ

3 голосов
/ 12 июля 2020

Ваш гибкий файл содержит серию правил, каждое из которых состоит из шаблона и действия. Вопреки распространенному мнению, вам не нужно «объявлять» свои шаблоны перед их использованием.

Если вы хотите игнорировать пробелы в вашем лексере, вам нужно правило, которое ничего не делает.

Вы в вашем шаблоне ключей была ошибка, которую я исправил; ваш шаблон не принял бы ключи с более чем одной буквой. Кроме того, набирать exit в вашем сканере - это очень плохой стиль. Позвольте синтаксическому анализатору обрабатывать ошибки.

%{
    #include "y.tab.h"
%}
%option noyywrap
%%
   /* Removed the COMMA rule. See text below. */
   /* ","               {return COMMA;} */
   /* Compare this pattern with the one you used */
[[:alpha:]][[:alnum:]]* {return KEY;}
   /* Recognise and ignore whitespace. */
[[:space:]]+            ; /* Do nothing */
   /* Send unrecognised input to the parser. */
.                       {return *yytext;}

Вашему синтаксическому анализатору не требуется IGNORE, что в любом случае было бессмысленно, потому что грамматика не производит его. Bison, вероятно, предупреждал вас об этом.

Вы можете упростить свой синтаксический анализатор другими способами:

  • yywrap не требуется, так как ваш лексер имеет %option noyywrap.
  • Терминал COMMA может быть записан как ',', если вы просто удалите шаблон "," из лексера (поскольку резервное правило
    .  { return *yytext; }
    
    будет работать правильно для любого односимвольного литерала).

Для тестирования вы, вероятно, захотите анализировать одну строку за раз вместо игнорирования синтаксических ошибок.

Я бы также рекомендовал не использовать флаг «устаревшего» * ​​1028 * при вызове bison; этот флаг следует использовать только для старых существующих файлов грамматики ya cc, поскольку он может мешать работе современных функций Bison. Без -y bison запишет сгенерированный код C в <i>filename</i>.tab.c, а сгенерированный заголовок - в <i>filename</i>.tab.h. Если вам не нравятся эти имена, вы можете использовать флаг -o, чтобы указать имя сгенерированного кода C (и заголовок будет иметь то же имя с расширением, измененным на .h).

Это может дать что-то вроде этого:

(Обратите внимание, что я изменил KEY_SET на key_set, потому что обычный стиль в грамматиках таков, что ALL_CAPS - это токены, а нетерминальные - строчные. Я также изменил его с праворекурсивного на леворекурсивный, чтобы избежать проблемы, которую вы могли бы заметить, если бы ваше производственное действие напечатало значение токена KEY, если ваш лексер дал ему значение.)

file parser.y

%{
    #include<stdio.h>
    void yyerror (char const *s);
    int yylex(void);
    /* Defined in the flex file */
    void set_input(const char* input);
%}
%token  KEY
%%
key_set : KEY             { printf("keyset 1\n"); }
        | key_set ',' KEY { printf("keyset 2\n"); };
%%
int main(int argc, char **argv)
{
    char buffer[BUFSIZ];
    while (1)
    {
      printf("****************\n");
      char* input = fgets(buffer, sizeof buffer, stdin);
      if (buffer == NULL) break;
      set_input(input);
      yyparse();
    }
    return 0;
}

void yyerror (char const *s) {
   fprintf (stderr, "%s\n", s);
}

файл lexer.l

%{
    #include "parser.tab.h"
%}
%option noinput nounput nodefault yylineno
%option noyywrap
%%
[[:alpha:]][[:alnum:]]* {return KEY;}
[[:space:]]+            ; /* Do nothing */
.                       {return *yytext;}
%%
static YY_BUFFER_STATE flex_buffer;
void set_input(const char* input) {
  yy_delete_buffer(flex_buffer);
  flex_buffer = yy_scan_string(input);
}

Процедура сборки:

flex lexer.l
bison -d parser.y
gcc lex.yy.c parser.tab.c -o parser.exe

Ваша грамматика не допускает пустой ввод, но это нормально. Однако в целях тестирования вы можете добавить тест в l oop, который считывает строки ввода, чтобы вызывать синтаксический анализатор только в том случае, если строка не пуста.

...