Язык TL-B
Эта страница переведена сообществом на русский язык, но нуждается в улучшениях. Если вы хотите принять участие в переводе свяжитесь с @alexgton.
TL-B (Type Language - Binary) служит для описания системы типов, конструкторов и существующих функций. Например, мы
можем использовать схемы TL-B для построения двоичных структур, связанных с блокчейном TON. Специальные парсеры TL-B могут считывать схемы для
дес ериализации двоичных данных в различные объекты. TL-B описывает схемы данных для объектов Cell
. Если вы не знакомы с Cells
, пожалуйста, прочтите статью Ячейки пакеты Ячеек (BOC).
Общие сведения
Мы называем любой набор конструкций TL-B документами TL-B. Документ TL-B обычно состоит из объявлений типов (
т. е. их конструкторов) и функциональных комбинаторов. Объявление каждого комбинатора заканчивается точкой с запятой (;
).
Вот пример возможного объявления комбинатора:


Конструкторы
Левая часть каждого уравнения описывает способ определения или сериализации значения типа, указанного в правой части. Такое описание начинается с имени конструктора.


Конструкторы используются для указания типа комбинатора, включая состояние при сериализации. Например, конструкторы
также могут использоваться, когда вы хотите указать op
(код операции) в запросе к смарт-контракту в TON.
// ....
transfer#5fcc3d14 <...> = InternalMsgBody;
// ....
- имя конструктора:
transfer
- префиксный код конструктора:
#5fcc3d14
Обратите внимание, что за каждым именем конструктора сразу следует необязательный тег конструктора, такой как #_
или $10
, который
описывает битовую строку, используемую для кодирования (сериализации) рассматриваемого конструктора.
message#3f5476ca value:# = CoolMessage;
bool_true$0 = Bool;
bool_false$1 = Bool;
Левая часть каждого уравнения описывает способ определения или сериализации значения типа, указанного в
правой части. Такое описание начинается с имени конструктора, например message
или bool_true
, за которым сразу следует необязательный тег конструктора, например #3f5476ca
или $0
, который описывает биты, используемые для кодирования (
сериализации) рассматриваемого конструктора.
конструктор | сериализация |
---|---|
some#3f5476ca | 32-битный uint сериализуется из шестнадцатеричного значения |
some#5fe | 12-битный uint сериализуется из шестнадцатеричного значения |
some$0101 | сериализует 0101 необработанные биты |
some или some# | сериализует crc32(уравнение) | 0x80000000 |
some#_ или some$_ или _ | не сериализуется |
Имена конструкторов (some
в этом примере) используются как переменные в codegen. Например:
bool_true$1 = Bool;
bool_false$0 = Bool;
Тип Bool
имеет два тега 0
и 1
. Псевдокод Codegen может выглядеть так:
class Bool:
tags = [1, 0]
tags_names = ['bool_true', 'bool_false']
Если вы не хотите определять имя для текущего конструктора, просто передайте _
, например, _ a:(## 32) = 32Int;
Теги конструктора могут быть заданы либо в двоичной (после знака доллара), либо в шестнадцатеричной нотации (после знака решетки). Если
тег не указан явно, анализатор TL-B должен вычислить 32-битный тег конструктора по ум олчанию, хешируя с помощью алгоритма CRC32 текст "уравнения" с | 0x80000000
, определяя этот конструктор определенным образом. Поэтому пустые теги
должны быть явно указаны #_
или $_
.
Этот тег будет использоваться для определения текущего типа битовой строки в процессе десериализации. Например, у нас есть 1 битовая строка 0
,
если мы скажем TLB проанализировать эту битовую строку в типе Bool
, он проанализирует ее как Bool.bool_false
.
Допустим, у нас есть более сложные примеры:
tag_a$10 val:(## 32) = A;
tag_b$00 val(## 64) = A;
Если мы проанализируем 1000000000000000000000000000000000000001
(1 и 32 нуля и 1) в типе TLB A
- сначала нам нужно получить первые
два бита для определения тега. В этом примере 10
- это два первых бита, и они представляют tag_a
. Теперь мы знаем, что следующие 32
бита - это переменная val
, 1
в нашем примере. Некоторые "проанализированные" переменные псевдокода могут выглядеть так:
A.tag = 'tag_a'
A.tag_bits = '10'
A.val = 1
Все имена конструкторов должны быть разными, а теги конструкторов для одного типа должны составлять префиксный код (иначе десериализация не будет уникальной); т. е. ни один тег не может быть префиксом любого другого в том же типе.
Максимальное количество конструкторов для одного типа: 64
Максимальное количество бит для тега: 63
example_a$10 = A;
example_b$01 = A;
example_c$11 = A;
example_d$00 = A;
Псевдокод Codegen может выглядеть так:
class A:
tags = [2, 1, 3, 0]
tags_names = ['example_a', 'example_b', 'example_c', 'example_d']
example_a#0 = A;
example_b#1 = A;
example_c#f = A;
Псевдокод Codegen может выглядеть так:
class A:
tags = [0, 1, 15]
tags_names = ['example_a', 'example_b', 'example_c']
Если вы используете тег hex
, имейте в виду, что он будет сериализован как 4 бита для каждого шестнадцатеричного символа. Максимальное значение — 63-битное целое число без знака. Это означает:
a#32 a:(## 32) = AMultiTagInt;
b#1111 a:(## 32) = AMultiTagInt;
c#5FE a:(## 32) = AMultiTagInt;
d#3F5476CA a:(## 32) = AMultiTagInt;
конструктор | сериализация |
---|---|
a#32 | 8-битный uint сериализуется из шестнадцатеричного значения |
b#1111 | 16-битный uint сериализуется из шестнадцатеричного значения |
c#5FE | 12-битный uint сериализуется из шестнадцатеричного значения |
d#3F5476CA | 32-битный uint сериализуется из шестнадцатеричного значения |
Также шестнадцатеричные значения разрешены как в верхнем, так и в нижнем регистре.
Подробнее о шестнадцатеричных тегах
В дополнение к классическому определению шестнадцатеричного тега, за шестнадцатеричным числом может следовать символ подчеркивания. Это означает, что тег равен указанному шестнадцатеричному числу без младшего бита. Например, есть такая схема:
vm_stk_int#0201_ value:int257 = VmStackValue;
И тег на самом деле не равен 0x0201
. Чтобы вычислить его, нам нужно удалить LSb из двоичного представления 0x0201
:
0000001000000001 -> 000000100000000
Таким образом, тег равен 15-битному двоичному числу 0b000000100000000
.
Определения полей
За конструктором и его необязательным тегом следуют определения полей. Каждое определение поля имеет
форму ident:type-expr
, где ident — это идентификатор с именем поля (заменяется на подчеркивание для
анонимных полей), а type-expr — это тип поля. Тип, представленный здесь, является выражением типа, которое может включать
простые типы, параметризованные типы с подходящими параметрами или сложные выражения.
1023
бит и 4
ссылки)
Простые типы
_ a:# = Type;
-Type.a
здесь 32-битное целое число_ a:(## 64) = Type;
-Type.a
здесь 64-битное целое число_ a:Owner = NFT;
-NFT.a
здесь типOwner
_ a:^Owner = NFT;
-NFT.a
здесь ссылка на ячейку типаOwner
означает, чтоOwner
хранится в следующей ссылке на ячейку.
Анонимные поля
_ _:# = A;
- первое поле - анонимное 32-битное целое число
Расширяем ячейку ссылками
_ a:(##32) ^[ b:(## 32) c:(## 32) d:(## 32)] = A;
- Если по какой-то причине мы хотим отделить некоторые поля в другую ячейку, мы можем использовать синтаксис
^[ ... ]
. В этом примереA.a
/A.b
/A.c
/A.d
- это 32-битные целые числа без знака, ноA.a
хранится в первой ячейке, аA.b
/A.c
/A.d
хранятся в следующей ячейке (1 ссылка)
_ ^[ a:(## 32) ^[ b:(## 32) ^[ c:(## 32) ] ] ] = A;
- Также допускаются цепочки ссылок. В этом примере каждая из
переменные (
a
,b
,c
) хранятся в отдельных ячейках
Параметризованные типы
Предположим, у нас есть тип IntWithObj
:
_ {X:Type} a:# b:X = IntWithObj X;
Теперь мы можем использовать его в других типах:
_ a:(IntWithObj uint32) = IntWithUint32;
Сложные выражения
-
Условные поля (только для
Nat
) (E?T
означает, что если выражениеE
истинно, то поле имеет типT
)_ a:(## 1) b:a?(## 32) = Example;
В типе
Example
переменнаяb
сериализуется, только еслиa
равно1
-
Выражение умножения для создания кортежей (
x * T
означает создание кортежа длиныx
типаT
):a$_ a:(## 32) = A;
b$_ b:(2 * A) = B;_ (## 1) = Bit;
_ 2bits:(2 * Bit) = 2Bits; -
Выборка бита (только для
Nat
) (E . B
означает взятие битаB
изNat
E
)_ a:(## 2) b:(a . 1)?(## 32) = Example;
В типе
Example
переменнаяb
сериализуется, только если второй битa
равен1
-
Также разрешены другие операторы
Nat
(см.Разрешенные ограничения
)
Примечание: можно объединить несколько сложных выражений:
_ a:(## 1) b:(## 1) c:(## 2) d:(a?(b?((c . 1)?(## 64)))) = A;
Встроенные типы
#
-Nat
32-битное целое число без знака## x
-Nat
сx
битами#< x
-Nat
меньшеx
бит целое число без знака, сохраненное какlenBits(x - 1)
бит, до 31 бита#<= x
-Nat
меньше или равноx
бит целое число без знака, сохраненное какlenBits(x)
бит, до 32 битAny
/Cell
- остальные биты и ссылки ячейкиInt
- 257 битUInt
- 256 битBits
- 1023 битаuint1
-uint256
- 1 - 256 битint1
-int257
- 1 - 257 битbits1
-bits1023
- 1 - 1023 битаuint X
/int X
/bits X
- то же, что иuintX
, но в этих типах можно использовать параметризованныйX