pascalabcnet/documentation/Compiler/Compilation algorithm/Compilation algorithm.md
AlexanderZemlyak 271d5792ce
Documentation (#3211)
* Add Compiler documentation

* Add Intellisense documentation

* Move documentation folder to root directory
2024-10-08 10:00:14 +03:00

13 KiB
Raw Permalink Blame History

Owner: Alexander Zemlyak Tags: Infrastructure

Алгоритм на псевдокоде

  • Полная версия

    CompileUnit(ИмяФайла)
    1.CompileUnit(new СписокМодулей, ИмяФайла)
    2.Докомпилировать модули из СписокОтложенойКомпиляции;
    
    CompileUnit(СписокМодулей, ИмяФайла)
    1.ТекущийМодуль = ТаблицаМодулей[ИмяФайла];
      Если (ТекущийМодуль != 0) то
         Если (ТекущийМодуль.Состояние != BeginCompilation)
          СписокМодулей.Добавить(ТекущийМодуль);
          Выход;
        иначе перейти к пункту 5
    
    2.Если ЭтоФайлDLL(ИмяФайла) то
        Если ((ТекущийМодуль = СчитатьDLL(ИмяФайла)) != 0) то
          СписокМодулей.Добавить(ТекущийМодуль);
          ТаблицаМодулей.Добавить(ТекущийМодуль);
          Выход;
        иначе
          Ошибка("Не могу подключить сборку");
          Выход;
    
    3.Если ЭтоФайлPCU(ИмяФайла) то
        Если ((ТекущийМодуль = СчитатьPCU(ИмяФайла)) != 0) то
          СписокМодулей.Добавить(ТекущийМодуль);
          ТаблицаМодулей.Добавить(ТекущийМодуль);
          Выход;
        иначе
          иначе перейти к пункту 4;
    
    4.ТекущийМодуль = новыйМодуль();
      ТекущийМодуль.СинтаксическоеДерево = Парасеры.Парсить(ИмяФайла,ТекущийМодуль.СписокОшибок);
      Если (ТекущийМодуль.СинтаксическоеДерево == 0) то
         Если (ТекущийМодуль.СписокОшибок.Количество == 0) то
           Ошибка("Модуль не неайден");
         иначе
           Ошибка(ТекущийМодуль.СписокОшибок[0]);
      ТаблицаМодулей[ИмяФайла] = ТекущийМодуль;
      ТекущийМодуль.Состояние = BeginCompilation;
    
    5.СинтаксическийСписокМодулей = ТекущийМодуль.СинтаксическоеДерево.Interface.usesList;
      Для(i = СинтаксическийСписокМодулей.Количество - 1 - ТекущийМодуль.КомпилированыеВInterface.Количество; i >= 0; i--)
         ТекушийМодуль.ТекущийUsesМодуль = СинтаксическийСписокМодулей[i].ИмяФайла;
         ИмяUsesФайла = СинтаксическийСписокМодулей[i].ИмяФайла;
         Если (ТаблицаМодулей[ИмяUsesФайла] != 0)
           Если (ТаблицаМодулей[ИмяUsesФайла].Состояние == BeginCompilation)
             Если (ТаблицаМодулей[ТаблицаМодулей[ИмяUsesФайла].ТекущийUsesМодуль].Состояние=BeginCompilation)
                Ошибка("Циклическая связь модулей");
         CompileUnit(ТекущийМодуль.КомпилированыеВInterface, ИмяUsesФайла);
         Если (ТекушийМодуль.Состояние == Compiled) то
           СписокМодулей.Добавить(ТекушийМодуль);
           Выход;
    
    6.ТекущийМодуль.СемантическоеДерево = КонверторДерева.КонвертироватьInterfaceЧасть(
                                          ТекущийМодуль.СинтаксическоеДерево,
                                          ТекущийМодуль.КомпилированыеВInterface,
                                          ТекущийМодуль.СписокОшибок);
      СписокМодулей.Добавить(ТекущийМодуль);
      СинтаксическийСписокМодулей = ТекущийМодуль.СинтаксическоеДерево.Implementation.usesList;
      Для(i = СинтаксическийСписокМодулей.Количество - 1; i >= 0; i--)
        Если (ТаблицаМодулей[СинтаксическийСписокМодулей[i].ИмяФайла].Состояние == BeginCompilation)
          СписокОтложенойКомпиляции.Добавить(ТаблицаМодулей[СинтаксическийСписокМодулей[i].ИмяФайла]);
        иначе
          CompileUnit(ТекущийМодуль.КомпилированыеВImplementation, СинтаксическийСписокМодулей[i].ИмяФайла);
      Если(ДобавлялиХотябыОдинВСписокОтложенойКомпиляции)
        СписокОтложенойКомпиляции.Добавить(ТекущийМодуль);
        выход;
      иначе
        КонверторДерева.КонвертироватьImplementationЧасть(
                                          ТекущийМодуль.СинтаксическоеДерево,
                                          ТекущийМодуль.СемантическоеДерево,
                                          ТекущийМодуль.КомпилированыеВImplementation
                                          ТекущийМодуль.СписокОшибок);
      ТекущийМодуль.Состояние = Compiled;
      СохранитьPCU(ТекущийМодуль);
    
  • Краткая версия (меньше технических подробностей)

    CompileUnit(ИмяФайла)
      1.CompileUnit(new СписокМодулей, ИмяФайла)
      2.Докомпилировать модули из СписокОтложенойКомпиляции;
    
    CompileUnit(СписокМодулей, ИмяФайла);
      1.Если у этого модуля откомпилирован хотябы интерфейс то
          добавить его в СписокМодулей
          выход
      2.Если это DLL то
          считать
          добавить его в СписокМодулей
          выход
      3.Если это PCU то
          считать
          добавить его в СписокМодулей
          выход
      4.создать новый компилируемыйМодуль
        РаспарситьТекст(ИмяФайла)
        Состояние компилируемогоМодуля установить на BeginCompilation
      5.Для всех модулей из Interface части компилируемогоМодуля справа налево
          Если мы уже начаинали компилировать этот модуль
            Если состояние модуля BeginCompilation
              Если состояние последнего компилируемого им модуля BeginCompilation
                ошибка("Циклическая связь модулей")
                выход
          CompileUnit(Список из Interface части компилируемогоМодуля,модуль.имя)
          Если компилируемыйМодуль.Состояние Compiled то
            добавить его в СписокМодулей
            выход
      6.Откомпилировать Interface часть компилируемогоМодуля
        Для всех модулей из Implementation части компилируемогоМодуля справа налево
          Если состояние очередного модуля BeginCompilation то
            добавить его в список отложеной компиляции;
          иначе
            CompileUnit(Список из Implementation части компилируемогоМодуля,модуль.имя)
        Если Добавляли Хотя бы Один В Список Отложеной Компиляции то
          добавить компилируемыйМодуль в список отложеной компиляции
          выход
        Откомпилировать Implementation часть компилируемогоМодуля
        Состояние компилируемогоМодуля установить на Compiled
        добавить его в СписокМодулей
        Сохранить компилируемыйМодуль в виде PCU файла на диск
    

Словесное описание алгоритма

Модули компилируются в порядке, соответствующем порядку обхода в глубину графа зависимостей. Обход в глубину реализуется рекурсивными вызовами функции CompileUnit.

Сначала рекурсивно обходятся все зависимости интерфейсных частей модулей, причем в порядке обратном порядку в списке uses. Ясно, что может существовать циклическая зависимость интерфейсных частей модулей. Она запрещается (во время компиляции выдается соотв. ошибка). Цикл обнаруживается, если мы встречаем модуль с не откомпилированным интерфейсом, в котором мы уже были.

Пример допустимого цикла !допустимая циклическая зависимость.png

Если все зависимости интерфейса скомпилированы, то в том же порядке начинают рекурсивно обходиться зависимости части реализации (implementation). Отложенная компиляция происходит сразу после окончания первого обхода всех модулей, когда абсолютно все интерфейсные части модулей уже скомпилированы. Это условие гарантирует отсутствие новых циклических интерфейсных зависимостей. Модули добавляются в список отложенной компиляции в случае, если в некотором модуле в списке uses части реализации есть ранее встреченный модуль с еще не откомпилированным интерфейсом (в список добавляются оба этих модуля).

if (currentUnit.State != UnitState.BeginCompilation || currentUnit.SemanticTree != null) 
{
	AddCurrentUnitAndItsReferencesToUsesLists(unitsFromUsesSection, directUnitsFromUsesSection,
	currentUnitNode, currentUnit, GetReferences(currentUnit));
	return currentUnit;
}