Генерация трёхадресного кода

На основе предыдущей работы, добавим генератор трёхадресного кода, использующего следующие команды:

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, тип булевских значений, булевские операторы (инверсия, конъюнкция, дизъюнкция), операторы сравнения (равно, больше, меньше). Добавьте в трёхадресный код соответствующие команды (условный, безусловный переходы, булевские операции, операции сравнения). Убедитесь, что кодогенератор и интерпретатор правильно обрабатывают рекурсию.