* Add Compiler documentation * Add Intellisense documentation * Move documentation folder to root directory
163 lines
13 KiB
Markdown
163 lines
13 KiB
Markdown
|
||
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. Ясно, что может существовать **циклическая зависимость интерфейсных частей** модулей. Она запрещается (во время компиляции выдается соотв. ошибка). Цикл обнаруживается, если мы встречаем модуль с не откомпилированным интерфейсом, в котором мы уже были.
|
||
|
||
<aside>
|
||
💡 Однако после обнаружения требуется еще проверка на то, что цикл образован целиком из “интерфейсных связей”, поскольку в случае присутствия в нем связи через implementation часть модуля он не представляет опасности благодаря механизму отложенной компиляции, про которую читайте далее.
|
||
|
||
</aside>
|
||
|
||
**Пример допустимого цикла**
|
||
![[допустимая циклическая зависимость.png]]
|
||
---
|
||
|
||
Если все зависимости интерфейса скомпилированы, то в том же порядке начинают рекурсивно обходиться зависимости части реализации (implementation). **Отложенная компиляция** происходит сразу после окончания первого обхода всех модулей, когда абсолютно все интерфейсные части модулей уже скомпилированы. Это условие гарантирует отсутствие новых циклических интерфейсных зависимостей. Модули добавляются в список отложенной компиляции в случае, если в некотором модуле в списке uses части реализации есть ранее встреченный модуль с еще не откомпилированным интерфейсом (в список добавляются оба этих модуля).
|
||
|
||
<aside>
|
||
💡 Может возникнуть вопрос, что происходит в случае циклической зависимости частей реализации. В этой ситуации мы в какой-то момент попадем во второй раз в модуль с откомпилированным интерфейсом и проверка, приведенная далее, в начале CompileUnit приведет к выходу из функции. А данный модуль докомпилируется позже в другом рекурсивном вызове.
|
||
|
||
</aside>
|
||
|
||
```csharp
|
||
if (currentUnit.State != UnitState.BeginCompilation || currentUnit.SemanticTree != null)
|
||
{
|
||
AddCurrentUnitAndItsReferencesToUsesLists(unitsFromUsesSection, directUnitsFromUsesSection,
|
||
currentUnitNode, currentUnit, GetReferences(currentUnit));
|
||
return currentUnit;
|
||
}
|
||
``` |