Модуль и s-выражения
Разбираем по косточкам синтаксис s-выражений в WAT.
Модуль — это корневой контейнер Wasm:
(module ...), внутри которого живут функции, память, импорты и экспорты.
Всё начинается с module
Любой WAT-файл — это одно большое s-выражение, начинающееся с (module и заканчивающееся закрывающей скобкой. Внутри перечислены составные части модуля. Пустой модуль выглядит так — он валиден, но ничего не делает:
(module)
Что такое s-выражение
S-выражение — это узел дерева в скобках: первый элемент после скобки задаёт тип узла (ключевое слово), остальное — его содержимое или вложенные узлы. Например, (param $x i32) — это узел типа param с именем $x и типом i32. Узлы вкладываются друг в друга, образуя дерево всей программы.
(module
(func ...) ;; узел-функция внутри модуля
(memory ...) ;; узел-память внутри модуля
(export ...)) ;; узел-экспорт внутри модуля
Имена и комментарии
Идентификаторы в WAT начинаются с символа $: $add, $x, $counter. Это удобные псевдонимы для людей — при компиляции в бинарь они превращаются в числовые индексы. Комментарии бывают двух видов: строчные начинаются с ;; и идут до конца строки, блочные оборачиваются в (; ... ;).
(module
;; это строчный комментарий
(; а это
блочный комментарий ;)
(func $nichego))
Два стиля записи: плоский и вложенный
WAT допускает два эквивалентных способа писать тело функции. Плоский (линейный) стиль — это последовательность инструкций, как настоящий стековый байткод. Вложенный (folded) — те же инструкции, но записанные деревом в скобках, что нагляднее показывает, что во что вкладывается.
;; плоский стиль
(func $add (param $a i32) (param $b i32) (result i32)
local.get $a
local.get $b
i32.add)
;; вложенный стиль — то же самое
(func $add2 (param $a i32) (param $b i32) (result i32)
(i32.add (local.get $a) (local.get $b)))
Оба варианта компилируются в один и тот же байткод. Вложенный читается как формула, плоский — как пошаговая работа со стеком.
Как работает под капотом
Когда wat2wasm разбирает файл, он строит из s-выражений дерево, проверяет типы и затем «разворачивает» вложенные узлы обратно в линейную последовательность стековых инструкций — ведь бинарный формат именно линейный. То есть folded-стиль это синтаксический сахар: для удобства чтения, но в байтах всё равно окажется плоская цепочка push-операций.
Частые ошибки
- Несбалансированные скобки — самая частая ошибка в WAT. Каждой
(нужна своя). - Забытый
$перед именем —local.get addвместоlocal.get $addне скомпилируется. - Смешение стилей без понимания — можно мешать плоский и вложенный, но новичку лучше выбрать один.
Итоги
- Корень WAT — это
(module ...). - S-выражение — скобочный узел: ключевое слово + содержимое.
- Имена начинаются с
$, комментарии —;;и(; ;). - Плоский и вложенный стили эквивалентны; folded — это сахар.