Архитектура

Описание реализация архитектуры для лучшего понимания отдельных важных моментов

Ключем поиска объектов явялется строка с полным именем в иерархии имен относительно корня

Система классов реализации

RunTime - единственный класс для приложения (процесса) - интерфейс для взаимодействия с операционной системой

  • Загружат и выгружает модули и является их владельцем (shared_ptr)
  • Хранит глобальные объекты (ObjPtr) для глобальных объектов (встроенных типов и функций). Ключем явялется строка с полным именем объекта в иерархии имен относительно корня
  • Хранит список глобальных объектов (типы данных и прототипы функций в TermPtr, которые создаются на этапе компиляции). Используется парсером и компилятором для разбора исходного текста и создания исполняемого файла.

Module - класс для модуля

  • Хранение объектов модуля (shared_ptr) и добавляет weak_ptr в глобальный список объектов в RunTime

Context - класс для хранения временных данных при выполнении программы

  • Один класс создается сразу при создании RunTime
  • В дальнейшем создается по одному объекту для каждого нового потока (:Thread)
  • Хранение списка объектов (weak_ptr)
  • Хранение локальных объектов (ObjPtr), которые создаются на этапе выполнения

Исходный текст -> Парсинг -> AST (TermPtr) -+-> Компиляция в модуль (LLVM) -> Выполнение модуля | +-> Интерпретация (Выполнение AST)

Реализация загрузки модулей и пакетов

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

Загружаемый модуль парсится как обучный файл (включая обработку вложенных модулей), после чего парсер завершает работу. В результате получается отдельное AST загружаемого модуля и общая база макросов. После этого в AST модуля раскрываются области имен, производится проверка имен объетов и создание единой базы глобальных объектов.

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

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

Загрузка пакета обработывается точто таким же образом, как и загрузка моделя.

Шаги сборки компилятора

  • Сборка nlc
  • Генерация с помощью nlc --no-runtime --no-dsl -emit-cpp модуля dsl
  • Сборка модуля dsl
  • Генерация с помощью nlc --no-runtime -emit-cpp пакета runtime
  • Сборка пакета runtime

В результате имеем готовый nlc с модулями runtime.nlm и dsl.nlm, а заодно и проверяем:

  • генерацию С++ кода без макросов
  • сборку модуля
  • загрузку модуля
  • генерацию С++ кода с макросами
  • сборку пакета
  • загрузку пакета

Выполнение тестов с модулями runtime.nlm и dsl.nlm

Опции компилятора

  • -no-dsl - не использовать автоматически загружаемый модуль dsl
  • -no-runtime - не использовать автоматически загружаемый модуль runtime
  • -emit-cpp - генерировать выходной *.cpp файл

5.5. alignas(N)

Этот спецификатор появился в C++11. Применяется к простым переменным, массивам и классам. N — это выражение, вычисляемое при компиляции, его значение должно быть степенью двойки. Переменная при этом будет размещена по адресу, кратному значению N. Например:

    alignas(64) char cacheline[64];

    cacheline:Int8[64](__alignas__=64) := _;

constexpr

Этот спецификатор появился в C++11. Применяется к простым переменным, массивам, функциям и функциям-членам. Для простой переменной или массива это означает, что ее значение вычисляется на этапе компиляции и не может быть изменено. Для функции это означает, что ее возвращаемое значение вычисляется на этапе компиляции, если значения аргументов известно на стадии компиляции. (Но такую функцию можно использовать и с обычными аргументами.) Вот пример:

    constexpr double PI = 3.1415926535897932;
    constexpr int Square(int x) { return x * x; }


    PI^:Double = 3.1415926535897932;
    Square^(x:Int64):Int64 ::=  { return x * x; }

5.7. consteval

Этот спецификатор появился в C++20. Применяется к функциям и функциям-членам. Это более строгий вариант constexpr, при вызове такой функции значения аргументов должны быть всегда известны на стадии компиляции.

?????????????????????????????????????
    Square^(x^:Int64):Int64 ::=  { return x * x; }

5.8. noexcept

Этот спецификатор появился в C++11. Применяется к функциям и функциям-членам и располагается в конце инструкции, после списка параметров. Этот спецификатор гарантирует отсутствие исключений в процессе выполнения тела функции.

    func() ::= {*

    *};

5.9. mutable

Применяются к нестатическим членам класса, такие члены можно изменять в константных функциях-членах (см. раздел 5.10.1).

В C++20 для агрегатных типов появилась еще назначенная инициализация (designated initialization):

struct Point { int X; int Y; };

Point pt = { .X = 1, .Y = 2 };

Эта инициализация позаимствована из C. Инициализируемые члены могут быть опущены и не только в конце, для них гарантируется инициализация по умолчанию. В отличие от C, порядок членов изменять нельзя, он должен быть таким же, как и при объявлении.