Разделы сайта находятся в разработке.
Материалы могут быть неполными или содержать ошибки. Содержимое сайта будет синхронизировано с реализацией при выходе следующей версии, тогда как текущая (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-интерфейс методов. Подробности — в Обобщённом программировании.