%{ #include #include #include void yyerror(const char *s) { fprintf(stderr, "Error: %s\n", s); } extern int yylex(); extern FILE *yyin; %} // %union задаёт все возможные типы значений, которые могут быть у токенов и нетерминалов. %union { char *str; } // Этот блок в Bison (%token) определяет терминальные символы (токены), // которые лексический анализатор (Flex) передаёт парсеру (Bison) %token TEXT // — указывает, что этот токен имеет ассоциированное значение типа str (как объявлено в %union). %token LBRACE RBRACE // LBRACE и RBRACE — это токены без ассоциированного значения. Они просто возвращают символы {,} %% /* правило задающее блок - структуру вида {...}, разбирается как открывающая скобка { (LBRACE) затем content (содержимое внутри скобок), затем закрывающая скобка } (RBRACE).*/ block: LBRACE content RBRACE ; /* правило, задающее content если контент пустой, то ничего не происходит если контент это контент и TEXT, то освобождается память выделенная под предыдущий результат еще контент может быть блоком */ content: /* пусто */ | content TEXT { printf("TOKEN ('%s')\n", $2); free($2); } | content block ; %% // 3. Как работает разбор на примере { A { B } C } // Лексер (Flex) разбивает вход на токены: // LBRACE ({), TEXT (A), LBRACE ({), TEXT (B), RBRACE (}), TEXT (C), RBRACE (}) // Парсер (Bison) применяет правила: // Видит { → начинает block. // Внутри block ожидается content. // content может быть TEXT (A). // Затем content может включать block ({ B }). // Затем ещё TEXT (C). // Закрывается } → конец block. int main(int argc, char **argv) { if (argc > 1) { FILE *f = fopen(argv[1], "r"); if (!f) { perror("Fail open file"); return 1; } yyin = f; } yyparse(); return 0; }