Нативные типы

Нативные типы данных

Нативными (машинными) данными могут быть как переменные, так и функции, а имя нативных объектов начинается на символ процента “%”.

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

Чтобы использовать уже существующие библиотеки, нужно либо импортировать нативный объект, тем самым создав объкт TrustLang, но с реализацией на другом языке, например на С/С++, либо сделать предварительное определение (декларацию) нативного объекта.

Импорт нативных объектов

Импорт — связывание trust-имени функции/переменной с именем существующей нативной функции/переменной. В отличие от предварительного объявления, импортируемая функция регистрируется под своим trust-именем, а её имя и аргументы могут отличаться от имени и аргументов импортируемой нативной функции.

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

  • Если типы регистрируемой и нативной функции совпадают (преобразование не требуется), нативное имя можно указать с многоточием — оно означает, что аргументы вызова повторяют аргументы левой сигнатуры (дублировать их не нужно):

    sqr(x:Float64):Float64 := %std::sqrt...;   # аргументы как у sqr
    
  • В общем случае аргументы и тип импортируемой функции задаются явно; при генерации вставляется вызов правой функции, а при необходимости аргументы приводятся через static_cast:

    sqr(x:Float64):Float64 := %std::sqrt(x:Float128, true):Float128;
    

При импорте аргументы автоматически преобразуются к нативным типам импортируемой функции. Импорт может выполняться во время компиляции программы или во время выполнения (динамическая загрузка библиотеки).

:FILE ::= :Plain;

fopen(filename:StrChar, modes:StrChar):FILE := %fopen...;
fclose(f:FILE):Int32 := %fclose...;
fflush(f:FILE):Int32 := %fflush...;
fprintf(f:FILE, format:FmtChar, ...):Int32 := %fprintf...;

fremove(filename:String):Int32 := :Native('%remove...');
frename(old:String, new:String):Int32 := :Native('%rename...');

Если нативная функция/переменная требует линковки внешней библиотеки, имя библиотеки указывается атрибутом @[link("имя")]@ перед декларацией. Атрибут добавляет флаг -l<имя> к шагу линковки (build.conf LIBS). Существование символа/библиотеки не проверяется — ответственность линковщика. Поддерживается только статическая линковка.

@[link("m")]@
fsqrt(x:Float64):Float64 := %sqrt...;

@[link("z")]@
fcrc(crc:UInt32):UInt32 := %crc32...;

Предварительное объявление нативных объектов

Предварительное объявление (forward-declaration) — регистрация нативной функции/переменной, имя которой и есть нативный C-style идентификатор (напр. %sqrt — функция sqrt). Имя и аргументы объявляемой функции совпадают с нативной сигнатурой — никакой адаптации не выполняется. Реализация может быть расположена ниже в текущем файле или во внешней библиотеке — линковщик найдёт символ по имени на этапе сборки.

Правило линковки нативного имени: если оно без :: (напр. %sqrt, %open, %abs) — декларация линкуется как C-символ (extern "C"), что позволяет корректно вызывать функции libc/libm; если имя содержит :: (напр. %std::sqrt) — как C++-символ (extern "C" не добавляется).

Синтаксис такой же, как у создания, но нативное имя стоит слева, а справа — символ многоточия (тела нет). Типы аргументов и возвращаемого значения указываются явно.

    
    %open(path:StrChar, flags:Int32):Int32 := ...;
    %close(fd:Int32):Int32 := ...;
    %read(fd:Int32, buf:Buffer, cnt:SizeT):SizeT := ...;


/*
    int fd, sz;
    char* c = (char*)calloc(100, sizeof(char));

    fd = open("foo.txt", O_RDONLY);
    if (fd < 0) {
        perror("r1");
        exit(1);
    }

    sz = read(fd, c, 10);
    printf("called read(% d, c, 10). returned that"
           " %d bytes were read.\n",
           fd, sz);
    c[sz] = '\0';
    printf("Those bytes are as follows: % s\n", c);
*/

    %fd^:Int32 := 0;
    %sz^:SizeT := 0;
    %buf^:StrChar := StrChar( :Tensor[100](0, ... ) );

    fd = %open("foo.txt", O_RDONLY);
    if (fd < 0) {
        @perror("r1");
        @exit(1);
    }

    sz = %read(fd, buf, 10);
    %printf("called read(%d, buf, 10). returned that"
           " %d bytes were read.\n",
           fd, sz);
    buf[sz] = 0';
    %printf("Those bytes are as follows: %s\n", :Pointer(buf));

Последующее описание находится в процессе разработки.

:Pointer — указатель на системную область памяти или нативную функцию

⚠️ НЕ РЕАЛИЗОВАНО: тип :Pointer и его использование в текущей версии не реализованы; ввод/вывод через системные типы (FILE, Buffer, SizeT) не соответствует текущей реализации. Нижеследующий пример устарел

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

:Binary Иштфкн Plain — указатель на представление данных в бинарном виде

Для взаимодействия с внешними библиотеками требуется еще и обмен данными. И для этих целей служит тип данных :Plain — который также является указателем, но на двоичное представление данных в области памяти. Конечно, если их можно представить в виде одного фрагмента.

Перечисление, структура и объединение

Некоторые типы данных являются полными аналогами нативных типов, что требуется для взаимодействия с внешними библиотеками, в том числе и для блоков кода с нативным синтаксисом.

https://habr.com/ru/companies/otus/articles/770404/

C++98: std::map, std::set, std::multimap и std::multiset
C++11: std::unordered_map, std::unordered_set, std::unordered_multimap и std::unordered_multiset
C++23: std::flat_map, std::flat_set, std::flat_multimap и std::flat_multiset

std::list, std::deque и т.д.

:Map, :MultiMap :Pair???????????????????????????????????????

:StrChar, :StrWide, :Array, :Set, :Struct, :Union, :Enum и :Optional — это такие же словари или перечисления, но на их элементы накладываются определённые ограничения. Каждый элемент должен иметь уникальное имя, а его тип данных должен быть простым, т.е. числом или строкой фиксированного размера. Типы данных :Plain могут быть представлены в двоичном виде в одной области машинной памяти и бинарно совместимы с соответствующими типами данных C/C++.