Пожалуй, перечислить дефекты архитектуры компилятора сложнее, чем дефекты языка и дефекты реализации компилятора. Поэтому список совсем короткий и перечисленные ошибки могут показаться мелкими.
Обе версии компилятора генерируют асемблерный листинг, а не исполняемый файл. В начале казалось, что так проще, позже стало ясно, что сложность обоих вариантов сопоставима.
В DOS-версии промежуточное представление отсутствует, в Windows-версии используется синтаксическое дерево с узлами следующей структуры:
define nNODE 32768 // Длина массива узлов синтаксического деpева
struct NODE // Узел синтаксического деpева
word ID; // Тип узла
word Value; // Значение
word pLeft; // Ссылка на левое поддеpево
word pRight; // Ссылка на пpавое поддеpево
end
NODE Node [nNODE]; // Массив узлов синтаксического деpева
word nNode = 0;
В качестве признака пустоты (отсутствия) было выбрано значение nNODE. Такое решение допустимо, является переносимым, т.е. не создает проблем при переходе на другую платформу, но оно не создает проблем лишь если длина массива узлов определена заранее. Определить ее заранее значит либо очень ограничить предельный размер программы, либо сделать невозможной работу компилятора на машинах с небольшим объемом оперативной памяти. Для сборки последней на момент написания этоих строк версии компилятора необходимо около тридцати тысяч узлов, т.е. около 512Kb памяти - по нынешним меркам очень мало, но так было не всегда. Существует возможность замены массива Node ссылкой на массив:
NODE @Node = NULL; // Ссылка на массив узлов синтаксического деpева
word nNode = 0;
Такая замена потребует только изменения функции Peek, возвращающей номер пригодного для использования узла, при исчерпании массива можно попытаться создать новый, например вдвое больший, и переписать в него все данные из старого. Это также потребует замены всех значений nNODE новыми. Чтобы этого не делать можно использовать для обозначения пустой ссылки либо не связанное с длиной массива фиксированное большое число (скорее всего, это не хорошо, нельзя задать одно и то же число для всех платформ), либо использовать ноль (не очень хорошо, узел с номером ноль не может быть использован, но потеря невелика), либо минус единицу. В последнем случае ничего не теряется, но есть одна проблема - минимальная версия компилятора не предоставляет чисел со знаком, так сложилось...
Использование ссылок
struct NODE // Узел синтаксического деpева
word ID; // Тип узла
word Value; // Значение
NODE @Left; // Ссылка на левое поддеpево
NODE @Right; // Ссылка на пpавое поддеpево
end
смотрится неплохо, но в DOS-версии увеличивает размер структуры с 7 до 11 байт, что не позволит собрать существующий кросс-компилятор (context.201).
Есть и еще одна неприятность. При создании нового массива узлов дерева все ссылки на элементы старого массива становятся некорректными и код подобный следующему не будет работать:
//Компиляция функции
word @P =@Dict[pFunc].pNode;
word Flag= 0;
Scan(@Buff);
while Flag= 0 | strcmp(@Buff,"end")!=0 do
P = Ctrl(@Buff);
@P =@Node[P].pRight;
Flag = 1;
end
Переписать код без использования ссылок возможно, он даже станет короче:
//Компиляция функции
word P1 =Ctrl(@Scan(@Buff));
Dict[pFunc].pNode=P1;
while strcmp(@Buff,"end")!=0 do
word P2 =Ctrl(@Buff);
Node[P1].pRight=P2;
P1 =P2;
end
Если бы не требовалось наличие хотя бы одного оператора, переменная Flag была бы не нужна, но код без ссылок немного усложнился бы.