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 части реализации есть ранее встреченный модуль с еще не откомпилированным интерфейсом (в список добавляются оба этих модуля). ```csharp if (currentUnit.State != UnitState.BeginCompilation || currentUnit.SemanticTree != null) { AddCurrentUnitAndItsReferencesToUsesLists(unitsFromUsesSection, directUnitsFromUsesSection, currentUnitNode, currentUnit, GetReferences(currentUnit)); return currentUnit; } ```