Перейти к основному содержимому

Глубокое погружение в Fift

warning

Эта страница переведена сообществом на русский язык, но нуждается в улучшениях. Если вы хотите принять участие в переводе свяжитесь с @alexgton.

Высокоуровневый стековый язык Fift используется для локальной манипуляции ячейками и другими примитивами TVM, в основном для преобразования ассемблерного кода TVM в код контракта bag-of-cells.

предупреждение

В этом разделе описывается взаимодействие с специфичными для TON функциями на очень низком уровне. Требуется серьезное понимание основ стековых языков.

Простая арифметика

Вы можете использовать интерпретатор Fift как калькулятор, записывая выражения в обратной польской нотации.

6 17 17 * * 289 + .
2023 ok

Стандартный выход

27 emit ."[30;1mgrey text" 27 emit ."[37m"
grey text ok

emit берет число с вершины стека и выводит символ Unicode с указанным кодом в stdout. ."..." выводит строку-константу.

Определение функций (Fift-слов)

Основной способ определения слова - заключить его значения в фигурные скобки, затем написать ":" и название слова.

{ minmax drop } : min
{ minmax nip } : max

Fift.fif

Хотя есть несколько "определяющих слов", а не только ":". Они отличаются в смысле слов, определяемых некоторыми из них, активные (работают внутри фигурных скобок), а некоторые префиксные (не требуют пробела после них):

{ bl word 1 2 ' (create) } "::" 1 (create)
{ bl word 0 2 ' (create) } :: :
{ bl word 2 2 ' (create) } :: :_
{ bl word 3 2 ' (create) } :: ::_
{ bl word 0 (create) } : create

Fift.fif

Условное выполнение

Блоки кода (разделенные фигурными скобками) могут выполняться как условно, так и безоговорочно.

{ { ."true " } { ."false " } cond } : ?.   4 5 = ?.  4 5 < ?.
false true ok
{ ."hello " } execute ."world"
hello world ok

Циклы

// ( l c -- l')  deletes first c elements from list l
{ ' safe-cdr swap times } : list-delete-first

GetOpt.fif

Слово цикла times принимает два аргумента — назовем их cont и n — и выполняет cont n раз. Здесь list-delete-first берет продолжение safe-cdr (команда удаления заголовка из списка в стиле Lisp), помещает его под c, а затем c times удаляет заголовок из списка, присутствующего в стеке.

Есть также слова while и until.

Комментарии

{ 0 word drop 0 'nop } :: //
{ char " word 1 { swap { abort } if drop } } ::_ abort"
{ { bl word dup "" $= abort"comment extends after end of file" "*/" $= } until 0 'nop } :: /*

Fift.fif

Комментарии определены в Fift.fif. Однострочный комментарий начинается с // и продолжается до конца строки; многострочный комментарий начинается с /* и заканчивается */.

Давайте разберемся, почему они так работают. Программа Fift по сути является последовательностью слов, каждое из которых каким-либо образом преобразует стек или определяет новые слова. Первая строка Fift.fif (код показан выше) является объявлением нового слова //. Комментарии должны работать даже при определении новых слов, поэтому они должны работать во вложенной среде. Вот почему они определены как активные слова с помощью ::. Действия создаваемого слова перечислены в фигурных скобках:

  1. 0: ноль помещается в стек
  2. word: эта команда считывает символы до тех пор, пока не будет достигнут один, равный вершине стека, и помещает считанные данные в стек как строку. Ноль — это особый случай: здесь word пропускает начальные пробелы и затем читает до конца текущей входной строки.
  3. drop: верхний элемент (данные комментария) удаляется из стека.
  4. 0: ноль снова помещается в стек — число результатов, использованное потому, что слово определено как ::.
  5. nop помещает токен выполнения, ничего не делая при вызове. Это практически эквивалентно { nop }.

Использование Fift для определения кодов сборки TVM

x{00} @Defop NOP
{ 1 ' @addop does create } : @Defop
{ tuck sbitrefs @ensurebitrefs swap s, } : @addop
{ @havebitrefs ' @| ifnot } : @ensurebitrefs
{ 2 pick brembitrefs 1- 2x<= } : @havebitrefs
{ rot >= -rot <= and } : 2x<=
...

Asm.fif (порядок строк обратный)

@Defop проверяет, достаточно ли места для кода операции (@havebitrefs), и если его нет, он продолжает запись в другой сборщик (@|; также известно как неявный переход). Вот почему вы обычно не хотите писать x{A988} s в качестве кода операции: для размещения этого кода операции может быть недостаточно места, поэтому компиляция завершится неудачей; вместо этого вам следует написать x{A988} @addop.

Вы можете использовать Fift для включения большого bag-of-cells в контракт:

<b 8 4 u, 8 4 u, "fift/blob.boc" file>B B>boc ref, b> <s @Defop LDBLOB

Эта команда определяет код операции, который при включении в программу записывает x{88} (PUSHREF) и ссылку на предоставленный bag-of-cells. Поэтому, когда запускается инструкция LDBLOB, она помещает ячейку в стек TVM.

Специальные возможности

  • Шифрование Ed25519
  • newkeypair - генерирует пару закрытый-открытый ключ
  • priv>pub - генерирует открытый ключ из закрытого
  • ed25519_sign[_uint] - генерирует подпись по заданным данным и закрытому ключу
  • ed25519_chksign - проверяет подпись Ed25519
  • Взаимодействие с TVM
  • runvmcode и подобное - вызывает TVM с фрагментом кода, взятым из стека
  • Запись BOC в файлы: boc>B ".../contract.boc" B>file

Продолжаем изучение