Выражения типа Bindat не ограничиваются типами, описанными ранее. Они также могут быть произвольными формами Lisp, возвращающими выражения типа Bindat. Например, нижеописанный тип описывает данные, которые могут содержать либо 24-битный код ошибки, либо вектор байтов:
(bindat-type (len u8) (payload . (if (zerop len) (uint 24) (vec (1- len)))))
Кроме того, хотя составные типы обычно распаковываются в (и упаковываются из) ассоциативных списков, это поведение можно изменить с помощью следующих специальных аргументов ключевых слов:
:unpack-val вырЕсли список полей заканчивается этим аргументом ключевого слова, то при распаковке возвращается значение выр вместо стандартного ассоциативного списка. выр может ссылаться на все предыдущие поля по их именам.
:pack-val вырЕсли за типом поля следует этот аргумент ключевого слова, то значение, упаковываемое в это поле, возвращается выр вместо извлечения из ассоциативного списка.
:pack-var имяЕсли списку полей предшествует этот аргумент ключевого слова, то все последующие аргументы :pack-val могут ссылаться на общее значение, упаковываемое в этот составной тип, через переменную с именем имя.
Например, можно описать 16-битное целое число со знаком следующим образом:
(defconst sint16-bindat-spec
(let* ((max (ash 1 15))
(wrap (+ max max)))
(bindat-type :pack-var v
(n uint 16 :pack-val (if (< v 0) (+ v wrap) v))
:unpack-val (if (>= n max) (- n wrap) n))))
После чего его поведение будет следующим:
(bindat-pack sint16-bindat-spec -8)
⇒ "\377\370"
(bindat-unpack sint16-bindat-spec "\300\100")
⇒ -16320
Наконец, вы можете определять новые формы типа Bindat для использования в выражениях типа Bindat с помощью bindat-defmacro:
Определяет новое выражение типа Bindat с именем имя, принимающее аргументы арги. Его поведение соответствует поведению defmacro, с важным отличием: новые формы могут использоваться только внутри выражений типа Bindat.