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

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

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

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

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

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

У статических объектов (типов и классов), внутренее имя соотвествует полному имени объекта с учетом всех областей имен, тогда как внутреннее имя автоматичеких объектов включает в себя и пустые (не именованные) блоки кода, которые заменяются на монотонно возрастающий порядковый номер блока (т.е. ::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");   
                    ^