Дефекты архитектуры компилятора

Пожалуй, перечислить дефекты архитектуры компилятора сложнее, чем дефекты языка и дефекты реализации компилятора. Поэтому список совсем короткий и перечисленные ошибки могут показаться мелкими.

Использование ассемблера

Обе версии компилятора генерируют асемблерный листинг, а не исполняемый файл. В начале казалось, что так проще, позже стало ясно, что сложность обоих вариантов сопоставима.

Структура промежуточного представления

В 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 была бы не нужна, но код без ссылок немного усложнился бы.

Сайт создан в системе uCoz