лаб 4
This commit is contained in:
7
lab4/cmilan/test/incdec/inc.mil
Normal file
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
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
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
5
lab4/cmilan/test/incdec/invalid.mil
Normal file
@@ -0,0 +1,5 @@
|
||||
BEGIN
|
||||
x := 10;
|
||||
y := 10 - --x; /* Корректно: минус и декремент разделены пробелом */
|
||||
y := 10 ---x; /* Некорректно: три минуса подряд */
|
||||
END
|
||||
BIN
lab4/report/img/result1.png
Normal file
BIN
lab4/report/img/result1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.4 KiB |
BIN
lab4/report/img/result2.png
Normal file
BIN
lab4/report/img/result2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.0 KiB |
BIN
lab4/report/img/result3.png
Normal file
BIN
lab4/report/img/result3.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.9 KiB |
BIN
lab4/report/img/result4.png
Normal file
BIN
lab4/report/img/result4.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.9 KiB |
@@ -391,45 +391,558 @@
|
||||
\phantom{text}
|
||||
|
||||
\section{Особенности реализации}
|
||||
\subsection{Token}
|
||||
\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{Результаты работы программы}
|
||||
Результаты работы программы представлены на Рис.~\ref{fig:result1}.
|
||||
\subsection*{Программа №1}
|
||||
Исходный код программы представлен в листинге~\ref{lst:program1}.
|
||||
|
||||
% \begin{figure}[h!]
|
||||
% \centering
|
||||
% \includegraphics[width=1\linewidth]{img/result1.png}
|
||||
% \caption{Результаты работы программы.}
|
||||
% \label{fig:result1}
|
||||
% \end{figure}
|
||||
\begin{lstlisting}[caption=Исходный код программы №1, label=lst:program1]
|
||||
BEGIN
|
||||
x := 0;
|
||||
write(x++);
|
||||
write(x);
|
||||
write(++x);
|
||||
write(x)
|
||||
END
|
||||
\end{lstlisting}
|
||||
|
||||
% % \newpage
|
||||
Последовательность команд, сгенерированная компилятором для данной программы, представлена в листинге~\ref{lst:program1_commands}.
|
||||
|
||||
% \begin{figure}[h!]
|
||||
% \centering
|
||||
% \includegraphics[width=0.5\linewidth]{img/wrong.png}
|
||||
% \caption{Реакция программы на некорректный пользовательский ввод.}
|
||||
% \label{fig:wrong}
|
||||
% \end{figure}
|
||||
|
||||
На Рис.~\ref{fig:wrong} представлена реакция программы на некорректный пользовательский ввод.
|
||||
\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}{Заключение}
|
||||
В ходе выполнения лабораторной работы была построена контекстно-свободная грамматика для подмножества немецкого языка, описывающая простое прошедшее время Претерит. На основе разработанной грамматики была реализована программа, которая проверяет принадлежность входной строки заданному языку и генерирует случайные корректные предложения. Для анализа предложений использовался алгоритм LL(1)-разбора, основанный на построении множеств FIRST и FOLLOW для всех нетерминалов грамматики и создании таблицы синтаксического анализа.
|
||||
В ходе выполнения лабораторной работы была успешно реализована поддержка операций инкремента и декремента в компиляторе языка MiLan. Были добавлены как префиксные (\texttt{++i}, \texttt{--i}), так и постфиксные (\texttt{i++}, \texttt{i--}) операторы с корректной семантикой их выполнения. Модификации затронули как лексический анализатор (добавление новых токенов \texttt{T\_INC} и \texttt{T\_DEC}), так и синтаксический анализатор (расширение метода \texttt{factor()} для обработки новых конструкций).
|
||||
|
||||
Из достоинств выполнения лабораторной работы можно выделить возможность задания грамматики в отдельном текстовом файле, что позволяет легко изменять и расширять её без модификации программного кода. Также программа автоматически проверяет, что введенная грамматика является LL(1)-грамматикой. В противном случае, программа выводит сообщение об ошибке, в указывается на конкретные правила грамматики, между выбором которых возникает неоднозначность.
|
||||
Расширенная грамматика языка MiLan сохранила свойство LL(1), что позволило избежать значительных изменений в существующей архитектуре компилятора. Было добавлено лишь несколько новых правил в определение нетерминала \texttt{<factor>}.
|
||||
|
||||
К недостаткам текущей реализации можно отнести ограниченность словарного запаса, что сужает разнообразие генерируемых предложений. Также алгоритм генерации не контролирует длину предложений, что может приводить к избыточно длинным или коротким конструкциям. В текущей версии система не учитывает некоторые грамматические особенности немецкого языка, например, склонение прилагательных и согласование артиклей с родом существительных.
|
||||
Из достоинств реализации можно отметить минимальность вносимых изменений в существующую архитектуру компилятора и сохранение всех ранее реализованных функций. При этом реализация выполняет поставленные задачи, корректно обрабатывая возможные случаи использования операторов инкремента и декремента.
|
||||
|
||||
Функционал программы несложно масштабировать. Грамматику легко расширять, добавляя новые слова и правила в текстовый файл без необходимости изменения программного кода. Класс Grammar может служить хорошей основой для создания полноценного LL(k) анализатора.
|
||||
К недостаткам текущей реализации можно отнести отсутствие каких-либо оптимизаций при генерации команд виртуальной машины, что может приводить к избыточному количеству инструкций. Также в коде наблюдается некоторое дублирование логики между обработкой инкремента и декремента.
|
||||
|
||||
На выполнение лабораторной работы ушло около 12 часов. Работа была выполнена в среде разработки Visual Studio Code. Программа написана на Python версии 3.13.
|
||||
В качестве направлений масштабирования можно предложить добавление составных операторов присваивания (\texttt{+=}, \texttt{-=}, \texttt{*=}, \texttt{/=}), для которых генерируется схожая последовательность низкоуровневых команд. Также возможна реализация оптимизаций генерируемого кода, таких как устранение избыточных команд в простых случаях.
|
||||
|
||||
На выполнение лабораторной работы ушло около 6 часов. Работа была выполнена в среде разработки Visual Studio Code.
|
||||
|
||||
\newpage
|
||||
\section*{Список литературы}
|
||||
|
||||
Reference in New Issue
Block a user