Внутреннее имя и манглинг имен

Именование объектов основано на правилах и выбирается из расчёта удобства для программиста. Название переменной или функции должно нести смысл, понятные параметры и тип возвращаемого значения, которые упрощают понимание алгоритма при последующем чтении исходного текста программы.

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

Внутреннее имя объекта

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

Внутреннее имя формируется во время анализа синтаксического дерева программы. Оно включает в себя имя объекта, его квалификатор и полное пространство имен, в котором объект был опеределен.

У статических объектов (типов и классов), внутренее имя соотвествует полному имени объекта с учетом всех областей имен, тогда как внутреннее имя автоматичеких объектов включает в себя и пустые (не именованные) блоки кода, которые заменяются на монотонно возрастающий порядковый номер блока (т.е. ::1::, ::2:: и т.д.). Включение в пространство имен пустых блоков кода необходимо для реализации возможности перекрытия имен у объектов, определенных в разных областях видимости.

Кроме этого, все квалификаторы объектов переносятся в конец имени, что и является отличительным признаком внутреннего имени (последний символ должен быть “$” или “:”).

Примеры внутренних имен:

# Глобальный статический объект
::var ::= 0; # ::var::

# Локальный объект модуля
$var := 0;  # var$

# Статический объект модуля
@::var ::= 0; # var::

# Глобальные тип и конструктор класса
:: :type := :Type;     # ::type:::
:: :cls := Class(){};  # ::cls:::

# Синоним типа или конструктор класса модуля
:type := :Type;     # type:::
:cls := Class(){};  # cls:::

# Тип или конструктор класса всегда статические
@:: :type := :Type;     # type:::
@:: :cls := Class(){};  # cls:::

ns {
    $var := 0;      # ns::var$
    var ::= 0;      # ns::var$
    @::var ::= 0;   # ns::var:: - статический объект

    :type := :Type;     # ns::type:::
    :cls := Class(){};  # ns::cls:::

    @::func1() ::= {};   # ns::func1::
    func2() ::= {};      # ns::func2::

    # Не именованный блок кода
    {  
        # Локальные имена перекрывают объявления выше
        $var := 0;  # ns::1::var$
        var := 0;   # ns::1::var$

        # Статический объект соответствует области имен
        @::var ::= 0; # ns::var::

        # Типы данных всегда статические
        :type := :Type;     # ns::type:::
        :cls := Class(){};  # ns::cls:::

        @::func3() := {};    # ns::func3::
        # Локальная функция с ограниченной областью видимости ?????
        func4() := {};       # ns::1::func4$
    }
};

# Не именованный блок кода 
{
    $var := 0;      # 2::var$
    var ::= 0;      # 2::var$
    @::var = 0;     # var:: - статический объект

    :type := :Type;     # type:::
    :cls := Class(){};  # cls:::
};

Манглинг внутренних имен

Внутреннее имя уникально только в рамках одного модуля, но внутренние имена в разных модулях могут пересекаться.

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

Внутреннее имя объекта после манглинга является корректным идентификатором для языков C/C++, что позволяет обращатся к объектам NewLang из внешних библиотек и приложений на языке реализации (C/C++) по семантически похожему имени (в отличии от манглинга имен в С++).

Манглинг внутреннего имени заключается в замене каждого символа двоеточия на символ $, а у имени типа квалификатор заменяется тремя символами ‘$’, т.е. ’type $$$’.

При манглинге, к внутренним именам объектов добавляется специальный префикс, который кодирует имя компилируемого модуля. Имя текущего модуля находится в переменной препроцессора "@$$" и по умолчанию, содержит имя файла модуля (у главного модуля приложения имя модуля отсутствует).

Имя модуля может быть однократно переопределено самым первым оператором модуля или заменено во время импорта модуля с помощью системного параметра __package__.

    # Задать имя модуля
    @$$ = "\dir\file";

    # Импорт модуля с переопределением имени
    \dir\file(__package__="\new\module\name");

Тоже само с использвоанием DSL:

@package \dir\name;  # @$$ = "\dir\file";

@import \dir\name as \new\module\name; # \dir\file(__package__="\new\module\name");

При манглинге имя модуля, по умолчанию имя файла отностительно каталога главного файла программы, размещается в начале внутреннего системного имени. Так как имя модуля, согласно правилам синтаксиса, может содерждать только строчные буквы и цифры, то разделитель каталогов (обратный слеш) заменяется на подчерк, а начало и конец имени модуля отмечаются комбинациями символов “_$” и “$_” соответственно.

Так как локальной переменной с одним подчерком в качестве имени быть не может, то начало строки на “_$” служит отличительным признаком манглинга внутреннего имени объекта.

@$$ = "\dir\file";

# Глобальный статический объект
::var ::= 0; # _$dir_file$_$$var$$

# Локальный объект модуля
$var := 0;  # _$dir_file$_var$

# Статический объект модуля
@::var ::= 0; # _$dir_file$_var$$

# Глобальные тип и конструктор класса
:: :type := :Type;     # _$dir_file$_$$type$$$
:: :cls := Class(){};  # _$dir_file$_$$cls$$$

# Синоним типа или конструктор класса модуля
:type := :Type;     # _$dir_file$_type$$$
:cls := Class(){};  # _$dir_file$_cls$$$

ns {
    $var := 0;  # _$dir_file$_ns$$var$
    var ::= 0;  # _$dir_file$_ns$$var$
    @::var ::= 0; # _$dir_file$_ns$$var$$ - статический объект

    :type := :Type;     # _$dir_file$_ns$$type$$$
    :cls := Class(){};  # _$dir_file$_ns$$cls$$$

    @::func() ::= {};      # _$dir_file$_ns$$func$$
    func() ::= {};      # _$dir_file$_ns$$func$

    # Не именованный блок кода
    {  
        # Локальные имена перекрывают обяъявления выше
        $var := 0;  # _$dir_file$_ns$$1$$var$
        var := 0;  # _$dir_file$_ns$$1$$var$

        # Статический объект соответствует области имен
        @::var ::= 0; # _$dir_file$_ns$$var$$

        # Типы данных и функции всегда статические
        :type := :Type;     # _$dir_file$_ns$$type$$$
        :cls := Class(){};  # _$dir_file$_ns$$cls$$$

        @::func() := {};      # _$dir_file$_ns$$func$$
        func() := {};      # _$dir_file$_ns$$1$$func$ ????????
    }
};

# Не именованный блок кода 
{
    $var := 0;      # _$dir_file$_2::var$
    var ::= 0;      # _$dir_file$_2::var$
    @::var = 0;     # _$dir_file$_var$$ - статический объект

    :type := :Type;     # _$dir_file$_type$$$
    :cls := Class(){};  # _$dir_file$_cls$$$
};

Манглинг имен блока кода главного модуля программы:

{
    $var := 0;      # _$$_1::var$
    var ::= 0;      # _$$_1::var$
    @::var = 0;     # _$$_var$$ - статический объект

    :type := :Type;     # _$$_type$$$
    :cls := Class(){};  # _$$_cls$$$
};

Встроенный блок кода и вызов функций

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

Это досупкается как для обычных переменных, так и для имен функций, реализованных в NewLang, и для того, что бы компилятор C/C++ имел возможность создать правильный код, при раскрытии встроенного блока кода перед ним добавляются сигнатуры переменных и функций, которые используются во строенном блоке кода.

Для имен переменных дополнительный манглинг имен не производится, а для функций автоматичсеки создается дополнительная сигнатура с описанием типов аргументов и их значений по умолчанию, если таковые используются.

Каждая функция NewLang имеет две сигнатуры, первая (основная) принимает все аргументы в виде словаря, а вторая (дополнительная) с развернутым списком аргументов, чтобы её можно было вызывать как обычную C++ функцию и проверка типов могла происходить С/С++ непосредственно компилятором.

Манглинг второго (дополнительного) имени функции заключается в добавлении в конец имени двух символов “_$”.

    
    # Определение функции в NewLang
    ::func_embed(int64_t arg_long, uint8_t arg_byte = 10): Int64 ::= {
        @return arg_long + arg_byte;
    }

    # С/С++ прототип для первого (основоного) имени
    # extern "C" _$$_func_embed$( Obj &args );

    # С/С++ прототип для второго (дополнительного) имени
    # extern "C" _$$_func_embed$_$(int64_t arg_long, uint8_t arg_byte = 10);
    
    # Тогда при раскрытии блока с расширенным синтаксисом 
    # $func_embed заменяется на _$$_func_embed$_$, а компилятор С/С++ 
    # будет в состоянии проверить её аргументы
    {%
        int64_t value = $func_embed(10, 20);
    %}

Хотя может имеет смысл сделать inline шаблон для подобного вызова, а не создавать кополнительное имя?

./object.h:732:25: error: deduced conflicting types ('int' vs 'const char *') for initializer list element type   
            auto list = {args...};   
                        ^~~~~
test/object_test.cpp:131:21: note: in instantiation of function template specialization 'newlang::Obj::operator()<int, const char *>' requested here   
    str3 = (*format)(-1, "222");   
                    ^