From a60d2a9db7a1ff775ae978012c8ae6044c9276d0 Mon Sep 17 00:00:00 2001 From: Arity-T Date: Wed, 14 May 2025 21:39:37 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9E=D1=82=D1=87=D1=91=D1=82=20=D0=BF=D0=BE?= =?UTF-8?q?=20lab3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lab3/img/result1.png | Bin 0 -> 71173 bytes lab3/img/wrong.png | Bin 0 -> 9216 bytes lab3/programm/grammar.txt | 2 +- lab3/report.tex | 951 +++++++++++++++++++++++++++++--------- 4 files changed, 745 insertions(+), 208 deletions(-) create mode 100644 lab3/img/result1.png create mode 100644 lab3/img/wrong.png diff --git a/lab3/img/result1.png b/lab3/img/result1.png new file mode 100644 index 0000000000000000000000000000000000000000..3ed3e8c17d260fc42a7ed3658fe43ea1e5c33888 GIT binary patch literal 71173 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

MtfkIX@4$Ru_Iwrc> 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 "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" \ No newline at end of file +Глагол -> "las" | "schrieb" | "kochte" | "aß" | "ging" | "kam" | "sagte" | "machte" | "liebte" \ No newline at end of file diff --git a/lab3/report.tex b/lab3/report.tex index 113645b..b896c9f 100644 --- a/lab3/report.tex +++ b/lab3/report.tex @@ -149,25 +149,38 @@ \section*{Введение} \addcontentsline{toc}{section}{Введение} - Лабораторная №3 по дисциплине <<Математическая логика>> заключается в построении контексно-свободной грамматики подмножества естественного языка. В данном варианте рассматривается грамматика простого прошедшего времени немецкого языка. + Лабораторная №3 заключается в построении контексно-свободной грамматики подмножества естественного языка и написании программы для генерации предложений языка и проверки предложений на принадлежность языку. В данном варианте рассматривается грамматика простого прошедшего времени немецкого языка. - Простое прошедшее время в немецком языке называется \textit{Претерит} или \textit{Претеритум} (нем. \textit{Präteritum} или нем. \textit{Imperfekt}). \textit{Претеритум} служит для передачи действия в прошлом. Это время не требует образования сложных конструкций, что позволяет относить его к простым формам. - + Простое прошедшее время в немецком языке называется \textit{Претерит} или \textit{Претеритум} (нем. \textit{Präteritum} или нем. \textit{Imperfekt}). Это время не требует образования сложных конструкций, что позволяет относить его к простым формам. \newpage \section {Математическое описание} - \subsection{КС-грамматика} + \subsection{Особенности грамматики} + Грамматика, рассматриваемая в рамках данной лабораторной работы, имеет следующие особенности: + \begin{itemize} + \item Рассматриваются только повествовательные и повествовательные отрицательные предложения. + \item Учтены два возможных порядка членов предложения: прямой и обратный. В прямом порядке сначала идет подлежащее, потом сказуемое, а затем второстепенные члены предложения. В обратном порядке на первом месте стоит обстоятельство, на втором сказуемое, на третьем подлежащее, а затем второстепенные члены предложения. В \textit{Претеритуме} сказуемое всегда стоит на втором месте. + \item Придаточные предложения могут относится только к подлежащему и начинаться с союзов \textit{welcher} (который), \textit{welche} (которая), \textit{welches} (которое). При этом вложенность придаточных предложений не ограничена. + \item Порядок второстепеннных членов не фиксирован. Несмотря на то, что в немецком языке существует предпочтительный порядок слов, в данной грамматике он не учитывается, потому что порядок не явлется строгим и реальные предложения языка далеко не всегда ему соответствуют. + \item Рассматриваются только простые сказуемые, не содержащие вспомогательных глаголов. + \item Рассматриваются три вида обстоятельств: обстоятельства времени, места и образа действия. В реальных предложениях языка могут встречаться и другие виды, но перечисленные являются наиболее часто встречающимися. + \end{itemize} + + \subsection{Грамматика в виде списка продукций} + \label{subsec:grammar} + + Формально можно описать грамматику в виде списка продукций: \begin{verbatim} - Предложение -> ПрямойПорядок | Инверсия + Предложение -> Повествовательное "." + Повествовательное -> ПрямойПорядок | Инверсия ПрямойПорядок -> Подлежащее ДополнениеКПодлежащему Глагол ВторостепенныеЧлены Отрицание Инверсия -> Обстоятельство Глагол Подлежащее ВторостепенныеЧлены Отрицание Подлежащее -> ИменнаяГруппа ПридаточноеПредложение | Местоимение - ПридаточноеПредложение -> ", " Союз Подлежащее Глагол - ВторостепенныеЧлены Отрицание ", " | + ПридаточноеПредложение -> "," Союз Подлежащее Глагол Отрицание "," | epsilon ВторостепенныеЧлены -> ВторостепенныйЧлен ВторостепенныеЧлены | epsilon @@ -205,245 +218,771 @@ "kam" | "sagte" | "machte" \end{verbatim} - \subsection{БНФ-нотация} + Терминальными являются все символы в кавычках, все остальные символы являются нетерминальными. - % \begin{verbatim} - % Предложение ::= ПрямойПорядок | Инверсия - - % ПрямойПорядок ::= Подлежащее [ПридаточноеПредложение] - % Глагол {ВторостепенныйЧлен} [Отрицание] + Язык, пораждаемый данной грамматикой, как и сама грамматика, является контекстно-свободным, так как: + \begin{itemize} + \item В левых частях всех продукций присутствует только один нетерминальный символ, а значит язык не является контекстно-зависимым. + \item Язык не является автоматным из-за наличия придаточных предложений неограниченной вложенности. Придаточные предложения являются аналогом скобочной структуры, которая не может быть описана конечным автоматом. + \end{itemize} - % ПридаточноеПредложение ::= ", " Союз Подлежащее [ПридаточноеПредложение] - % Глагол [Отрицание] ", " - - % Инверсия ::= Обстоятельство Глагол Подлежащее - % [ПридаточноеПредложение] {ВторостепенныйЧлен} [Отрицание] - - % Обстоятельство ::= ОбстоятельствоВремени | - % ОбстоятельствоМеста | - % ОбстоятельствоОбразаДействия - - % ВторостепенныйЧлен ::= Обстоятельство | - % КосвенноеДополнение | - % ПрямоеДополнение - - % Подлежащее ::= Местоимение | ИменнаяГруппа - - % ИменнаяГруппа ::= [Артикль | ПритяжательноеМестоимение] - % {Прилагательное} Существительное - % [ДополнениеКПодлежащему] - - % ДополнениеКПодлежащему ::= Предлог Существительное - - % КосвенноеДополнение ::= Предлог ИменнаяГруппа - - % ПрямоеДополнение ::= [Артикль | ПритяжательноеМестоимение] - % {Прилагательное} Существительное - - % ОбстоятельствоМеста ::= Предлог [Артикль | ПритяжательноеМестоимение] - % Существительное - - % Союз ::= "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 - - + Также эта грамматика является LL(1)-грамматикой. Множества FIRST и FOLLOW для всех нетерминальных символов не пересекаются. В дальнейшем это особенность используется для распознавания предложений языка. + \subsection{Грамматика в БНФ} - \subsection{Примеры предложений} + \vspace{-0.2cm} + Грамматика в БНФ имеет следующий вид: + + \vspace{-0.5cm} + \begin{verbatim} + <Предложение> ::= <Повествовательное> "." + <Повествовательное> ::= <ПрямойПорядок> | <Инверсия> + <ПрямойПорядок> ::= <Подлежащее> [ДополнениеКПодлежащему] <Глагол> + {ВторостепенныйЧлен} [Отрицание] + <Инверсия> ::= <Обстоятельство> <Глагол> <Подлежащее> + {ВторостепенныйЧлен} [Отрицание] + <Подлежащее> ::= <ИменнаяГруппа> [ПридаточноеПредложение] | Местоимение + <ПридаточноеПредложение> ::= "," <Союз> <Подлежащее> <Глагол> [Отрицание] "," + <ВторостепенныйЧлен> ::= <Обстоятельство> | <Дополнение> + <Обстоятельство> ::= <ОбстоятельствоВремени> | <ОбстоятельствоМеста> | + <ОбстоятельствоОбразаДействия> + <ИменнаяГруппа> ::= [Артикль | ПритяжательноеМестоимение] + {Прилагательное} <Существительное> + <ДополнениеКПодлежащему> ::= <Предлог> <Существительное> + <Дополнение> ::= [Артикль | ПритяжательноеМестоимение] + {Прилагательное} <Существительное> + <ОбстоятельствоМеста> ::= <Предлог> + [Артикль | ПритяжательноеМестоимение] <Существительное> + <Союз> ::= "welcher" | "welche" | "welches" + <Отрицание> ::= "nicht" + <Местоимение> ::= "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" | "liebte" + \end{verbatim} - \textbf{Пример 1: Der alte Mann las ein Buch.} (Старый мужчина читал книгу.) + \subsection{Пример вывода предложения} + На Рис.~1 представлено дерево вывода предложения \textit{<>} + + \addtocounter{figure}{1} + \includepdf[pages={1}, fitpaper, pagecommand={ + \thispagestyle{empty} + \begin{tikzpicture}[remember picture, overlay] + \node at (current page.south) [anchor=north, yshift=45pt] {\large{Рис 1. Пример дерева вывода предложения.}}; + \end{tikzpicture} + }]{pdf/tree.pdf} + + \subsection{Алгоритм построения LL(1) таблицы синтаксического анализа} + Чтобы заполнить таблицу синтаксического анализа, необходимо установить, какое правило грамматики синтаксический анализатор должен выбрать, если нетерминальное $A$ находится на вершине его стека и символ $a$ — во входном потоке. Легко заметить, что такое правило должно иметь форму $A \rightarrow w$ и что у языка, соответствующего $w$, должна быть по крайней мере одна строка, начинающаяся с $a$. + + С этой целью мы определяем \textbf{множество \texttt{FIRST()} для $w$}, обозначенное как $\texttt{FIRST}(w)$, как множество терминалов, которые могут быть найдены в начале любой строки в $w$, и $\varepsilon$, если пустая строка также принадлежит $w$. - \begin{tabularx}{\textwidth}{|l|l|l|X|} - \hline - Позиция & Компонент & Элемент & Разбор по БНФ \\ - \hline - 1 & Подлежащее & der alte Mann & ИменнаяГруппа → Артикль + Прилагательное + Существительное \\ - \hline - 2 & Сказуемое & las & Глагол \\ - \hline - 3 & Второстепенные члены & ein Buch & ПрямоеДополнение → Артикль + Существительное \\ - \hline - \end{tabularx} + Учитывая грамматику с правилами $A_1 \rightarrow w_1, \dots, A_n \rightarrow w_n$, можно вычислить $\texttt{FIRST}(w_i)$ и $\texttt{FIRST}(A_i)$ для каждого правила следующим образом: - \vspace{0.5cm} + \begin{enumerate} + \item Инициализировать каждое множество $\texttt{FIRST}(A_i)$ пустым множеством. + \item Добавить $\texttt{FIRST}(w_i)$ к $\texttt{FIRST}(A_i)$ для каждого правила $A_i \rightarrow w_i$, где $\texttt{FIRST}(w_i)$ определяется следующим образом: + \begin{itemize} + \item $\texttt{FIRST}(a w') = \{ a \}$ для каждого терминала $a$ + \item $\texttt{FIRST}(A w') = \texttt{FIRST}(A)$ для каждого нетерминального $A$ с $\varepsilon \notin \texttt{FIRST}(A)$ + \item $\texttt{FIRST}(A w') = \left( \texttt{FIRST}(A) \setminus \{ \varepsilon \} \right) \cup \texttt{FIRST}(w')$ для каждого нетерминального $A$ с $\varepsilon \in \texttt{FIRST}(A)$, включая случай $A_i \rightarrow A$, то есть $w' = \varepsilon$, в этом случае $\texttt{FIRST}(A w') = \texttt{FIRST}(A)$ + \item $\texttt{FIRST}(\varepsilon) = \{ \varepsilon \}$ + \end{itemize} + \item Повторять шаг 2, пока в множествах $\texttt{FIRST}$ происходят изменения. + \end{enumerate} - \textbf{Пример 2: Gestern las der alte Mann ein Buch.} (Вчера старый мужчина читал книгу.) + Однако множеств \texttt{FIRST()} недостаточно, чтобы построить таблицу синтаксического анализа. Так происходит, потому что правая сторона $w$ правила могла бы в конечном счёте быть приведена к пустой строке. Таким образом синтаксический анализатор должен также использовать правило $A \rightarrow w$, если $\varepsilon \in \texttt{FIRST}(w)$ и во входном потоке символ, который может следовать за $A$. Поэтому также необходимо построить \textbf{множество \texttt{FOLLOW()} для $A$}, которое определяется как множество терминалов $a$, таких что существует строка символов $\alpha A a \beta$, которая может быть получена из начального символа. - \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} + Вычисление множеств \texttt{FOLLOW()} для нетерминалов в грамматике выполняется следующим образом: - \vspace{0.5cm} + \begin{enumerate} + \item Инициализировать $\texttt{FOLLOW}(S) = \{ \$ \}$, а все остальные множества $\texttt{FOLLOW}(A_i)$ пустыми. + \item Если есть правило формы $A_j \rightarrow w A_i w'$, тогда: + \begin{itemize} + \item Если терминал $a \in \texttt{FIRST}(w')$, то добавить $a$ в $\texttt{FOLLOW}(A_i)$ + \item Если $\varepsilon \in \texttt{FIRST}(w')$, то добавить $\texttt{FOLLOW}(A_j)$ в $\texttt{FOLLOW}(A_i)$ + \item Если $w'$ имеет длину 0, то добавить $\texttt{FOLLOW}(A_j)$ в $\texttt{FOLLOW}(A_i)$ + \end{itemize} + \item Повторять шаг 2, пока в множествах \texttt{FOLLOW()} происходят изменения. + \end{enumerate} - \textbf{Пример 3: Mein Freund aus Berlin schrieb mir gestern einen Brief.} + Теперь можно точно определить, какие правила будут содержаться в таблице синтаксического анализа. Если $T[A, a]$ обозначает ячейку в таблице для нетерминального $A$ и терминала $a$, то: - (Мой друг из Берлина написал мне вчера письмо.) + \begin{itemize} + \item $T[A,a]$ содержит правило $A \rightarrow w$ тогда и только тогда, когда: + \begin{itemize} + \item $a \in \texttt{FIRST}(w)$ при проходе правила $A \rightarrow w$, или + \item $\varepsilon \in \texttt{FIRST}(w)$ и $a \in \texttt{FOLLOW}(A)$ + \end{itemize} + \end{itemize} - \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} + Если таблица будет содержать не более одного правила в каждой ячейке, то синтаксический анализатор сможет однозначно определить, какое правило необходимо применить на каждом шаге разбора. В этом случае грамматику является \textbf{LL(1)} грамматикой. + + \subsection{Алгоритм разбора предложений} + + Перед запуском алгоритма разбора в стек помещаются два символа: + \begin{itemize} + \item Специальный символ~\$ — признак конца входной цепочки. + \item Начальный символ грамматики (на вершину стека). + \end{itemize} + + Cинтаксический анализатор выполняет три различных вида действий в зависимости от того, находится ли на вершине стека нетерминал, терминал или специальный символ~\$. - \vspace{0.5cm} + \begin{enumerate} + \item \textbf{Если на вершине стека — нетерминал}, то в таблице синтаксического анализа ищется правило грамматики, находящееся на пересечении строки и столбца, соответствующих этому нетерминалу на вершине стека и текущему входному символу. Если же в указанной ячейке таблицы правило отсутствует — синтаксический анализатор останавливается и сообщает об ошибке. - \textbf{Пример 4: Am Abend kochte sie schnell das Abendessen in der Küche.} + \item \textbf{Если на вершине стека — терминал}, то синтаксический анализатор сравнивает его с текущим входным символом. Если они равны, то входной символ считывается, а соответствующий символ из вершины стека — удаляется. - (Вечером она быстро приготовила ужин на кухне.) + \item \textbf{Если на вершине стека — специальный символ~\$}, и текущий символ на ленте также равен~\$, то синтаксический анализатор сообщает об успешном распознавании цепочки. В противном случае — сообщает об ошибке. В обоих случаях происходит останов анализатора. + \end{enumerate} - \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} + \subsection{Алгоритм генерации предложения} + + Генерация предложения происходит следующим образом: + \begin{enumerate} + \item В список помещается начальный символ грамматики. + \item Осуществляется проход по списку. Первый встреченный нетерминал заменяется на его случайно выбранную продукцию, после чего проход начинается заново. + \item Алгоритм завершается, когда в списке не осталось нетерминалов. + \end{enumerate} + + Этот алгоритм не позволяет контролировать длину предложения. Также алгоритм может не сходиться в общем случае. Но вероятность того, что алгоритм попадет в петлю убывает геометрически с ростом длины цепочки, так что на практике этот метод работает. \newpage \section{Особенности реализации} + \subsection{Задание правил грамматики} + + Грамматика задаётся в виде текстового файла, в котором каждая строка соответствует одной продукции. Синтаксис задания продукций соответствует общепринятому, за исключением того, что вместо $\varepsilon$ используется слово <<\texttt{epsilon}>>. Терминалы записываются в кавычках, нетерминалы — без кавычек. Левая и правая части продукции разделяются двумя символами -- <<\texttt{->}>>. Таким образом содержимое файла, соответствует тексту описания грамматики в разделе \ref{subsec:grammar}. + + + \subsection{Класс \texttt{Grammar}} + + Класс \texttt{Grammar} содержит в себе всю информацию о грамматике, а также методы для работы с ней. Код конструктора класса представлен в листинге \ref{lst:grammar_constructor}. Конструктор класса принимает на вход строку, содержащую описание грамматики. Вызывает методы для парсинга продукций, нахождения терминалов, вычисления множеств FIRST и FOLLOW, заполнения таблицы синтаксического анализа. + +\begin{lstlisting}[caption={Код конструктора класса \texttt{Grammar}.}, label={lst:grammar_constructor}] +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 +\end{lstlisting} + + + \subsubsection{Метод \texttt{\_parse\_productions}} + Метод \texttt{\_parse\_productions} выполняет разбор текстового описания грамматики и создаёт внутреннее представление продукций. Код метода представлен в листинге~\ref{lst:parse_productions}. + + Метод принимает ссылку на объект класса Grammar и строку text типа str, содержащую текстовое представление грамматики. + + Метод не возвращает значений, но заполняет словарь productions и устанавливает переменную start\_symbol в объекте класса. + +\begin{lstlisting}[caption={Код метода \texttt{\_parse\_productions}.}, label={lst:parse_productions}] +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) +\end{lstlisting} + + \subsubsection{Метод \texttt{\_calculate\_first\_sets}} + + Метод \texttt{\_calculate\_first\_sets} вычисляет множества FIRST для всех символов грамматики. Код метода представлен в листинге~\ref{lst:calculate_first_sets}. + + Метод принимает ссылку на объект класса Grammar. + + Метод не возвращает значений, но заполняет словарь first\_sets в объекте класса. + +\begin{lstlisting}[caption={Код метода \texttt{\_calculate\_first\_sets}.}, label={lst:calculate_first_sets}] +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 +\end{lstlisting} + + \subsubsection{Метод \texttt{\_calculate\_follow\_sets}} + + Метод \texttt{\_calculate\_follow\_sets} вычисляет множества FOLLOW для нетерминальных символов грамматики. Код метода представлен в листинге~\ref{lst:calculate_follow_sets}. + + Метод принимает ссылку на объект класса Grammar. + + Метод не возвращает значений, но заполняет словарь follow\_sets в объекте класса. + +\begin{lstlisting}[caption={Код метода \texttt{\_calculate\_follow\_sets}.}, label={lst:calculate_follow_sets}] +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 + + # Если epsilon находится в 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 +\end{lstlisting} + + \subsubsection{Метод \texttt{\_first\_of\_sequence}} + + Метод \texttt{\_first\_of\_sequence} вычисляет множество FIRST для последовательности символов. Код метода представлен в листинге~\ref{lst:first_of_sequence}. + + Метод принимает ссылку на объект класса Grammar и список символов sequence. + + Метод возвращает множество (set) строк, представляющих FIRST для заданной последовательности. + +\begin{lstlisting}[caption={Код метода \texttt{\_first\_of\_sequence}.}, label={lst:first_of_sequence}] +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 +\end{lstlisting} + + \subsubsection{Метод \texttt{\_fill\_lookup\_table}} + + Метод \texttt{\_fill\_lookup\_table} заполняет таблицу синтаксического анализа для LL(1)-грамматики. Код метода представлен в листинге~\ref{lst:fill_lookup_table}. + + Метод принимает ссылку на объект класса Grammar. + + Метод не возвращает значений, но заполняет словарь lookup\_table в объекте класса или вызывает исключение, если грамматика не является LL(1). + +\begin{lstlisting}[caption={Код метода \texttt{\_fill\_lookup\_table}.}, label={lst:fill_lookup_table}] +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 +\end{lstlisting} + + \subsubsection{Метод \texttt{format\_rules}} + + Метод \texttt{format\_rules} форматирует все правила грамматики для удобного представления. Код метода представлен в листинге~\ref{lst:format_rules}. + + Метод принимает ссылку на объект класса Grammar. + + Метод возвращает строку (str), содержащую все правила грамматики с их номерами. + +\begin{lstlisting}[caption={Код метода \texttt{format\_rules}.}, label={lst:format_rules}] +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) +\end{lstlisting} + + \subsubsection{Метод \texttt{format\_lookup\_table}} + + Метод \texttt{format\_lookup\_table} форматирует таблицу синтаксического анализа для удобного представления. Код метода представлен в листинге~\ref{lst:format_lookup_table}. + + Метод принимает ссылку на объект класса Grammar. + + Метод возвращает строку (str), содержащую таблицу синтаксического анализа в виде таблицы с использованием библиотеки PrettyTable. + +\begin{lstlisting}[caption={Код метода \texttt{format\_lookup\_table}.}, label={lst:format_lookup_table}] +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) +\end{lstlisting} + + \subsubsection{Метод \texttt{format\_first\_sets}} + + Метод \texttt{format\_first\_sets} форматирует множества FIRST для удобного представления. Код метода представлен в листинге~\ref{lst:format_first_sets}. + + Метод принимает ссылку на объект класса Grammar. + + Метод возвращает строку (str), содержащую множества FIRST для всех символов грамматики. + +\begin{lstlisting}[caption={Код метода \texttt{format\_first\_sets}.}, label={lst:format_first_sets}] +def format_first_sets(self) -> str: + """Форматирует множества FIRST в читаемый вид.""" + result = [] + result.append("Множества FIRST:") + result.append("=" * 40) + + # Сортируем для гарантии порядка вывода + for symbol in sorted(self.first_sets.keys()): + # Заменяем пустую строку на эпсилон для лучшей читаемости + first_set = { + self.EPSILON if item == "" else item for item in self.first_sets[symbol] + } + result.append(f"FIRST({symbol}) = {{{', '.join(sorted(first_set))}}}") + + return "\n".join(result) +\end{lstlisting} + + \subsubsection{Метод \texttt{format\_follow\_sets}} + + Метод \texttt{format\_follow\_sets} форматирует множества FOLLOW для удобного представления. Код метода представлен в листинге~\ref{lst:format_follow_sets}. + + Метод принимает ссылку на объект класса Grammar. + + Метод возвращает строку (str), содержащую множества FOLLOW для всех нетерминалов грамматики. + +\begin{lstlisting}[caption={Код метода \texttt{format\_follow\_sets}.}, label={lst:format_follow_sets}] +def format_follow_sets(self) -> str: + """Форматирует множества FOLLOW в читаемый вид.""" + result = [] + result.append("Множества FOLLOW:") + result.append("=" * 40) + + # Обрабатываем только нетерминалы + for non_terminal in sorted(self.productions.keys()): + follow_set = self.follow_sets.get(non_terminal, set()) + result.append( + f"FOLLOW({non_terminal}) = {{{', '.join(sorted(follow_set))}}}" + ) + + return "\n".join(result) +\end{lstlisting} + + \subsubsection{Метод \texttt{analyze}} + + Метод \texttt{analyze} выполняет синтаксический анализ входной последовательности токенов с использованием LL(1)-алгоритма. Код метода представлен в листинге~\ref{lst:analyze}. + + Метод принимает ссылку на объект класса Grammar и список input\_tokens типа list[str], представляющий последовательность входных токенов. + + Метод возвращает список (list) целых чисел, содержащий номера применённых правил в процессе анализа. + +\begin{lstlisting}[caption={Код метода \texttt{analyze}.}, label={lst:analyze}] +def analyze(self, input_tokens: list[str]) -> list[int]: + input_tokens = input_tokens.copy() + input_tokens += ["$"] + 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 +\end{lstlisting} + + \subsubsection{Метод \texttt{generate}} + + Метод \texttt{generate} генерирует случайное предложение по заданной грамматике. Код метода представлен в листинге~\ref{lst:generate}. + + Метод принимает ссылку на объект класса Grammar и необязательный параметр symbol типа str или None, указывающий начальный символ для генерации. + + Метод возвращает кортеж (tuple), содержащий список терминалов и список номеров применённых правил. + +\begin{lstlisting}[caption={Код метода \texttt{generate}.}, label={lst:generate}] +def generate(self, symbol: str | None = None) -> tuple[list[str], list[int]]: + """Генерирует предложение по заданной грамматике. Возвращает список терминалов + и список номеров применённых правил.""" + + if symbol is None: + return self.generate(self.start_symbol) + + # Если символ - терминал, возвращаем его + if symbol not in self.productions: + return [symbol], [] + + # Выбираем случайное правило для нетерминала + rules = self.productions[symbol] + chosen_rule = random.choice(rules) + + # Получаем номер выбранного правила + rule_number = self.rule_numbers[(symbol, tuple(chosen_rule))] + + # Инициализируем результаты + terminals = [] + rule_numbers = [rule_number] + + # Разворачиваем каждый символ в правой части правила + for s in chosen_rule: + sub_terminals, sub_rules = self.generate(s) + terminals.extend(sub_terminals) + rule_numbers.extend(sub_rules) + + return terminals, rule_numbers +\end{lstlisting} + + \subsubsection{Метод \texttt{generate\_derivation\_steps}} + + Метод \texttt{generate\_derivation\_steps} преобразует список номеров правил в последовательность шагов вывода. Код метода представлен в листинге~\ref{lst:generate_derivation_steps}. + + Метод принимает ссылку на объект класса Grammar и список rule\_numbers типа list[int], содержащий номера правил для применения. + + Метод возвращает список (list) строк, представляющий каждый шаг вывода предложения. + +\begin{lstlisting}[caption={Код метода \texttt{generate\_derivation\_steps}.}, label={lst:generate_derivation_steps}] +def generate_derivation_steps(self, rule_numbers: list[int]) -> list[str]: + """Преобразует список номеров правил в последовательность шагов вывода. + Возвращает список строк, представляющих каждый шаг вывода.""" + + # Получаем соответствие между номерами правил и самими правилами + rule_details = {num: rule for rule, num in self.rule_numbers.items()} + + # Начинаем с начального символа + current = self.start_symbol + steps = [current] + + # Применяем каждое правило по порядку + for rule_num in rule_numbers: + if rule_num in rule_details: + 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) + steps.append(current) + + return steps +\end{lstlisting} + + \subsection{Функция \texttt{main}} + +Функция \texttt{main} реализует интерактивную консольную оболочку для взаимодействия с пользователем. Код функции представлен в листинге~\ref{lst:main}. + +\begin{lstlisting}[caption={Код функции \texttt{main}.}, label={lst:main}] +def main(): + print("Программа для работы с LL(1)-грамматиками") + print("=" * 60) + print("Варианты команд:") + print(" - load <файл> - загрузить грамматику из файла (по умолчанию grammar.txt)") + print(" - check <строка> - проверить, соответствует ли строка грамматике") + print(" - generate - сгенерировать случайную строку по грамматике") + print(" - exit - выход из программы") + print("=" * 60) + + # Загружаем грамматику по умолчанию при старте + grammar = load_grammar() + + while True: + command = input("\nВведите команду: ").strip() + + if not command: + continue + + parts = command.split(maxsplit=1) + cmd = parts[0].lower() + + if cmd == "exit": + print("Выход из программы.") + break + + elif cmd == "load": + filename = "grammar.txt" + if len(parts) > 1: + filename = parts[1].strip() + grammar = load_grammar(filename) + + elif cmd == "check": + input_string = "" + if len(parts) > 1: + input_string = parts[1].strip() + check_string(grammar, input_string) + + elif cmd == "generate": + generate_string(grammar) + + else: + print(f"Неизвестная команда: {cmd}") + print("Доступные команды: load, check, generate, exit") +\end{lstlisting} + \newpage \section{Результаты работы программы} - % Результаты работы программы представлены на Рис.~\ref{fig:result1}. + Результаты работы программы представлены на Рис.~\ref{fig:result1}. - % \begin{figure}[h!] - % \centering - % \includegraphics[width=1\linewidth]{img/result1.png} - % \caption{Результаты работы программы.} - % \label{fig:result1} - % \end{figure} + \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} + \begin{figure}[h!] + \centering + \includegraphics[width=0.5\linewidth]{img/wrong.png} + \caption{Реакция программы на некорректный пользовательский ввод.} + \label{fig:wrong} + \end{figure} - % На Рис.~\ref{fig:wrong} представлена реакция программы на некорректный пользовательский ввод. + На Рис.~\ref{fig:wrong} представлена реакция программы на некорректный пользовательский ввод. \newpage \section*{Заключение} \addcontentsline{toc}{section}{Заключение} - В ходе выполнения лабораторной работы было построено регулярное выражение для распознавания различных форматов вещественных чисел. В соответствии с теоремой Клини по заданному регулярному выражению, задающему регулярный - язык, был построен недетерминированный конечный автомат-распознаватель. Затем полученный конечный автомат был детерминирован. На основе разработанного автомата была реализована программа, которая проверяет соответствие входной строки заданному формату и генерирует случайные корректные строки. + В ходе выполнения лабораторной работы была построена контекстно-свободная грамматика для подмножества немецкого языка, описывающая простое прошедшее время Претерит. На основе разработанной грамматики была реализована программа, которая проверяет принадлежность входной строки заданному языку и генерирует случайные корректные предложения. Для анализа предложений использовался алгоритм LL(1)-разбора, основанный на построении множеств FIRST и FOLLOW для всех нетерминалов грамматики и создании таблицы синтаксического анализа. - Из достоинств выполнения лабораторной работы можно выделить структурирование кода за счёт использования ООП. Вся логика работы с конечными автоматами вынесена в отдельный класс \texttt{FiniteAutomaton} с четко разделенными методами для проверки строк и генерации случайных строк. Создана удобная интерактивная консольная оболочка для взаимодействия с пользователем, позволяющая выполнять различные команды. + Из достоинств выполнения лабораторной работы можно выделить возможность задания грамматики в отдельном текстовом файле, что позволяет легко изменять и расширять её без модификации программного кода. Использование объектно-ориентированного подхода в реализации обеспечило хорошую структуру кода. Вся логика работы с грамматикой реализована в классе Grammar, а обработка пользовательского ввода вынесена в функцию main. - К недостаткам текущей реализации можно отнести следующие аспекты. Во-первых, переходы в автомате представлены в виде строк, содержащих допустимые символы, такой способ представления переходов не является самым оптимальным с точки зрения производительности. Во-вторых, в реализации генерации случайных строк вероятность остановки одинакова для всех финальных состояний, что может приводить к неравномерному распределению различных форматов чисел в генерируемых строках. + К недостаткам текущей реализации можно отнести ограниченность словарного запаса, что сужает разнообразие генерируемых предложений. Также алгоритм генерации не контролирует длину предложений, что может приводить к избыточно длинным или коротким конструкциям. В текущей версии система не учитывает некоторые грамматические особенности немецкого языка, например, склонение прилагательных и согласование артиклей с родом существительных. - Функционал программы несложно масштабировать. Класс \texttt{FiniteAutomaton} может быть использован для работы с различными конечными автоматами-распознавателями. Для изменения распознаваемого языка достаточно задать новые параметры для автомата, не меняя базовую логику программы. Однако, текущая реализация работает только с символьными переходами, поэтому задать строчные переходы в виде, например, регулярных выражений не представляется возможным. Однако подобный функционал также несложно реализовать, взяв за основу существующий код. + Функционал программы несложно масштабировать. Грамматику легко расширять, добавляя новые слова и правила в текстовый файл без необходимости изменения программного кода. Класс Grammar может служить хорошей основой для создания полноценного LL(k) анализатора. - На выполнение лабораторной работы ушло около 10 часов. Работа была выполнена в среде разработки Visual Studio Code. Программа написана на Python версии 3.10. + На выполнение лабораторной работы ушло около 12 часов. Работа была выполнена в среде разработки Visual Studio Code. Программа написана на Python версии 3.13. \newpage \section*{Список литературы} @@ -455,8 +994,6 @@ Востров, А.В. Курс лекций по дисциплине <<Математическая логика>>. 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} \ No newline at end of file