Compare commits
11 Commits
7520f5ec2f
...
c001ccb12a
| Author | SHA1 | Date | |
|---|---|---|---|
| c001ccb12a | |||
| fdd30a8854 | |||
| 35cf9b5ed4 | |||
| e3918eb442 | |||
| 207b428ad9 | |||
| 09da4c5c62 | |||
| cc5fbb2163 | |||
| 037a4cd8a0 | |||
| b64f7a3205 | |||
| b835502ed4 | |||
| 796ac33829 |
1
lab4/.gitignore
vendored
@@ -1,4 +1,5 @@
|
|||||||
*.zip
|
*.zip
|
||||||
*.cmil
|
*.cmil
|
||||||
|
*.o
|
||||||
cmilan/src/cmilan.exe
|
cmilan/src/cmilan.exe
|
||||||
!cmilan/doc/cmilan.pdf
|
!cmilan/doc/cmilan.pdf
|
||||||
|
|||||||
@@ -156,6 +156,7 @@ void Parser::factor()
|
|||||||
/*
|
/*
|
||||||
Множитель описывается следующими правилами:
|
Множитель описывается следующими правилами:
|
||||||
<factor> -> number | identifier | -<factor> | (<expression>) | READ
|
<factor> -> number | identifier | -<factor> | (<expression>) | READ
|
||||||
|
| ++ identifier | -- identifier | identifier++ | identifier--
|
||||||
*/
|
*/
|
||||||
if(see(T_NUMBER)) {
|
if(see(T_NUMBER)) {
|
||||||
int value = scanner_->getIntValue();
|
int value = scanner_->getIntValue();
|
||||||
@@ -168,6 +169,28 @@ void Parser::factor()
|
|||||||
next();
|
next();
|
||||||
codegen_->emit(LOAD, varAddress);
|
codegen_->emit(LOAD, varAddress);
|
||||||
//Если встретили переменную, то выгружаем значение, лежащее по ее адресу, на вершину стека
|
//Если встретили переменную, то выгружаем значение, лежащее по ее адресу, на вершину стека
|
||||||
|
|
||||||
|
// Постфиксный инкремент или декремент
|
||||||
|
if(see(T_INC) || see(T_DEC)) {
|
||||||
|
codegen_->emit(DUP);
|
||||||
|
codegen_->emit(PUSH, 1);
|
||||||
|
codegen_->emit(see(T_INC) ? ADD : SUB);
|
||||||
|
codegen_->emit(STORE, varAddress);
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Префиксный инкремент или декремент
|
||||||
|
else if(see(T_INC) || see(T_DEC)) {
|
||||||
|
bool isIncrement = see(T_INC);
|
||||||
|
next();
|
||||||
|
mustBe(T_IDENTIFIER);
|
||||||
|
int varAddress = findOrAddVariable(scanner_->getStringValue());
|
||||||
|
|
||||||
|
codegen_->emit(LOAD, varAddress);
|
||||||
|
codegen_->emit(PUSH, 1);
|
||||||
|
codegen_->emit(isIncrement ? ADD : SUB);
|
||||||
|
codegen_->emit(DUP);
|
||||||
|
codegen_->emit(STORE, varAddress);
|
||||||
}
|
}
|
||||||
else if(see(T_ADDOP) && scanner_->getArithmeticValue() == A_MINUS) {
|
else if(see(T_ADDOP) && scanner_->getArithmeticValue() == A_MINUS) {
|
||||||
next();
|
next();
|
||||||
|
|||||||
@@ -28,6 +28,8 @@ static const char * tokenNames_[] = {
|
|||||||
"'('",
|
"'('",
|
||||||
"')'",
|
"')'",
|
||||||
"';'",
|
"';'",
|
||||||
|
"'++'",
|
||||||
|
"'--'",
|
||||||
};
|
};
|
||||||
|
|
||||||
void Scanner::nextToken()
|
void Scanner::nextToken()
|
||||||
@@ -185,15 +187,31 @@ void Scanner::nextToken()
|
|||||||
//Знаки операций. Для "+"/"-" получим лексему операции типа сложнения, и соответствующую операцию.
|
//Знаки операций. Для "+"/"-" получим лексему операции типа сложнения, и соответствующую операцию.
|
||||||
//для "*" - лексему операции типа умножения
|
//для "*" - лексему операции типа умножения
|
||||||
case '+':
|
case '+':
|
||||||
|
nextChar();
|
||||||
|
|
||||||
|
// Ищем оператор инкремента
|
||||||
|
if(ch_ == '+') {
|
||||||
|
token_ = T_INC;
|
||||||
|
nextChar();
|
||||||
|
}
|
||||||
|
else {
|
||||||
token_ = T_ADDOP;
|
token_ = T_ADDOP;
|
||||||
arithmeticValue_ = A_PLUS;
|
arithmeticValue_ = A_PLUS;
|
||||||
nextChar();
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case '-':
|
case '-':
|
||||||
|
nextChar();
|
||||||
|
|
||||||
|
// Ищем оператор декремента
|
||||||
|
if(ch_ == '-') {
|
||||||
|
token_ = T_DEC;
|
||||||
|
nextChar();
|
||||||
|
}
|
||||||
|
else {
|
||||||
token_ = T_ADDOP;
|
token_ = T_ADDOP;
|
||||||
arithmeticValue_ = A_MINUS;
|
arithmeticValue_ = A_MINUS;
|
||||||
nextChar();
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case '*':
|
case '*':
|
||||||
|
|||||||
@@ -29,7 +29,9 @@ enum Token {
|
|||||||
T_CMP, // Сводная лексема для операторов отношения
|
T_CMP, // Сводная лексема для операторов отношения
|
||||||
T_LPAREN, // Открывающая скобка
|
T_LPAREN, // Открывающая скобка
|
||||||
T_RPAREN, // Закрывающая скобка
|
T_RPAREN, // Закрывающая скобка
|
||||||
T_SEMICOLON // ";"
|
T_SEMICOLON, // ";"
|
||||||
|
T_INC, // Оператор инкремента
|
||||||
|
T_DEC // Оператор декремента
|
||||||
};
|
};
|
||||||
|
|
||||||
// Функция tokenToString возвращает описание лексемы.
|
// Функция tokenToString возвращает описание лексемы.
|
||||||
|
|||||||
7
lab4/cmilan/test/incdec/inc.mil
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
BEGIN
|
||||||
|
x := 0;
|
||||||
|
write(x++);
|
||||||
|
write(x);
|
||||||
|
write(++x);
|
||||||
|
write(x)
|
||||||
|
END
|
||||||
6
lab4/cmilan/test/incdec/incif.mil
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
BEGIN
|
||||||
|
i := 1;
|
||||||
|
j := 2;
|
||||||
|
|
||||||
|
IF i < --j THEN WRITE(100) ELSE WRITE(-100) FI
|
||||||
|
END
|
||||||
13
lab4/cmilan/test/incdec/incxy.mil
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
BEGIN
|
||||||
|
y := x++;
|
||||||
|
write(y);
|
||||||
|
write(x);
|
||||||
|
|
||||||
|
y := 10 - --x;
|
||||||
|
write(y);
|
||||||
|
write(x);
|
||||||
|
|
||||||
|
y := 10 -++x;
|
||||||
|
write(y);
|
||||||
|
write(x)
|
||||||
|
END
|
||||||
5
lab4/cmilan/test/incdec/invalid.mil
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
BEGIN
|
||||||
|
x := 10;
|
||||||
|
y := 10 - --x; /* Корректно: минус и декремент разделены пробелом */
|
||||||
|
y := 10 ---x; /* Некорректно: три минуса подряд */
|
||||||
|
END
|
||||||
5
lab4/report/.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
**/*
|
||||||
|
!.gitignore
|
||||||
|
!report.tex
|
||||||
|
!img
|
||||||
|
!img/**
|
||||||
BIN
lab4/report/img/compiler.png
Normal file
|
After Width: | Height: | Size: 39 KiB |
BIN
lab4/report/img/result1.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
lab4/report/img/result2.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
lab4/report/img/result3.png
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
BIN
lab4/report/img/result4.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
lab4/report/img/scheme.png
Normal file
|
After Width: | Height: | Size: 88 KiB |
BIN
lab4/report/img/syntax_diagram_addop.png
Normal file
|
After Width: | Height: | Size: 9.8 KiB |
BIN
lab4/report/img/syntax_diagram_cmpi.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
lab4/report/img/syntax_diagram_digit.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
lab4/report/img/syntax_diagram_expression.png
Normal file
|
After Width: | Height: | Size: 9.3 KiB |
BIN
lab4/report/img/syntax_diagram_factor.png
Normal file
|
After Width: | Height: | Size: 51 KiB |
BIN
lab4/report/img/syntax_diagram_ident.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
lab4/report/img/syntax_diagram_letter.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
lab4/report/img/syntax_diagram_mulop.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
lab4/report/img/syntax_diagram_number.png
Normal file
|
After Width: | Height: | Size: 8.8 KiB |
BIN
lab4/report/img/syntax_diagram_program.png
Normal file
|
After Width: | Height: | Size: 8.6 KiB |
BIN
lab4/report/img/syntax_diagram_relation.png
Normal file
|
After Width: | Height: | Size: 7.5 KiB |
BIN
lab4/report/img/syntax_diagram_statement.png
Normal file
|
After Width: | Height: | Size: 49 KiB |
BIN
lab4/report/img/syntax_diagram_statementList.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
961
lab4/report/report.tex
Normal file
@@ -0,0 +1,961 @@
|
|||||||
|
\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{Лабораторная работа №4}\\
|
||||||
|
\large{<<Доработка компилятора языка MiLan>>}\\
|
||||||
|
\large{по дисциплине}\\
|
||||||
|
\large{<<Математическая логика и теория автоматов>>}\\
|
||||||
|
\large{Вариант 8}\\
|
||||||
|
|
||||||
|
% \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}{Введение}
|
||||||
|
Лабораторная №4 заключается в доработке компилятора языка MiLan согласно варианту работы.
|
||||||
|
|
||||||
|
\textit{Вариант 8}: В компилятор необходимо добавить поддержку операций инкремента и декремента, как постфиксных, так и префиксных: \texttt{++i}, \texttt{--i}, \texttt{i++}, \texttt{i--}. С точки зрения действия на операнд i между префиксными и постфиксными операциями разницы нет. Если эти выражения являются частью более сложного выражения, то при префиксной форме сначала изменяется операнд i, и уже измененный результат используется в выражении. При постфиксной форме операторов инкремента или декремента сначала операнд i используется в выражении, а потом уже изменяется.
|
||||||
|
|
||||||
|
|
||||||
|
\newpage
|
||||||
|
\section {Математическое описание}
|
||||||
|
\subsection{Обзор языка MiLan}
|
||||||
|
Язык Милан — учебный язык программирования, описанный в учебнике~\cite{karpov}.
|
||||||
|
|
||||||
|
Программа на Милане представляет собой последовательность операторов, заключенных между ключевыми словами \texttt{begin} и \texttt{end}. Операторы отделяются друг от друга точкой с запятой. После последнего оператора в блоке точка с запятой не ставится. Компилятор \texttt{CMilan} не учитывает регистр символов в именах переменных и ключевых словах.
|
||||||
|
|
||||||
|
В базовую версию языка Милан входят следующие конструкции: константы, идентификаторы, арифметические операции над целыми числами, операторы чтения чисел со стандартного ввода и печати чисел на стандартный вывод, оператор присваивания, условный оператор, оператор цикла с предусловием.
|
||||||
|
|
||||||
|
Программа может содержать комментарии, которые могут быть многострочными. Комментарий начинается символами \texttt{/*} и заканчивается символами \texttt{*/}. Вложенные комментарии не допускаются.
|
||||||
|
|
||||||
|
В данной лабораторной рассматривается добавление поддержки операций инкремента и декремента, как постфиксных, так и префиксных: \texttt{++i}, \texttt{--i}, \texttt{i++}, \texttt{i--}. С точки зрения действия на операнд i между префиксными и постфиксными операциями разницы нет. Если эти выражения являются частью более сложного выражения, то при префиксной форме сначала изменяется операнд i, и уже измененный результат используется в выражении. При постфиксной форме операторов инкремента или декремента сначала операнд i используется в выражении, а потом уже изменяется.
|
||||||
|
|
||||||
|
\subsection{Лексический анализ}
|
||||||
|
В реальных трансляторах языков программирования (ЯП) первой фазой является так называемый лексический анализ входной программы — предварительная
|
||||||
|
обработка входного текста с выделением в нем структурно значимых единиц — лексем. На Рис.~\ref{fig:scheme} представлена схема транслятора.
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
|
\centering
|
||||||
|
\includegraphics[width=0.7\linewidth]{img/scheme.png}
|
||||||
|
\caption{Схема транслятора.}
|
||||||
|
\label{fig:scheme}
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
|
Лексемы — минимальные единицы языка, которые имеют смысл.
|
||||||
|
|
||||||
|
Значение лексемы, определяющее подстроку символов входной цепочки, соответствующих распознанному классу лексемы. В зависимости от класса, значение
|
||||||
|
лексемы может быть преобразовано во внутреннее представление уже на этапе лексического анализа.
|
||||||
|
|
||||||
|
Класс лексемы, определяющий общее название для категории элементов, обладающих общими свойствами (идентификатор, целое число, строка символов...).
|
||||||
|
|
||||||
|
Лексический анализатор обрабатывает входную цепочку, а на его вход подаются
|
||||||
|
символы, сгруппированные по категориям. Поэтому перед лексическим анализом
|
||||||
|
осуществляется дополнительная обработка, сопоставляющая с каждым символом его
|
||||||
|
класс, что позволяет сканеру манипулировать единым понятием для целой группы
|
||||||
|
символов.
|
||||||
|
|
||||||
|
Лексический анализатор (лексер) — это конечный автомат, который преобразует входную строку символов в последовательность токенов. Формально его можно описать следующим образом:
|
||||||
|
|
||||||
|
Пусть заданы:
|
||||||
|
\begin{itemize}
|
||||||
|
\item $\Sigma$ — входной алфавит (множество допустимых символов)
|
||||||
|
\item $T$ — множество типов токенов
|
||||||
|
\item $D$ — множество допустимых значений токенов
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
Тогда лексический анализатор реализует отображение:
|
||||||
|
\[
|
||||||
|
F_{\text{lexer}} : \Sigma^* \rightarrow (T \times D)^*
|
||||||
|
\]
|
||||||
|
|
||||||
|
где:
|
||||||
|
\begin{itemize}
|
||||||
|
\item $\Sigma^*$ — множество всех возможных строк над алфавитом $\Sigma$
|
||||||
|
\item $(T \times D)^*$ — множество последовательностей пар (тип токена, значение)
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
Процесс лексического анализа можно представить как \textbf{детерминированный конечный автомат (ДКА)}:
|
||||||
|
\[
|
||||||
|
M = (Q, \Sigma, \delta, q_0, F),
|
||||||
|
\]
|
||||||
|
где:
|
||||||
|
\begin{itemize}
|
||||||
|
\item $Q$ — множество состояний автомата
|
||||||
|
\item $\delta : Q \times \Sigma \rightarrow Q$ — функция переходов
|
||||||
|
\item $q_0 \in Q$ — начальное состояние
|
||||||
|
\item $F \subseteq Q$ — множество конечных состояний
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
Для каждого распознанного токена $t_i$ выполняется:
|
||||||
|
\[
|
||||||
|
t_i = (\text{type}, \text{value}), \quad \text{где } \text{type} \in T, \text{value} \in D
|
||||||
|
\]
|
||||||
|
|
||||||
|
\subsection{Синтаксический анализ}
|
||||||
|
Синтаксический анализ — процесс сопоставления линейной последовательности лексем естественного или формального языка с его формальной грамматикой.
|
||||||
|
Результатом обычно является дерево разбора. Обычно применяется совместно с лексическим анализом.
|
||||||
|
|
||||||
|
Синтаксический анализатор выражений (парсер) — часть программы,
|
||||||
|
выполняющая чтение и анализ выражения.
|
||||||
|
|
||||||
|
Существует два типа алгоритмов синтаксического анализа: нисходящий и восходящий:
|
||||||
|
\begin{itemize}
|
||||||
|
\item Нисходящий парсер — продукции грамматики раскрываются, начиная со стартового символа, до получения требуемой последовательности токенов.
|
||||||
|
\item Восходящий парсер — продукции восстанавливаются из правых частей, начиная с токенов и кончая стартовым символом.
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
Грамматика языка MiLan использует нисходящий парсер. При восстановлении
|
||||||
|
синтаксического дерева при нисходящем разборе слева направо последовательно анализирует все поддеревья, принадлежащие самому левому нетерминалу. Когда самым
|
||||||
|
левым становится другой нетерминал, анализируется уже он.
|
||||||
|
|
||||||
|
Компилятор CMilan включает три компонента (Рис.~\ref{fig:compiler}):
|
||||||
|
\begin{enumerate}
|
||||||
|
\item лексический анализатор;
|
||||||
|
\item синтаксический анализатор;
|
||||||
|
\item генератор команд виртуальной машины Милана.
|
||||||
|
\end{enumerate}
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
|
\centering
|
||||||
|
\includegraphics[width=0.7\linewidth]{img/compiler.png}
|
||||||
|
\caption{Компоненты компилятора CMilan.}
|
||||||
|
\label{fig:compiler}
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
|
\subsection{Грамматика языка MiLan}
|
||||||
|
Грамматика языка Милан является контекстно-свободной, так как удовлетворяет определению КС-грамматики, т.е. продукции грамматики имеют вид $A \rightarrow \beta$, где $A$ – одиночный нетерминал, а $\beta$ – произвольная цепочка из терминалов и нетерминалов. Более того, грамматика языка Милан является LL(1) грамматикой, так как необходимо просмотреть поток всего на один символ вперед при принятии решения о том, какое правило грамматики необходимо применить. В данной работе в грамматику были добавлены операции инкремента и декремента, однако это не повлияло на LL(1) свойство грамматики.
|
||||||
|
|
||||||
|
Грамматика языка Милан, расширенная операцией инкремента и декремента, в форме Бэкуса-Наура приведена ниже:
|
||||||
|
|
||||||
|
\begin{verbatim}
|
||||||
|
<program> ::= ‘begin’ <statementList> ‘end’
|
||||||
|
<statementList> ::= <statement> ‘;’ <statementList>
|
||||||
|
| epsilon
|
||||||
|
<statement> ::= <ident> ‘:=’ <expression>
|
||||||
|
| ‘if’ <relation> ‘then’
|
||||||
|
<statementList> [‘else’ <statementList>] ‘fi’
|
||||||
|
| ‘while’ <relation> ‘do’ <statementList> ‘od’
|
||||||
|
| ‘write’ ‘(’ <expression> ‘)’
|
||||||
|
<expression> ::= <term> {<addop> <term>}
|
||||||
|
<term> ::= <factor> {<mulop> <factor>}
|
||||||
|
<factor> ::= <ident>
|
||||||
|
| <number>
|
||||||
|
| ‘(’ <expression> ‘)’
|
||||||
|
| ‘read’
|
||||||
|
| ‘-’ <factor>
|
||||||
|
| <ident> ‘++’
|
||||||
|
| <ident> ‘--’
|
||||||
|
| ‘++’ <ident>
|
||||||
|
| ‘--’ <ident>
|
||||||
|
<relation> ::= <expression> <cmpi> <expression>
|
||||||
|
<addop> ::= ‘+’|‘-’
|
||||||
|
<mulop> ::= ‘*’|‘/’
|
||||||
|
<cmpi> ::= ‘=’|‘!=’|‘<’|‘<=’|‘>’|‘>=’
|
||||||
|
<number> ::= <digit> {<digit>}
|
||||||
|
<ident> ::= <letter> {<letter> | <digit>}
|
||||||
|
<letter> ::= ‘a’|‘b’|‘c’ | ...| ‘z’|‘A’|‘B’|‘C’ | ...| ‘Z’
|
||||||
|
<digit> ::= ‘0’|‘1’|‘2’|‘3’|‘4’|‘5’|‘6’|‘7’|‘8’|‘9’
|
||||||
|
\end{verbatim}
|
||||||
|
|
||||||
|
Изменения коснулись только правила \texttt{<factor>}, в котором были добавлены 4 новые продукции, описывающие операции постфиксного и префиксного инкремента и декремента.
|
||||||
|
|
||||||
|
Синтаксические диаграммы для всех нетерминалов грамматики приведены на Рис.~\ref{fig:syntax_diagram_program} — Рис.~\ref{fig:syntax_diagram_digit}. Обновлённая синтаксическая диаграмма для нетерминала \texttt{<factor>} приведена на Рис.~\ref{fig:syntax_diagram_factor}.
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
|
\centering
|
||||||
|
\includegraphics[width=0.9\linewidth]{img/syntax_diagram_program.png}
|
||||||
|
\caption{Синтаксическая диаграмма для нетерминала \texttt{<program>}.}
|
||||||
|
\label{fig:syntax_diagram_program}
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
|
\centering
|
||||||
|
\includegraphics[width=0.7\linewidth]{img/syntax_diagram_statementList.png}
|
||||||
|
\caption{Синтаксическая диаграмма для нетерминала \texttt{<statementList>}.}
|
||||||
|
\label{fig:syntax_diagram_statementList}
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
|
\centering
|
||||||
|
\includegraphics[width=1\linewidth]{img/syntax_diagram_statement.png}
|
||||||
|
\caption{Синтаксическая диаграмма для нетерминала \texttt{<statement>}.}
|
||||||
|
\label{fig:syntax_diagram_statement}
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
|
\centering
|
||||||
|
\includegraphics[width=0.7\linewidth]{img/syntax_diagram_expression.png}
|
||||||
|
\caption{Синтаксическая диаграмма для нетерминала \texttt{<expression>}.}
|
||||||
|
\label{fig:syntax_diagram_expression}
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
|
\centering
|
||||||
|
\includegraphics[width=1\linewidth]{img/syntax_diagram_relation.png}
|
||||||
|
\caption{Синтаксическая диаграмма для нетерминала \texttt{<relation>}.}
|
||||||
|
\label{fig:syntax_diagram_relation}
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
|
\centering
|
||||||
|
\includegraphics[width=0.5\linewidth]{img/syntax_diagram_addop.png}
|
||||||
|
\caption{Синтаксическая диаграмма для нетерминала \texttt{<addop>}.}
|
||||||
|
\label{fig:syntax_diagram_addop}
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
|
\centering
|
||||||
|
\includegraphics[width=0.5\linewidth]{img/syntax_diagram_mulop.png}
|
||||||
|
\caption{Синтаксическая диаграмма для нетерминала \texttt{<mulop>}.}
|
||||||
|
\label{fig:syntax_diagram_mulop}
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
|
\centering
|
||||||
|
\includegraphics[width=0.7\linewidth]{img/syntax_diagram_factor.png}
|
||||||
|
\caption{Синтаксическая диаграмма для нетерминала \texttt{<factor>}, дополненная операциями инкремента и декремента (отмечены зеленым цветом).}
|
||||||
|
\label{fig:syntax_diagram_factor}
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
|
\centering
|
||||||
|
\includegraphics[width=0.6\linewidth]{img/syntax_diagram_number.png}
|
||||||
|
\caption{Синтаксическая диаграмма для нетерминала \texttt{<number>}.}
|
||||||
|
\label{fig:syntax_diagram_number}
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
|
\centering
|
||||||
|
\includegraphics[width=0.6\linewidth]{img/syntax_diagram_ident.png}
|
||||||
|
\caption{Синтаксическая диаграмма для нетерминала \texttt{<ident>}.}
|
||||||
|
\label{fig:syntax_diagram_ident}
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
|
\centering
|
||||||
|
\includegraphics[width=1\linewidth]{img/syntax_diagram_letter.png}
|
||||||
|
\caption{Синтаксическая диаграмма для нетерминала \texttt{<letter>}.}
|
||||||
|
\label{fig:syntax_diagram_letter}
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
|
\centering
|
||||||
|
\includegraphics[width=1\linewidth]{img/syntax_diagram_digit.png}
|
||||||
|
\caption{Синтаксическая диаграмма для нетерминала \texttt{<digit>}.}
|
||||||
|
\label{fig:syntax_diagram_digit}
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
|
\newpage
|
||||||
|
\phantom{text}
|
||||||
|
|
||||||
|
\newpage
|
||||||
|
\phantom{text}
|
||||||
|
|
||||||
|
\newpage
|
||||||
|
\phantom{text}
|
||||||
|
|
||||||
|
\section{Особенности реализации}
|
||||||
|
\subsection{Изменения в лексическом анализаторе}
|
||||||
|
|
||||||
|
\subsubsection{Файл \texttt{Scanner.h}}
|
||||||
|
В перечисление \texttt{Token} в файле \texttt{Scanner.h} были добавлены новые токены: \texttt{INC}, \texttt{DEC}. Код обновлённого перечисления представлен в листинге~\ref{lst:token}, добавлены строки 24-25.
|
||||||
|
|
||||||
|
\begin{lstlisting}[language=C++, caption=Обновлённое перечисление \texttt{Token}, label=lst:token]
|
||||||
|
enum Token {
|
||||||
|
T_EOF, // Конец текстового потока
|
||||||
|
T_ILLEGAL, // Признак недопустимого символа
|
||||||
|
T_IDENTIFIER, // Идентификатор
|
||||||
|
T_NUMBER, // Целочисленный литерал
|
||||||
|
T_BEGIN, // Ключевое слово "begin"
|
||||||
|
T_END, // Ключевое слово "end"
|
||||||
|
T_IF, // Ключевое слово "if"
|
||||||
|
T_THEN, // Ключевое слово "then"
|
||||||
|
T_ELSE, // Ключевое слово "else"
|
||||||
|
T_FI, // Ключевое слово "fi"
|
||||||
|
T_WHILE, // Ключевое слово "while"
|
||||||
|
T_DO, // Ключевое слово "do"
|
||||||
|
T_OD, // Ключевое слово "od"
|
||||||
|
T_WRITE, // Ключевое слово "write"
|
||||||
|
T_READ, // Ключевое слово "read"
|
||||||
|
T_ASSIGN, // Оператор ":="
|
||||||
|
T_ADDOP, // Сводная лексема для "+" и "-" (операция типа сложения)
|
||||||
|
T_MULOP, // Сводная лексема для "*" и "/" (операция типа умножения)
|
||||||
|
T_CMP, // Сводная лексема для операторов отношения
|
||||||
|
T_LPAREN, // Открывающая скобка
|
||||||
|
T_RPAREN, // Закрывающая скобка
|
||||||
|
T_SEMICOLON, // ";"
|
||||||
|
T_INC, // Оператор инкремента
|
||||||
|
T_DEC // Оператор декремента
|
||||||
|
};
|
||||||
|
\end{lstlisting}
|
||||||
|
|
||||||
|
\subsubsection{Файл \texttt{Scanner.cpp}}
|
||||||
|
В массив названий токенов \texttt{tokenNames\_} были добавлены новые строки, соответствующие инкременту и декременту: \texttt{"++"} и \texttt{"\textminus\textminus"}, соответственно. Код обновлённого массива представлен в листинге~\ref{lst:token_names}, добавлены строки 24-25.
|
||||||
|
|
||||||
|
\begin{lstlisting}[language=C++, caption=Обновлённый массив \texttt{tokenNames\_}, label=lst:token_names]
|
||||||
|
static const char * tokenNames_[] = {
|
||||||
|
"end of file",
|
||||||
|
"illegal token",
|
||||||
|
"identifier",
|
||||||
|
"number",
|
||||||
|
"'BEGIN'",
|
||||||
|
"'END'",
|
||||||
|
"'IF'",
|
||||||
|
"'THEN'",
|
||||||
|
"'ELSE'",
|
||||||
|
"'FI'",
|
||||||
|
"'WHILE'",
|
||||||
|
"'DO'",
|
||||||
|
"'OD'",
|
||||||
|
"'WRITE'",
|
||||||
|
"'READ'",
|
||||||
|
"':='",
|
||||||
|
"'+' or '-'",
|
||||||
|
"'*' or '/'",
|
||||||
|
"comparison operator",
|
||||||
|
"'('",
|
||||||
|
"')'",
|
||||||
|
"';'",
|
||||||
|
"'++'",
|
||||||
|
"'--'",
|
||||||
|
};
|
||||||
|
\end{lstlisting}
|
||||||
|
|
||||||
|
Также была обновлена функция \texttt{nextToken()}. В ней были добавлены новые условия для распознавания инкремента и декремента. Код обновлённой функции представлен в листинге~\ref{lst:next_token}, изменения коснулись строк 158-181.
|
||||||
|
|
||||||
|
\begin{lstlisting}[language=C++, caption=Обновлённая функция \texttt{nextToken()}, label=lst:next_token]
|
||||||
|
void Scanner::nextToken()
|
||||||
|
{
|
||||||
|
skipSpace();
|
||||||
|
|
||||||
|
// Пропускаем комментарии
|
||||||
|
// Если встречаем "/", то за ним должна идти "*". Если "*" не встречена, считаем, что встретили операцию деления
|
||||||
|
// и лексему - операция типа умножения. Дальше смотрим все символы, пока не находим звездочку или символ конца файла.
|
||||||
|
// Если нашли * - проверяем на наличие "/" после нее. Если "/" не найден - ищем следующую "*".
|
||||||
|
while(ch_ == '/') {
|
||||||
|
nextChar();
|
||||||
|
if(ch_ == '*') {
|
||||||
|
nextChar();
|
||||||
|
bool inside = true;
|
||||||
|
while(inside) {
|
||||||
|
while(ch_ != '*' && !input_.eof()) {
|
||||||
|
nextChar();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(input_.eof()) {
|
||||||
|
token_ = T_EOF;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
nextChar();
|
||||||
|
if(ch_ == '/') {
|
||||||
|
inside = false;
|
||||||
|
nextChar();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
token_ = T_MULOP;
|
||||||
|
arithmeticValue_ = A_DIVIDE;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
skipSpace();
|
||||||
|
}
|
||||||
|
|
||||||
|
//Если встречен конец файла, считаем за лексему конца файла.
|
||||||
|
if(input_.eof()) {
|
||||||
|
token_ = T_EOF;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//Если встретили цифру, то до тех пока дальше идут цифры - считаем как продолжение числа.
|
||||||
|
//Запоминаем полученное целое, а за лексему считаем целочисленный литерал
|
||||||
|
|
||||||
|
if(isdigit(ch_)) {
|
||||||
|
int value = 0;
|
||||||
|
while(isdigit(ch_)) {
|
||||||
|
value = value * 10 + (ch_ - '0'); //поразрядное считывание, преобразуем символьное значение к числу.
|
||||||
|
nextChar();
|
||||||
|
}
|
||||||
|
token_ = T_NUMBER;
|
||||||
|
intValue_ = value;
|
||||||
|
}
|
||||||
|
//Если же следующий символ - буква ЛА - тогда считываем до тех пор, пока дальше буквы ЛА или цифры.
|
||||||
|
//Как только считали имя переменной, сравниваем ее со списком зарезервированных слов. Если не совпадает ни с одним из них,
|
||||||
|
//считаем, что получили переменную, имя которой запоминаем, а за текущую лексему считаем лексему идентификатора.
|
||||||
|
//Если совпадает с каким-либо словом из списка - считаем что получили лексему, соответствующую этому слову.
|
||||||
|
else if(isIdentifierStart(ch_)) {
|
||||||
|
string buffer;
|
||||||
|
while(isIdentifierBody(ch_)) {
|
||||||
|
buffer += ch_;
|
||||||
|
nextChar();
|
||||||
|
}
|
||||||
|
|
||||||
|
transform(buffer.begin(), buffer.end(), buffer.begin(), ::tolower);
|
||||||
|
|
||||||
|
map<string, Token>::iterator kwd = keywords_.find(buffer);
|
||||||
|
if(kwd == keywords_.end()) {
|
||||||
|
token_ = T_IDENTIFIER;
|
||||||
|
stringValue_ = buffer;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
token_ = kwd->second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//Символ не является буквой, цифрой, "/" или признаком конца файла
|
||||||
|
else {
|
||||||
|
switch(ch_) {
|
||||||
|
//Признак лексемы открывающей скобки - встретили "("
|
||||||
|
case '(':
|
||||||
|
token_ = T_LPAREN;
|
||||||
|
nextChar();
|
||||||
|
break;
|
||||||
|
//Признак лексемы закрывающей скобки - встретили ")"
|
||||||
|
case ')':
|
||||||
|
token_ = T_RPAREN;
|
||||||
|
nextChar();
|
||||||
|
break;
|
||||||
|
//Признак лексемы ";" - встретили ";"
|
||||||
|
case ';':
|
||||||
|
token_ = T_SEMICOLON;
|
||||||
|
nextChar();
|
||||||
|
break;
|
||||||
|
//Если встречаем ":", то дальше смотрим наличие символа "=". Если находим, то считаем что нашли лексему присваивания
|
||||||
|
//Иначе - лексема ошибки.
|
||||||
|
case ':':
|
||||||
|
nextChar();
|
||||||
|
if(ch_ == '=') {
|
||||||
|
token_ = T_ASSIGN;
|
||||||
|
nextChar();
|
||||||
|
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
token_ = T_ILLEGAL;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
//Если встретили символ "<", то либо следующий символ "=", тогда лексема нестрогого сравнения. Иначе - строгого.
|
||||||
|
case '<':
|
||||||
|
token_ = T_CMP;
|
||||||
|
nextChar();
|
||||||
|
if(ch_ == '=') {
|
||||||
|
cmpValue_ = C_LE;
|
||||||
|
nextChar();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
cmpValue_ = C_LT;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
//Аналогично предыдущему случаю
|
||||||
|
case '>':
|
||||||
|
token_ = T_CMP;
|
||||||
|
nextChar();
|
||||||
|
if(ch_ == '=') {
|
||||||
|
cmpValue_ = C_GE;
|
||||||
|
nextChar();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
cmpValue_ = C_GT;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
//Если встретим "!", то дальше должно быть "=", тогда считаем, что получили лексему сравнения
|
||||||
|
//и знак "!=" иначе считаем, что у нас лексема ошибки
|
||||||
|
case '!':
|
||||||
|
nextChar();
|
||||||
|
if(ch_ == '=') {
|
||||||
|
nextChar();
|
||||||
|
token_ = T_CMP;
|
||||||
|
cmpValue_ = C_NE;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
token_ = T_ILLEGAL;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
//Если встретим "=" - лексема сравнения и знак "="
|
||||||
|
case '=':
|
||||||
|
token_ = T_CMP;
|
||||||
|
cmpValue_ = C_EQ;
|
||||||
|
nextChar();
|
||||||
|
break;
|
||||||
|
//Знаки операций. Для "+"/"-" получим лексему операции типа сложнения, и соответствующую операцию.
|
||||||
|
//для "*" - лексему операции типа умножения
|
||||||
|
case '+':
|
||||||
|
nextChar();
|
||||||
|
|
||||||
|
// Ищем оператор инкремента
|
||||||
|
if(ch_ == '+') {
|
||||||
|
token_ = T_INC;
|
||||||
|
nextChar();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
token_ = T_ADDOP;
|
||||||
|
arithmeticValue_ = A_PLUS;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '-':
|
||||||
|
nextChar();
|
||||||
|
|
||||||
|
// Ищем оператор декремента
|
||||||
|
if(ch_ == '-') {
|
||||||
|
token_ = T_DEC;
|
||||||
|
nextChar();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
token_ = T_ADDOP;
|
||||||
|
arithmeticValue_ = A_MINUS;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '*':
|
||||||
|
token_ = T_MULOP;
|
||||||
|
arithmeticValue_ = A_MULTIPLY;
|
||||||
|
nextChar();
|
||||||
|
break;
|
||||||
|
//Иначе лексема ошибки.
|
||||||
|
default:
|
||||||
|
token_ = T_ILLEGAL;
|
||||||
|
nextChar();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
|
||||||
|
\subsection{Изменения в компиляторе}
|
||||||
|
|
||||||
|
\subsubsection{Файл \texttt{Parser.cpp}}
|
||||||
|
В метод \texttt{factor()} объекта \texttt{Parser} была добавлена логика генерации команд для префиксного и постфиксного инкремента и декремента. Код обновлённого метода представлен в листинге~\ref{lst:parser_cpp}, изменения коснулись строк 20-27 (постфиксный инкремент и декремент) и строк 29-41 (префиксный инкремент и декремент).
|
||||||
|
|
||||||
|
Для постфиксного инкремента и декремента генерируется следующая последовательность команд виртуальной машины языка MiLan:
|
||||||
|
\begin{itemize}
|
||||||
|
\item \texttt{LOAD <адрес переменной>} - загрузка значения переменной на вершину стека.
|
||||||
|
\item \texttt{DUP} - дублирование значения на вершине стека до выполнения операции инкремента или декремента, так как постфиксная операция возвращает старое значение переменной.
|
||||||
|
\item \texttt{PUSH 1} - загрузка константы 1 на вершину стека
|
||||||
|
\item \texttt{ADD} или \texttt{SUB} - сложение или вычитание значения на вершине стека с константой 1.
|
||||||
|
\item \texttt{STORE <адрес переменной>} - сохранение результата на вершине стека по адресу переменной.
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
Для префиксного инкремента и декремента генерируется следующая последовательность команд виртуальной машины языка MiLan:
|
||||||
|
\begin{itemize}
|
||||||
|
\item \texttt{LOAD <адрес переменной>} - загрузка значения переменной на вершину стека.
|
||||||
|
\item \texttt{PUSH 1} - загрузка константы 1 на вершину стека
|
||||||
|
\item \texttt{ADD} или \texttt{SUB} - сложение или вычитание значения на вершине стека с константой 1.
|
||||||
|
\item \texttt{DUP} - дублирование значения на вершине стека после выполнения операции инкремента или декремента, так как префиксная операция возвращает новое значение переменной.
|
||||||
|
\item \texttt{STORE <адрес переменной>} - сохранение результата на вершине стека по адресу переменной.
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
|
||||||
|
\begin{lstlisting}[language=C++, caption=Обновлённый метод \texttt{factor()}, label=lst:parser_cpp]
|
||||||
|
void Parser::factor()
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
Множитель описывается следующими правилами:
|
||||||
|
<factor> -> number | identifier | -<factor> | (<expression>) | READ
|
||||||
|
| ++ identifier | -- identifier | identifier++ | identifier--
|
||||||
|
*/
|
||||||
|
if(see(T_NUMBER)) {
|
||||||
|
int value = scanner_->getIntValue();
|
||||||
|
next();
|
||||||
|
codegen_->emit(PUSH, value);
|
||||||
|
//Если встретили число, то преобразуем его в целое и записываем на вершину стека
|
||||||
|
}
|
||||||
|
else if(see(T_IDENTIFIER)) {
|
||||||
|
int varAddress = findOrAddVariable(scanner_->getStringValue());
|
||||||
|
next();
|
||||||
|
codegen_->emit(LOAD, varAddress);
|
||||||
|
//Если встретили переменную, то выгружаем значение, лежащее по ее адресу, на вершину стека
|
||||||
|
|
||||||
|
// Постфиксный инкремент или декремент
|
||||||
|
if(see(T_INC) || see(T_DEC)) {
|
||||||
|
codegen_->emit(DUP);
|
||||||
|
codegen_->emit(PUSH, 1);
|
||||||
|
codegen_->emit(see(T_INC) ? ADD : SUB);
|
||||||
|
codegen_->emit(STORE, varAddress);
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Префиксный инкремент или декремент
|
||||||
|
else if(see(T_INC) || see(T_DEC)) {
|
||||||
|
bool isIncrement = see(T_INC);
|
||||||
|
next();
|
||||||
|
mustBe(T_IDENTIFIER);
|
||||||
|
int varAddress = findOrAddVariable(scanner_->getStringValue());
|
||||||
|
|
||||||
|
codegen_->emit(LOAD, varAddress);
|
||||||
|
codegen_->emit(PUSH, 1);
|
||||||
|
codegen_->emit(isIncrement ? ADD : SUB);
|
||||||
|
codegen_->emit(DUP);
|
||||||
|
codegen_->emit(STORE, varAddress);
|
||||||
|
}
|
||||||
|
else if(see(T_ADDOP) && scanner_->getArithmeticValue() == A_MINUS) {
|
||||||
|
next();
|
||||||
|
factor();
|
||||||
|
codegen_->emit(INVERT);
|
||||||
|
//Если встретили знак "-", и за ним <factor> то инвертируем значение, лежащее на вершине стека
|
||||||
|
}
|
||||||
|
else if(match(T_LPAREN)) {
|
||||||
|
expression();
|
||||||
|
mustBe(T_RPAREN);
|
||||||
|
//Если встретили открывающую скобку, тогда следом может идти любое арифметическое выражение и обязательно
|
||||||
|
//закрывающая скобка.
|
||||||
|
}
|
||||||
|
else if(match(T_READ)) {
|
||||||
|
codegen_->emit(INPUT);
|
||||||
|
//Если встретили зарезервированное слово READ, то записываем на вершину стека идет запись со стандартного ввода
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
reportError("expression expected.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
|
||||||
|
\newpage
|
||||||
|
\section{Результаты работы программы}
|
||||||
|
\subsection*{Программа №1}
|
||||||
|
Исходный код программы представлен в листинге~\ref{lst:program1}.
|
||||||
|
|
||||||
|
\begin{lstlisting}[caption=Исходный код программы №1, label=lst:program1]
|
||||||
|
BEGIN
|
||||||
|
x := 0;
|
||||||
|
write(x++);
|
||||||
|
write(x);
|
||||||
|
write(++x);
|
||||||
|
write(x)
|
||||||
|
END
|
||||||
|
\end{lstlisting}
|
||||||
|
|
||||||
|
Последовательность команд, сгенерированная компилятором для данной программы, представлена в листинге~\ref{lst:program1_commands}.
|
||||||
|
|
||||||
|
|
||||||
|
\begin{lstlisting}[caption={Последовательность команд, сгенерированная компилятором для программы №1.}, label={lst:program1_commands}, numbers=none]
|
||||||
|
0: PUSH 0
|
||||||
|
1: STORE 0
|
||||||
|
2: LOAD 0
|
||||||
|
3: DUP
|
||||||
|
4: PUSH 1
|
||||||
|
5: ADD
|
||||||
|
6: STORE 0
|
||||||
|
7: PRINT
|
||||||
|
8: LOAD 0
|
||||||
|
9: PRINT
|
||||||
|
10: LOAD 0
|
||||||
|
11: PUSH 1
|
||||||
|
12: ADD
|
||||||
|
13: DUP
|
||||||
|
14: STORE 0
|
||||||
|
15: PRINT
|
||||||
|
16: LOAD 0
|
||||||
|
17: PRINT
|
||||||
|
18: STOP
|
||||||
|
\end{lstlisting}
|
||||||
|
|
||||||
|
Результаты работы виртуальной машины для программы №1 представлены на Рис.~\ref{fig:result1}.
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
|
\centering
|
||||||
|
\includegraphics[width=0.4\linewidth]{img/result1.png}
|
||||||
|
\caption{Результаты работы виртуальной машины для программы №1.}
|
||||||
|
\label{fig:result1}
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
|
\subsection*{Программа №2}
|
||||||
|
Исходный код программы представлен в листинге~\ref{lst:program2}.
|
||||||
|
|
||||||
|
\begin{lstlisting}[caption=Исходный код программы №2, label=lst:program2]
|
||||||
|
BEGIN
|
||||||
|
i := 1;
|
||||||
|
j := 2;
|
||||||
|
|
||||||
|
IF i < --j THEN WRITE(100) ELSE WRITE(-100) FI
|
||||||
|
END
|
||||||
|
\end{lstlisting}
|
||||||
|
|
||||||
|
Последовательность команд, сгенерированная компилятором для данной программы, представлена в листинге~\ref{lst:program2_commands}.
|
||||||
|
|
||||||
|
|
||||||
|
\begin{lstlisting}[caption={Последовательность команд, сгенерированная компилятором для программы №2.}, label={lst:program2_commands}, numbers=none]
|
||||||
|
0: PUSH 1
|
||||||
|
1: STORE 0
|
||||||
|
2: PUSH 2
|
||||||
|
3: STORE 1
|
||||||
|
4: LOAD 0
|
||||||
|
5: LOAD 1
|
||||||
|
6: PUSH 1
|
||||||
|
7: SUB
|
||||||
|
8: DUP
|
||||||
|
9: STORE 1
|
||||||
|
10: COMPARE 2
|
||||||
|
11: JUMP_NO 15
|
||||||
|
12: PUSH 100
|
||||||
|
13: PRINT
|
||||||
|
14: JUMP 18
|
||||||
|
15: PUSH 100
|
||||||
|
16: INVERT
|
||||||
|
17: PRINT
|
||||||
|
18: STOP
|
||||||
|
\end{lstlisting}
|
||||||
|
|
||||||
|
Результаты работы виртуальной машины для программы №2 представлены на Рис.~\ref{fig:result2}.
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
|
\centering
|
||||||
|
\includegraphics[width=0.4\linewidth]{img/result2.png}
|
||||||
|
\caption{Результаты работы виртуальной машины для программы №2.}
|
||||||
|
\label{fig:result2}
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
|
\subsection*{Программа №3}
|
||||||
|
Исходный код программы представлен в листинге~\ref{lst:program3}.
|
||||||
|
|
||||||
|
\begin{lstlisting}[caption=Исходный код программы №3, label=lst:program3]
|
||||||
|
BEGIN
|
||||||
|
y := x++;
|
||||||
|
write(y);
|
||||||
|
write(x);
|
||||||
|
|
||||||
|
y := 10 - --x;
|
||||||
|
write(y);
|
||||||
|
write(x);
|
||||||
|
|
||||||
|
y := 10 -++x;
|
||||||
|
write(y);
|
||||||
|
write(x)
|
||||||
|
END
|
||||||
|
\end{lstlisting}
|
||||||
|
|
||||||
|
Последовательность команд, сгенерированная компилятором для данной программы, представлена в листинге~\ref{lst:program3_commands}.
|
||||||
|
|
||||||
|
\begin{lstlisting}[caption={Последовательность команд, сгенерированная компилятором для программы №3.}, label={lst:program3_commands}, numbers=none]
|
||||||
|
0: LOAD 1
|
||||||
|
1: DUP
|
||||||
|
2: PUSH 1
|
||||||
|
3: ADD
|
||||||
|
4: STORE 1
|
||||||
|
5: STORE 0
|
||||||
|
6: LOAD 0
|
||||||
|
7: PRINT
|
||||||
|
8: LOAD 1
|
||||||
|
9: PRINT
|
||||||
|
10: PUSH 10
|
||||||
|
11: LOAD 1
|
||||||
|
12: PUSH 1
|
||||||
|
13: SUB
|
||||||
|
14: DUP
|
||||||
|
15: STORE 1
|
||||||
|
16: SUB
|
||||||
|
17: STORE 0
|
||||||
|
18: LOAD 0
|
||||||
|
19: PRINT
|
||||||
|
20: LOAD 1
|
||||||
|
21: PRINT
|
||||||
|
22: PUSH 10
|
||||||
|
23: LOAD 1
|
||||||
|
24: PUSH 1
|
||||||
|
25: ADD
|
||||||
|
26: DUP
|
||||||
|
27: STORE 1
|
||||||
|
28: SUB
|
||||||
|
29: STORE 0
|
||||||
|
30: LOAD 0
|
||||||
|
31: PRINT
|
||||||
|
32: LOAD 1
|
||||||
|
33: PRINT
|
||||||
|
34: STOP
|
||||||
|
\end{lstlisting}
|
||||||
|
|
||||||
|
Результаты работы виртуальной машины для программы №3 представлены на Рис.~\ref{fig:result3}.
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
|
\centering
|
||||||
|
\includegraphics[width=0.4\linewidth]{img/result3.png}
|
||||||
|
\caption{Результаты работы виртуальной машины для программы №3.}
|
||||||
|
\label{fig:result3}
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
|
\subsection*{Программа №4}
|
||||||
|
Исходный код программы представлен в листинге~\ref{lst:program4}.
|
||||||
|
|
||||||
|
\begin{lstlisting}[caption=Исходный код программы №4, label=lst:program4]
|
||||||
|
BEGIN
|
||||||
|
x := 10;
|
||||||
|
y := 10 - --x; /* Корректно: минус и декремент разделены пробелом */
|
||||||
|
y := 10 ---x; /* Некорректно: три минуса подряд */
|
||||||
|
END
|
||||||
|
\end{lstlisting}
|
||||||
|
|
||||||
|
Результат запуска компилятора для данной программы представлен на Рис.~\ref{fig:result4}.
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
|
\centering
|
||||||
|
\includegraphics[width=0.5\linewidth]{img/result4.png}
|
||||||
|
\caption{Результат запуска компилятора для программы №4.}
|
||||||
|
\label{fig:result4}
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
|
|
||||||
|
\newpage
|
||||||
|
\section*{Заключение}
|
||||||
|
\addcontentsline{toc}{section}{Заключение}
|
||||||
|
В ходе выполнения лабораторной работы была успешно реализована поддержка операций инкремента и декремента в компиляторе языка MiLan. Были добавлены как префиксные (\texttt{++i}, \texttt{--i}), так и постфиксные (\texttt{i++}, \texttt{i--}) операторы с корректной семантикой их выполнения. Модификации затронули как лексический анализатор (добавление новых токенов \texttt{T\_INC} и \texttt{T\_DEC}), так и синтаксический анализатор (расширение метода \texttt{factor()} для обработки новых конструкций).
|
||||||
|
|
||||||
|
Расширенная грамматика языка MiLan сохранила свойство LL(1), что позволило избежать значительных изменений в существующей архитектуре компилятора. Было добавлено лишь несколько новых правил в определение нетерминала \texttt{<factor>}.
|
||||||
|
|
||||||
|
Из достоинств реализации можно отметить минимальность вносимых изменений в существующую архитектуру компилятора и сохранение всех ранее реализованных функций. При этом реализация выполняет поставленные задачи, корректно обрабатывая возможные случаи использования операторов инкремента и декремента.
|
||||||
|
|
||||||
|
К недостаткам текущей реализации можно отнести отсутствие каких-либо оптимизаций при генерации команд виртуальной машины, что может приводить к избыточному количеству инструкций. Также в коде наблюдается некоторое дублирование логики между обработкой инкремента и декремента.
|
||||||
|
|
||||||
|
В качестве направлений масштабирования можно предложить добавление составных операторов присваивания (\texttt{+=}, \texttt{-=}, \texttt{*=}, \texttt{/=}), для которых генерируется схожая последовательность низкоуровневых команд. Также возможна реализация оптимизаций генерируемого кода, таких как устранение избыточных команд в простых случаях.
|
||||||
|
|
||||||
|
На выполнение лабораторной работы ушло около 6 часов. Работа была выполнена в среде разработки Visual Studio Code.
|
||||||
|
|
||||||
|
\newpage
|
||||||
|
\section*{Список литературы}
|
||||||
|
\addcontentsline{toc}{section}{Список литературы}
|
||||||
|
|
||||||
|
\vspace{-1.5cm}
|
||||||
|
\begin{thebibliography}{0}
|
||||||
|
\bibitem{vostrov}
|
||||||
|
Востров, А.В. Курс лекций по дисциплине <<Математическая логика>>. URL \url{https://tema.spbstu.ru/compiler/} (дата обращения 01.05.2025 г.)
|
||||||
|
\bibitem{aho}
|
||||||
|
А. Ахо, М. Лам, Р. Сети, Дж. Ульман, Компиляторы: принципы, технологии и инструментарий, 2-е изд. М.: Вильямс, 2011.
|
||||||
|
\bibitem{karpov}
|
||||||
|
Ю.Г. Карпов, Теория и технология программирования. Основы построения трансляторов. СПб.: БХВ-Петербург, 2005.
|
||||||
|
\end{thebibliography}
|
||||||
|
|
||||||
|
\end{document}
|
||||||
BIN
lab5/img/tree.png
Normal file
|
After Width: | Height: | Size: 40 KiB |
@@ -36,6 +36,7 @@
|
|||||||
\usepackage{xcolor} % цвета
|
\usepackage{xcolor} % цвета
|
||||||
\usepackage{hyperref}% для гиперссылок
|
\usepackage{hyperref}% для гиперссылок
|
||||||
\usepackage{enumitem} %для перечислений
|
\usepackage{enumitem} %для перечислений
|
||||||
|
\usepackage{latexsym} %для символа \leadsto
|
||||||
|
|
||||||
\newcommand{\specialcell}[2][l]{\begin{tabular}[#1]{@{}l@{}}#2\end{tabular}}
|
\newcommand{\specialcell}[2][l]{\begin{tabular}[#1]{@{}l@{}}#2\end{tabular}}
|
||||||
|
|
||||||
@@ -156,7 +157,40 @@
|
|||||||
\item Назначить семантические действия части заданных продукций (например, генерация машинноориентированного кода).
|
\item Назначить семантические действия части заданных продукций (например, генерация машинноориентированного кода).
|
||||||
\end{itemize}
|
\end{itemize}
|
||||||
|
|
||||||
\textit{Грамматика в варианте 15 состоит из следующих продукций}:
|
\textit{Исходная грамматика в варианте 15 состояла из следующих продукций}:
|
||||||
|
\vspace{-0.3cm}
|
||||||
|
\begin{verbatim}
|
||||||
|
S -> B A b
|
||||||
|
A -> a A B C | b B | a
|
||||||
|
B -> b
|
||||||
|
C -> c A
|
||||||
|
\end{verbatim}
|
||||||
|
|
||||||
|
Однако, как будет показано в разделе математического описания, данная грамматика не является LL(1)-грамматикой из-за неоднозначности в выборе продукций для нетерминала $A$. Поэтому в работе используется модифицированная версия грамматики с удаленной продукцией $A \rightarrow a$.
|
||||||
|
|
||||||
|
|
||||||
|
\newpage
|
||||||
|
\section {Математическое описание}
|
||||||
|
\subsection{Анализ исходной грамматики и её модификация}
|
||||||
|
|
||||||
|
Исходная грамматика варианта 15 содержала следующие продукции:
|
||||||
|
\vspace{-0.3cm}
|
||||||
|
\begin{verbatim}
|
||||||
|
S -> B A b
|
||||||
|
A -> a A B C | b B | a
|
||||||
|
B -> b
|
||||||
|
C -> c A
|
||||||
|
\end{verbatim}
|
||||||
|
|
||||||
|
При попытке построения LL(1)-анализатора для данной грамматики обнаруживается неоднозначность в таблице синтаксического анализа. Конкретно, при рассмотрении нетерминала $A$ и входного терминала $a$ возникает конфликт между двумя продукциями:
|
||||||
|
\begin{itemize}
|
||||||
|
\item $A \rightarrow a$
|
||||||
|
\item $A \rightarrow a A B C$
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
Обе продукции начинаются с терминала $a$, что означает, что $a \in \texttt{FIRST}(a)$ и $a \in \texttt{FIRST}(a A B C)$. Следовательно, анализатор не может однозначно определить, какую продукцию применить при встрече символа $a$ на входе и нетерминала $A$ на вершине стека.
|
||||||
|
|
||||||
|
Для получения корректной LL(1)-грамматики была выполнена модификация: удалена продукция $A \rightarrow a$. Таким образом, итоговая грамматика, используемая в данной работе, имеет вид:
|
||||||
\vspace{-0.3cm}
|
\vspace{-0.3cm}
|
||||||
\begin{verbatim}
|
\begin{verbatim}
|
||||||
S -> B A b
|
S -> B A b
|
||||||
@@ -165,9 +199,8 @@
|
|||||||
C -> c A
|
C -> c A
|
||||||
\end{verbatim}
|
\end{verbatim}
|
||||||
|
|
||||||
|
Данная модификация устраняет неоднозначность и позволяет построить корректную таблицу синтаксического анализа для LL(1)-анализатора. Язык, порождаемый модифицированной грамматикой, остается достаточно выразительным для демонстрации принципов работы LL(1)-анализатора.
|
||||||
|
|
||||||
\newpage
|
|
||||||
\section {Математическое описание}
|
|
||||||
\subsection{Иерархия грамматик Хомского}
|
\subsection{Иерархия грамматик Хомского}
|
||||||
Иерархия Хомского — классификация формальных языков и формальных грамматик, согласно которой они делятся на 4 типа по их условной сложности. На Рис.~\ref{fig:homsky} представлены все четыре типа грамматик (по типу продукций, из которых они состоят) и отношения между ними.
|
Иерархия Хомского — классификация формальных языков и формальных грамматик, согласно которой они делятся на 4 типа по их условной сложности. На Рис.~\ref{fig:homsky} представлены все четыре типа грамматик (по типу продукций, из которых они состоят) и отношения между ними.
|
||||||
|
|
||||||
@@ -184,7 +217,7 @@
|
|||||||
|
|
||||||
Контекстно-свободные грамматики являются частным случаем формальных грамматик, в которых левые части всех продукций содержат только один нетерминальный символ. Это означает, что все продукции имеют вид $A \rightarrow \beta$, где $A$ — нетерминальный символ, а $\beta$ — произвольная цепочка терминалов и нетерминалов.
|
Контекстно-свободные грамматики являются частным случаем формальных грамматик, в которых левые части всех продукций содержат только один нетерминальный символ. Это означает, что все продукции имеют вид $A \rightarrow \beta$, где $A$ — нетерминальный символ, а $\beta$ — произвольная цепочка терминалов и нетерминалов.
|
||||||
|
|
||||||
Грамматика этого варианта лабораторной работы задаётся в виде следующего списка продукций:
|
Модифицированная грамматика этого варианта лабораторной работы задаётся в виде следующего списка продукций:
|
||||||
\begin{verbatim}
|
\begin{verbatim}
|
||||||
S -> B A b
|
S -> B A b
|
||||||
A -> a A B C | b B
|
A -> a A B C | b B
|
||||||
@@ -196,6 +229,15 @@
|
|||||||
|
|
||||||
Язык, порождаемый грамматикой, очевидно, не сложнее контекстно-свободного. Также, по продукции \texttt{A -> a A B C}, можно сделать вывод, что язык не является автоматным. Эта продукция является рекурсивной и порождает вложенную структуру, которая не может быть описана конечным автоматом.
|
Язык, порождаемый грамматикой, очевидно, не сложнее контекстно-свободного. Также, по продукции \texttt{A -> a A B C}, можно сделать вывод, что язык не является автоматным. Эта продукция является рекурсивной и порождает вложенную структуру, которая не может быть описана конечным автоматом.
|
||||||
|
|
||||||
|
Пример дерева вывода для цепочки языка, порождаемого грамматикой, представлен на Рис.~\ref{fig:tree}.
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
|
\centering
|
||||||
|
\includegraphics[width=0.7\linewidth]{img/tree.png}
|
||||||
|
\caption{Дерево вывода для цепочки $b a b b b c b b b$.}
|
||||||
|
\label{fig:tree}
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
|
|
||||||
\subsection{LL(k)-анализаторы и LL(k)-грамматики}
|
\subsection{LL(k)-анализаторы и LL(k)-грамматики}
|
||||||
|
|
||||||
@@ -297,6 +339,7 @@
|
|||||||
\label{tab:syntax_analysis}
|
\label{tab:syntax_analysis}
|
||||||
\end{table}
|
\end{table}
|
||||||
|
|
||||||
|
Если при распознавании цепочки синтаксический анализатор встречает ячейку с прочерком, то это означает, что входная цепочка не принадлежит языку, порождаемому грамматикой. В таком случае синтаксический анализатор завершает работу и возвращает сообщение об ошибке.
|
||||||
|
|
||||||
\subsection{Алгоритм разбора предложений}
|
\subsection{Алгоритм разбора предложений}
|
||||||
|
|
||||||
@@ -347,11 +390,11 @@
|
|||||||
|
|
||||||
Для этого продукции были сопоставлены со следующими инструкциями:
|
Для этого продукции были сопоставлены со следующими инструкциями:
|
||||||
\begin{itemize}
|
\begin{itemize}
|
||||||
\item $S \rightarrow B A b$ -- \texttt{iconst\_1}
|
\item $S \rightarrow B A b$ $\leadsto$ \texttt{iconst\_1}
|
||||||
\item $A \rightarrow a A B C$ -- \texttt{iconst\_2 isub}
|
\item $A \rightarrow a A B C$ $\leadsto$ \texttt{iconst\_2 isub}
|
||||||
\item $A \rightarrow b B$ -- \texttt{iconst\_1 iadd}
|
\item $A \rightarrow b B$ $\leadsto$ \texttt{iconst\_1 iadd}
|
||||||
\item $B \rightarrow b$ -- \texttt{iconst\_3 iadd}
|
\item $B \rightarrow b$ $\leadsto$ \texttt{iconst\_3 iadd}
|
||||||
\item $C \rightarrow c A$ -- \texttt{iconst\_3 iadd}
|
\item $C \rightarrow c A$ $\leadsto$ \texttt{iconst\_3 iadd}
|
||||||
\end{itemize}
|
\end{itemize}
|
||||||
|
|
||||||
Тогда при левом выводе цепочки \texttt{babbbcbbb}, будет сгенерирована следующая последовательность инструкций:
|
Тогда при левом выводе цепочки \texttt{babbbcbbb}, будет сгенерирована следующая последовательность инструкций:
|
||||||
@@ -1089,7 +1132,7 @@ FOLLOW(S) = {$}
|
|||||||
|
|
||||||
Из достоинств выполнения лабораторной работы можно выделить, во-первых, возможность задания грамматики в отдельном текстовом файле, что позволяет легко изменять и расширять её без модификации программного кода. Во-вторых, программа автоматически проверяет, что введенная грамматика является LL(1)-грамматикой, и вычисляет множества FIRST и FOLLOW для всех нетерминалов, а также таблицу синтаксического анализа. В противном случае, программа выводит сообщение об ошибке, в котором указывается на конкретные правила грамматики, между выбором которых возникает неоднозначность. В третьих, программа позволяет легко задавать произвольные семантические действия в виде функций или объектов колбэков, которые будут вызываться при применении продукций.
|
Из достоинств выполнения лабораторной работы можно выделить, во-первых, возможность задания грамматики в отдельном текстовом файле, что позволяет легко изменять и расширять её без модификации программного кода. Во-вторых, программа автоматически проверяет, что введенная грамматика является LL(1)-грамматикой, и вычисляет множества FIRST и FOLLOW для всех нетерминалов, а также таблицу синтаксического анализа. В противном случае, программа выводит сообщение об ошибке, в котором указывается на конкретные правила грамматики, между выбором которых возникает неоднозначность. В третьих, программа позволяет легко задавать произвольные семантические действия в виде функций или объектов колбэков, которые будут вызываться при применении продукций.
|
||||||
|
|
||||||
К недостаткам текущей реализации можно отнести ограниченность словарного запаса, что сужает разнообразие генерируемых предложений. Также алгоритм генерации не контролирует длину предложений, что может приводить к избыточно длинным или коротким конструкциям.
|
К недостаткам текущей реализации можно отнести то, что алгоритм генерации не контролирует длину предложений, что может приводить к избыточно длинным или коротким цепочкам.
|
||||||
|
|
||||||
Функционал программы несложно масштабировать. Например, несложно добавить функцию для визуализации дерева вывода распознаваемой цепочки, пусть даже в текстовом виде, в классе Grammar уже есть все необходимые для этого данные. Кроме того, класс Grammar может служить хорошей основой для создания полноценного LL(k) анализатора.
|
Функционал программы несложно масштабировать. Например, несложно добавить функцию для визуализации дерева вывода распознаваемой цепочки, пусть даже в текстовом виде, в классе Grammar уже есть все необходимые для этого данные. Кроме того, класс Grammar может служить хорошей основой для создания полноценного LL(k) анализатора.
|
||||||
|
|
||||||
|
|||||||