лаб 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}
|
\phantom{text}
|
||||||
|
|
||||||
\section{Особенности реализации}
|
\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
|
\newpage
|
||||||
\section{Результаты работы программы}
|
\section{Результаты работы программы}
|
||||||
Результаты работы программы представлены на Рис.~\ref{fig:result1}.
|
\subsection*{Программа №1}
|
||||||
|
Исходный код программы представлен в листинге~\ref{lst:program1}.
|
||||||
|
|
||||||
% \begin{figure}[h!]
|
\begin{lstlisting}[caption=Исходный код программы №1, label=lst:program1]
|
||||||
% \centering
|
BEGIN
|
||||||
% \includegraphics[width=1\linewidth]{img/result1.png}
|
x := 0;
|
||||||
% \caption{Результаты работы программы.}
|
write(x++);
|
||||||
% \label{fig:result1}
|
write(x);
|
||||||
% \end{figure}
|
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
|
\newpage
|
||||||
\section*{Заключение}
|
\section*{Заключение}
|
||||||
\addcontentsline{toc}{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
|
\newpage
|
||||||
\section*{Список литературы}
|
\section*{Список литературы}
|
||||||
|
|||||||
Reference in New Issue
Block a user