Внутреннее имя и манглинг имен
Именование объектов основано на правилах и выбирается из расчёта удобства для программиста. Название переменной или функции должно нести смысл, понятные параметры и тип возвращаемого значения, которые упрощают понимание алгоритма при последующем чтении исходного текста программы.
Но обычные имена мало о чём говорят компьютеру. Поэтому при компиляции программы, имена объектов преобразуются для удобства машинной обработки и к ним добавляется служебная информация.
Внутреннее имя объекта
Каждый объект имеет свое внутреннее имя, уникальное в рамках текущего модуля, с помощью которого комипилятор языка однозначно связывает объект с данными в памяти компьютера.
Внутреннее имя формируется во время анализа синтаксического дерева программы. Оно включает в себя имя объекта, его квалификатор и полное пространство имен, в котором объект был опеределен.
У статических объектов (типов и классов), внутренее имя соотвествует полному имени объекта с учетом всех областей имен, тогда как внутреннее имя автоматичеких объектов включает в себя и пустые (не именованные) блоки кода, которые заменяются на монотонно возрастающий порядковый номер блока (т.е. ::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");
^