Разделы сайта находятся в разработке.

Материалы могут быть неполными или содержать ошибки. Содержимое сайта будет синхронизировано с реализацией при выходе следующей версии, тогда как текущая (0.6.0) предназначена для прототипирования синтаксиса и создания песочницы.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Если нативная декларация/тип требует подключения C++-заголовка, укажите его атрибутом @[include("имя")@] перед объявлением (или мнемоникой @include("имя") без ;). Голое имя → угловой инклуд #include <имя>; аргумент с ведущим "/< — директива как есть. Заголовок эмитится в сгенерированный C++ только вместе с этим объявлением:

@include("cstdlib")
%abort(x:Int32):Void := ...;   # → #include <cstdlib>

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

Предварительное объявление (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:StrChar, cnt:SizeT):SizeT := ...;

    %fd^:Int32 := 0;
    fd = %open('foo.txt', 0);   # вызов нативной функции по имени

Другие нативные типы (статус)

Пользовательские типы Enum/Variant/Tuple описаны в Словарях и наборах.

Нативные шаблоны-типы

Любой нативный C++-шаблон-тип можно объявить в коде TrustLang (регистрация при анализе AST); заголовок C++ подключается on-use при использовании типа. Объявление — префикс типовых параметров

  • нативное C++-имя:
@include("vector")
<T> %std::vector() := ...;    # тип vector<T>, C++-имя std::vector

v: vector<Int32> := [1, 2, 3,];
# → #include <vector>  +  std::vector<int32_t> c_v = std::vector<int32_t>{1, 2, 3};

Типовой аргумент — голое имя Int32 (мягкое предупреждение о сигиле :) или явный тип :Int32/:MyClass/:MyClass<:Int8>. Если C++-имя совпадает со встроенным контейнером (std::vector/std::array от :Array), выдаётся мягкая диагностика, а инстанциация резолвится через встроенный тип. Подробнее — Generics.

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

Нативный C++-класс (не шаблон) можно использовать в коде TrustLang через предварительное объявление по аналогии с forward-объявлением функций: trust-имя класса связывается с нативным C++-именем, а члены-интерфейс (методы, поля, конструкторы, статические члены) описываются в теле { ... }. Тело класса не содержит реализации — каждый член — это forward-объявление (:= ...): класс определён в C++-заголовке, который подключается on-use через атрибут @[include].

@include("string")
String ::= %std::string {          # trust-класс String ↔ C++ std::string
    %size(): UInt64 := ...;        # метод объекта → s.size()
    %empty(): Bool := ...;         # метод объекта → s.empty()
    %c_str(): CString := ...;      # метод объекта → s.c_str()
    %data: CString := ...;         # поле объекта → s.data
};

Синтаксис:

  • String ::= %std::string { ... }; — RHS — ЛЮБОЕ имя; нативное — с ведущим % (C++-имя без %). Доверенное имя класса — слева от ::=.
  • Члены (все — forward, := ...):
    • %Name(...):Ret := ...; — метод объекта → obj.name(...);
    • @::st(...):Ret := ...; — статический метод класса (имя с ::; @:: → ns::Class::) → Cls::st(...);
    • %field:Type := ...; — поле объекта → obj.field;
    • @::static_field:Type := ...; — статическое поле → Cls::static_field;
    • %String(...):String := ...; — конструктор (имя совпадает с классом) → std::string(...).
  • Классификация: статический член — имя содержит :: (задаётся @::name); экземплярный — без ::. Правильная регистрация экземплярного члена — ведущая точка (.name); без неё — диагностика -Wclass-member-dot (по умолчанию ignore; -Wclass-member-dot=warning|error).
  • Доступ к статическому члену: Cls::name (namespace-стиль, без предупреждения) или Cls.name (как к полю) → cppName::name; доступ Cls.name выдаёт -Wstatic-member-as-field (по умолчанию warning; -Wstatic-member-as-field=ignore|error).
  • Атрибут @[include("заголовок")@] (или мнемоника @include("заголовок")) перед объявлением подключает C++-заголовок только при использовании данного типа (on-use).

C++-struct для нативного класса не генерируется — используется существующее C++-имя.

@include("string")
MyStr ::= %std::string {
    %size(): UInt64 := ...;
    %data: CString := ...;   # поле объекта → s.data
};

s: MyStr := MyStr('hello');              # конструктор → std::string("hello")
n: UInt64 := s.%size();                  # метод → (c_s).size()
d: CString := s.%data;                   # поле → (c_s).data
# → #include <string>

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

Нативный C++-шаблон-класс объявляется так же, но с типовыми параметрами слева и явной реализацией через инстанциацию %std::pair<T1,T2> справа (основная форма):

@include("utility")
<T1,T2> Pair ::= %std::pair<T1,T2> {    # trust-шаблон Pair ↔ std::pair
    %first(): T1 := ...;                # Pair<A,B>.first
    %second(): T2 := ...;               # Pair<A,B>.second
};

p: Pair<Int32, StrChar> := ...;
# → std::pair<int32_t, std::string> + #include <utility>

Обобщённая форма <T1,T2> Pair ::= <T1,T2> %std::pair { ... } (шаблон привязывается обобщённо, без явного списка аргументов в RHS) — сахар, реализация отложена: при разборе выдаётся диагностика «not implemented»; используйте явную форму %std::pair<T1,T2>. Формат записи одинаков для нативного и не-нативного имени и отличается только наличием/отсутствием %.

Интеграция с C/C++: embed-вставки и обзор (взаимодействие)

TrustLang глубоко интегрирован с экосистемой C/C++: прямые вызовы функций, встраивание C++-кода, использование стандартной библиотеки и вызовы функций TrustLang из C++.

Готовые, запускаемые примеры — в песочнице: нативные функции, нативная линковка.

Импорт нативных функций: @extern / @forward

Мнемонические команды связывают trust-имя с нативным C/C++-символом:

@extern abs(x:Int32):Int32   # abs(x:Int32):Int32 := %abs ...
@forward open(path:StrChar, flags:Int32):Int32
  • @extern — импорт по нативному имени (trust-имя = нативное имя без префикса %).
  • @forward — предварительное объявление (имя указывается первым, тело — многоточие).

Сырая запись: %name(...):T := ...; (forward-декларация) и name(...):T := %native...; (импорт-алиас).

Встраивание C++ кода: блоки {% ... %}

{% for (int i = 0; i < 10; ++i) { printf("%d\n", i); } %}

Нативный синтаксис вставляется в выходной C++ код напрямую.

Атрибуты нативных деклараций

  • @[link("имя")] — добавить -l<имя> к линковке.
  • @[include("header")] — подключить заголовок C++ для нативной декларации/типа. Голое имя (@[include("vector")@]) → угловой инклуд #include <vector>; аргумент с ведущим "/< → директива как есть (#include "my/header.h" / #include <cstdlib>). Заголовок эмитится в сгенерированный C++ только вместе с объявлением (не глобально). Мнемоника @include("name") (без ;, перед объявлением) раскрывается в этот атрибут.
  • @[libstd] — линковать со стандартной библиотекой.
  • @[format("printf", ...)] — проверка аргументов против printf-строки формата.

Пример подключения заголовка нативной функции:

@include("cstdlib")
%abort(x:Int32):Void := ...;   # → #include <cstdlib> + extern "C" void abort(int32_t);

Линковка

Линковка нативной декларации зависит от наличия :: в нативном имени: без :: (%sqrt) — extern "C" (C-символ libc/libm); с :: (%std::sqrt) — C++-линковка без extern "C". Подробнее о генерации C++ — в Архитектуре.

Для модулей на самом TrustLang применяется другой механизм — семантический ABI: тип объекта восстанавливается компилятором языка из приложенного семантического определения, а не из декорированного имени символа. Принцип описан в Семантическом ABI модулей.

Ссылки

Нативные шаблоны-типы и шаблон-классы

C++-шаблоны-типы и нативные классы/шаблон-классы объявляются через типовые параметры и forward-интерфейс методов. Подробности — в Обобщённом программировании.