На основе предыдущей работы, добавим генератор трёхадресного кода, использующего следующие команды:
AddInt i, j
- Сложение целых
SubInt i, j
- Вычитание целых
MulInt i, j
- Умножение целых
DivInt i, j
- Деление целых
PowInt x, i
- Возведение дробного в целую степень
NegInt i
- Отрицание целых
AddFloat x, y
- Сложение дробных
SubFloat x, y
- Вычитание дробных
MulFloat x, y
- Умножение дробных
DivFloat x, y
- Деление дробных
PowFloat x, y
- Возведение дробного в дробную степень
NegFloat x
- Отрицание дробных
IntToFloat x
- Преобразование целого в дробное
Call s, l
- Вызов функции с именем
s
с аргументами в спискеl
(некоторое отступление от стандартного трёхадресного кода в интересах упрощения), при этом вl
содержатся ссылки на значения или предыдущие команды. Return w, n
- Возврат из функции с результатом
w
, из функцииn
аргументов GetParam n
- Получить параметр функции с номером
n
Exit w
- Завершение программы с результатом
w
Дополнительно введём в трёхадресный код следующие сущности:
Var s
- Переменная с именем
s
Int i
- Целый числовой литерал
i
Float x
- Дробный числовой литерал
x
Аргументом команды может быть одна из этих сущностей, ссылка на другую команду, или литерал строки (s
) или целого (n
).
Упражнение 1. Реализуйте генератор трёхадресного кода путём обхода синтаксического дерева. Результат – массив (вектор, список) команд трёхадресного кода. Команды рекомендуется представлять в виде косвенных троек (т.е. операнды – указатели/ссылки на предыдущие команды). Для выбора корректной команды используйте информацию, полученную на этапе проверки типов: если результат выражения имеет тип int
, то используются команды *Int
, если float
, то *Float
. Если операнд команды *Float
имеет тип int
, он сначала должен быть преобразован к float
командой IntToFloat
.
Упражнение 2. Реализуйте интерпретатор трёхадресного кода через последовательный проход по массиву команд. Вызов функций можно реализовать рекурсивным вызовом интерпретатора (проще) или помещая код функций в один массив с основным кодом и совершая переходы по индексу.
Упражнение 3 (*). Реализуйте возможность сохранения сгенерированного трёхадресного кода в виде двоичного файла и измените интерпретатор таким образом, чтобы он мог читать и интерпретировать такой двоичный файл.
Упражнение 4 (**). Добавьте в язык ветвление при помощи конструкции if ... then ... else
, тип булевских значений, булевские операторы (инверсия, конъюнкция, дизъюнкция), операторы сравнения (равно, больше, меньше). Добавьте в трёхадресный код соответствующие команды (условный, безусловный переходы, булевские операции, операции сравнения). Убедитесь, что кодогенератор и интерпретатор правильно обрабатывают рекурсию.