Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Мячина_8ЛР.docx
Скачиваний:
0
Добавлен:
02.08.2026
Размер:
269 Кб
Скачать

МИНИСТЕРСТВО ЦИФРОВОГО РАЗВИТИЯ, СВЯЗИ И МАССОВЫХ КОММУНИКАЦИЙ РОССИЙСКОЙ ФЕДЕРАЦИИ

Ордена Трудового Красного Знамени федеральное государственное бюджетное образовательное учреждение высшего образования

Московский технический университет связи и информатики

(МТУСИ)

Кафедра «Системное программирование»

Лабораторная работа №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;

}

Соседние файлы в предмете Системное программное обеспечение