МИНИСТЕРСТВО ЦИФРОВОГО РАЗВИТИЯ, СВЯЗИ И МАССОВЫХ КОММУНИКАЦИЙ РОССИЙСКОЙ ФЕДЕРАЦИИ
Ордена Трудового Красного Знамени федеральное государственное бюджетное образовательное учреждение высшего образования
Московский технический университет связи и информатики
(МТУСИ)
Кафедра «Системное программирование»
Лабораторная работа №8
«Автоматизация проектирования лексических анализаторов»
по дисциплине «Системное программное обеспечение»
Выполнила:
студентка группы БФИ2203
Мячина Лада Андреевна
Проверила:
Старший преподаватель
Алексанян Диана Ашотовна
Москва, 2025 г.
Цель работы
изучить возможности автоматизированной генерации парсера КСграмматик
ознакомиться со структурой программы Bison
приобрести практические навыки использования Bison для осуществления синтаксического разбора
Задание
Задание 1. Генерация парсера калькулятора
Написать программу, позволяющую выполнять арифметические операции с целыми числами. В отчет включить КС-грамматику, листинги файлов *.y, .l и скриншоты запуска с проверкой работоспособности.
Задание 2. Расширение парсера калькулятора
Расширить калькулятор, написанный в задании 1 добавив операции: унарный минус, подсчет sin и cos, возведения в степень ^. Дополнительные баллы – реализация работы калькулятора с числами с плавающей точкой.
Задание 3. Создание лексического анализатора
Для грамматики вашего языка из предыдущих работ (5 и 6) создайте синтаксический анализатор с использованием Bison. Токены должны валидироваться лексическим анализатором, полученным с использованием Flex из предыдущей работы. Парсер должен работать как в интерактивном режиме, так и с программой, путь до файла которой передается при запуске. Вывод результатов работы осуществить в терминал. В отчет включить фрагменты файла с информацией о состояниях, сгенерированного Bison автомата с пояснениями.
Список лексем
<программа> ::= <оператор> (';' <оператор>)*
<оператор> ::= <цикл_for> | <комментарий>
<цикл_for> ::= 'for' '(' [<инициализация>] ';' [<условие>] ';' [<приращение>] ')' 'do'
<инициализация> ::= <присваивание>
<условие> ::= <выражение> <оператор_сравнения> <выражение>
<приращение> ::= <присваивание>
<присваивание> ::= <идентификатор> ':=' <выражение>
<выражение> ::= <идентификатор> | <римское_число>
<оператор_сравнения> ::= '<' | '>' | '='
<римское_число> ::= ('I' | 'V' | 'X')+
<идентификатор> ::= [a-zA-Z_][a-zA-Z0-9_]{0,31}
<комментарий> ::= '{' <текст_комментария> '}'
<текст_комментария> ::= [^}]*
Ход работы Задание 1
Скомпилируем предложенные файлы, а затем запустим калькулятор (см. рисунок 1-2)
Рисунок 1 – Результат работы калькулятора и его компиляция
Рисунок 2 – Результат работы калькулятора и его компиляция
КС-грамматика:
input : /* empty */
| input line
;
line : NEWLINE
| expr NEWLINE
| error NEWLINE
;
expr : NUMBER
| expr '+' expr
| expr '-' expr
| expr '*' expr
| expr '/' expr
| '(' expr ')'
;
Листинг parser.y:
%{
#include <stdio.h>
#include <stdlib.h>
int yylex(void);
void yyerror(const char *s);
int lines = 0; // кол-во обработанных строк
%}
%token NUMBER // токены вернет лексер
%token NEWLINE
// Приоритеты и ассоциативность операций
%left '+' '-'
%left '*' '/'
%%
// Начальное правило
input:
/* пусто */
| input line
;
line:
NEWLINE { lines++; }
| expr NEWLINE { printf("= %d\n", $1); lines++; }
| error NEWLINE { yyerrok; } // Восстановление после ошибки
;
expr:
NUMBER { $$ = $1; }
| expr '+' expr { $$ = $1 + $3; }
| expr '-' expr { $$ = $1 - $3; }
| expr '*' expr { $$ = $1 * $3; }
| expr '/' expr {
if ($3 == 0) {
yyerror("Деление на ноль!");
$$ = 0;
} else {
$$ = $1 / $3;
}
}
| '(' expr ')' { $$ = $2; }
;
%%
// Функция обработки ошибок
void yyerror(const char *s) {
fprintf(stderr, "Строка %d: Ошибка: %s\n", lines + 1, s);
}
int main(int argc, char **argv) {
printf("Калькулятор с целыми числами\n");
printf("Введите выражения (или Ctrl+D для выхода):\n");
int result = yyparse();
printf("Обработано строк: %d\n", lines);
return result;
}
Листинг lexer.lex:
%{
#include "parser.tab.h"
#include <stdlib.h>
int yywrap(void);
%}
%%
[0-9]+ {
yylval = atoi(yytext);
return NUMBER;
}
\n { return NEWLINE; }
[ \t] ; // Игнорируем пробелы и табы
"+" { return '+'; }
"-" { return '-'; }
"*" { return '*'; }
"/" { return '/'; }
"(" { return '('; }
")" { return ')'; }
. {
fprintf(stderr, "Неизвестный символ: '%s'\n", yytext);
return yytext[0];
}
%%
// Функция yywrap - вызывается при достижении конца файла
int yywrap(void) {
return 1; // Завершаем работу
}
Задание 2.
Исправим парсер и лексер, а затем проверим улучшенный калькулятор. (см. рисунок 2)
Рисунок 2 – Исправленный калькулятор
Листинг parser.y:
%{
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
int yylex(void);
void yyerror(const char *s);
int lines = 0;
%}
%union {
double num;
}
%token <num> NUMBER
%token NEWLINE
%token SIN COS POWER
%left '+' '-'
%left '*' '/'
%right POWER
%right UMINUS
%type <num> expr
%%
input:
/* пусто */
| input line
;
line:
NEWLINE { lines++; }
| expr NEWLINE { printf("= %g\n", $1); lines++; }
| error NEWLINE { yyerrok; }
;
expr:
NUMBER { $$ = $1; }
| expr '+' expr { $$ = $1 + $3; }
| expr '-' expr { $$ = $1 - $3; }
| expr '*' expr { $$ = $1 * $3; }
| expr '/' expr {
if ($3 == 0.0) {
yyerror("Деление на ноль!");
$$ = 0;
} else {
$$ = $1 / $3;
}
}
| '-' expr %prec UMINUS { $$ = -$2; }
| expr POWER expr { $$ = pow($1, $3); }
| SIN '(' expr ')' { $$ = sin($3); }
| COS '(' expr ')' { $$ = cos($3); }
| '(' expr ')' { $$ = $2; }
;
%%
void yyerror(const char *s) {
fprintf(stderr, "Строка %d: Ошибка: %s\n", lines + 1, s);
}
int main(int argc, char **argv) {
printf("Расширенный калькулятор с числами с плавающей точкой\n");
printf("Поддерживается: + - * / ^ sin() cos() унарный минус\n");
printf("Введите выражения (или Ctrl+D для выхода):\n");
int result = yyparse();
printf("Обработано строк: %d\n", lines);
return result;
}
Листинг lexer.lex:
%{
#include "parser.tab.h"
#include <stdlib.h>
#include <math.h>
int yywrap(void);
%}
%%
[0-9]+ {
yylval.num = atof(yytext);
return NUMBER;
}
[0-9]+\.[0-9]* {
yylval.num = atof(yytext);
return NUMBER;
}
\n { return NEWLINE; }
[ \t] ; // Игнорируем пробелы и табы
"+" { return '+'; }
"-" { return '-'; }
"*" { return '*'; }
"/" { return '/'; }
"^" { return POWER; }
"sin" { return SIN; }
"cos" { return COS; }
"(" { return '('; }
")" { return ')'; }
. {
fprintf(stderr, "Неизвестный символ: '%s'\n", yytext);
return yytext[0];
}
%%
int yywrap(void) {
return 1;
}
