Распознавание и ll(1) тема

This commit is contained in:
2025-05-06 15:40:22 +03:00
parent c186941dce
commit 529da8d98e
6 changed files with 876 additions and 0 deletions

8
lab3/.gitignore vendored Normal file
View File

@@ -0,0 +1,8 @@
**/*
!.gitignore
!report.tex
!img
!img/**
!programm
!programm/*.py
!programm/*.txt

2
lab3/programm/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
grammar_*
analysis_*

318
lab3/programm/grammar.py Normal file
View File

@@ -0,0 +1,318 @@
import re
from collections import OrderedDict
from prettytable import PrettyTable
class Grammar:
EPSILON: str = "epsilon"
def __init__(self, text: str):
self.productions: OrderedDict[str, list[list[str]]] = OrderedDict()
self.start_symbol: str = ""
self._parse_productions(text)
self.terminals: set[str] = set()
self._find_terminals()
self.first_sets: dict[str, set[str]] = {}
self._calculate_first_sets()
self.follow_sets: dict[str, set[str]] = {}
self._calculate_follow_sets()
self.lookup_table: dict[str, dict[str, list[str]]] = {}
self._fill_lookup_table()
# Сопостовляем уникальный номер с каждым правилом
self.rule_numbers = {}
rule_idx = 1
for nt, rules in self.productions.items():
for rule in rules:
self.rule_numbers[(nt, tuple(rule))] = rule_idx
rule_idx += 1
def _parse_productions(self, text: str):
for line in text.splitlines():
line = line.strip()
if not line:
continue
non_terminal, rule = line.split("->")
if self.start_symbol == "":
self.start_symbol = non_terminal.strip()
non_terminal = non_terminal.strip()
rules = [
[
symbol.strip('"')
for symbol in re.findall(r"\".*?\"|\S+", rule.strip())
if symbol.strip('"') != self.EPSILON
]
for rule in rule.split("|")
]
if non_terminal not in self.productions:
self.productions[non_terminal] = []
self.productions[non_terminal].extend(rules)
def _find_terminals(self):
for rules in self.productions.values():
for rule in rules:
for symbol in rule:
if symbol not in self.productions:
self.terminals.add(symbol)
def _calculate_first_sets(self):
# Инициализация FIRST для всех символов
for non_terminal in self.productions:
self.first_sets[non_terminal] = set()
# Для терминалов FIRST содержит только сам терминал
for terminal in self.terminals:
self.first_sets[terminal] = {terminal}
# Вычисление FIRST для нетерминалов
changed = True
while changed:
changed = False
for non_terminal, rules in self.productions.items():
for rule in rules:
if not rule: # Пустое правило (эпсилон)
if "" not in self.first_sets[non_terminal]:
self.first_sets[non_terminal].add("")
changed = True
else:
all_can_derive_epsilon = True
for i, symbol in enumerate(rule):
# Добавляем все символы из FIRST(symbol) кроме эпсилон
first_without_epsilon = self.first_sets[symbol] - {""}
old_size = len(self.first_sets[non_terminal])
self.first_sets[non_terminal].update(first_without_epsilon)
if len(self.first_sets[non_terminal]) > old_size:
changed = True
# Если symbol не может порождать эпсилон, прерываем
if "" not in self.first_sets[symbol]:
all_can_derive_epsilon = False
break
# Если все символы в правиле могут порождать эпсилон,
# то и нетерминал может порождать эпсилон
if (
all_can_derive_epsilon
and "" not in self.first_sets[non_terminal]
):
self.first_sets[non_terminal].add("")
changed = True
def _calculate_follow_sets(self):
# инициализировать Fo(S) = { $ }, а все остальные Fo(Ai) пустыми множествами
for non_terminal in self.productions:
self.follow_sets[non_terminal] = set()
# Добавляем символ конца строки $ для начального символа
self.follow_sets[self.start_symbol].add("$")
# Повторяем, пока в наборах Follow происходят изменения
changed = True
while changed:
changed = False
# Для каждого нетерминала Aj в грамматике
for non_terminal_j, rules in self.productions.items():
# Для каждого правила Aj → w
for rule in rules:
# Для каждого символа в правиле
for i, symbol in enumerate(rule):
# Если символ - нетерминал Ai
if symbol in self.productions:
# w' - остаток правила после Ai
remainder = rule[i + 1 :] if i + 1 < len(rule) else []
# Если есть терминалы после Ai (w' не пусто)
if remainder:
# Вычисляем First(w')
first_of_remainder = self._first_of_sequence(remainder)
# Если терминал a находится в First(w'), то добавляем a к Follow(Ai)
for terminal in first_of_remainder - {""}:
if terminal not in self.follow_sets[symbol]:
self.follow_sets[symbol].add(terminal)
changed = True
# Если ε находится в First(w'), то добавляем Follow(Aj) к Follow(Ai)
if "" in first_of_remainder:
old_size = len(self.follow_sets[symbol])
self.follow_sets[symbol].update(
self.follow_sets[non_terminal_j]
)
if len(self.follow_sets[symbol]) > old_size:
changed = True
# Если w' пусто (Ai в конце правила), то добавляем Follow(Aj) к Follow(Ai)
else:
old_size = len(self.follow_sets[symbol])
self.follow_sets[symbol].update(
self.follow_sets[non_terminal_j]
)
if len(self.follow_sets[symbol]) > old_size:
changed = True
def _first_of_sequence(self, sequence):
"""Вычисляет множество FIRST для последовательности символов"""
if not sequence:
return {""}
result = set()
all_can_derive_epsilon = True
for symbol in sequence:
# Добавляем First(symbol) без эпсилон к результату
result.update(self.first_sets[symbol] - {""})
# Если symbol не может порождать эпсилон, останавливаемся
if "" not in self.first_sets[symbol]:
all_can_derive_epsilon = False
break
# Если все символы могут порождать эпсилон, добавляем эпсилон к результату
if all_can_derive_epsilon:
result.add("")
return result
def _fill_lookup_table(self):
# Для каждого нетерминала A
for non_terminal, rules in self.productions.items():
# Формируем таблицу синтаксического анализа
self.lookup_table[non_terminal] = {}
# Для каждого правила A → w
for rule in rules:
# Вычисляем First(w)
first_of_rule = self._first_of_sequence(rule)
# Для каждого терминала a в First(w)
for terminal in first_of_rule - {""}:
# Если T[A,a] уже содержит правило, грамматика не LL(1)
if terminal in self.lookup_table[non_terminal]:
raise ValueError(
"Грамматика не является LL(1)-грамматикой.\n"
f"Конфликт в ячейке [{non_terminal}, {terminal}]\n"
f"Новое правило: {non_terminal} -> {' '.join(rule)};\n"
f"Существующее правило: {non_terminal} -> {self.lookup_table[non_terminal][terminal]}"
)
# Добавляем правило в таблицу
self.lookup_table[non_terminal][terminal] = rule
# Если эпсилон в First(w), то для каждого b в Follow(A)
if "" in first_of_rule:
for terminal in self.follow_sets[non_terminal]:
# Если T[A,b] уже содержит правило, грамматика не LL(1)
if terminal in self.lookup_table[non_terminal]:
raise ValueError(
"Грамматика не является LL(1)-грамматикой.\n"
f"Конфликт в ячейке [{non_terminal}, {terminal}]\n"
f"Новое правило: {non_terminal} -> {' '.join(rule)};\n"
f"Существующее правило: {non_terminal} -> {self.lookup_table[non_terminal][terminal]}"
)
# Добавляем правило в таблицу
self.lookup_table[non_terminal][terminal] = rule
def format_rules(self) -> str:
result = []
sorted_rules = sorted(self.rule_numbers.items(), key=lambda x: x[1])
for rule, number in sorted_rules:
non_terminal, symbols = rule
rule_text = f"{number}: {non_terminal} -> {' '.join(symbols)}"
result.append(rule_text)
return "\n".join(result)
def format_lookup_table(self) -> str:
table = PrettyTable()
terminals = list(self.terminals)
table.field_names = [""] + terminals + ["$"]
for non_terminal in self.productions:
row = [non_terminal]
for terminal in terminals + ["$"]:
if terminal in self.lookup_table[non_terminal]:
rule = self.lookup_table[non_terminal][terminal]
rule_num = self.rule_numbers.get((non_terminal, tuple(rule)), "")
row.append(f"{rule_num}: {' '.join(rule)}")
else:
row.append(" - ")
table.add_row(row)
return str(table)
def analyze(self, string: str) -> list[int]:
input_tokens = string.split() + ["$"]
input_pos = 0
# Инициализируем стек с терминальным символом и начальным символом
stack = ["$", self.start_symbol]
rules_applied = []
while stack:
top = stack[-1]
current_symbol = (
input_tokens[input_pos] if input_pos < len(input_tokens) else "$"
)
# Случай 1: Верхний символ стека - нетерминал
if top in self.productions:
# Ищем правило в таблице синтаксического анализа
if current_symbol in self.lookup_table[top]:
# Получаем правило
production = self.lookup_table[top][current_symbol]
# Удаляем нетерминал из стека
stack.pop()
# Добавляем правило в rules_applied
rule_number = self.rule_numbers[(top, tuple(production))]
rules_applied.append(rule_number)
# Добавляем правило в стек в обратном порядке
for symbol in reversed(production):
stack.append(symbol)
else:
expected_symbols = list(self.lookup_table[top].keys())
raise ValueError(
f"Syntax error: expected one of {expected_symbols}, got '{current_symbol}'"
)
# Случай 2: Верхний символ стека - терминал
elif top != "$":
if top == current_symbol:
# Удаляем терминал из стека
stack.pop()
# Переходим к следующему символу ввода
input_pos += 1
else:
raise ValueError(
f"Syntax error: expected '{top}', got '{current_symbol}'"
)
# Случай 3: Верхний символ стека - $
else: # top == "$"
if current_symbol == "$":
# Успешный синтаксический анализ
stack.pop() # Удаляем $ из стека
else:
raise ValueError(
f"Syntax error: unexpected symbols at end of input: '{current_symbol}'"
)
return rules_applied

25
lab3/programm/grammar.txt Normal file
View File

@@ -0,0 +1,25 @@
Предложение -> ПрямойПорядок | Инверсия
ПрямойПорядок -> Подлежащее ДополнениеКПодлежащему Глагол ВторостепенныеЧлены Отрицание
Инверсия -> Обстоятельство Глагол Подлежащее ВторостепенныеЧлены Отрицание
Подлежащее -> ИменнаяГруппа ПридаточноеПредложение | Местоимение
ПридаточноеПредложение -> ", " Союз Подлежащее Глагол ВторостепенныеЧлены Отрицание ", " | epsilon
ВторостепенныеЧлены -> ВторостепенныйЧлен ВторостепенныеЧлены | epsilon
ВторостепенныйЧлен -> Обстоятельство | Дополнение
Обстоятельство -> ОбстоятельствоВремени | ОбстоятельствоМеста | ОбстоятельствоОбразаДействия
ИменнаяГруппа -> АртикльЛибоМестоимение Прилагательные Существительное
АртикльЛибоМестоимение -> Артикль | ПритяжательноеМестоимение | epsilon
Прилагательные -> Прилагательное Прилагательные | epsilon
ДополнениеКПодлежащему -> Предлог Существительное | epsilon
Дополнение -> АртикльЛибоМестоимение Прилагательные Существительное
ОбстоятельствоМеста -> Предлог АртикльЛибоМестоимение Существительное
Союз -> "welcher" | "welche" | "welches"
Отрицание -> "nicht" | epsilon
Местоимение -> "ich" | "du" | "er" | "sie" | "es" | "wir" | "ihr" | "Sie"
ПритяжательноеМестоимение -> "mein" | "dein" | "sein" | "unser" | "euer" | "ihre"
Артикль -> "der" | "die" | "das" | "ein" | "eine" | "einen" | "einem" | "einer"
Прилагательное -> "alt" | "jung" | "groß" | "klein" | "schön" | "freundlich" | "süß" | "ruhig"
Существительное -> "Mann" | "Frau" | "Kind" | "Buch" | "Brief" | "Freund" | "Abendessen" | "Suppe"
Предлог -> "in" | "auf" | "unter" | "aus" | "mit" | "für" | "zu" | "am"
ОбстоятельствоВремени -> "gestern" | "heute" | "morgen" | "damals" | "jetzt" | "früh" | "spät" | "immer"
ОбстоятельствоОбразаДействия -> "schnell" | "langsam" | "gut" | "schlecht" | "laut" | "leise" | "gern" | "fleißig"
Глагол -> "las" | "schrieb" | "kochte" | "aß" | "ging" | "kam" | "sagte" | "machte"

61
lab3/programm/main.py Normal file
View File

@@ -0,0 +1,61 @@
import json
from grammar import Grammar
with open("grammar.txt", "r", encoding="utf-8") as file:
text = file.read()
# text = """
# S -> A b B | d
# A -> C A b | B
# B -> c S d | epsilon
# C -> a | e d
# """
# text = """
# S -> F | ( S + F )
# F -> 1
# """
grammar = Grammar(text)
print("FIRST sets:", grammar.first_sets)
print("FOLLOW sets:", grammar.follow_sets)
with open("grammar_rules.txt", "w", encoding="utf-8") as output_file:
output_file.write(grammar.format_rules())
print("Правила грамматики с номерами сохранены в grammar_rules.txt")
with open("grammar_lookup_table.txt", "w", encoding="utf-8") as output_file:
output_file.write(grammar.format_lookup_table())
print("Таблица синтаксического анализа сохранена в grammar_lookup_table.txt")
input_string = "ich las gestern ein alt Buch"
input_string = "der alt Mann las ein Buch"
input_string = "gestern las der alt Mann ein Buch"
print(f"Analyzing input '{input_string}':")
parse_result = grammar.analyze(input_string)
print(f"Applied rules: {parse_result}")
rule_details = {num: rule for rule, num in grammar.rule_numbers.items()}
with open("analysis_result.txt", "w", encoding="utf-8") as f:
f.write(f"Input: {input_string}\n")
f.write("Applied rules: ")
f.write(str(parse_result))
f.write("\n\n")
f.write("Derivation steps:\n")
current = grammar.start_symbol
f.write(f"{current}\n")
for rule_num in parse_result:
non_terminal, replacement = rule_details[rule_num]
words = current.split()
for i, word in enumerate(words):
if word == non_terminal:
words[i : i + 1] = replacement
break
current = " ".join(words)
f.write(f"{current}\n")
print("Результат анализа сохранен в analysis_result.txt")

462
lab3/report.tex Normal file
View File

@@ -0,0 +1,462 @@
\documentclass[a4paper, final]{article}
%\usepackage{literat} % Нормальные шрифты
\usepackage[14pt]{extsizes} % для того чтобы задать нестандартный 14-ый размер шрифта
\usepackage{tabularx}
\usepackage[T2A]{fontenc}
\usepackage[utf8]{inputenc}
\usepackage[russian]{babel}
\usepackage{amsmath}
\usepackage[left=25mm, top=20mm, right=20mm, bottom=20mm, footskip=10mm]{geometry}
\usepackage{ragged2e} %для растягивания по ширине
\usepackage{setspace} %для межстрочно го интервала
\usepackage{moreverb} %для работы с листингами
\usepackage{indentfirst} % для абзацного отступа
\usepackage{moreverb} %для печати в листинге исходного кода программ
\usepackage{pdfpages} %для вставки других pdf файлов
\usepackage{tikz}
\usepackage{graphicx}
\usepackage{afterpage}
\usepackage{longtable}
\usepackage{float}
% \usepackage[paper=A4,DIV=12]{typearea}
\usepackage{pdflscape}
% \usepackage{lscape}
\usepackage{array}
\usepackage{multirow}
\renewcommand\verbatimtabsize{4\relax}
\renewcommand\listingoffset{0.2em} %отступ от номеров строк в листинге
\renewcommand{\arraystretch}{1.4} % изменяю высоту строки в таблице
\usepackage[font=small, singlelinecheck=false, justification=centering, format=plain, labelsep=period]{caption} %для настройки заголовка таблицы
\usepackage{listings} %листинги
\usepackage{xcolor} % цвета
\usepackage{hyperref}% для гиперссылок
\usepackage{enumitem} %для перечислений
\newcommand{\specialcell}[2][l]{\begin{tabular}[#1]{@{}l@{}}#2\end{tabular}}
\setlist[enumerate,itemize]{leftmargin=1.2cm} %отступ в перечислениях
\hypersetup{colorlinks,
allcolors=[RGB]{010 090 200}} %красивые гиперссылки (не красные)
% подгружаемые языки — подробнее в документации listings (это всё для листингов)
\lstloadlanguages{ SQL}
% включаем кириллицу и добавляем кое−какие опции
\lstset{tabsize=2,
breaklines,
basicstyle=\footnotesize,
columns=fullflexible,
flexiblecolumns,
numbers=left,
numberstyle={\footnotesize},
keywordstyle=\color{blue},
inputencoding=cp1251,
extendedchars=true
}
\lstdefinelanguage{MyC}{
language=SQL,
% ndkeywordstyle=\color{darkgray}\bfseries,
% identifierstyle=\color{black},
% morecomment=[n]{/**}{*/},
% commentstyle=\color{blue}\ttfamily,
% stringstyle=\color{red}\ttfamily,
% morestring=[b]",
% showstringspaces=false,
% morecomment=[l][\color{gray}]{//},
keepspaces=true,
escapechar=\%,
texcl=true
}
\textheight=24cm % высота текста
\textwidth=16cm % ширина текста
\oddsidemargin=0pt % отступ от левого края
\topmargin=-1.5cm % отступ от верхнего края
\parindent=24pt % абзацный отступ
\parskip=5pt % интервал между абзацами
\tolerance=2000 % терпимость к "жидким" строкам
\flushbottom % выравнивание высоты страниц
% Настройка листингов
\lstset{
language=python,
extendedchars=\true,
inputencoding=utf8,
keepspaces=true,
% captionpos=b, % подписи листингов снизу
}
\begin{document} % начало документа
% НАЧАЛО ТИТУЛЬНОГО ЛИСТА
\begin{center}
\hfill \break
\hfill \break
\normalsize{МИНИСТЕРСТВО НАУКИ И ВЫСШЕГО ОБРАЗОВАНИЯ РОССИЙСКОЙ ФЕДЕРАЦИИ\\
федеральное государственное автономное образовательное учреждение высшего образования «Санкт-Петербургский политехнический университет Петра Великого»\\[10pt]}
\normalsize{Институт компьютерных наук и кибербезопасности}\\[10pt]
\normalsize{Высшая школа технологий искусственного интеллекта}\\[10pt]
\normalsize{Направление: 02.03.01 <<Математика и компьютерные науки>>}\\
\hfill \break
\hfill \break
\hfill \break
\hfill \break
\large{Лабораторная работа №3}\\
\large{<<Грамматика простого прошедшего времени}\\
\large{немецкого языка>>}\\
\large{по дисциплине}\\
\large{<<Математическая логика и теория автоматов>>}\\
\large{Вариант 15}\\
% \hfill \break
\hfill \break
\end{center}
\small{
\begin{tabular}{lrrl}
\!\!\!Студент, & \hspace{2cm} & & \\
\!\!\!группы 5130201/20102 & \hspace{2cm} & \underline{\hspace{3cm}} &Тищенко А. А. \\\\
\!\!\!Преподаватель & \hspace{2cm} & \underline{\hspace{3cm}} & Востров А. В. \\\\
&&\hspace{4cm}
\end{tabular}
\begin{flushright}
<<\underline{\hspace{1cm}}>>\underline{\hspace{2.5cm}} 2025г.
\end{flushright}
}
\hfill \break
% \hfill \break
\begin{center} \small{Санкт-Петербург, 2025} \end{center}
\thispagestyle{empty} % выключаем отображение номера для этой страницы
% КОНЕЦ ТИТУЛЬНОГО ЛИСТА
\newpage
\tableofcontents
\newpage
\section*{Введение}
\addcontentsline{toc}{section}{Введение}
Лабораторная №3 по дисциплине <<Математическая логика>> заключается в построении контексно-свободной грамматики подмножества естественного языка. В данном варианте рассматривается грамматика простого прошедшего времени немецкого языка.
Простое прошедшее время в немецком языке называется \textit{Претерит} или \textit{Претеритум} (нем. \textit{Präteritum} или нем. \textit{Imperfekt}). \textit{Претеритум} служит для передачи действия в прошлом. Это время не требует образования сложных конструкций, что позволяет относить его к простым формам.
\newpage
\section {Математическое описание}
\subsection{КС-грамматика}
\begin{verbatim}
Предложение -> ПрямойПорядок | Инверсия
ПрямойПорядок -> Подлежащее ДополнениеКПодлежащему Глагол
ВторостепенныеЧлены Отрицание
Инверсия -> Обстоятельство Глагол Подлежащее
ВторостепенныеЧлены Отрицание
Подлежащее -> ИменнаяГруппа ПридаточноеПредложение | Местоимение
ПридаточноеПредложение -> ", " Союз Подлежащее Глагол
ВторостепенныеЧлены Отрицание ", " |
epsilon
ВторостепенныеЧлены -> ВторостепенныйЧлен ВторостепенныеЧлены |
epsilon
ВторостепенныйЧлен -> Обстоятельство | Дополнение
Обстоятельство -> ОбстоятельствоВремени | ОбстоятельствоМеста |
ОбстоятельствоОбразаДействия
ИменнаяГруппа -> АртикльЛибоМестоимение Прилагательные
Существительное
АртикльЛибоМестоимение -> Артикль | ПритяжательноеМестоимение |
epsilon
Прилагательные -> Прилагательное Прилагательные | epsilon
ДополнениеКПодлежащему -> Предлог Существительное | epsilon
Дополнение -> АртикльЛибоМестоимение Прилагательные Существительное
ОбстоятельствоМеста -> Предлог АртикльЛибоМестоимение Существительное
Союз -> "welcher" | "welche" | "welches"
Отрицание -> "nicht" | epsilon
Местоимение -> "ich" | "du" | "er" | "sie" | "es" | "wir" |
"ihr" | "Sie"
ПритяжательноеМестоимение -> "mein" | "dein" | "sein" |
"unser" | "euer" | "ihre"
Артикль -> "der" | "die" | "das" | "ein" | "eine" | "einen" |
"einem" | "einer"
Прилагательное -> "alt" | "jung" | "groß" | "klein" | "schön" |
"freundlich" | "süß" | "ruhig"
Существительное -> "Mann" | "Frau" | "Kind" | "Buch" | "Brief" |
"Freund" | "Abendessen" | "Suppe"
Предлог -> "in" | "auf" | "unter" | "aus" | "mit" | "für" |
"zu" | "am"
ОбстоятельствоВремени -> "gestern" | "heute" | "morgen" | "damals" |
"jetzt" | "früh" | "spät" | "immer"
ОбстоятельствоОбразаДействия -> "schnell" | "langsam" | "gut" |
"schlecht" | "laut" | "leise" |
"gern" | "fleißig"
Глагол -> "las" | "schrieb" | "kochte" | "aß" | "ging" |
"kam" | "sagte" | "machte"
\end{verbatim}
\subsection{БНФ-нотация}
% \begin{verbatim}
% Предложение ::= ПрямойПорядок | Инверсия
% ПрямойПорядок ::= Подлежащее [ПридаточноеПредложение]
% Глагол {ВторостепенныйЧлен} [Отрицание]
% ПридаточноеПредложение ::= ", " Союз Подлежащее [ПридаточноеПредложение]
% Глагол [Отрицание] ", "
% Инверсия ::= Обстоятельство Глагол Подлежащее
% [ПридаточноеПредложение] {ВторостепенныйЧлен} [Отрицание]
% Обстоятельство ::= ОбстоятельствоВремени |
% ОбстоятельствоМеста |
% ОбстоятельствоОбразаДействия
% ВторостепенныйЧлен ::= Обстоятельство |
% КосвенноеДополнение |
% ПрямоеДополнение
% Подлежащее ::= Местоимение | ИменнаяГруппа
% ИменнаяГруппа ::= [Артикль | ПритяжательноеМестоимение]
% {Прилагательное} Существительное
% [ДополнениеКПодлежащему]
% ДополнениеКПодлежащему ::= Предлог Существительное
% КосвенноеДополнение ::= Предлог ИменнаяГруппа
% ПрямоеДополнение ::= [Артикль | ПритяжательноеМестоимение]
% {Прилагательное} Существительное
% ОбстоятельствоМеста ::= Предлог [Артикль | ПритяжательноеМестоимение]
% Существительное
% Союз ::= "welcher" (которого) | "welche" (которую)
% | "welches" (которого, ср. р)
% Отрицание ::= "nicht"
% Местоимение ::= "ich" (я) | "du" (ты) | "er" (он) | "sie" (она/они) |
% "es" (оно) | "wir" (мы) | "ihr" (вы) | "Sie" (Вы)
% ПритяжательноеМестоимение ::= "mein" (мой) | "dein" (твой) |
% "sein" (его) | "ihr" (её) | "unser" (наш) |
% "euer" (ваш) | "ihre" (их)
% Артикль ::= "der" | "die" | "das" | "ein" |
% "eine" | "einen" | "einem" | "einer"
% Прилагательное ::= "alt" (старый) | "jung" (молодой) |
% "groß" (большой) | "klein" (маленький) | "gut" (хороший) |
% "schlecht" (плохой) | "schnell" (быстрый) | "langsam" (медленный)
% Существительное ::= "Mann" (мужчина) | "Frau" (женщина) |
% "Kind" (ребёнок) | "Buch" (книга) | "Brief" (письмо) |
% "Freund" (друг) | "Abendessen" (ужин) | "Suppe" (суп)
% Предлог ::= "in" (в) | "auf" (на) | "unter" (под) | "aus" (из) |
% "mit" (с) | "für" (для) | "zu" (к) | "am" (на/в)
% ОбстоятельствоВремени ::= "gestern" (вчера) | "heute" (сегодня) |
% "morgen" (завтра) | "damals" (тогда) |
% "jetzt" (сейчас) | "früh" (рано) |
% "spät" (поздно) | "immer" (всегда)
% ОбстоятельствоОбразаДействия ::= "schnell" (быстро) |
% "langsam" (медленно) |
% "gut" (хорошо) | "schlecht" (плохо) |
% "laut" (громко) | "leise" (тихо) |
% "gern" (охотно) | "fleißig" (прилежно)
% Глагол ::= "las" (читал) | "schrieb" (писал) |
% "kochte" (готовил) | "aß" (ел) |
% "ging" (шёл) | "kam" (пришёл) |
% "sagte" (сказал) | "machte" (делал)
% Инфинитив ::= "lesen" (читать) | "schreiben" (писать) |
% "kochen" (готовить) | "essen" (есть) |
% "gehen" (идти) | "kommen" (приходить) |
% "sagen" (говорить) | "machen" (делать)
% Причастие ::= "gelesen" (прочитанный) | "geschrieben" (написанный) |
% "gekocht" (приготовленный) | "gegessen" (съеденный) |
% "gegangen" (ушедший) | "gekommen" (пришедший) |
% "gesagt" (сказанный) | "gemacht" (сделанный)
% \end{verbatim}
\subsection{Примеры предложений}
Ich las gestern ein altes Buch
Der alte Mann las ein Buch
\subsection{Примеры предложений}
\textbf{Пример 1: Der alte Mann las ein Buch.} (Старый мужчина читал книгу.)
\begin{tabularx}{\textwidth}{|l|l|l|X|}
\hline
Позиция & Компонент & Элемент & Разбор по БНФ \\
\hline
1 & Подлежащее & der alte Mann & ИменнаяГруппа → Артикль + Прилагательное + Существительное \\
\hline
2 & Сказуемое & las & Глагол \\
\hline
3 & Второстепенные члены & ein Buch & ПрямоеДополнение → Артикль + Существительное \\
\hline
\end{tabularx}
\vspace{0.5cm}
\textbf{Пример 2: Gestern las der alte Mann ein Buch.} (Вчера старый мужчина читал книгу.)
\begin{tabularx}{\textwidth}{|l|l|l|X|}
\hline
Позиция & Компонент & Элемент & Разбор по БНФ \\
\hline
1 & Обстоятельство & Gestern & ОбстоятельствоВремени → НаречиеВремени \\
\hline
2 & Сказуемое & las & Глагол \\
\hline
3 & Подлежащее & der alte Mann & ИменнаяГруппа → Артикль + Прилагательное + Существительное \\
\hline
4 & Второстепенные члены & ein Buch & ПрямоеДополнение → Артикль + Существительное \\
\hline
\end{tabularx}
\vspace{0.5cm}
\textbf{Пример 3: Mein Freund aus Berlin schrieb mir gestern einen Brief.}
(Мой друг из Берлина написал мне вчера письмо.)
\begin{tabularx}{\textwidth}{|l|l|l|X|}
\hline
Позиция & Компонент & Элемент & Разбор по БНФ \\
\hline
1 & Подлежащее & Mein Freund aus Berlin & ИменнаяГруппа → ПритяжательноеМестоимение + Существительное + ДополнениеКПодлежащему (Предлог + Существительное) \\
\hline
2 & Сказуемое & schrieb & ПростойГлагол ВПретерите \\
\hline
3 & Второстепенные члены & mir & Косвенное Дополнение → Местоимение \\
\hline
4 & Второстепенные члены & gestern & Обстоятельство Времени → НаречиеВремени \\
\hline
5 & Второстепенные члены & einen Brief & ПрямоеДополнение → Артикль + Существительное \\
\hline
\end{tabularx}
\vspace{0.5cm}
\textbf{Пример 4: Am Abend kochte sie schnell das Abendessen in der Küche.}
(Вечером она быстро приготовила ужин на кухне.)
\begin{tabularx}{\textwidth}{|l|l|l|X|}
\hline
Позиция & Компонент & Элемент & Разбор по БНФ \\
\hline
1 & Обстоятельство & Am Abend & ОбстоятельствоВремени → Предлог + Существительное \\
\hline
2 & Сказуемое & kochte & Глагол \\
\hline
3 & Подлежащее & sie & Местоимение \\
\hline
4 & Второстепенные члены & schnell & ОбстоятельствоОбразаДействия \\
\hline
5 & Второстепенные члены & das Abendessen & ПрямоеДополнение → Артикль + Существительное \\
\hline
6 & Второстепенные члены & in der Küche & ОбстоятельствоМеста → Предлог + Артикль + Существительное \\
\hline
\end{tabularx}
\vspace{0.5cm}
\textbf{Пример 5: Er wollte ein Buch lesen.} (Он хотел прочитать книгу.)
\begin{tabularx}{\textwidth}{|l|l|l|X|}
\hline
Позиция & Компонент & Элемент & Разбор по БНФ \\
\hline
1 & Подлежащее & Er & Местоимение \\
\hline
2 & Сказуемое & wollte lesen & СоставноеСказуемое → МодальныйГлаголВПретерите + Инфинитив \\
\hline
3 & Второстепенные члены & ein Buch & ПрямоеДополнение → Артикль + Существительное \\
\hline
\end{tabularx}
\newpage
\section{Особенности реализации}
\newpage
\section{Результаты работы программы}
% Результаты работы программы представлены на Рис.~\ref{fig:result1}.
% \begin{figure}[h!]
% \centering
% \includegraphics[width=1\linewidth]{img/result1.png}
% \caption{Результаты работы программы.}
% \label{fig:result1}
% \end{figure}
% \newpage
% \begin{figure}[h!]
% \centering
% \includegraphics[width=0.8\linewidth]{img/wrong.png}
% \caption{Реакция программы на некорректный пользовательский ввод.}
% \label{fig:wrong}
% \end{figure}
% На Рис.~\ref{fig:wrong} представлена реакция программы на некорректный пользовательский ввод.
\newpage
\section*{Заключение}
\addcontentsline{toc}{section}{Заключение}
В ходе выполнения лабораторной работы было построено регулярное выражение для распознавания различных форматов вещественных чисел. В соответствии с теоремой Клини по заданному регулярному выражению, задающему регулярный
язык, был построен недетерминированный конечный автомат-распознаватель. Затем полученный конечный автомат был детерминирован. На основе разработанного автомата была реализована программа, которая проверяет соответствие входной строки заданному формату и генерирует случайные корректные строки.
Из достоинств выполнения лабораторной работы можно выделить структурирование кода за счёт использования ООП. Вся логика работы с конечными автоматами вынесена в отдельный класс \texttt{FiniteAutomaton} с четко разделенными методами для проверки строк и генерации случайных строк. Создана удобная интерактивная консольная оболочка для взаимодействия с пользователем, позволяющая выполнять различные команды.
К недостаткам текущей реализации можно отнести следующие аспекты. Во-первых, переходы в автомате представлены в виде строк, содержащих допустимые символы, такой способ представления переходов не является самым оптимальным с точки зрения производительности. Во-вторых, в реализации генерации случайных строк вероятность остановки одинакова для всех финальных состояний, что может приводить к неравномерному распределению различных форматов чисел в генерируемых строках.
Функционал программы несложно масштабировать. Класс \texttt{FiniteAutomaton} может быть использован для работы с различными конечными автоматами-распознавателями. Для изменения распознаваемого языка достаточно задать новые параметры для автомата, не меняя базовую логику программы. Однако, текущая реализация работает только с символьными переходами, поэтому задать строчные переходы в виде, например, регулярных выражений не представляется возможным. Однако подобный функционал также несложно реализовать, взяв за основу существующий код.
На выполнение лабораторной работы ушло около 10 часов. Работа была выполнена в среде разработки Visual Studio Code. Программа написана на Python версии 3.10.
\newpage
\section*{Список литературы}
\addcontentsline{toc}{section}{Список литературы}
\vspace{-1.5cm}
\begin{thebibliography}{0}
\bibitem{vostrov}
Востров, А.В. Курс лекций по дисциплине <<Математическая логика>>. URL \url{https://tema.spbstu.ru/compiler/} (дата обращения 01.04.2025 г.)
\bibitem{lutz}
Лутц, М. Изучаем Python. 5-е изд. / М. Лутц. — СПб.: Питер, 2019. — 1216 с.
\bibitem{friedl}
Фридл, Дж. Регулярные выражения = Mastering Regular Expressions / Дж. Фридл. — СПб.: Питер, 2001. — 352 с. — (Библиотека программиста).
\end{thebibliography}
\end{document}