Bindat型の式は、前述の型に限定されるものではありません。Bindat型の式を返す任意のLisp形式である場合もあります。例えば、以下の型は、24ビットのエラーコードまたはバイトベクトルのいずれかを保持できるデータを記述しています:
(bindat-type (len u8) (payload . (if (zerop len) (uint 24) (vec (1- len)))))
さらに、複合型は通常アソシエーションリストに展開(およびそこからパック)されますが、以下の特殊なキーワード引数を用いることでこれを変更できます。
:unpack-val expフィールドのリストがこのキーワード引数で終わっている場合、アンパック時に返される値は、標準的なalistではなくexpの値となります。expでは、それまでのすべてのフィールドを名前で参照できます。
:pack-val expフィールドの型の後にこのキーワード引数が続く場合、このフィールドに格納された値は、alistから抽出されるのではなく exp によって返されます。
:pack-var nameフィールドのリストの前にこのキーワード引数がある場合、それ以降のすべての :pack-val
引数は、name という名前の変数を介して、この複合型にパックする全体の値を参照できます。
例えば、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-defmacro
を使用して、Bindat型式で用いる新しいBindat型フォームを定義できます:
name という名前で、引数 args を取る新しい
Bindat 型の式を定義します。その挙動は defmacro
に従いますが、重要な違いは、新しい形式が Bindat 型の式内でのみ使用可能であるという点です。