Компилятор и вм милана
This commit is contained in:
4
lab4/.gitignore
vendored
Normal file
4
lab4/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
*.zip
|
||||
*.cmil
|
||||
cmilan/src/cmilan.exe
|
||||
!cmilan/doc/cmilan.pdf
|
||||
10
lab4/cmilan/doc/Makefile
Normal file
10
lab4/cmilan/doc/Makefile
Normal file
@@ -0,0 +1,10 @@
|
||||
cmilan.pdf: cmilan.tex
|
||||
pdflatex cmilan.tex
|
||||
pdflatex cmilan.tex
|
||||
pdflatex cmilan.tex
|
||||
|
||||
clean:
|
||||
rm -f cmilan.aux
|
||||
rm -f cmilan.toc
|
||||
rm -f cmilan.log
|
||||
|
||||
BIN
lab4/cmilan/doc/cmilan.pdf
Normal file
BIN
lab4/cmilan/doc/cmilan.pdf
Normal file
Binary file not shown.
956
lab4/cmilan/doc/cmilan.tex
Normal file
956
lab4/cmilan/doc/cmilan.tex
Normal file
@@ -0,0 +1,956 @@
|
||||
\documentclass[a4paper,12pt]{article}
|
||||
\usepackage{ucs}
|
||||
\usepackage{cmap}
|
||||
\usepackage[utf8x]{inputenc}
|
||||
\usepackage[T2A]{fontenc}
|
||||
\usepackage[english,russian]{babel}
|
||||
\usepackage[hmargin=2cm,vmargin=2cm]{geometry}
|
||||
\usepackage{indentfirst}
|
||||
\usepackage{listings}
|
||||
\usepackage{syntax}
|
||||
\usepackage[section]{placeins}
|
||||
|
||||
\setlength{\grammarparsep}{10pt plus 1pt minus 1pt}
|
||||
\setlength{\grammarindent}{9em}
|
||||
|
||||
\lstset{
|
||||
frame=TB,
|
||||
morekeywords={begin,end,if,then,else,fi,while,do,od,write,read}
|
||||
}
|
||||
|
||||
\title{Компилятор \textsc{CMilan}}
|
||||
\author{Э. Ф. Аллахвердиев, Д. А. Тимофеев}
|
||||
|
||||
\begin{document}
|
||||
\maketitle
|
||||
\tableofcontents
|
||||
|
||||
\section{Обзор языка Милан}
|
||||
|
||||
Язык Милан --- учебный язык программирования, описанный в
|
||||
учебнике~\cite{karpov05}.
|
||||
|
||||
Программа на Милане представляет собой последовательность операторов,
|
||||
заключенных между ключевыми словами \texttt{begin} и \texttt{end}. Операторы
|
||||
отделяются друг от друга точкой с запятой. После последнего оператора в блоке
|
||||
точка с запятой не ставится. Компилятор \textsc{CMilan} не учитывает регистр
|
||||
символов в именах переменных и ключевых словах.
|
||||
|
||||
В базовую версию языка Милан входят следующие конструкции: константы,
|
||||
идентификаторы, арифметические операции над целыми числами, операторы чтения
|
||||
чисел со стандартного ввода и печати чисел на стандартный вывод, оператор
|
||||
присваивания, условный оператор, оператор цикла с предусловием.
|
||||
|
||||
Программа может содержать комментарии, которые могут быть многострочными.
|
||||
Комментарий начинается символами `\texttt{/*}' и заканчивается символами
|
||||
`\texttt{*/}'. Вложенные комментарии не допускаются.
|
||||
|
||||
Грамматика языка Милан в расширенной форме Бэкуса-Наура приведена на
|
||||
рисунке~\ref{milan-grammar}.
|
||||
|
||||
\begin{figure}
|
||||
\begin{grammar}
|
||||
|
||||
<program> ::= `begin' <statementList> `end'
|
||||
|
||||
<statementList> ::= <statement> `;' <statementList>
|
||||
\alt $\epsilon$
|
||||
|
||||
<statement> ::= <ident> `:=' <expression>
|
||||
\alt `if' <relation> `then' <statementList> [`else' <statementList>] `fi'
|
||||
\alt `while' <relation> `do' <statementList> `od'
|
||||
\alt `write' `(' <expression> `)'
|
||||
|
||||
<expression> ::= <term> \{<addop> <term>\}
|
||||
|
||||
<term> ::= <factor> \{<mulop> <factor>\}
|
||||
|
||||
<factor> ::= <ident> | <number> | `(' <expression> `)'
|
||||
|
||||
<relation> ::= <expression> <cmp> <expression>
|
||||
|
||||
<addop> ::= `+' | `-'
|
||||
|
||||
<multop> ::= `*' | `/'
|
||||
|
||||
<cmp> ::= `=' | `!=' | `<' | `<=' | `>' | `>='
|
||||
|
||||
<ident> ::= <letter> \{<letter> | <digit>\}
|
||||
|
||||
<letter> ::= `a' | `b' | `c' | \ldots | `z' | `A' | `B' | `C' | \ldots | `Z'
|
||||
|
||||
<digit> ::= `0' | `1' | `2' | `3' | `4' | `5' | `6' | `7' | `8' | `9'
|
||||
|
||||
\end{grammar}
|
||||
\label{milan-grammar}
|
||||
\caption{Грамматика языка Милан}
|
||||
\end{figure}
|
||||
|
||||
\subsection{Константы}
|
||||
|
||||
В языке реализована поддержка знаковых целочисленных констант. Синтаксически
|
||||
константы представляют собой последовательность цифр, перед которой может
|
||||
находиться знак `-'. Примеры констант: \texttt{0}, \texttt{9}, \texttt{1234},
|
||||
\texttt{-19}, \texttt{-0}.
|
||||
|
||||
\subsection{Идентификаторы}
|
||||
|
||||
Идентификаторы представляют собой последовательность букв латинского алфавита и
|
||||
цифр. Первым символом идентификатора должна быть буква. Максимальная длина
|
||||
идентификатора ограничена 63 символами. Регистр символов не учитывается.
|
||||
Идентификаторы не должны совпадать с ключевыми словами \texttt{begin},
|
||||
\texttt{end}, \texttt{if}, \texttt{then}, \texttt{else}, \texttt{fi},
|
||||
\texttt{do}, \texttt{od}, \texttt{while}, \texttt{read}, \texttt{write}.
|
||||
|
||||
Примеры идентификаторов: \texttt{a}, \texttt{X}, \texttt{GCD}, \texttt{Milan23}.
|
||||
Цепочки символов, недопустимые в качестве идентификаторов: \texttt{12a} (первый
|
||||
символ не является буквой), \texttt{Begin} (цепочка символов совпадает с
|
||||
ключевым словом), \texttt{a\_1} (цепочка содержит символ подчеркивания).
|
||||
|
||||
\subsection{Арифметические выражения}
|
||||
|
||||
\begin{grammar}
|
||||
<expression> ::= <term> \{<addop> <term>\}
|
||||
|
||||
<term> ::= <factor> \{<mulop> <factor>\}
|
||||
|
||||
<factor> ::= <ident> | <number> | `(' <expression> `)'
|
||||
\end{grammar}
|
||||
|
||||
Арифметические выражения строятся по традиционным для языков программирования
|
||||
правилам. Элементами арифметических выражений могут быть константы,
|
||||
идентификаторы, знаки арифметических операций `\texttt{+}' (сложение),
|
||||
`\texttt{-}' (вычитание), `\texttt{*}' (умножение), `\texttt{/}'
|
||||
(деление), скобки и ключевое слово \texttt{read}, которое обозначает операцию
|
||||
чтения числа со стандартного ввода.
|
||||
|
||||
Примеры правильных выражений: \texttt{12 + 4}, \texttt{i}, \texttt{(x+1)*y},
|
||||
\texttt{2*x+1}, \texttt{read}, \texttt{read * (x - read)}.
|
||||
|
||||
Операции `\texttt{*}' и `\texttt{/}' имеют более высокий приоритет, чем
|
||||
операции `\texttt{+}' и `\texttt{-}'. Все эти операции левоассоциативны.
|
||||
|
||||
Значения арифметических выражений вычисляются слева направо с учетом приоритета
|
||||
операций. Порядок вычисления важен из-за присутствия операции \texttt{read},
|
||||
благодаря которой вычисление значения выражения имеет побочный эффект.
|
||||
|
||||
\subsection{Печать чисел на стандартный вывод}
|
||||
|
||||
\begin{grammar}
|
||||
<statement> ::= `write' `(' <expression> `)'
|
||||
\end{grammar}
|
||||
|
||||
Печать чисел осуществляется с помощью оператора \texttt{write}, аргументом
|
||||
которого является произвольное арифметическое выражение. Примеры использования
|
||||
оператора \texttt{write}: \texttt{write(5)},
|
||||
\texttt{write(n+7)}, \texttt{write((2+read)*3)}, \texttt{write(read)}.
|
||||
|
||||
\subsection{Оператор присваивания}
|
||||
|
||||
\begin{grammar}
|
||||
<statement> ::= <ident> `:=' <expression>
|
||||
\end{grammar}
|
||||
|
||||
При выполнении оператора присваивания сначала вычисляется значение выражения,
|
||||
записанного в его правой части. Затем результат записывается в ячейку памяти,
|
||||
соответствующую переменной в левой части оператора присваивания.
|
||||
|
||||
Последовательность символов `\texttt{:=}' является неделимой и не может
|
||||
содержать пробелов между символами `\texttt{:}' и `\texttt{=}'.
|
||||
|
||||
Примеры корректных операторов присваивания: \texttt{a := b + 1}, \texttt{c :=
|
||||
read}.
|
||||
|
||||
\subsection{Условный оператор}
|
||||
|
||||
\begin{grammar}
|
||||
<statement> ::= `if' <relation> `then' <statementList> [`else' <statementList>] `fi'
|
||||
|
||||
<relation> ::= <expression> <cmp> <expression>
|
||||
|
||||
<cmp> ::= `=' | `!=' | `<' | `<=' | `>' | `>='
|
||||
\end{grammar}
|
||||
|
||||
Условный оператор включает:
|
||||
\begin{enumerate}
|
||||
\item условие, которое представляет собой проверку на равенство или неравенство
|
||||
двух арифметических выражений,
|
||||
\item последовательность операторов, которая должна быть выполнена, если условие
|
||||
истинно (блок \texttt{then}),
|
||||
\item необязательную последовательность операторов, которая должна быть
|
||||
выполнена, если условие ложно (блок \texttt{else}).
|
||||
\end{enumerate}
|
||||
|
||||
Проверка условия $a ? b$, где $a$ и $b$ --- арифметические выражения, а <<$?$>>
|
||||
--- один из операторов сравнения, производится следующим образом:
|
||||
\begin{enumerate}
|
||||
\item вычисляется значение выражения $a$;
|
||||
\item вычисляется значение выражения $b$;
|
||||
\item проверяется выполнение условия.
|
||||
\end{enumerate}
|
||||
|
||||
Поддерживаются следующие отношения: `\texttt{=}' (<<равно>>), `\texttt{!=}'
|
||||
(<<не равно>>), `\texttt{<}' (<<меньше>>), `\texttt{<=}' (<<меньше или равно>>),
|
||||
`\texttt{>}' (<<больше>>), `\texttt{>=}' (<<больше или равно>>).
|
||||
|
||||
Условный оператор завершается ключевым словом \texttt{fi}. Оно используется,
|
||||
чтобы избежать известной проблемы <<висячего else>>, которая встречается, в
|
||||
частности, в языках C, C++ и Java. Наличие <<закрывающей скобки>> \texttt{fi}
|
||||
позволяет однозначно интерпретировать вложенные условные операторы.
|
||||
|
||||
Примеры использования условного оператора приведены на рисунках~\ref{ifthen} и
|
||||
\ref{ifthenelse}. Обе эти программы считывают со стандартного ввода два числа.
|
||||
Программа на рисунке~\ref{ifthen} печатает наибольшее из двух чисел. Программа
|
||||
на рисунке~\ref{ifthenelse} печатает числа в порядке возрастания.
|
||||
|
||||
\begin{figure}
|
||||
\begin{lstlisting}
|
||||
begin
|
||||
x := read;
|
||||
y := read;
|
||||
max := x;
|
||||
if y > max then
|
||||
max := y
|
||||
fi;
|
||||
write (max)
|
||||
end
|
||||
\end{lstlisting}
|
||||
\label{ifthen}
|
||||
\caption{Пример условного оператора \texttt{if..then..fi}}
|
||||
\end{figure}
|
||||
|
||||
\begin{figure}
|
||||
\begin{lstlisting}
|
||||
begin
|
||||
x := read;
|
||||
y := read;
|
||||
if x <= y then
|
||||
write (x);
|
||||
write (y)
|
||||
else
|
||||
write (y);
|
||||
write (x)
|
||||
fi
|
||||
end
|
||||
\end{lstlisting}
|
||||
\label{ifthenelse}
|
||||
\caption{Пример условного оператора \texttt{if..then..else..fi}}
|
||||
\end{figure}
|
||||
|
||||
\subsection{Цикл с предусловием}
|
||||
|
||||
\begin{grammar}
|
||||
<statement> ::= `while' <relation> `do' <statementList> `od'
|
||||
|
||||
<relation> ::= <expression> <cmp> <expression>
|
||||
|
||||
<cmp> ::= `=' | `!=' | `<' | `<=' | `>' | `>='
|
||||
\end{grammar}
|
||||
|
||||
При выполнении цикла с предусловием сначала проверяется, истинно ли условие.
|
||||
Если оно истинно, последовательно выполняются операторы, составляющие тело
|
||||
цикла. После того, как последний оператор будет выполнен, управление снова
|
||||
возвращается к проверке условия. Если условие оказывается ложным, выполнение
|
||||
цикла завершается. Вычисление выражений, необходимых для проверки условия,
|
||||
производится так же, как и при выполнении условного оператора.
|
||||
|
||||
Пример программы, использующей цикл, приведен на рисунке~\ref{while}. Эта
|
||||
программа считывает со стандартного ввода число и печатает его факториал.
|
||||
|
||||
\begin{figure}
|
||||
\begin{lstlisting}
|
||||
begin
|
||||
n := read;
|
||||
factorial := 1;
|
||||
x := 1;
|
||||
while x <= n do
|
||||
factorial := factorial * x;
|
||||
x := x + 1
|
||||
od;
|
||||
write (factorial)
|
||||
end
|
||||
\end{lstlisting}
|
||||
\label{while}
|
||||
\caption{Пример цикла с предусловием}
|
||||
\end{figure}
|
||||
|
||||
\section{Использование компилятора}
|
||||
|
||||
Компилятор \textsc{CMilan} преобразует программы на языке Милан в
|
||||
последовательность команд виртуальной машины Милана. Общее описание виртуальной
|
||||
машины можно найти в учебнике~\cite{karpov05}, а более детальное
|
||||
описание --- или в документации, прилагаемой к исходным текстам виртуальной
|
||||
машины.
|
||||
|
||||
Исполняемый файл компилятора называется \texttt{cmilan} (в операционных системах
|
||||
семейства Unix) или \texttt{cmilan.exe} (в Windows). Компилятор имеет интерфейс
|
||||
командной строки. Чтобы скомпилировать программу, записанную в файле
|
||||
<<program.mil>>, нужно выполнить команду <<\texttt{cmilan program.mil}>>.
|
||||
Результатом работы компилятора является либо последовательность команд для
|
||||
виртуальной машины Милана, которая печатается на стандарный вывод, либо набор
|
||||
сообщений о синтаксических ошибках, которые выводятся в стандартный поток
|
||||
ошибок.
|
||||
|
||||
При вызове программы \texttt{cmilan} без параметров компилятор печатает краткую
|
||||
информацию о порядке его вызова.
|
||||
|
||||
Чтобы сохранить генерируемую компилятором программу для виртуальной машины в
|
||||
файл, достаточно перенаправить в этот файл стандартный поток вывода.
|
||||
|
||||
На рисунке~\ref{cmilan-usage} приведен пример сеанса работы с компилятором.
|
||||
|
||||
\begin{figure}
|
||||
\begin{verbatim}
|
||||
bash$ cat factorial.mil
|
||||
BEGIN
|
||||
n := READ;
|
||||
factorial := 1;
|
||||
i := 1;
|
||||
WHILE i <= n DO
|
||||
factorial := factorial * i;
|
||||
i := i + 1
|
||||
OD;
|
||||
WRITE(factorial)
|
||||
END
|
||||
bash$ cmilan factorial.mil > factorial.out
|
||||
bash$ milanvm factorial.out
|
||||
Reading input from factorial.out
|
||||
> 5
|
||||
120
|
||||
\end{verbatim}
|
||||
\label{cmilan-usage}
|
||||
\caption{Пример использования компилятора \textsc{CMilan}}
|
||||
\end{figure}
|
||||
|
||||
\subsection{Сборка компилятора в Unix}
|
||||
|
||||
Для сборки компилятора в операционных системах семейства Unix (в частности,
|
||||
GNU/Linux и FreeBSD) необходим компилятор C++, в качестве которого рекомендуется
|
||||
использовать GCC, и GNU Make. Для сборки компилятора достаточно перейти в
|
||||
каталог \texttt{src}, в котором расположены исходные тексты компилятора
|
||||
\textsc{CMilan}, и выполнить команду \texttt{make}.
|
||||
|
||||
\subsection{Сборка компилятора в Windows}
|
||||
|
||||
Для сборки компилятора \textsc{CMilan} в Windows можно использовать компилятор
|
||||
GCC или Microsoft Visual Studio. При использовании GCC (Cygwin, MinGW) сборка
|
||||
производится так же, как и в Unix. Для сборки компилятора с помощью Visual
|
||||
Studio 2010 в дистрибутив компилятора включены файлы проекта (каталог
|
||||
\texttt{src\\cmilan\_vs2011}). Если необходимо использовать другие версии Visual
|
||||
Studio, достаточно создать пустой проект консольного приложения Win32 и добавить
|
||||
в проект существующие исходные тексты из каталога \texttt{src}.
|
||||
|
||||
\section{Устройство компилятора}
|
||||
|
||||
\subsection{Архитектура компилятора}
|
||||
Компилятор \textsc{CMilan} включает три компонента:
|
||||
\begin{enumerate}
|
||||
\item лексический анализатор;
|
||||
\item синтаксический анализатор;
|
||||
\item генератор команд виртуальной машины Милана.
|
||||
\end{enumerate}
|
||||
|
||||
Лексический анализатор посимвольно читает из входного потока текст программы и преобразует
|
||||
группы символов в лексемы (терминальные символы грамматики языка Милан). При
|
||||
этом он отслеживает номер текущей строки, который используется синтаксическим
|
||||
анализатором при формировании сообщений об ошибках. Также лексический анализатор
|
||||
удаляет пробельные символы и комментарии. При формировании лексем анализатор
|
||||
идентифицирует ключевые слова и последовательности символов (например, оператор
|
||||
присваивания), а также определяет значения числовых констант и имена переменных.
|
||||
Эти значения становятся значениями атрибутов, связанных с лексемами.
|
||||
|
||||
Синтаксический анализатор читает сформированную лексическим анализатором
|
||||
последовательность лексем и проверяет ее соответствие грамматике языка Милан.
|
||||
Для этого используется метод рекурсивного спуска. Если в процессе
|
||||
синтаксического анализа обнаруживается ошибка, анализатор формирует сообщение об
|
||||
ошибке, включающее сведения об ошибке и номер строки, в которой она возникла.
|
||||
В процессе анализа программы синтаксический анализатор генерирует набор машинных
|
||||
команд, соответствующие каждой конструкции языка.
|
||||
|
||||
Генератор кода представляет собой служебный компонент, ответственный за
|
||||
формирование внешнего представления генерируемого кода. Генератор поддерживает
|
||||
буфер команд и предоставляет синтаксическому анализатору набор функций,
|
||||
позволяющий записать указанную команду по определенному адресу. После того, как
|
||||
синтаксический анализатор заканчивает формирование программы, генератор кода
|
||||
используется для печати на стандартный вывод отсортированной по возрастанию
|
||||
адресов последовательности инструкций. Использование генератора кода несколько
|
||||
упрощает устройство компилятора, поскольку синтаксический анализатор может
|
||||
генерировать команды не в порядке их следования в программе, а по мере получения
|
||||
всей необходимой информации для их формирования. Это особенно важно при
|
||||
трансляции таких инструкций, как условные операторы или циклы.
|
||||
|
||||
Все три компонента объединяются <<драйвером>> --- управляющей программой
|
||||
компилятора. Драйвер анализирует аргументы командной строки, инициализирует
|
||||
синтаксический анализатор и вызывает его метод \texttt{parse()}, запуская
|
||||
процесс трансляции.
|
||||
|
||||
\textsc{CMilan} является примером простейшего однопроходного компилятора,
|
||||
в котором синтаксический анализ и генерация кода выполняются совместно. При этом
|
||||
компилятор никак не оптимизирует код. Реальные компиляторы
|
||||
обычно выполняют трансляцию и оптимизацию кода в несколько проходов, используя
|
||||
несколько видов внутреннего представления программы. Сведения об архитектуре
|
||||
таких компиляторов можно найти, в частности, в классической <<книге
|
||||
дракона>>~\cite{dragonbook11}. В случае \textsc{CMilan}, тем не менее,
|
||||
предпочтение было отдано не качеству генерируемого кода, а простоте реализации и
|
||||
легкости расширения.
|
||||
|
||||
\subsection{Генератор кода}
|
||||
|
||||
Основной задачей генератора кода является хранение и заполнение буфера
|
||||
инструкций последовательностью команд для виртуальной машины Милана. Генератор
|
||||
кода не отвечает за правильность этой последовательности и не выполняет никаких
|
||||
семантических преобразований. Тем не менее, генератор кода вполне мог бы быть
|
||||
расширен для того, чтобы выполнять простые оптимизации на уровне машинных
|
||||
команд.
|
||||
|
||||
Генератор кода описан в файлах \texttt{codegen.h} и \texttt{codegen.cpp}. Его
|
||||
реализация состоит из двух классов. Класс \texttt{Command} описывает машинные
|
||||
инструкций и отвечает за их вывод. В классе Codegen реализованы функции добавления
|
||||
инструкций в буфер, получения текущего адреса в буфере, резервирования ячейки
|
||||
для инструкции, которая должна быть добавлена в код позднее, и печати
|
||||
последовательности инструкций из буфера на стандартный вывод.
|
||||
|
||||
Приведем краткое описание методов генератора кода.
|
||||
\begin{itemize}
|
||||
\item \texttt{void Command::print(int address, ostream\& os)}
|
||||
|
||||
Печать инструкции с указанием адреса \texttt{address} в поток вывода \texttt{os}.
|
||||
|
||||
\item \texttt{void CodeGen::emit(Instruction instruction)}
|
||||
|
||||
Добавление инструкции \texttt{instruction} без аргументов в конец буфера.
|
||||
|
||||
\item \texttt{void CodeGen::emit(Instruction instruction, int arg)}
|
||||
|
||||
Добавление инструкции \texttt{instruction} с аргументом \texttt{arg} в конец буфера.
|
||||
|
||||
\item \texttt{void CodeGen::emitAt(int address, Instruction instruction)}
|
||||
|
||||
Запись инструкции \texttt{instruction} без аргументов в буфер со смещением
|
||||
\texttt{address}.
|
||||
|
||||
\item \texttt{void CodeGen::emitAt(int address, Instruction instruction, int
|
||||
arg)}
|
||||
|
||||
Запись инструкции \texttt{instruction} с аргументом \texttt{arg} в буфер
|
||||
со смещением \texttt{address}
|
||||
|
||||
\item \texttt{int CodeGen::getCurrentAddress()}
|
||||
|
||||
Возврат адреса, по которому будет записана очередная инструкция.
|
||||
|
||||
\item \texttt{int CodeGen::reserve()}
|
||||
|
||||
Резервирование ячейки памяти для инструкции. Метод добавляет в конец
|
||||
буфера инструкцию \texttt{NOP} и возвращает ее адрес.
|
||||
|
||||
\item \texttt{void CodeGen::flush()}
|
||||
|
||||
Вывод последовательности инструкций в выходной поток.
|
||||
\end{itemize}
|
||||
|
||||
\subsection{Лексический анализатор}
|
||||
|
||||
Лексический анализатор преобразует считываемые из входного потока символы в
|
||||
лексемы языка Милан. Например, последовательность символов `\texttt{B}',
|
||||
`\texttt{E}', `\texttt{G}', `\texttt{I}', `\texttt{N}' соответствует ключевому
|
||||
слову `\texttt{begin}'. Каждой лексеме соответствует отдельный элемент
|
||||
перечислимого типа \texttt{Token}. В частности, ключевому слову `\texttt{begin}'
|
||||
соответствует константа \texttt{T\_BEGIN}. С некоторыми лексемами связана
|
||||
дополнительная информация --- значение атрибута. Так, с лексемой
|
||||
\texttt{T\_NUMBER} (целочисленная константа) связано число, равное значению этой
|
||||
константы, а с лексемой \texttt{T\_IDENTIFIER} (идентификатор) --- строка,
|
||||
содержащая имя переменной.
|
||||
|
||||
В таблице~\ref{lexemes} приведен полный список лексем и значений связанных с ними
|
||||
атрибутов.
|
||||
|
||||
\begin{table}
|
||||
\begin{center}
|
||||
\begin{tabular}{|l|l|p{6cm}|}
|
||||
\hline
|
||||
\hline
|
||||
Имя лексемы & Значение & Атрибут \\
|
||||
\hline
|
||||
\hline
|
||||
\texttt{T\_EOF} & Конец текстового потока & \\
|
||||
\hline
|
||||
\texttt{T\_ILLEGAL} & Недопустимый символ & \\
|
||||
\hline
|
||||
\texttt{T\_IDENTIFIER}& Идентификатор & Имя переменной \\
|
||||
\hline
|
||||
\texttt{T\_NUMBER} & Константа & Значение константы \\
|
||||
\hline
|
||||
\texttt{T\_BEGIN} & Ключевое слово `\texttt{begin}' & \\
|
||||
\hline
|
||||
\texttt{T\_END} & Ключевое слово `\texttt{end}' & \\
|
||||
\hline
|
||||
\texttt{T\_IF} & Ключевое слово `\texttt{if}' & \\
|
||||
\hline
|
||||
\texttt{T\_THEN} & Ключевое слово `\texttt{then}' & \\
|
||||
\hline
|
||||
\texttt{T\_ELSE} & Ключевое слово `\texttt{else}' & \\
|
||||
\hline
|
||||
\texttt{T\_FI} & Ключевое слово `\texttt{fi}' & \\
|
||||
\hline
|
||||
\texttt{T\_WHILE} & Ключевое слово `\texttt{while}' & \\
|
||||
\hline
|
||||
\texttt{T\_DO} & Ключевое слово `\texttt{do}' & \\
|
||||
\hline
|
||||
\texttt{T\_OD} & Ключевое слово `\texttt{od}' & \\
|
||||
\hline
|
||||
\texttt{T\_WRITE} & Ключевое слово `\texttt{write}' & \\
|
||||
\hline
|
||||
\texttt{T\_READ} & Ключевое слово `\texttt{read}' & \\
|
||||
\hline
|
||||
\texttt{T\_ASSIGN} & Оператор `\texttt{:=}' & \\
|
||||
\hline
|
||||
\texttt{T\_ADDOP} & Операция типа сложения &
|
||||
\texttt{A\_PLUS}~(`\texttt{+}'), \texttt{A\_MINUS}~(`\texttt{-}') \\
|
||||
\hline
|
||||
\texttt{T\_MULOP} & Операция типа умножения &
|
||||
\texttt{A\_MULTIPLY}~(`\texttt{*}'), \texttt{A\_DIVIDE}~(`\texttt{/}') \\
|
||||
\hline
|
||||
\texttt{T\_CMP} & Оператор отношения &
|
||||
\texttt{C\_EQ}~(`\texttt{=}'), \texttt{C\_NE}~(`\texttt{!=}'),
|
||||
\texttt{C\_LT}~(`\texttt{<}'), \texttt{C\_LE}~(`\texttt{<=}'),
|
||||
\texttt{C\_GT}~(`\texttt{>}'), \texttt{C\_GE}~(`\texttt{=}') \\
|
||||
\hline
|
||||
\texttt{T\_LPAREN} & Открывающая скобка & \\
|
||||
\hline
|
||||
\texttt{T\_RPAREN} & Закрывающая скобка & \\
|
||||
\hline
|
||||
\texttt{T\_SEMICOLON} & `\texttt{;}' & \\
|
||||
\hline
|
||||
\hline
|
||||
\end{tabular}
|
||||
\end{center}
|
||||
\caption{Лексемы языка Милан}
|
||||
\label{lexemes}
|
||||
\end{table}
|
||||
|
||||
Все ключевые слова перечислены в ассоциативном массиве \texttt{keywords\_}. При
|
||||
этом ключом является строка, соответствующая ключевому слову в нижнем регистре,
|
||||
а значением --- соответствующая лексема. С помощью массива \texttt{tokenNames\_}
|
||||
каждой лексеме сопоставлено ее строковое внешнее представление, которое
|
||||
используется при формировании сообщений об ошибках. Последовательность
|
||||
символов, не соответствующая ни одной из лексем, считается ошибочной. В этом
|
||||
случае лексический анализатор возвращает значение \texttt{T\_ILLEGAL}.
|
||||
|
||||
Исходный текст лексического анализатора находится в файлах \texttt{scanner.h} и
|
||||
\texttt{scanner.cpp}. Алгоритм лексического анализа реализован в классе
|
||||
\texttt{Scanner}. Он содержит следующие открытые методы:
|
||||
|
||||
\begin{itemize}
|
||||
\item \texttt{Token token()}
|
||||
|
||||
Получение текущей лексемы. Одна и та же лексема может быть прочитана
|
||||
многократно.
|
||||
|
||||
\item \texttt{void nextToken()}
|
||||
|
||||
Переход к следующей лексеме.
|
||||
|
||||
\item \texttt{int getIntValue()}
|
||||
|
||||
Получение целочисленного атрибута текущей лексемы. В базовой версии Милана
|
||||
этот метод используется только для получения значений целочисленных
|
||||
констант.
|
||||
|
||||
\item \texttt{string getStringValue()}
|
||||
|
||||
Получение строкового атрибута текущей лексемы. В базовой версии Милана
|
||||
этот метод используется для получения имен идентификаторов.
|
||||
|
||||
\item \texttt{Cmp getCmpValue()}
|
||||
|
||||
Получение кода операции сравнения текущей лексемы (используется только
|
||||
вместе с лексемой \texttt{T\_CMP}).
|
||||
|
||||
\item \texttt{Arithmetic getArithmeticValue()}
|
||||
|
||||
Получение кода арифметической операции (используется вместе с лексемами
|
||||
\texttt{T\_ADDOP} и \texttt{T\_MULOP}).
|
||||
|
||||
\item \texttt{const int getLineNumber()}
|
||||
|
||||
Получение номера текущей строки в исходном файле (используется при
|
||||
формировании сообщений об ошибках).
|
||||
\end{itemize}
|
||||
|
||||
Поля класса описывают состояние лексического анализатора:
|
||||
\begin{itemize}
|
||||
\item \texttt{const string fileName\_}
|
||||
|
||||
Имя входного файла.
|
||||
|
||||
\item \texttt{int lineNumber\_}
|
||||
|
||||
Номер текущей строки в анализируемой программе.
|
||||
|
||||
\item \texttt{Token token\_}
|
||||
|
||||
Текущая лексема.
|
||||
|
||||
\item \texttt{int intValue\_}
|
||||
|
||||
Целочисленный атрибут лексемы.
|
||||
|
||||
\item \texttt{string stringValue\_}
|
||||
|
||||
Строковый атрибут лексемы.
|
||||
|
||||
\item \texttt{Cmp cmpValue\_}
|
||||
|
||||
Код оператора сравнения.
|
||||
|
||||
\item \texttt{Arithmetic arithmeticValue\_}
|
||||
|
||||
Код арифметической операции.
|
||||
|
||||
\item \texttt{map<string, Token> keywords\_}
|
||||
|
||||
Ассоциативный массив, описывающий ключевые слова языка Милан. Используется
|
||||
при обнаружении цепочки символов, которая может быть как идентификатором,
|
||||
так и ключевым словом.
|
||||
|
||||
\item \texttt{ifstream input\_}
|
||||
|
||||
Входной поток для чтения из файла.
|
||||
|
||||
\item \texttt{char ch\_}
|
||||
|
||||
Очередной символ программы.
|
||||
\end{itemize}
|
||||
|
||||
Закрытые методы класса (служебные функции):
|
||||
\begin{itemize}
|
||||
\item \texttt{bool isIdentifierStart(char c)}
|
||||
|
||||
Метод возвращает значение \texttt{true}, если символ \texttt{c} может быть
|
||||
первым символом идентификатора (в базовой версии языка Милан это означает,
|
||||
что символ является буквой латинского алфавита).
|
||||
|
||||
\item \texttt{bool isIdentifierBody(char c)}
|
||||
|
||||
Метод возвращает значение \texttt{true}, если символ \texttt{c} может
|
||||
быть частью идентификатора. В базовой версии языка Милан эт означает, что
|
||||
символ является буквой латинского алфавита или цифрой.
|
||||
|
||||
\item \texttt{void skipSpace()}
|
||||
|
||||
Пропуск всех пробельных символов (символов пробела, табуляции, перевода
|
||||
строки). При чтении символа перевода строки увеличивается номер текущей
|
||||
строки \texttt{lineNumber\_}.
|
||||
|
||||
\item \texttt{void nextChar()}
|
||||
|
||||
Переход к следующему символу программы.
|
||||
\end{itemize}
|
||||
|
||||
Основная часть алгоритма лексического анализа реализована в методе
|
||||
\texttt{nextToken}. Для того, чтобы перейти к следующей лексеме, выполняется
|
||||
следующая последовательность действий,
|
||||
|
||||
Прежде всего необходимо удалить пробелы и комментарии. Признаком начала
|
||||
комментария является последовательность символов `\texttt{/*}'. При обнаружении
|
||||
символа `\texttt{/}' необходимо проверить следующий за ним символ. Если он не
|
||||
совпадает с `\texttt{*}', значит, был найден оператор деления. В этом случае
|
||||
переменной \texttt{token\_} присваивается значение \texttt{T\_MULOP}, а атрибуту
|
||||
\texttt{arithmeticValue\_} --- значение \texttt{A\_DIVIDE}. Если за символом
|
||||
`\texttt{/}' непосредственно следует `\texttt{*}', лексический анализатор
|
||||
пропускает все символы, пока не встретит закрывающую комментарий
|
||||
последовательность `\texttt{*/}' или конец файла. После того, как был найден
|
||||
признак конца комментария, еще раз вызывается функция \texttt{skipSpace}.
|
||||
Операция удаления комментариев повторяется многократно, пока очередной
|
||||
непробельный символ не окажется отличным от символа `\texttt{/}'. Если в
|
||||
процессе удаления пробелов или комментариев был встречен конец файла, очередной
|
||||
лексемой считается \texttt{T\_EOF}.
|
||||
|
||||
После удаления пробелов и комментариев происходит анализ очередного символа. Он
|
||||
выполняется по следующим правилам.
|
||||
\begin{enumerate}
|
||||
\item Если символ является цифрой, то очередная лексема --- целочисленная константа
|
||||
(\texttt{T\_NUMBER}). Лексический анализатор считывает из входного потока эту
|
||||
и все следующие за ней цифры, преобразуя полученную последовательность в целое
|
||||
число. Это число становится значением атрибута \texttt{intValue\_}.
|
||||
|
||||
\item Если символ может быть началом идентификатора, из потока считываются все
|
||||
последующие символы, которые могут быть частью идентификатора. Полученная
|
||||
последовательность проверяется на совпадение с ключевым словом. В случае
|
||||
совпадения очередной лексемой считается лексема, соответствующая этому ключевому
|
||||
слову. Если цепочка символов не совпала ни с одним ключевым словом, очередная
|
||||
лексема считается идентификатором (\texttt{T\_IDENTIFIER}), а сама цепочка
|
||||
становится значением строкового атрибута \texttt{stringValue\_}.
|
||||
|
||||
\item Если символ равен `\texttt{:}', лексический анализатор считывает следующий
|
||||
символ и проверяет, что он равен `\texttt{=}'. В этом случае возвращается
|
||||
лексема \texttt{T\_ASSIGN}. Если следом за `\texttt{:}' идет любой другой
|
||||
символ, лексема считается ошибочной (\texttt{T\_ILLEGAL}).
|
||||
|
||||
\item Если символ равен `\texttt{!}', производится аналогичная проверка на
|
||||
равенство следующего символа `\texttt{=}'. В случае успеха текущей лексемой
|
||||
становится лексема \texttt{T\_CMP}, а атрибут \texttt{cmpValue\_} принимает
|
||||
значение \texttt{T\_NE}.
|
||||
|
||||
\item Если символ равен `\texttt{<}', `\texttt{>}' или `\texttt{=}', текущей
|
||||
лексемой становится \texttt{T\_CMP}. Чтобы определить значение атрибута
|
||||
\texttt{cmpValue\_}, лексический анализатор может прочитать еще один символ для
|
||||
того, чтобы отличить оператор `\texttt{<}' от оператора `\texttt{>}', а оператор
|
||||
`\texttt{>}' от оператора `\texttt{>=}'.
|
||||
|
||||
\item Если символ равен `\texttt{+}' или `\texttt{-}', переменная
|
||||
\texttt{token\_} принимает значение \texttt{T\_ADDOP}, при этом соответствующим
|
||||
образом устанавливается значение атрибута \texttt{arithmeticValue\_}. Аналогично
|
||||
обрабатываются символ `\texttt{*}' (лексема \texttt{T\_MULOP}). Оператор деления
|
||||
обрабатывать таким же образом не нужно, поскольку он обнаруживается в ходе
|
||||
удаления комментариев.
|
||||
|
||||
\item Если символ совпадает с `\texttt{;}', `\texttt{(}', `\texttt{)}',
|
||||
очередная лексема устанавливается в соответствующее символу значение.
|
||||
\end{enumerate}
|
||||
|
||||
\subsection{Синтаксический анализатор}
|
||||
|
||||
Задачами синтаксического анализатора является проверка соответствия программы
|
||||
грамматике языка Милан (рисунок~\ref{milan-grammar}) и формирование кода для
|
||||
виртуальной машины Милана в соответствии со структурой программы. Синтаксический
|
||||
анализ выполняется методом рекурсивного спуска. Каждому нетерминальному символу
|
||||
грамматики сопоставлен метод, выполняющий проверку соответствия
|
||||
последовательности лексем одному из тех правил грамматики, в левой части которых
|
||||
стоит данный нетерминальный символ. Семантические действия (генерация кода)
|
||||
встроены в код метода.
|
||||
|
||||
Исходный текст синтаксического анализатора находится в файлах \texttt{parser.h}
|
||||
и \texttt{parser.cpp}. Алгоритм синтаксического анализа реализован в классе
|
||||
\texttt{Parser}. Конструктор класса в качестве аргумента принимает имя файла, в
|
||||
котором находится анализируемый текст, и создает экземпляры лексического
|
||||
анализатора и генератора кода.
|
||||
|
||||
Синтаксический анализатор предоставляет один открытый метод \texttt{void parse()},
|
||||
который используется для того, чтобы начать процесс анализа.
|
||||
|
||||
Состояние анализатора описывается следующими полями:
|
||||
\begin{itemize}
|
||||
\item \texttt{Scanner* scanner\_}
|
||||
|
||||
Экземпляр лексического анализатора.
|
||||
|
||||
\item \texttt{CodeGen* codegen\_}
|
||||
|
||||
Экземпляр генератора кода.
|
||||
|
||||
\item \texttt{std::ostream\& output\_}
|
||||
|
||||
Выходной поток, в который должны быть выведены инструкции программы.
|
||||
Базовая версия компилятора Милан использует в качестве выходного потока
|
||||
стандартный вывод (\texttt{std::cout}).
|
||||
|
||||
\item \texttt{bool error\_}
|
||||
|
||||
Признак ошибки в тексте программы. Если \texttt{error\_} принимает
|
||||
значение <<истина>>, генерируемый машинный код не выводится.
|
||||
|
||||
\item \texttt{map<string, int> variables\_}
|
||||
|
||||
Таблица имен, найденных в программе. Она сопоставляет каждой переменной ее
|
||||
адрес в памяти виртуальной машины. Поскольку базовая версия языка Милан не
|
||||
содержит вложенных блоков, процедур или функций, таблица имен представляет
|
||||
собой простой ассоциативный массив.
|
||||
|
||||
\item \texttt{int lastVar\_}
|
||||
|
||||
Адрес последней найденной переменной.
|
||||
\end{itemize}
|
||||
|
||||
Синтаксический анализатор включает ряд вспомогательных функций (закрытые методы
|
||||
класса).
|
||||
|
||||
\begin{itemize}
|
||||
\item \texttt{bool see(Token t)}
|
||||
|
||||
Сравнение текущей лексемы с образцом. Текущая позиция в потоке лексем не
|
||||
изменяется.
|
||||
|
||||
\item \texttt{bool match(Token t)}
|
||||
|
||||
Проверка совпадения текущей лексемы с образцом. Если лексема и образец
|
||||
совпадают, лексема изымается из потока.
|
||||
|
||||
\item \texttt{void mustBe(Token t)}
|
||||
|
||||
Проверка совпадения текущей лексемы с образцом. Если лексема и образец
|
||||
совпадают, лексема изымается из потока. В противном случае формируется
|
||||
сообщение об ошибке, а программа считается некорректной.
|
||||
|
||||
\item \texttt{void next()}
|
||||
|
||||
Переход к следующей лексеме.
|
||||
|
||||
\item \texttt{void reportError(const string\& message)}
|
||||
|
||||
Формирование сообщения об ошибке. Каждое сообщение включает текст
|
||||
\texttt{message} и номер строки, в которой обнаружена ошибка.
|
||||
|
||||
\item \texttt{void recover(Token t)}
|
||||
|
||||
Восстановление после ошибки. Используется примитивный алгоритм: если
|
||||
очередная лексема не совпадает с ожидаемой, анализатор пропускает все
|
||||
последующие лексемы, пока не встретит ожидаемую лексему или
|
||||
конец файла. Хотя такой метод восстановления не отличается точностью, он,
|
||||
тем не менее, позволяет продолжить анализ и, возможно, найти ошибки в
|
||||
оставшейся части программы.
|
||||
|
||||
\item \texttt{int findOrAddVariable(const string\&)}
|
||||
|
||||
Поиск имени переменной в таблице имен. Если имя найдено, метод возвращает
|
||||
адрес переменной, в противном случае имя добавляется в таблицу имен, и для
|
||||
соответствующей переменной резервируется новый адрес.
|
||||
\end{itemize}
|
||||
|
||||
Кроме служебных методов, класс \texttt{Parser} содержит закрытые методы
|
||||
\texttt{void program()}, \texttt{void statementList()}, \texttt{void
|
||||
statement()}, \texttt{void expression()}, \texttt{void term()}, \texttt{void
|
||||
factor()}, \texttt{void relation()}, которые соответствуют нетерминальным
|
||||
символам грамматики. Эти методы не возвращают значений и не принимают
|
||||
аргументов, поскольку с нетерминальными символами не связаны явные атрибуты: все
|
||||
семантические действия, которые производятся во время анализа программы,
|
||||
модифицируют буфер общего для всех методов генератора кода.
|
||||
|
||||
\subsubsection{Распознавание операторов}
|
||||
|
||||
Продемонстрируем алгоритм работы синтаксического анализатора на примере анализа
|
||||
операторов (метод \texttt{statement}).
|
||||
|
||||
В соответствии с грамматикой языка Милан, в качестве оператора может выступать
|
||||
оператор присваивания, условный оператор \texttt{if}, оператор цикла
|
||||
\texttt{while} или оператор печати \texttt{write}. Для выбора нужной
|
||||
альтернативы достаточно прочитать первую лексему оператора.
|
||||
|
||||
Если очередная лексема равна \texttt{T\_IDENTIFIER}, синтаксический анализатор
|
||||
имеет дело с оператором присваивания. Он считывает имя переменной, стоящей в
|
||||
левой части присваивания, и определяет ее адрес. Затем анализатор проверяет, что
|
||||
следующая лексема совпадает с \texttt{T\_ASSIGN}. После знака `\texttt{:=}' в
|
||||
программе должно следовать арифметическое выражение, поэтому анализатор вызывает
|
||||
метод \texttt{expression()}. Этот метод проверяет правильность арифметического
|
||||
выражения и генерирует последовательность команд для его вычисления. В
|
||||
результате выполнены этой последовательности на вершине стека будет находиться
|
||||
значение выражения. Чтобы выполнить присваивание, достаточно записать значение с
|
||||
вершины стека в память по адресу переменной в левой части оператора
|
||||
присваивания. Для этого после вызова \texttt{expression()} нужно добавить в
|
||||
буфер команд инструкцию \texttt{STORE}, аргументом которой будет запомненный
|
||||
ранее адрес переменной.
|
||||
|
||||
Если очередная лексема равна \texttt{T\_IF}, анализатор должен начать разбор
|
||||
условного оператора. Сначала он вызывает метод \texttt{relation()}, проверяя,
|
||||
что после лексемы \texttt{T\_IF} следует правильное сравнение двух выражений.
|
||||
Метод \texttt{relation()} генерирует код, вычисляющий и сравнивающий два
|
||||
выражения. При выполнении этого кода на вершине стека останется значение $1$,
|
||||
если условие выполнено, и $0$ в противном случае. Если условие не выполнено,
|
||||
должен произойти переход к списку операторов блока \texttt{else} или, если этого
|
||||
блока нет, по адресу следующей за условным оператором инструкции. Однако этот
|
||||
адрес еще не известен синтаксическому анализатору. Чтобы решить эту проблему,
|
||||
применяется так называемый <<метод обратных поправок>>~\cite{dragonbook11}.
|
||||
Анализатор резервирует место для условного перехода \texttt{JUMP\_NO} и
|
||||
запоминает адрес этой инструкции в переменной \texttt{jumpNoAddress}, после чего
|
||||
переходит к дальнейшему анализу условного оператора.
|
||||
|
||||
Следом за условием в тексте программы должна находиться лексема
|
||||
\texttt{T\_THEN}. Если она обнаружена, анализатор переходит к следующей лексеме
|
||||
и вызывает метод \texttt{statementList()}, который анализирует список операторов
|
||||
и генерирует для них исполняемый код. Дальнейшие действия зависят от того,
|
||||
присутствует ли в условном операторе ветвь \texttt{else}. Если следующая лексема
|
||||
равна \texttt{T\_ELSE}, синтаксический анализатор должен сгенерировать
|
||||
инструкцию \texttt{JUMP} для выхода из условного оператора, после чего
|
||||
приступить к анализу блока \texttt{else}. Если условный оператор не содержит
|
||||
блока \texttt{else}, инструкция безусловного перехода в конце блока
|
||||
\texttt{then} не нужна.
|
||||
|
||||
Генерация безусловного перехода также требует знания адреса, следующего за
|
||||
последней инструкцией условного оператора, поэтому анализатор снова должен
|
||||
зарезервировать ячейку памяти для команды перехода. Следующий за этой ячейкой
|
||||
адрес содержит первую команду блока \texttt{else}. Это именно тот адрес, по
|
||||
которому необходимо было выполнить переход в случае, если условие не было
|
||||
выполнено. Поскольку теперь этот адрес известен, синтаксический анализатор
|
||||
генерирует инструкцию безусловного перехода и помещает ее в буфер команд по
|
||||
адресу, который записан в переменной \texttt{jumpNoAddress}. В случае отсутствия
|
||||
блока \texttt{else} поправка происходит таким же образом, но в качестве адреса
|
||||
перехода просто используется следующий за последней инструкцией блока
|
||||
\texttt{then} адрес. После вызова функции \texttt{statementList}, которая
|
||||
разбирает список операторов блока \texttt{else}, аналогично исправляется
|
||||
зарезервированная инструкция безусловного перехода.
|
||||
|
||||
Структура кода, соответствующая условному оператору, приведена на
|
||||
рисунке~\ref{ifthenelse-code}.
|
||||
|
||||
\begin{figure}
|
||||
\begin{verbatim}
|
||||
Вычисление выражений
|
||||
...
|
||||
COMPARE operator
|
||||
JUMP_NO elseLabel
|
||||
Операторы блока THEN
|
||||
...
|
||||
JUMP endLabel
|
||||
elseLabel: Операторы блока ELSE
|
||||
...
|
||||
endLabel:
|
||||
\end{verbatim}
|
||||
\caption{Структура исполняемого кода условного оператора}
|
||||
\label{ifthenelse-code}
|
||||
\end{figure}
|
||||
|
||||
Если очередная лексема равна \texttt{T\_WHILE}, синтаксическому анализатору
|
||||
предстоит разобрать оператор цикла. Анализатор должен запомнить текущий адрес
|
||||
команды, поскольку после каждой итерации цикла по этому адресу нужно будет
|
||||
возвращаться. Затем анализатор вызывает метод \texttt{relation()}, чтобы
|
||||
сгенерировать код проверки условия. Если условие окажется ложным, нужно будет
|
||||
выполнить переход на следующий за циклом оператор. Для этого синтаксический
|
||||
анализатор резервирует ячейку памяти для команды \texttt{JUMP\_NO}. После этого
|
||||
он проверяет наличие лексемы \texttt{T\_DO} и вызывает метод
|
||||
\texttt{statementList()} для обработки тела цикла. Затем анализатор генерирует
|
||||
инструкцию безусловного перехода назад, к началу проверки условия, и записывает
|
||||
в зарезервированную ячейку инструкцию условного перехода по известному теперь
|
||||
адресу конца цикла. Для завершения анализа инструкции достаточно убедиться, что
|
||||
очередная лексема равна ожидаемому значению \texttt{T\_OD}. Структура
|
||||
исполняемого кода для цикла приведена на рисунке~\ref{while-code}.
|
||||
|
||||
\begin{figure}
|
||||
\begin{verbatim}
|
||||
whileLabel: Вычисление выражений
|
||||
...
|
||||
COMPARE operator
|
||||
JUMP_NO endLabel
|
||||
...
|
||||
Операторы тела цикла
|
||||
...
|
||||
JUMP whileLabel
|
||||
endLabel:
|
||||
\end{verbatim}
|
||||
\caption{Структура исполняемого кода цикла с предусловием}
|
||||
\label{while-code}
|
||||
\end{figure}
|
||||
|
||||
Если очередная лексема равна \texttt{T\_WRITE}, необходимо проверить, что
|
||||
следующая лексема равна \texttt{T\_LPAREN}, затем вызвать метод
|
||||
\texttt{expression()} и проверить, что за выражением в потоке следует лексема
|
||||
\texttt{T\_RPAREN}. Поскольку код, вычисляющий значение выражения, оставляет его
|
||||
значение на вершине стека, для выполнения печати достаточно после вызова
|
||||
\texttt{expression()} сгенерировать инструкцию \texttt{PRINT}.
|
||||
|
||||
\begin{thebibliography}{9}
|
||||
\addcontentsline{toc}{section}{Список литературы}
|
||||
|
||||
\bibitem{dragonbook11}
|
||||
А. Ахо, М. Лам, Р. Сети, Дж. Ульман,
|
||||
\emph{Компиляторы: принципы, технологии и инструментарий}, 2-е изд.
|
||||
М.: Вильямс, 2011.
|
||||
|
||||
\bibitem{karpov05}
|
||||
Ю.Г. Карпов,
|
||||
\emph{Теория и технология программирования. Основы построения трансляторов}.
|
||||
СПб.: БХВ-Петербург, 2005.
|
||||
\end{thebibliography}
|
||||
|
||||
\end{document}
|
||||
|
||||
23
lab4/cmilan/src/Makefile
Normal file
23
lab4/cmilan/src/Makefile
Normal file
@@ -0,0 +1,23 @@
|
||||
CFLAGS = -Wall -W -Werror -O2
|
||||
LDFLAGS =
|
||||
|
||||
HEADERS = scanner.h \
|
||||
parser.h \
|
||||
codegen.h
|
||||
|
||||
OBJS = main.o \
|
||||
codegen.o \
|
||||
scanner.o \
|
||||
parser.o \
|
||||
|
||||
EXE = cmilan
|
||||
|
||||
$(EXE): $(OBJS) $(HEADERS)
|
||||
$(CXX) $(LDFLAGS) -o $@ $(OBJS)
|
||||
|
||||
.cpp.o:
|
||||
$(CXX) $(CFLAGS) -c $< -o $@
|
||||
|
||||
clean:
|
||||
-@rm -f $(EXE) $(OBJS)
|
||||
|
||||
20
lab4/cmilan/src/cmilan_vs2011/cmilan_vs2011.sln
Normal file
20
lab4/cmilan/src/cmilan_vs2011/cmilan_vs2011.sln
Normal file
@@ -0,0 +1,20 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 11.00
|
||||
# Visual Studio 2010
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cmilan_vs2011", "cmilan_vs2011.vcxproj", "{1D542465-2019-4442-864E-DC785FA067A8}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Win32 = Debug|Win32
|
||||
Release|Win32 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{1D542465-2019-4442-864E-DC785FA067A8}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{1D542465-2019-4442-864E-DC785FA067A8}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{1D542465-2019-4442-864E-DC785FA067A8}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{1D542465-2019-4442-864E-DC785FA067A8}.Release|Win32.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
92
lab4/cmilan/src/cmilan_vs2011/cmilan_vs2011.vcxproj
Normal file
92
lab4/cmilan/src/cmilan_vs2011/cmilan_vs2011.vcxproj
Normal file
@@ -0,0 +1,92 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{1D542465-2019-4442-864E-DC785FA067A8}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>cmilan_vs2011</RootNamespace>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<TargetName>cmilan</TargetName>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<TargetName>cmilan</TargetName>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>
|
||||
</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<PrecompiledHeader>
|
||||
</PrecompiledHeader>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\codegen.h" />
|
||||
<ClInclude Include="..\parser.h" />
|
||||
<ClInclude Include="..\scanner.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\codegen.cpp" />
|
||||
<ClCompile Include="..\main.cpp" />
|
||||
<ClCompile Include="..\parser.cpp" />
|
||||
<ClCompile Include="..\scanner.cpp" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
42
lab4/cmilan/src/cmilan_vs2011/cmilan_vs2011.vcxproj.filters
Normal file
42
lab4/cmilan/src/cmilan_vs2011/cmilan_vs2011.vcxproj.filters
Normal file
@@ -0,0 +1,42 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\codegen.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\parser.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\scanner.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\codegen.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\main.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\parser.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\scanner.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
3
lab4/cmilan/src/cmilan_vs2011/cmilan_vs2011.vcxproj.user
Normal file
3
lab4/cmilan/src/cmilan_vs2011/cmilan_vs2011.vcxproj.user
Normal file
@@ -0,0 +1,3 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
</Project>
|
||||
129
lab4/cmilan/src/codegen.cpp
Normal file
129
lab4/cmilan/src/codegen.cpp
Normal file
@@ -0,0 +1,129 @@
|
||||
#include "codegen.h"
|
||||
|
||||
void Command::print(int address, ostream& os)
|
||||
{
|
||||
os << address << ":\t";
|
||||
switch(instruction_) {
|
||||
case NOP:
|
||||
os << "NOP";
|
||||
break;
|
||||
|
||||
case STOP:
|
||||
os << "STOP";
|
||||
break;
|
||||
|
||||
case LOAD:
|
||||
os << "LOAD\t" << arg_;
|
||||
break;
|
||||
|
||||
case STORE:
|
||||
os << "STORE\t" << arg_;
|
||||
break;
|
||||
|
||||
case BLOAD:
|
||||
os << "BLOAD\t" << arg_;
|
||||
break;
|
||||
|
||||
case BSTORE:
|
||||
os << "BSTORE\t" << arg_;
|
||||
break;
|
||||
|
||||
case PUSH:
|
||||
os << "PUSH\t" << arg_;
|
||||
break;
|
||||
|
||||
case POP:
|
||||
os << "POP";
|
||||
break;
|
||||
|
||||
case DUP:
|
||||
os << "DUP";
|
||||
break;
|
||||
|
||||
case ADD:
|
||||
os << "ADD";
|
||||
break;
|
||||
|
||||
case SUB:
|
||||
os << "SUB";
|
||||
break;
|
||||
|
||||
case MULT:
|
||||
os << "MULT";
|
||||
break;
|
||||
|
||||
case DIV:
|
||||
os << "DIV";
|
||||
break;
|
||||
|
||||
case INVERT:
|
||||
os << "INVERT";
|
||||
break;
|
||||
|
||||
case COMPARE:
|
||||
os << "COMPARE\t" << arg_;
|
||||
break;
|
||||
|
||||
case JUMP:
|
||||
os << "JUMP\t" << arg_;
|
||||
break;
|
||||
|
||||
case JUMP_YES:
|
||||
os << "JUMP_YES\t" << arg_;
|
||||
break;
|
||||
|
||||
case JUMP_NO:
|
||||
os << "JUMP_NO\t" << arg_;
|
||||
break;
|
||||
|
||||
case INPUT:
|
||||
os << "INPUT";
|
||||
break;
|
||||
|
||||
case PRINT:
|
||||
os << "PRINT";
|
||||
break;
|
||||
}
|
||||
|
||||
os << endl;
|
||||
}
|
||||
|
||||
void CodeGen::emit(Instruction instruction)
|
||||
{
|
||||
commandBuffer_.push_back(Command(instruction));
|
||||
}
|
||||
|
||||
void CodeGen::emit(Instruction instruction, int arg)
|
||||
{
|
||||
commandBuffer_.push_back(Command(instruction, arg));
|
||||
}
|
||||
|
||||
void CodeGen::emitAt(int address, Instruction instruction)
|
||||
{
|
||||
commandBuffer_[address] = Command(instruction);
|
||||
}
|
||||
|
||||
void CodeGen::emitAt(int address, Instruction instruction, int arg)
|
||||
{
|
||||
commandBuffer_[address] = Command(instruction, arg);
|
||||
}
|
||||
|
||||
int CodeGen::getCurrentAddress()
|
||||
{
|
||||
return commandBuffer_.size();
|
||||
}
|
||||
|
||||
int CodeGen::reserve()
|
||||
{
|
||||
emit(NOP);
|
||||
return commandBuffer_.size() - 1;
|
||||
}
|
||||
|
||||
void CodeGen::flush()
|
||||
{
|
||||
int count = commandBuffer_.size();
|
||||
for(int address = 0; address < count; ++address) {
|
||||
commandBuffer_[address].print(address, output_);
|
||||
}
|
||||
output_.flush();
|
||||
}
|
||||
100
lab4/cmilan/src/codegen.h
Normal file
100
lab4/cmilan/src/codegen.h
Normal file
@@ -0,0 +1,100 @@
|
||||
#ifndef CMILAN_CODEGEN_H
|
||||
#define CMILAN_CODEGEN_H
|
||||
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
|
||||
// Инструкции виртуальной машины Милана
|
||||
|
||||
enum Instruction
|
||||
{
|
||||
NOP, // отсутствие операции
|
||||
STOP, // остановка машины, завершение работы программы
|
||||
LOAD, // LOAD addr - загрузка слова данных в стек из памяти по адресу addr
|
||||
STORE, // STORE addr - запись слова данных с вершины стека в память по адресу addr
|
||||
BLOAD, // BLOAD addr - загрузка слова данных в стек из памяти по адресу addr + значение на вершине стека
|
||||
BSTORE, // BSTORE addr - запись слова данных по адресу addr + значение на вершине стека
|
||||
PUSH, // PUSH n - загрузка в стек константы n
|
||||
POP, // удаление слова с вершины стека
|
||||
DUP, // копирование слова на вершине стека
|
||||
ADD, // сложение двух слов на вершине стека и запись результата вместо них
|
||||
SUB, // вычитание двух слов на вершине стека и запись результата вместо них
|
||||
MULT, // умножение двух слов на вершине стека и запись результата вместо них
|
||||
DIV, // деление двух слов на вершине стека и запись результата вместо них
|
||||
INVERT, // изменение знака слова на вершине стека
|
||||
COMPARE, // COMPARE cmp - сравнение двух слов на вершине стека с помощью операции сравнения с кодом cmp
|
||||
JUMP, // JUMP addr - безусловный переход по адресу addr
|
||||
JUMP_YES, // JUMP_YES addr - переход по адресу addr, если на вершине стека значение 1
|
||||
JUMP_NO, // JUMP_NO addr - переход по адресу addr, если на вершине стека значение 0
|
||||
INPUT, // чтение целого числа со стандартного ввода и загрузка его в стек
|
||||
PRINT // печать на стандартный вывод числа с вершины стека
|
||||
};
|
||||
|
||||
// Класс Command представляет машинные инструкции.
|
||||
|
||||
class Command
|
||||
{
|
||||
public:
|
||||
// Конструктор для инструкций без аргументов
|
||||
Command(Instruction instruction)
|
||||
: instruction_(instruction), arg_(0)
|
||||
{}
|
||||
|
||||
// Конструктор для инструкций с одним аргументом
|
||||
Command(Instruction instruction, int arg)
|
||||
: instruction_(instruction), arg_(arg)
|
||||
{}
|
||||
|
||||
// Печать инструкции
|
||||
// int address - адрес инструкции
|
||||
// ostream& os - поток вывода, куда будет напечатана инструкция
|
||||
void print(int address, ostream& os);
|
||||
|
||||
private:
|
||||
Instruction instruction_; // Код инструкции
|
||||
int arg_; // Аргумент инструкции
|
||||
};
|
||||
|
||||
// Кодогенератор.
|
||||
// Назначение кодогенератора:
|
||||
// - Формировать программу для виртуальной машины Милана
|
||||
// - Отслеживать адрес последней инструкции
|
||||
// - Буферизовать программу и печатать ее в указанный поток вывода
|
||||
|
||||
class CodeGen
|
||||
{
|
||||
public:
|
||||
explicit CodeGen(ostream& output)
|
||||
: output_(output)
|
||||
{
|
||||
}
|
||||
|
||||
// Добавление инструкции без аргументов в конец программы
|
||||
void emit(Instruction instruction);
|
||||
|
||||
// Добавление инструкции с одним аргументом в конец программы
|
||||
void emit(Instruction instruction, int arg);
|
||||
|
||||
// Запись инструкции без аргументов по указанному адресу
|
||||
void emitAt(int address, Instruction instruction);
|
||||
|
||||
// Запись инструкции с одним аргументом по указанному адресу
|
||||
void emitAt(int address, Instruction instruction, int arg);
|
||||
|
||||
// Получение адреса, непосредственно следующего за последней инструкцией в программе
|
||||
int getCurrentAddress();
|
||||
|
||||
// Формирование "пустой" инструкции (NOP) и возврат ее адреса
|
||||
int reserve();
|
||||
|
||||
// Запись последовательности инструкций в выходной поток
|
||||
void flush();
|
||||
|
||||
private:
|
||||
ostream& output_; // Выходной поток
|
||||
vector<Command> commandBuffer_; // Буфер инструкций
|
||||
};
|
||||
|
||||
#endif
|
||||
BIN
lab4/cmilan/src/codegen.o
Normal file
BIN
lab4/cmilan/src/codegen.o
Normal file
Binary file not shown.
32
lab4/cmilan/src/main.cpp
Normal file
32
lab4/cmilan/src/main.cpp
Normal file
@@ -0,0 +1,32 @@
|
||||
#include "parser.h"
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
|
||||
using namespace std;
|
||||
|
||||
void printHelp()
|
||||
{
|
||||
cout << "Usage: cmilan input_file" << endl;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
if(argc < 2) {
|
||||
printHelp();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
ifstream input;
|
||||
input.open(argv[1]);
|
||||
|
||||
if(input) {
|
||||
Parser p(argv[1], input);
|
||||
p.parse();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
else {
|
||||
cerr << "File '" << argv[1] << "' not found" << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
BIN
lab4/cmilan/src/main.o
Normal file
BIN
lab4/cmilan/src/main.o
Normal file
Binary file not shown.
270
lab4/cmilan/src/parser.cpp
Normal file
270
lab4/cmilan/src/parser.cpp
Normal file
@@ -0,0 +1,270 @@
|
||||
#include "parser.h"
|
||||
#include <sstream>
|
||||
|
||||
//Выполняем синтаксический разбор блока program. Если во время разбора не обнаруживаем
|
||||
//никаких ошибок, то выводим последовательность команд стек-машины
|
||||
void Parser::parse()
|
||||
{
|
||||
program();
|
||||
if(!error_) {
|
||||
codegen_->flush();
|
||||
}
|
||||
}
|
||||
|
||||
void Parser::program()
|
||||
{
|
||||
mustBe(T_BEGIN);
|
||||
statementList();
|
||||
mustBe(T_END);
|
||||
codegen_->emit(STOP);
|
||||
}
|
||||
|
||||
void Parser::statementList()
|
||||
{
|
||||
// Если список операторов пуст, очередной лексемой будет одна из возможных "закрывающих скобок": END, OD, ELSE, FI.
|
||||
// В этом случае результатом разбора будет пустой блок (его список операторов равен null).
|
||||
// Если очередная лексема не входит в этот список, то ее мы считаем началом оператора и вызываем метод statement.
|
||||
// Признаком последнего оператора является отсутствие после оператора точки с запятой.
|
||||
if(see(T_END) || see(T_OD) || see(T_ELSE) || see(T_FI)) {
|
||||
return;
|
||||
}
|
||||
else {
|
||||
bool more = true;
|
||||
while(more) {
|
||||
statement();
|
||||
more = match(T_SEMICOLON);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Parser::statement()
|
||||
{
|
||||
// Если встречаем переменную, то запоминаем ее адрес или добавляем новую если не встретили.
|
||||
// Следующей лексемой должно быть присваивание. Затем идет блок expression, который возвращает значение на вершину стека.
|
||||
// Записываем это значение по адресу нашей переменной
|
||||
if(see(T_IDENTIFIER)) {
|
||||
int varAddress = findOrAddVariable(scanner_->getStringValue());
|
||||
next();
|
||||
mustBe(T_ASSIGN);
|
||||
expression();
|
||||
codegen_->emit(STORE, varAddress);
|
||||
}
|
||||
// Если встретили IF, то затем должно следовать условие. На вершине стека лежит 1 или 0 в зависимости от выполнения условия.
|
||||
// Затем зарезервируем место для условного перехода JUMP_NO к блоку ELSE (переход в случае ложного условия). Адрес перехода
|
||||
// станет известным только после того, как будет сгенерирован код для блока THEN.
|
||||
else if(match(T_IF)) {
|
||||
relation();
|
||||
|
||||
int jumpNoAddress = codegen_->reserve();
|
||||
|
||||
mustBe(T_THEN);
|
||||
statementList();
|
||||
if(match(T_ELSE)) {
|
||||
//Если есть блок ELSE, то чтобы не выполнять его в случае выполнения THEN,
|
||||
//зарезервируем место для команды JUMP в конец этого блока
|
||||
int jumpAddress = codegen_->reserve();
|
||||
//Заполним зарезервированное место после проверки условия инструкцией перехода в начало блока ELSE.
|
||||
codegen_->emitAt(jumpNoAddress, JUMP_NO, codegen_->getCurrentAddress());
|
||||
statementList();
|
||||
//Заполним второй адрес инструкцией перехода в конец условного блока ELSE.
|
||||
codegen_->emitAt(jumpAddress, JUMP, codegen_->getCurrentAddress());
|
||||
}
|
||||
else {
|
||||
//Если блок ELSE отсутствует, то в зарезервированный адрес после проверки условия будет записана
|
||||
//инструкция условного перехода в конец оператора IF...THEN
|
||||
codegen_->emitAt(jumpNoAddress, JUMP_NO, codegen_->getCurrentAddress());
|
||||
}
|
||||
|
||||
mustBe(T_FI);
|
||||
}
|
||||
|
||||
else if(match(T_WHILE)) {
|
||||
//запоминаем адрес начала проверки условия.
|
||||
int conditionAddress = codegen_->getCurrentAddress();
|
||||
relation();
|
||||
//резервируем место под инструкцию условного перехода для выхода из цикла.
|
||||
int jumpNoAddress = codegen_->reserve();
|
||||
mustBe(T_DO);
|
||||
statementList();
|
||||
mustBe(T_OD);
|
||||
//переходим по адресу проверки условия
|
||||
codegen_->emit(JUMP, conditionAddress);
|
||||
//заполняем зарезервированный адрес инструкцией условного перехода на следующий за циклом оператор.
|
||||
codegen_->emitAt(jumpNoAddress, JUMP_NO, codegen_->getCurrentAddress());
|
||||
}
|
||||
else if(match(T_WRITE)) {
|
||||
mustBe(T_LPAREN);
|
||||
expression();
|
||||
mustBe(T_RPAREN);
|
||||
codegen_->emit(PRINT);
|
||||
}
|
||||
else {
|
||||
reportError("statement expected.");
|
||||
}
|
||||
}
|
||||
|
||||
void Parser::expression()
|
||||
{
|
||||
|
||||
/*
|
||||
Арифметическое выражение описывается следующими правилами: <expression> -> <term> | <term> + <term> | <term> - <term>
|
||||
При разборе сначала смотрим первый терм, затем анализируем очередной символ. Если это '+' или '-',
|
||||
удаляем его из потока и разбираем очередное слагаемое (вычитаемое). Повторяем проверку и разбор очередного
|
||||
терма, пока не встретим за термом символ, отличный от '+' и '-'
|
||||
*/
|
||||
|
||||
term();
|
||||
while(see(T_ADDOP)) {
|
||||
Arithmetic op = scanner_->getArithmeticValue();
|
||||
next();
|
||||
term();
|
||||
|
||||
if(op == A_PLUS) {
|
||||
codegen_->emit(ADD);
|
||||
}
|
||||
else {
|
||||
codegen_->emit(SUB);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Parser::term()
|
||||
{
|
||||
/*
|
||||
Терм описывается следующими правилами: <expression> -> <factor> | <factor> + <factor> | <factor> - <factor>
|
||||
При разборе сначала смотрим первый множитель, затем анализируем очередной символ. Если это '*' или '/',
|
||||
удаляем его из потока и разбираем очередное слагаемое (вычитаемое). Повторяем проверку и разбор очередного
|
||||
множителя, пока не встретим за ним символ, отличный от '*' и '/'
|
||||
*/
|
||||
factor();
|
||||
while(see(T_MULOP)) {
|
||||
Arithmetic op = scanner_->getArithmeticValue();
|
||||
next();
|
||||
factor();
|
||||
|
||||
if(op == A_MULTIPLY) {
|
||||
codegen_->emit(MULT);
|
||||
}
|
||||
else {
|
||||
codegen_->emit(DIV);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Parser::factor()
|
||||
{
|
||||
/*
|
||||
Множитель описывается следующими правилами:
|
||||
<factor> -> number | identifier | -<factor> | (<expression>) | READ
|
||||
*/
|
||||
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);
|
||||
//Если встретили переменную, то выгружаем значение, лежащее по ее адресу, на вершину стека
|
||||
}
|
||||
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.");
|
||||
}
|
||||
}
|
||||
|
||||
void Parser::relation()
|
||||
{
|
||||
//Условие сравнивает два выражения по какому-либо из знаков. Каждый знак имеет свой номер. В зависимости от
|
||||
//результата сравнения на вершине стека окажется 0 или 1.
|
||||
expression();
|
||||
if(see(T_CMP)) {
|
||||
Cmp cmp = scanner_->getCmpValue();
|
||||
next();
|
||||
expression();
|
||||
switch(cmp) {
|
||||
//для знака "=" - номер 0
|
||||
case C_EQ:
|
||||
codegen_->emit(COMPARE, 0);
|
||||
break;
|
||||
//для знака "!=" - номер 1
|
||||
case C_NE:
|
||||
codegen_->emit(COMPARE, 1);
|
||||
break;
|
||||
//для знака "<" - номер 2
|
||||
case C_LT:
|
||||
codegen_->emit(COMPARE, 2);
|
||||
break;
|
||||
//для знака ">" - номер 3
|
||||
case C_GT:
|
||||
codegen_->emit(COMPARE, 3);
|
||||
break;
|
||||
//для знака "<=" - номер 4
|
||||
case C_LE:
|
||||
codegen_->emit(COMPARE, 4);
|
||||
break;
|
||||
//для знака ">=" - номер 5
|
||||
case C_GE:
|
||||
codegen_->emit(COMPARE, 5);
|
||||
break;
|
||||
};
|
||||
}
|
||||
else {
|
||||
reportError("comparison operator expected.");
|
||||
}
|
||||
}
|
||||
|
||||
int Parser::findOrAddVariable(const string& var)
|
||||
{
|
||||
VarTable::iterator it = variables_.find(var);
|
||||
if(it == variables_.end()) {
|
||||
variables_[var] = lastVar_;
|
||||
return lastVar_++;
|
||||
}
|
||||
else {
|
||||
return it->second;
|
||||
}
|
||||
}
|
||||
|
||||
void Parser::mustBe(Token t)
|
||||
{
|
||||
if(!match(t)) {
|
||||
error_ = true;
|
||||
|
||||
// Подготовим сообщение об ошибке
|
||||
std::ostringstream msg;
|
||||
msg << tokenToString(scanner_->token()) << " found while " << tokenToString(t) << " expected.";
|
||||
reportError(msg.str());
|
||||
|
||||
// Попытка восстановления после ошибки.
|
||||
recover(t);
|
||||
}
|
||||
}
|
||||
|
||||
void Parser::recover(Token t)
|
||||
{
|
||||
while(!see(t) && !see(T_EOF)) {
|
||||
next();
|
||||
}
|
||||
|
||||
if(see(t)) {
|
||||
next();
|
||||
}
|
||||
}
|
||||
119
lab4/cmilan/src/parser.h
Normal file
119
lab4/cmilan/src/parser.h
Normal file
@@ -0,0 +1,119 @@
|
||||
#ifndef CMILAN_PARSER_H
|
||||
#define CMILAN_PARSER_H
|
||||
|
||||
#include "scanner.h"
|
||||
#include "codegen.h"
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
using namespace std;
|
||||
|
||||
/* Синтаксический анализатор.
|
||||
*
|
||||
* Задачи:
|
||||
* - проверка корректности программы,
|
||||
* - генерация кода для виртуальной машины в процессе анализа,
|
||||
* - простейшее восстановление после ошибок.
|
||||
*
|
||||
* Синтаксический анализатор языка Милан.
|
||||
*
|
||||
* Парсер с помощью переданного ему при инициализации лексического анализатора
|
||||
* читает по одной лексеме и на основе грамматики Милана генерирует код для
|
||||
* стековой виртуальной машины. Синтаксический анализ выполняется методом
|
||||
* рекурсивного спуска.
|
||||
*
|
||||
* При обнаружении ошибки парсер печатает сообщение и продолжает анализ со
|
||||
* следующего оператора, чтобы в процессе разбора найти как можно больше ошибок.
|
||||
* Поскольку стратегия восстановления после ошибки очень проста, возможна печать
|
||||
* сообщений о несуществующих ("наведенных") ошибках или пропуск некоторых
|
||||
* ошибок без печати сообщений. Если в процессе разбора была найдена хотя бы
|
||||
* одна ошибка, код для виртуальной машины не печатается.*/
|
||||
|
||||
class Parser
|
||||
{
|
||||
public:
|
||||
// Конструктор
|
||||
// const string& fileName - имя файла с программой для анализа
|
||||
//
|
||||
// Конструктор создает экземпляры лексического анализатора и генератора.
|
||||
|
||||
Parser(const string& fileName, istream& input)
|
||||
: output_(cout), error_(false), recovered_(true), lastVar_(0)
|
||||
{
|
||||
scanner_ = new Scanner(fileName, input);
|
||||
codegen_ = new CodeGen(output_);
|
||||
next();
|
||||
}
|
||||
|
||||
~Parser()
|
||||
{
|
||||
delete codegen_;
|
||||
delete scanner_;
|
||||
}
|
||||
|
||||
void parse(); //проводим синтаксический разбор
|
||||
|
||||
private:
|
||||
typedef map<string, int> VarTable;
|
||||
//описание блоков.
|
||||
void program(); //Разбор программы. BEGIN statementList END
|
||||
void statementList(); // Разбор списка операторов.
|
||||
void statement(); //разбор оператора.
|
||||
void expression(); //разбор арифметического выражения.
|
||||
void term(); //разбор слагаемого.
|
||||
void factor(); //разбор множителя.
|
||||
void relation(); //разбор условия.
|
||||
|
||||
// Сравнение текущей лексемы с образцом. Текущая позиция в потоке лексем не изменяется.
|
||||
bool see(Token t)
|
||||
{
|
||||
return scanner_->token() == t;
|
||||
}
|
||||
|
||||
// Проверка совпадения текущей лексемы с образцом. Если лексема и образец совпадают,
|
||||
// лексема изымается из потока.
|
||||
|
||||
bool match(Token t)
|
||||
{
|
||||
if(scanner_->token() == t) {
|
||||
scanner_->nextToken();
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Переход к следующей лексеме.
|
||||
|
||||
void next()
|
||||
{
|
||||
scanner_->nextToken();
|
||||
}
|
||||
|
||||
// Обработчик ошибок.
|
||||
void reportError(const string& message)
|
||||
{
|
||||
cerr << "Line " << scanner_->getLineNumber() << ": " << message << endl;
|
||||
error_ = true;
|
||||
}
|
||||
|
||||
void mustBe(Token t); //проверяем, совпадает ли данная лексема с образцом. Если да, то лексема изымается из потока.
|
||||
//Иначе создаем сообщение об ошибке и пробуем восстановиться
|
||||
void recover(Token t); //восстановление после ошибки: идем по коду до тех пор,
|
||||
//пока не встретим эту лексему или лексему конца файла.
|
||||
int findOrAddVariable(const string&); //функция пробегает по variables_.
|
||||
//Если находит нужную переменную - возвращает ее номер, иначе добавляет ее в массив, увеличивает lastVar и возвращает его.
|
||||
|
||||
Scanner* scanner_; //лексический анализатор для конструктора
|
||||
CodeGen* codegen_; //указатель на виртуальную машину
|
||||
ostream& output_; //выходной поток (в данном случае используем cout)
|
||||
bool error_; //флаг ошибки. Используется чтобы определить, выводим ли список команд после разбора или нет
|
||||
bool recovered_; //не используется
|
||||
VarTable variables_; //массив переменных, найденных в программе
|
||||
int lastVar_; //номер последней записанной переменной
|
||||
};
|
||||
|
||||
#endif
|
||||
BIN
lab4/cmilan/src/parser.o
Normal file
BIN
lab4/cmilan/src/parser.o
Normal file
Binary file not shown.
233
lab4/cmilan/src/scanner.cpp
Normal file
233
lab4/cmilan/src/scanner.cpp
Normal file
@@ -0,0 +1,233 @@
|
||||
#include "scanner.h"
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <cctype>
|
||||
|
||||
using namespace std;
|
||||
|
||||
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",
|
||||
"'('",
|
||||
"')'",
|
||||
"';'",
|
||||
};
|
||||
|
||||
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 '+':
|
||||
token_ = T_ADDOP;
|
||||
arithmeticValue_ = A_PLUS;
|
||||
nextChar();
|
||||
break;
|
||||
|
||||
case '-':
|
||||
token_ = T_ADDOP;
|
||||
arithmeticValue_ = A_MINUS;
|
||||
nextChar();
|
||||
break;
|
||||
|
||||
case '*':
|
||||
token_ = T_MULOP;
|
||||
arithmeticValue_ = A_MULTIPLY;
|
||||
nextChar();
|
||||
break;
|
||||
//Иначе лексема ошибки.
|
||||
default:
|
||||
token_ = T_ILLEGAL;
|
||||
nextChar();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Scanner::skipSpace()
|
||||
{
|
||||
while(isspace(ch_)) {
|
||||
if(ch_ == '\n') {
|
||||
++lineNumber_;
|
||||
}
|
||||
|
||||
nextChar();
|
||||
}
|
||||
}
|
||||
|
||||
void Scanner::nextChar()
|
||||
{
|
||||
ch_ = input_.get();
|
||||
}
|
||||
|
||||
const char * tokenToString(Token t)
|
||||
{
|
||||
return tokenNames_[t];
|
||||
}
|
||||
|
||||
164
lab4/cmilan/src/scanner.h
Normal file
164
lab4/cmilan/src/scanner.h
Normal file
@@ -0,0 +1,164 @@
|
||||
#ifndef CMILAN_SCANNER_H
|
||||
#define CMILAN_SCANNER_H
|
||||
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
using namespace std;
|
||||
|
||||
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 // ";"
|
||||
};
|
||||
|
||||
// Функция tokenToString возвращает описание лексемы.
|
||||
// Используется при печати сообщения об ошибке.
|
||||
const char * tokenToString(Token t);
|
||||
|
||||
// Виды операций сравнения
|
||||
enum Cmp {
|
||||
C_EQ, // Операция сравнения "="
|
||||
C_NE, // Операция сравнения "!="
|
||||
C_LT, // Операция сравнения "<"
|
||||
C_LE, // Операция сравнения "<="
|
||||
C_GT, // Операция сравнения ">"
|
||||
C_GE // Операция сравнения ">="
|
||||
};
|
||||
|
||||
// Виды арифметических операций
|
||||
enum Arithmetic {
|
||||
A_PLUS, //операция "+"
|
||||
A_MINUS, //операция "-"
|
||||
A_MULTIPLY, //операция "*"
|
||||
A_DIVIDE //операция "/"
|
||||
};
|
||||
|
||||
// Лексический анализатор
|
||||
|
||||
class Scanner
|
||||
{
|
||||
public:
|
||||
// Конструктор. В качестве аргумента принимает имя файла и поток,
|
||||
// из которого будут читаться символы транслируемой программы.
|
||||
|
||||
explicit Scanner(const string& fileName, istream& input)
|
||||
: fileName_(fileName), lineNumber_(1), input_(input)
|
||||
{
|
||||
keywords_["begin"] = T_BEGIN;
|
||||
keywords_["end"] = T_END;
|
||||
keywords_["if"] = T_IF;
|
||||
keywords_["then"] = T_THEN;
|
||||
keywords_["else"] = T_ELSE;
|
||||
keywords_["fi"] = T_FI;
|
||||
keywords_["while"] = T_WHILE;
|
||||
keywords_["do"] = T_DO;
|
||||
keywords_["od"] = T_OD;
|
||||
keywords_["write"] = T_WRITE;
|
||||
keywords_["read"] = T_READ;
|
||||
|
||||
nextChar();
|
||||
}
|
||||
|
||||
// Деструктор
|
||||
virtual ~Scanner()
|
||||
{}
|
||||
|
||||
//getters всех private переменных
|
||||
const string& getFileName() const //не используется
|
||||
{
|
||||
return fileName_;
|
||||
}
|
||||
|
||||
int getLineNumber() const
|
||||
{
|
||||
return lineNumber_;
|
||||
}
|
||||
|
||||
Token token() const
|
||||
{
|
||||
return token_;
|
||||
}
|
||||
|
||||
int getIntValue() const
|
||||
{
|
||||
return intValue_;
|
||||
}
|
||||
|
||||
string getStringValue() const
|
||||
{
|
||||
return stringValue_;
|
||||
}
|
||||
|
||||
Cmp getCmpValue() const
|
||||
{
|
||||
return cmpValue_;
|
||||
}
|
||||
|
||||
Arithmetic getArithmeticValue() const
|
||||
{
|
||||
return arithmeticValue_;
|
||||
}
|
||||
|
||||
// Переход к следующей лексеме.
|
||||
// Текущая лексема записывается в token_ и изымается из потока.
|
||||
void nextToken();
|
||||
private:
|
||||
|
||||
// Пропуск всех пробельные символы.
|
||||
// Если встречается символ перевода строки, номер текущей строки
|
||||
// (lineNumber) увеличивается на единицу.
|
||||
void skipSpace();
|
||||
|
||||
|
||||
void nextChar(); //переходит к следующему символу
|
||||
//проверка переменной на первый символ (должен быть буквой латинского алфавита)
|
||||
bool isIdentifierStart(char c)
|
||||
{
|
||||
return ((c >= 'a' && c <= 'z') ||
|
||||
(c >= 'A' && c <= 'Z'));
|
||||
}
|
||||
//проверка на остальные символы переменной (буква или цифра)
|
||||
bool isIdentifierBody(char c)
|
||||
{
|
||||
return isIdentifierStart(c) || isdigit(c);
|
||||
}
|
||||
|
||||
|
||||
const string fileName_; //входной файл
|
||||
int lineNumber_; //номер текущей строки кода
|
||||
|
||||
Token token_; //текущая лексема
|
||||
int intValue_; //значение текущего целого
|
||||
string stringValue_; //имя переменной
|
||||
Cmp cmpValue_; //значение оператора сравнения (>, <, =, !=, >=, <=)
|
||||
Arithmetic arithmeticValue_; //значение знака (+,-,*,/)
|
||||
|
||||
map<string, Token> keywords_; //ассоциативный массив с лексемами и
|
||||
//соответствующими им зарезервированными словами в качестве индексов
|
||||
|
||||
istream& input_; //входной поток для чтения из файла.
|
||||
char ch_; //текущий символ
|
||||
};
|
||||
|
||||
#endif
|
||||
BIN
lab4/cmilan/src/scanner.o
Normal file
BIN
lab4/cmilan/src/scanner.o
Normal file
Binary file not shown.
7
lab4/cmilan/test/add.mil
Normal file
7
lab4/cmilan/test/add.mil
Normal file
@@ -0,0 +1,7 @@
|
||||
BEGIN
|
||||
i := 1;
|
||||
j := 2;
|
||||
k := i + j;
|
||||
WRITE(k)
|
||||
END
|
||||
|
||||
14
lab4/cmilan/test/comment.mil
Normal file
14
lab4/cmilan/test/comment.mil
Normal file
@@ -0,0 +1,14 @@
|
||||
/* это комментарий */
|
||||
/* это продолжение комментария */
|
||||
|
||||
/*****************************
|
||||
* эта программа печатает 42 *
|
||||
****************************/
|
||||
|
||||
begin /* комментарий в конце строки */
|
||||
/* комментарий в начале строки */ write(42)
|
||||
/* многострочный
|
||||
комментарий */
|
||||
end
|
||||
|
||||
/* комментарий в конце файла */
|
||||
10
lab4/cmilan/test/factorial.mil
Normal file
10
lab4/cmilan/test/factorial.mil
Normal file
@@ -0,0 +1,10 @@
|
||||
BEGIN
|
||||
n := READ;
|
||||
factorial := 1;
|
||||
i := 1;
|
||||
WHILE i <= n DO
|
||||
factorial := factorial * i;
|
||||
i := i + 1
|
||||
OD;
|
||||
WRITE(factorial)
|
||||
END
|
||||
17
lab4/cmilan/test/fib.mil
Normal file
17
lab4/cmilan/test/fib.mil
Normal file
@@ -0,0 +1,17 @@
|
||||
/* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> N-<2D><> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> */
|
||||
|
||||
BEGIN
|
||||
a := 1;
|
||||
b := 1;
|
||||
n := READ;
|
||||
|
||||
WHILE n > 1 DO
|
||||
t := a;
|
||||
a := b;
|
||||
b := b + t;
|
||||
n := n - 1
|
||||
OD;
|
||||
|
||||
WRITE(a)
|
||||
END
|
||||
|
||||
17
lab4/cmilan/test/gcd.mil
Normal file
17
lab4/cmilan/test/gcd.mil
Normal file
@@ -0,0 +1,17 @@
|
||||
/* Greatest common divisor */
|
||||
|
||||
BEGIN
|
||||
a := READ;
|
||||
b := READ;
|
||||
|
||||
WHILE a != b DO
|
||||
IF a < b THEN
|
||||
b := b - a
|
||||
ELSE
|
||||
a := a - b
|
||||
FI
|
||||
OD;
|
||||
|
||||
WRITE(a)
|
||||
END
|
||||
|
||||
8
lab4/cmilan/test/if.mil
Normal file
8
lab4/cmilan/test/if.mil
Normal file
@@ -0,0 +1,8 @@
|
||||
BEGIN
|
||||
i := 1;
|
||||
j := 2;
|
||||
|
||||
IF i < j THEN WRITE(i) FI;
|
||||
IF i < j THEN WRITE(i) ELSE WRITE(j) FI
|
||||
END
|
||||
|
||||
5
lab4/cmilan/test/invalid.mil
Normal file
5
lab4/cmilan/test/invalid.mil
Normal file
@@ -0,0 +1,5 @@
|
||||
BEGIN
|
||||
IF 1 2 THEN WRITE(n) ELSE WRITE(2) FI;
|
||||
X := Y + 1;
|
||||
WRITE (X +)
|
||||
END
|
||||
14
lab4/cmilan/test/invert.mil
Normal file
14
lab4/cmilan/test/invert.mil
Normal file
@@ -0,0 +1,14 @@
|
||||
BEGIN
|
||||
x := READ;
|
||||
x := -x;
|
||||
|
||||
WRITE(-x);
|
||||
|
||||
IF -x > 0 THEN x := 1 FI;
|
||||
|
||||
x := x + -1;
|
||||
x := x - -x;
|
||||
|
||||
x := x -1
|
||||
END
|
||||
|
||||
17
lab4/cmilan/test/loop.mil
Normal file
17
lab4/cmilan/test/loop.mil
Normal file
@@ -0,0 +1,17 @@
|
||||
BEGIN
|
||||
/* Read a number */
|
||||
|
||||
i := READ;
|
||||
|
||||
/* Count from 0 to this number */
|
||||
|
||||
IF i > 0 THEN step := 1 ELSE step := -1 FI;
|
||||
k := 0;
|
||||
WHILE k != i DO
|
||||
WRITE(k);
|
||||
k := k + step
|
||||
OD;
|
||||
|
||||
WRITE(i)
|
||||
END
|
||||
|
||||
14
lab4/cmilan/test/power.mil
Normal file
14
lab4/cmilan/test/power.mil
Normal file
@@ -0,0 +1,14 @@
|
||||
BEGIN
|
||||
N := READ;
|
||||
P := READ;
|
||||
|
||||
S := 1;
|
||||
|
||||
WHILE P > 0 DO
|
||||
S := S * N;
|
||||
P := P - 1
|
||||
OD;
|
||||
|
||||
WRITE(S)
|
||||
END
|
||||
|
||||
8
lab4/cmilan/test/read2.mil
Normal file
8
lab4/cmilan/test/read2.mil
Normal file
@@ -0,0 +1,8 @@
|
||||
BEGIN
|
||||
IF READ < READ
|
||||
THEN
|
||||
WRITE(1)
|
||||
ELSE
|
||||
WRITE(2)
|
||||
FI
|
||||
END
|
||||
9
lab4/cmilan/test/sum.mil
Normal file
9
lab4/cmilan/test/sum.mil
Normal file
@@ -0,0 +1,9 @@
|
||||
BEGIN
|
||||
i := 8;
|
||||
j := 2;
|
||||
|
||||
WHILE i != j DO
|
||||
IF i < j THEN j := j - i ELSE i := i - j FI
|
||||
OD;
|
||||
WRITE(i)
|
||||
END
|
||||
BIN
lab4/vm/bin/milanvm.exe
Normal file
BIN
lab4/vm/bin/milanvm.exe
Normal file
Binary file not shown.
10
lab4/vm/doc/changelog.txt
Normal file
10
lab4/vm/doc/changelog.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>:
|
||||
==================
|
||||
|
||||
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> 1.2:
|
||||
* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> ymilan <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD>
|
||||
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>.
|
||||
* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> NOP (<28><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>).
|
||||
|
||||
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> 1.1:
|
||||
* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> ymilan 1.1.
|
||||
224
lab4/vm/doc/vm.txt
Normal file
224
lab4/vm/doc/vm.txt
Normal file
@@ -0,0 +1,224 @@
|
||||
== <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> ==
|
||||
|
||||
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>, <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD>.
|
||||
<EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>. <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD>
|
||||
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>. <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>: <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>.
|
||||
|
||||
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>.
|
||||
|
||||
NOP
|
||||
|
||||
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>; <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>.
|
||||
|
||||
STOP
|
||||
|
||||
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>; <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>.
|
||||
|
||||
LOAD <<3C><><EFBFBD><EFBFBD><EFBFBD>>
|
||||
|
||||
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD>, <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <<3C><><EFBFBD><EFBFBD><EFBFBD>>.
|
||||
|
||||
STORE <<3C><><EFBFBD><EFBFBD><EFBFBD>>
|
||||
|
||||
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <<3C><><EFBFBD><EFBFBD><EFBFBD>>.
|
||||
<20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>, <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>.
|
||||
|
||||
|
||||
BLOAD <<3C><><EFBFBD><EFBFBD><EFBFBD>>
|
||||
|
||||
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD>, <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>:
|
||||
<<3C><><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>> = <<3C><><EFBFBD><EFBFBD><EFBFBD>> + <<3C><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD>>. <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
<20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>.
|
||||
|
||||
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> BLOAD <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>, <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>, <20><><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
<20><><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>, <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>.
|
||||
|
||||
BSTORE <<3C><><EFBFBD><EFBFBD><EFBFBD>>
|
||||
|
||||
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>:
|
||||
<<3C><><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>> = <<3C><><EFBFBD><EFBFBD><EFBFBD>> + <<3C><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD>>, <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
<20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>. <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
<20><><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <<3C><><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>>.
|
||||
|
||||
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> BSTORE <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>, <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>.
|
||||
|
||||
<20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD>: [10, 20, ...]. <20> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> BSTORE 5
|
||||
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> 15 <20><><EFBFBD><EFBFBD><EFBFBD> 20.
|
||||
|
||||
PUSH <<3C><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>>
|
||||
|
||||
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <<3C><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>> <20> <20><><EFBFBD><EFBFBD>.
|
||||
|
||||
POP
|
||||
|
||||
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD>.
|
||||
|
||||
DUP
|
||||
|
||||
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>, <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD>.
|
||||
|
||||
ADD
|
||||
|
||||
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><>
|
||||
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>.
|
||||
|
||||
MULT
|
||||
|
||||
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><>
|
||||
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>.
|
||||
|
||||
SUB
|
||||
|
||||
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <a>, <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <b>
|
||||
<20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <b> - <a>.
|
||||
|
||||
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>: <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
|
||||
PUSH 10
|
||||
PUSH 8
|
||||
SUB
|
||||
|
||||
<20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> 2.
|
||||
|
||||
DIV
|
||||
|
||||
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <a>, <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <b>
|
||||
<20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <b> / <a>. <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>. <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <a> = 0, <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>.
|
||||
|
||||
INVERT
|
||||
|
||||
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>.
|
||||
|
||||
COMPARE <<3C><><EFBFBD>>
|
||||
|
||||
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <a>, <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <b>,
|
||||
<20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <b> <20> <a>.
|
||||
|
||||
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <<3C><><EFBFBD>>:
|
||||
|
||||
<<3C><><EFBFBD>> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
|
||||
0 <b> = <a>
|
||||
1 <b> != <a>
|
||||
2 <b> < <a>
|
||||
3 <b> > <a>
|
||||
4 <b> <= <a>
|
||||
5 <b> >= <a>
|
||||
|
||||
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> 1, <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>, <20> 0 <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>.
|
||||
|
||||
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>: <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
|
||||
PUSH 5
|
||||
PUSH 7
|
||||
COMPARE 2
|
||||
|
||||
<20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> 1 (<28><><EFBFBD> <20><><EFBFBD> 5 < 7).
|
||||
|
||||
JUMP <<3C><><EFBFBD><EFBFBD><EFBFBD>>
|
||||
|
||||
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <<3C><><EFBFBD><EFBFBD><EFBFBD>>, <20><> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD>
|
||||
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <<3C><><EFBFBD><EFBFBD><EFBFBD>>. <20><><EFBFBD><EFBFBD>
|
||||
<<3C><><EFBFBD><EFBFBD><EFBFBD>> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>, <20><><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
<20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>, <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>.
|
||||
|
||||
|
||||
JUMP_YES <<3C><><EFBFBD><EFBFBD><EFBFBD>>
|
||||
|
||||
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <<3C><><EFBFBD><EFBFBD><EFBFBD>>, <20><><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD>
|
||||
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>; <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD>.
|
||||
|
||||
JUMP_NO <<3C><><EFBFBD><EFBFBD><EFBFBD>>
|
||||
|
||||
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <<3C><><EFBFBD><EFBFBD><EFBFBD>>, <20><><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD>
|
||||
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> 0; <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD>. <20><><EFBFBD><EFBFBD>
|
||||
<<3C><><EFBFBD><EFBFBD><EFBFBD>> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>, <20><><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
<20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>, <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>.
|
||||
|
||||
INPUT
|
||||
|
||||
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD>
|
||||
<20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD> <20> <20><><EFBFBD><EFBFBD>. <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD>-<2D><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD>
|
||||
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>.
|
||||
|
||||
PRINT
|
||||
|
||||
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
<20><><EFBFBD><EFBFBD><EFBFBD>. <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD>.
|
||||
|
||||
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>.
|
||||
|
||||
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>, <20><><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>. <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>.
|
||||
|
||||
<EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD>. <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> ';' <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>, <20><><EFBFBD>
|
||||
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>, <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD> <20> <20><> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>, <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>.
|
||||
|
||||
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>, <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>, <20><><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>.
|
||||
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>, <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> ':'. <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> 0.
|
||||
|
||||
<EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>,
|
||||
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>: STOP. <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>, <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>.
|
||||
|
||||
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>:
|
||||
|
||||
------------------------------------------------------------
|
||||
0: INPUT
|
||||
1: STORE 42 ; n := READ
|
||||
2: LOAD 14
|
||||
3: PUSH 4
|
||||
4: LOAD 42
|
||||
5: COMPARE 2
|
||||
6: JUMP_NO 9
|
||||
7: PUSH 10
|
||||
8: STORE 42
|
||||
9: LOAD 42
|
||||
10: PRINT
|
||||
11: STOP
|
||||
-------------------------------------------------------------
|
||||
|
||||
<EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> SET:
|
||||
|
||||
SET <<3C><><EFBFBD><EFBFBD><EFBFBD>> <<3C><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>>
|
||||
|
||||
<EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> SET <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <<3C><><EFBFBD><EFBFBD><EFBFBD>> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <<3C><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>>.
|
||||
|
||||
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> SET <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>. <20><><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>. <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
<EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> SET <20><><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>.
|
||||
|
||||
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> SET <<3C><><EFBFBD><EFBFBD><EFBFBD>> <<3C><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
|
||||
PUSH <<3C><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>>
|
||||
STORE <<3C><><EFBFBD><EFBFBD><EFBFBD>>
|
||||
|
||||
<EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD>.
|
||||
|
||||
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> SET <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>. <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> SET:
|
||||
|
||||
------------------------------------
|
||||
SET 0 15
|
||||
SET 1 40
|
||||
|
||||
0: LOAD 0
|
||||
1: LOAD 1
|
||||
2: ADD
|
||||
3: PRINT
|
||||
4: STOP
|
||||
------------------------------------
|
||||
|
||||
<EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> 55.
|
||||
|
||||
15
lab4/vm/vm/Makefile
Normal file
15
lab4/vm/vm/Makefile
Normal file
@@ -0,0 +1,15 @@
|
||||
mvm: vm.c lex.yy.c vmparse.tab.h main.c
|
||||
gcc -o mvm main.c vm.c lex.yy.c vmparse.tab.c
|
||||
|
||||
lex.yy.c: vmlex.l
|
||||
flex vmlex.l
|
||||
|
||||
vmparse.tab.h: vmparse.y
|
||||
bison -d vmparse.y
|
||||
|
||||
clean:
|
||||
rm lex.yy.c vmparse.tab.h vmparse.tab.c
|
||||
|
||||
distclean:
|
||||
rm mvm lex.yy.c vmparse.tab.h vmparse.tab.c
|
||||
|
||||
1690
lab4/vm/vm/lex.yy.c
Normal file
1690
lab4/vm/vm/lex.yy.c
Normal file
File diff suppressed because it is too large
Load Diff
46
lab4/vm/vm/main.c
Normal file
46
lab4/vm/vm/main.c
Normal file
@@ -0,0 +1,46 @@
|
||||
#include "vm.h"
|
||||
#include "vmparse.tab.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
extern FILE *yyin;
|
||||
int need_close = 0;
|
||||
|
||||
int yyparse();
|
||||
|
||||
void milan_error(char const * msg)
|
||||
{
|
||||
if(need_close)
|
||||
fclose(yyin);
|
||||
|
||||
fprintf(stderr, msg);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
if(argc < 2) {
|
||||
yyin = stdin;
|
||||
printf("Reading input from stdin\n");
|
||||
}
|
||||
else {
|
||||
yyin = fopen(argv[1], "rt");
|
||||
if(!yyin) {
|
||||
printf("Unable to read %s\n", argv[1]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
need_close = 1;
|
||||
printf("Reading input from %s\n", argv[1]);
|
||||
}
|
||||
|
||||
if(0 == yyparse()) {
|
||||
run();
|
||||
}
|
||||
|
||||
if(need_close) {
|
||||
fclose(yyin);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
6
lab4/vm/vm/test/bop1.ms
Normal file
6
lab4/vm/vm/test/bop1.ms
Normal file
@@ -0,0 +1,6 @@
|
||||
4: STOP
|
||||
1: PUSH 20
|
||||
2: ADD
|
||||
3: PRINT
|
||||
0: PUSH 10
|
||||
2: MULT
|
||||
8
lab4/vm/vm/test/bop2.ms
Normal file
8
lab4/vm/vm/test/bop2.ms
Normal file
@@ -0,0 +1,8 @@
|
||||
0: PUSH 10
|
||||
1: PUSH -1
|
||||
2: BSTORE 2
|
||||
3: PUSH 0
|
||||
4: BLOAD 1
|
||||
5: PRINT
|
||||
6: STOP
|
||||
|
||||
37
lab4/vm/vm/test/fib.ms
Normal file
37
lab4/vm/vm/test/fib.ms
Normal file
@@ -0,0 +1,37 @@
|
||||
; <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> N-<2D><> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
|
||||
SET 0 0 ; <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> 0
|
||||
SET 1 1 ; <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> 1
|
||||
|
||||
SET 1000 0 ; <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> n
|
||||
SET 1001 1 ; <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> a
|
||||
SET 1002 1 ; <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> b
|
||||
|
||||
; <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
|
||||
0: INPUT
|
||||
1: STORE 1000 ; n := READ
|
||||
|
||||
2: LOAD 1000
|
||||
3: LOAD 1
|
||||
4: COMPARE 3 ; IF n > 1
|
||||
5: JUMP_NO 17
|
||||
|
||||
6: LOAD 1002 ; STACK <- b
|
||||
7: LOAD 1001 ; STACK <- a
|
||||
8: LOAD 1002
|
||||
9: STORE 1001 ; a := b
|
||||
10: ADD
|
||||
11: STORE 1002 ; b := <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>_<EFBFBD><5F><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>_a + b
|
||||
|
||||
12: LOAD 1000
|
||||
13: LOAD 1
|
||||
14: SUB
|
||||
15: STORE 1000 ; n := n - 1
|
||||
16: JUMP 2
|
||||
|
||||
17: LOAD 1001 ; WRITE(a)
|
||||
18: PRINT
|
||||
|
||||
19: STOP
|
||||
|
||||
21
lab4/vm/vm/test/fib2.ms
Normal file
21
lab4/vm/vm/test/fib2.ms
Normal file
@@ -0,0 +1,21 @@
|
||||
SET 0 1
|
||||
SET 1 1
|
||||
|
||||
0: INPUT
|
||||
1: DUP
|
||||
2: PUSH 1
|
||||
3: COMPARE 3
|
||||
4: JUMP_NO 14
|
||||
5: LOAD 1
|
||||
6: LOAD 0
|
||||
7: LOAD 1
|
||||
8: STORE 0
|
||||
9: ADD
|
||||
10: STORE 1
|
||||
11: PUSH 1
|
||||
12: SUB
|
||||
13: JUMP 1
|
||||
14: LOAD 0
|
||||
15: PRINT
|
||||
16: STOP
|
||||
|
||||
371
lab4/vm/vm/vm.c
Normal file
371
lab4/vm/vm/vm.c
Normal file
@@ -0,0 +1,371 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "vm.h"
|
||||
|
||||
void milan_error();
|
||||
|
||||
command vm_program[MAX_PROGRAM_SIZE];
|
||||
|
||||
int vm_memory[MAX_MEMORY_SIZE];
|
||||
int vm_stack[MAX_STACK_SIZE];
|
||||
|
||||
unsigned int vm_stack_pointer = 0;
|
||||
unsigned int vm_command_pointer = 0;
|
||||
|
||||
opcode_info opcodes_table[] = {
|
||||
{"NOP", 0},
|
||||
{"STOP", 0},
|
||||
{"LOAD", 1},
|
||||
{"STORE", 1},
|
||||
{"BLOAD", 1},
|
||||
{"BSTORE", 1},
|
||||
{"PUSH", 1},
|
||||
{"POP", 0},
|
||||
{"DUP", 0},
|
||||
{"INVERT", 0},
|
||||
{"ADD", 0},
|
||||
{"SUB", 0},
|
||||
{"MULT", 0},
|
||||
{"DIV", 0},
|
||||
{"COMPARE", 1},
|
||||
{"JUMP", 1},
|
||||
{"JUMP_YES", 1},
|
||||
{"JUMP_NO", 1},
|
||||
{"INPUT", 0},
|
||||
{"PRINT", 0}
|
||||
};
|
||||
|
||||
int opcodes_table_size = sizeof(opcodes_table) / sizeof(opcode_info);
|
||||
|
||||
typedef enum {
|
||||
BAD_DATA_ADDRESS,
|
||||
BAD_CODE_ADDRESS,
|
||||
BAD_RELATION,
|
||||
STACK_OVERFLOW,
|
||||
STACK_EMPTY,
|
||||
DIVISION_BY_ZERO,
|
||||
BAD_INPUT,
|
||||
UNKNOWN_COMMAND
|
||||
} runtime_error;
|
||||
|
||||
void vm_init()
|
||||
{
|
||||
vm_stack_pointer = 0;
|
||||
vm_command_pointer = 0;
|
||||
}
|
||||
|
||||
void vm_error(runtime_error error)
|
||||
{
|
||||
opcode_info* info;
|
||||
|
||||
switch(error) {
|
||||
case BAD_DATA_ADDRESS:
|
||||
fprintf(stderr, "Error: illegal data address\n");
|
||||
break;
|
||||
|
||||
case BAD_CODE_ADDRESS:
|
||||
fprintf(stderr, "Error: illegal address in JUMP* instruction\n");
|
||||
break;
|
||||
|
||||
case BAD_RELATION:
|
||||
fprintf(stderr, "Error: illegal comparison operator\n");
|
||||
break;
|
||||
|
||||
case STACK_OVERFLOW:
|
||||
fprintf(stderr, "Error: stack overflow\n");
|
||||
break;
|
||||
|
||||
case STACK_EMPTY:
|
||||
fprintf(stderr, "Error: stack is empty (no arguments are available)\n");
|
||||
break;
|
||||
|
||||
case DIVISION_BY_ZERO:
|
||||
fprintf(stderr, "Error: division by zero\n");
|
||||
break;
|
||||
|
||||
case BAD_INPUT:
|
||||
fprintf(stderr, "Error: illegal input\n");
|
||||
break;
|
||||
|
||||
case UNKNOWN_COMMAND:
|
||||
fprintf(stderr, "Error: unknown command, unable to execute\n");
|
||||
break;
|
||||
|
||||
default:
|
||||
fprintf(stderr, "Error: runtime error %d\n", error);
|
||||
}
|
||||
|
||||
fprintf(stderr, "Code:\n\n");
|
||||
|
||||
info = operation_info(vm_program[vm_command_pointer].operation);
|
||||
if(NULL == info) {
|
||||
fprintf(stderr, "%d\t(%d)\t\t%d\n", vm_command_pointer,
|
||||
vm_program[vm_command_pointer].operation,
|
||||
vm_program[vm_command_pointer].arg);
|
||||
}
|
||||
else {
|
||||
if(info->need_arg) {
|
||||
fprintf(stderr, "\t%d\t%s\t\t%d\n", vm_command_pointer, info->name,
|
||||
vm_program[vm_command_pointer].arg);
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "\t%d\t%s\n", vm_command_pointer, info->name);
|
||||
}
|
||||
}
|
||||
|
||||
milan_error("VM error");
|
||||
}
|
||||
|
||||
int vm_load(unsigned int address)
|
||||
{
|
||||
if(address < MAX_MEMORY_SIZE) {
|
||||
return vm_memory[address];
|
||||
}
|
||||
else {
|
||||
vm_error(BAD_DATA_ADDRESS);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void vm_store(unsigned int address, int word)
|
||||
{
|
||||
if(address < MAX_MEMORY_SIZE) {
|
||||
vm_memory[address] = word;
|
||||
}
|
||||
else {
|
||||
vm_error(BAD_DATA_ADDRESS);
|
||||
}
|
||||
}
|
||||
|
||||
int vm_read()
|
||||
{
|
||||
int n;
|
||||
|
||||
fprintf(stderr, "> "); fflush(stdout);
|
||||
if(scanf("%d", &n)) {
|
||||
return n;
|
||||
}
|
||||
else {
|
||||
vm_error(BAD_INPUT);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void vm_write(int n)
|
||||
{
|
||||
fprintf(stderr, "%d\n", n);
|
||||
}
|
||||
|
||||
int vm_pop()
|
||||
{
|
||||
if(vm_stack_pointer > 0) {
|
||||
return vm_stack[--vm_stack_pointer];
|
||||
}
|
||||
else {
|
||||
vm_error(STACK_EMPTY);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void vm_push(int word)
|
||||
{
|
||||
if(vm_stack_pointer < MAX_STACK_SIZE) {
|
||||
vm_stack[vm_stack_pointer++] = word;
|
||||
}
|
||||
else {
|
||||
vm_error(STACK_OVERFLOW);
|
||||
}
|
||||
}
|
||||
|
||||
int vm_run_command()
|
||||
{
|
||||
unsigned int index = vm_command_pointer;
|
||||
|
||||
operation op = vm_program[index].operation;
|
||||
unsigned int arg = vm_program[index].arg;
|
||||
int data;
|
||||
|
||||
switch(op) {
|
||||
case NOP:
|
||||
/* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> */
|
||||
break;
|
||||
|
||||
case STOP:
|
||||
return 0;
|
||||
break;
|
||||
|
||||
case LOAD:
|
||||
vm_push(vm_load(arg));
|
||||
break;
|
||||
|
||||
case STORE:
|
||||
vm_store(arg, vm_pop());
|
||||
break;
|
||||
|
||||
case BLOAD:
|
||||
vm_push(vm_load(arg + vm_pop()));
|
||||
break;
|
||||
|
||||
case BSTORE:
|
||||
data = vm_pop();
|
||||
vm_store(arg + data, vm_pop());
|
||||
break;
|
||||
|
||||
case PUSH:
|
||||
vm_push(arg);
|
||||
break;
|
||||
|
||||
case POP:
|
||||
vm_pop();
|
||||
break;
|
||||
|
||||
case DUP:
|
||||
data = vm_pop();
|
||||
vm_push(data);
|
||||
vm_push(data);
|
||||
break;
|
||||
|
||||
case INVERT:
|
||||
vm_push(-vm_pop());
|
||||
break;
|
||||
|
||||
case ADD:
|
||||
data = vm_pop();
|
||||
vm_push(vm_pop() + data);
|
||||
break;
|
||||
|
||||
case SUB:
|
||||
data = vm_pop();
|
||||
vm_push(vm_pop() - data);
|
||||
break;
|
||||
|
||||
case MULT:
|
||||
data = vm_pop();
|
||||
vm_push(vm_pop() * data);
|
||||
break;
|
||||
|
||||
case DIV:
|
||||
data = vm_pop();
|
||||
if(0 == data) {
|
||||
vm_error(DIVISION_BY_ZERO);
|
||||
}
|
||||
else {
|
||||
vm_push(vm_pop() / data);
|
||||
}
|
||||
break;
|
||||
|
||||
case COMPARE:
|
||||
data = vm_pop();
|
||||
switch(arg) {
|
||||
case EQ:
|
||||
vm_push((vm_pop() == data) ? 1 : 0);
|
||||
break;
|
||||
|
||||
case NE:
|
||||
vm_push((vm_pop() != data) ? 1 : 0);
|
||||
break;
|
||||
|
||||
case LT:
|
||||
vm_push((vm_pop() < data) ? 1 : 0);
|
||||
break;
|
||||
|
||||
case GT:
|
||||
vm_push((vm_pop() > data) ? 1 : 0);
|
||||
break;
|
||||
|
||||
case LE:
|
||||
vm_push((vm_pop() <= data) ? 1 : 0);
|
||||
break;
|
||||
|
||||
case GE:
|
||||
vm_push((vm_pop() >= data) ? 1 : 0);
|
||||
break;
|
||||
|
||||
default:
|
||||
vm_error(BAD_RELATION);
|
||||
}
|
||||
break;
|
||||
|
||||
case JUMP:
|
||||
if(arg < MAX_PROGRAM_SIZE) {
|
||||
vm_command_pointer = arg;
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
vm_error(BAD_CODE_ADDRESS);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case JUMP_YES:
|
||||
if(arg < MAX_PROGRAM_SIZE) {
|
||||
data = vm_pop();
|
||||
if(data) {
|
||||
vm_command_pointer = arg;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
vm_error(BAD_CODE_ADDRESS);
|
||||
}
|
||||
break;
|
||||
|
||||
case JUMP_NO:
|
||||
if(arg < MAX_PROGRAM_SIZE) {
|
||||
data = vm_pop();
|
||||
if(!data) {
|
||||
vm_command_pointer = arg;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
vm_error(BAD_CODE_ADDRESS);
|
||||
}
|
||||
break;
|
||||
|
||||
case INPUT:
|
||||
vm_push(vm_read());
|
||||
break;
|
||||
|
||||
case PRINT:
|
||||
vm_write(vm_pop());
|
||||
break;
|
||||
|
||||
default:
|
||||
vm_error(UNKNOWN_COMMAND);
|
||||
}
|
||||
|
||||
++vm_command_pointer;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void run()
|
||||
{
|
||||
vm_command_pointer = 0;
|
||||
while(vm_command_pointer < MAX_PROGRAM_SIZE) {
|
||||
if(!vm_run_command())
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
opcode_info* operation_info(operation op)
|
||||
{
|
||||
return (op < opcodes_table_size) ? &opcodes_table[op] : NULL;
|
||||
}
|
||||
|
||||
void put_command(unsigned int address, operation op, int arg)
|
||||
{
|
||||
if(address < MAX_PROGRAM_SIZE) {
|
||||
vm_program[address].operation = op;
|
||||
vm_program[address].arg = arg;
|
||||
}
|
||||
else {
|
||||
milan_error("Illegal address in put_command()");
|
||||
}
|
||||
}
|
||||
|
||||
void set_mem(unsigned int address, int value)
|
||||
{
|
||||
vm_memory[address] = value;
|
||||
}
|
||||
|
||||
88
lab4/vm/vm/vm.h
Normal file
88
lab4/vm/vm/vm.h
Normal file
@@ -0,0 +1,88 @@
|
||||
#ifndef _MILAN_VM_H
|
||||
#define _MILAN_VM_H
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
/* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> */
|
||||
|
||||
/* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> */
|
||||
#define MAX_PROGRAM_SIZE 65536
|
||||
|
||||
/* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> */
|
||||
#define MAX_MEMORY_SIZE 65536
|
||||
|
||||
/* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> */
|
||||
#define MAX_STACK_SIZE 8192
|
||||
|
||||
/* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> */
|
||||
typedef enum {
|
||||
NOP = 0, /* <20><><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> */
|
||||
STOP, /* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> */
|
||||
LOAD, /* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> */
|
||||
STORE, /* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> */
|
||||
BLOAD, /* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> */
|
||||
BSTORE, /* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> */
|
||||
PUSH, /* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD> */
|
||||
POP, /* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD> */
|
||||
DUP, /* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> */
|
||||
INVERT, /* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> */
|
||||
ADD, /* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> */
|
||||
SUB, /* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> */
|
||||
MULT, /* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> */
|
||||
DIV, /* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> */
|
||||
COMPARE, /* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> */
|
||||
JUMP, /* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> */
|
||||
JUMP_YES, /* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>, <20><><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><> 0 */
|
||||
JUMP_NO, /* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>, <20><><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> 0 */
|
||||
INPUT, /* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> */
|
||||
PRINT /* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> */
|
||||
} operation;
|
||||
|
||||
/* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> */
|
||||
typedef enum {
|
||||
EQ, /* = */
|
||||
NE, /* !- */
|
||||
LT, /* < */
|
||||
GT, /* > */
|
||||
LE, /* <= */
|
||||
GE /* >= */
|
||||
} compare_type;
|
||||
|
||||
/* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> */
|
||||
typedef struct {
|
||||
operation operation; /* <20><><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> */
|
||||
int arg; /* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> */
|
||||
} command;
|
||||
|
||||
/* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> */
|
||||
typedef struct opcode_info {
|
||||
char *name; /* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> */
|
||||
int need_arg; /* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>, <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> 1, <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> */
|
||||
} opcode_info;
|
||||
|
||||
/* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD> op.
|
||||
* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>, <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>, <20><> <20><><EFBFBD><EFBFBD><EFBFBD>.
|
||||
*/
|
||||
|
||||
opcode_info* operation_info(operation op);
|
||||
|
||||
/* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> address. */
|
||||
|
||||
void put_command(unsigned int address, operation op, int arg);
|
||||
|
||||
/* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>.
|
||||
*
|
||||
* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> 0 <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>,
|
||||
* <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> STOP <20><><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>.
|
||||
*/
|
||||
|
||||
void run();
|
||||
|
||||
/* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> value <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> address. */
|
||||
|
||||
void set_mem(unsigned int address, int value);
|
||||
|
||||
#endif
|
||||
|
||||
51
lab4/vm/vm/vmlex.l
Normal file
51
lab4/vm/vm/vmlex.l
Normal file
@@ -0,0 +1,51 @@
|
||||
%{
|
||||
#include "vmparse.tab.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifndef __GNUC__
|
||||
#define YY_NO_UNISTD_H
|
||||
#endif
|
||||
%}
|
||||
|
||||
%option noyywrap
|
||||
|
||||
WHITESPACE [ \t]+
|
||||
EOL \n
|
||||
INT -?[0-9]+
|
||||
COMMENT ;[^\n]*\n
|
||||
|
||||
%%
|
||||
|
||||
{EOL}
|
||||
{COMMENT}
|
||||
{WHITESPACE}
|
||||
{INT} { yylval = atoi(yytext); return T_INT; }
|
||||
|
||||
: { return T_COLON; }
|
||||
|
||||
SET { return T_SET; }
|
||||
STOP { return T_STOP; }
|
||||
LOAD { return T_LOAD; }
|
||||
STORE { return T_STORE; }
|
||||
BLOAD { return T_BLOAD; }
|
||||
BSTORE { return T_BSTORE; }
|
||||
PUSH { return T_PUSH; }
|
||||
POP { return T_POP; }
|
||||
DUP { return T_DUP; }
|
||||
INVERT { return T_INVERT; }
|
||||
ADD { return T_ADD; }
|
||||
SUB { return T_SUB; }
|
||||
MULT { return T_MULT; }
|
||||
DIV { return T_DIV; }
|
||||
COMPARE { return T_COMPARE; }
|
||||
JUMP { return T_JUMP; }
|
||||
JUMP_YES { return T_JUMP_YES; }
|
||||
JUMP_NO { return T_JUMP_NO; }
|
||||
INPUT { return T_INPUT; }
|
||||
PRINT { return T_PRINT; }
|
||||
NOP { return T_NOP; }
|
||||
|
||||
<<EOF>> { yyterminate(); }
|
||||
|
||||
%%
|
||||
|
||||
1736
lab4/vm/vm/vmparse.tab.c
Normal file
1736
lab4/vm/vm/vmparse.tab.c
Normal file
File diff suppressed because it is too large
Load Diff
79
lab4/vm/vm/vmparse.tab.h
Normal file
79
lab4/vm/vm/vmparse.tab.h
Normal file
@@ -0,0 +1,79 @@
|
||||
|
||||
/* A Bison parser, made by GNU Bison 2.4.1. */
|
||||
|
||||
/* Skeleton interface for Bison's Yacc-like parsers in C
|
||||
|
||||
Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006
|
||||
Free Software Foundation, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
/* As a special exception, you may create a larger work that contains
|
||||
part or all of the Bison parser skeleton and distribute that work
|
||||
under terms of your choice, so long as that work isn't itself a
|
||||
parser generator using the skeleton or a modified version thereof
|
||||
as a parser skeleton. Alternatively, if you modify or redistribute
|
||||
the parser skeleton itself, you may (at your option) remove this
|
||||
special exception, which will cause the skeleton and the resulting
|
||||
Bison output files to be licensed under the GNU General Public
|
||||
License without this special exception.
|
||||
|
||||
This special exception was added by the Free Software Foundation in
|
||||
version 2.2 of Bison. */
|
||||
|
||||
|
||||
/* Tokens. */
|
||||
#ifndef YYTOKENTYPE
|
||||
# define YYTOKENTYPE
|
||||
/* Put the tokens into the symbol table, so that GDB and other debuggers
|
||||
know about them. */
|
||||
enum yytokentype {
|
||||
T_INT = 258,
|
||||
T_SET = 259,
|
||||
T_NOP = 260,
|
||||
T_STOP = 261,
|
||||
T_LOAD = 262,
|
||||
T_STORE = 263,
|
||||
T_BLOAD = 264,
|
||||
T_BSTORE = 265,
|
||||
T_PUSH = 266,
|
||||
T_POP = 267,
|
||||
T_DUP = 268,
|
||||
T_INVERT = 269,
|
||||
T_ADD = 270,
|
||||
T_SUB = 271,
|
||||
T_MULT = 272,
|
||||
T_DIV = 273,
|
||||
T_COMPARE = 274,
|
||||
T_JUMP = 275,
|
||||
T_JUMP_YES = 276,
|
||||
T_JUMP_NO = 277,
|
||||
T_INPUT = 278,
|
||||
T_PRINT = 279,
|
||||
T_COLON = 280
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
|
||||
typedef int YYSTYPE;
|
||||
# define YYSTYPE_IS_TRIVIAL 1
|
||||
# define yystype YYSTYPE /* obsolescent; will be withdrawn */
|
||||
# define YYSTYPE_IS_DECLARED 1
|
||||
#endif
|
||||
|
||||
extern YYSTYPE yylval;
|
||||
|
||||
|
||||
70
lab4/vm/vm/vmparse.y
Normal file
70
lab4/vm/vm/vmparse.y
Normal file
@@ -0,0 +1,70 @@
|
||||
%{
|
||||
#include "vm.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define YYSTYPE int
|
||||
|
||||
int yylex();
|
||||
void yyerror(char const *);
|
||||
%}
|
||||
|
||||
%token T_INT
|
||||
%token T_SET
|
||||
%token T_NOP
|
||||
%token T_STOP
|
||||
%token T_LOAD
|
||||
%token T_STORE
|
||||
%token T_BLOAD
|
||||
%token T_BSTORE
|
||||
%token T_PUSH
|
||||
%token T_POP
|
||||
%token T_DUP
|
||||
%token T_INVERT
|
||||
%token T_ADD
|
||||
%token T_SUB
|
||||
%token T_MULT
|
||||
%token T_DIV
|
||||
%token T_COMPARE
|
||||
%token T_JUMP
|
||||
%token T_JUMP_YES
|
||||
%token T_JUMP_NO
|
||||
%token T_INPUT
|
||||
%token T_PRINT
|
||||
%token T_COLON
|
||||
|
||||
%%
|
||||
|
||||
program : line program
|
||||
| line
|
||||
;
|
||||
|
||||
line : T_INT T_COLON T_NOP { put_command($1, NOP, 0); }
|
||||
| T_INT T_COLON T_STOP { put_command($1, STOP, 0); }
|
||||
| T_INT T_COLON T_LOAD T_INT { put_command($1, LOAD, $4); }
|
||||
| T_INT T_COLON T_STORE T_INT { put_command($1, STORE, $4); }
|
||||
| T_INT T_COLON T_BLOAD T_INT { put_command($1, BLOAD, $4); }
|
||||
| T_INT T_COLON T_BSTORE T_INT { put_command($1, BSTORE, $4); }
|
||||
| T_INT T_COLON T_PUSH T_INT { put_command($1, PUSH, $4); }
|
||||
| T_INT T_COLON T_POP { put_command($1, POP, 0); }
|
||||
| T_INT T_COLON T_DUP { put_command($1, DUP, 0); }
|
||||
| T_INT T_COLON T_INVERT { put_command($1, INVERT, 0); }
|
||||
| T_INT T_COLON T_ADD { put_command($1, ADD, 0); }
|
||||
| T_INT T_COLON T_SUB { put_command($1, SUB, 0); }
|
||||
| T_INT T_COLON T_MULT { put_command($1, MULT, 0); }
|
||||
| T_INT T_COLON T_DIV { put_command($1, DIV, 0); }
|
||||
| T_INT T_COLON T_COMPARE T_INT { put_command($1, COMPARE, $4); }
|
||||
| T_INT T_COLON T_JUMP T_INT { put_command($1, JUMP, $4); }
|
||||
| T_INT T_COLON T_JUMP_YES T_INT { put_command($1, JUMP_YES, $4); }
|
||||
| T_INT T_COLON T_JUMP_NO T_INT { put_command($1, JUMP_NO, $4); }
|
||||
| T_INT T_COLON T_INPUT { put_command($1, INPUT, 0); }
|
||||
| T_INT T_COLON T_PRINT { put_command($1, PRINT, 0); }
|
||||
| T_SET T_INT T_INT { set_mem($2, $3); }
|
||||
;
|
||||
%%
|
||||
|
||||
void yyerror(char const *str)
|
||||
{
|
||||
printf("Error: %s\n", str);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user