From f5a2854934918ebdbea53b9d79eaec3c3ce13ca5 Mon Sep 17 00:00:00 2001 From: serr Date: Tue, 1 Apr 2025 15:04:30 +0300 Subject: [PATCH] block parse added --- analyzers/test/test.l | 22 ++++++++++++++ analyzers/test/test.y | 49 +++++++++++++++++++++++++++++++ main.py | 2 +- test.txt | 13 +++++++++ tutorial/test.l | 37 +++++++++++++++++++++++ tutorial/test.y | 68 +++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 190 insertions(+), 1 deletion(-) create mode 100644 analyzers/test/test.l create mode 100644 analyzers/test/test.y create mode 100644 test.txt create mode 100644 tutorial/test.l create mode 100644 tutorial/test.y diff --git a/analyzers/test/test.l b/analyzers/test/test.l new file mode 100644 index 0000000..41876a5 --- /dev/null +++ b/analyzers/test/test.l @@ -0,0 +1,22 @@ +%{ +#include "test.tab.h" +#include +#include + +int yylineno; + +%} + +%% +"{" { return LBRACE; } +"}" { return RBRACE; } +\n { yylineno++; } +[^{}]+ { + yylval.str = strdup(yytext); + return TEXT; + } +%% + +int yywrap() { + return 1; +} \ No newline at end of file diff --git a/analyzers/test/test.y b/analyzers/test/test.y new file mode 100644 index 0000000..7252c08 --- /dev/null +++ b/analyzers/test/test.y @@ -0,0 +1,49 @@ +%{ +#include +#include +#include + +extern int yylineno; +extern char *yytext; + +void yyerror(const char *s) { + fprintf(stderr, "\033[91mError at line %i\033[0m", yylineno); + exit(1); +} +extern int yylex(); +extern FILE *yyin; +%} + +%union { + char *str; +} + +%token TEXT +%token LBRACE RBRACE + +%% + +block: + LBRACE content RBRACE + ; + +content: + | content TEXT { printf("TOKEN ('%s')\n", $2); free($2); } + | content block {} + ; +%% + +int main(int argc, char **argv) { + yylineno = 1; + + if (argc > 1) { + FILE *f = fopen(argv[1], "r"); + if (!f) { + perror("\033[91mFail open file\033[0m"); + return 1; + } + yyin = f; + } + yyparse(); + return 0; +} \ No newline at end of file diff --git a/main.py b/main.py index 09e2a3a..c90ac86 100644 --- a/main.py +++ b/main.py @@ -3,7 +3,7 @@ import subprocess # ЭТИ ПУТИ НАДО ЗАДАТЬ ВРУЧНУЮ # *.l и *.y файлы из директории ANALYZERS_DIR ДОЛЖНЫ НАЗЫВАТЬСЯ как basename этой директории!!! -ANALYZERS_DIR = r'C:\Users\user\Desktop\УЧЕБА\6_СЕМ\КОМПИЛЯТОРЫ\flex_bison_test\analyzers\cpl' +ANALYZERS_DIR = r'C:\Users\user\Desktop\УЧЕБА\6_СЕМ\КОМПИЛЯТОРЫ\flex_bison_test\analyzers\test' FLEX_EXE_PATH = r"C:\tools\win_flex_bison\win_flex.exe" BISON_EXE_PATH = r"C:\tools\win_flex_bison\win_bison.exe" diff --git a/test.txt b/test.txt new file mode 100644 index 0000000..57acaa9 --- /dev/null +++ b/test.txt @@ -0,0 +1,13 @@ +{ + { 1 231233 + {1}{} + block1 { + block2 { block3 } + block4 asjdb asd bajds ba + } 126316 + block5 + } + { + 123 + } +} \ No newline at end of file diff --git a/tutorial/test.l b/tutorial/test.l new file mode 100644 index 0000000..65a2138 --- /dev/null +++ b/tutorial/test.l @@ -0,0 +1,37 @@ +%{ +#include "test.tab.h" +В файле test.l есть строка: +c +Copy + +#include "test.tab.h" + +// Этот заголовочный файл генерируется Bison'ом и содержит: +// 2. Содержимое test.tab.h (примерно) + +// typedef union { +// char *str; +// } YYSTYPE; +// extern YYSTYPE yylval; + +// #define TEXT 258 +// #define LBRACE 259 +// #define RBRACE 260 +#include +#include + +void yyerror(const char *s); +%} + +%% +"{" { return LBRACE; } +"}" { return RBRACE; } +[^{}]+ { // [^{}]+ = "один или больше символов, ни один из которых не является { или }" + yylval.str = strdup(yytext); // сохранение данных + return TEXT; } // возвращение типа токена +. { } +%% + +int yywrap() { + return 1; +} \ No newline at end of file diff --git a/tutorial/test.y b/tutorial/test.y new file mode 100644 index 0000000..13fc754 --- /dev/null +++ b/tutorial/test.y @@ -0,0 +1,68 @@ +%{ +#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; +} \ No newline at end of file