mirror of
https://github.com/ashinn/chibi-scheme.git
synced 2025-05-18 21:29:19 +02:00
160 lines
5.9 KiB
Scheme
160 lines
5.9 KiB
Scheme
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; Binary Records
|
|
|
|
;;> \macro{(define-binary-record-type <name> [<bindings> ...] (block: <fields> ...))}
|
|
;;>
|
|
;;> Defines a new record type that supports serializing to and from
|
|
;;> binary ports. The generated procedures accept keyword-style
|
|
;;> arguments:
|
|
;;>
|
|
;;> \itemlist[
|
|
;;> \item{\scheme{(make: <constructor-name>)}}
|
|
;;> \item{\scheme{(pred: <predicate-name>)}}
|
|
;;> \item{\scheme{(read: <reader-name>)}}
|
|
;;> \item{\scheme{(write: <writer-name>)}}
|
|
;;> \item{\scheme{(block: <fields> ...)}}
|
|
;;> ]
|
|
;;>
|
|
;;> The fields are also similar to \scheme{define-record-type} but
|
|
;;> with an additional type:
|
|
;;>
|
|
;;> \scheme{(field (type args ...) getter setter)}
|
|
;;>
|
|
;;> Built-in types include:
|
|
;;>
|
|
;;> \itemlist[
|
|
;;> \item{\scheme{(u8)} - a single byte in [0, 255]}
|
|
;;> \item{\scheme{(u16/le)} - a little-endian short integer}
|
|
;;> \item{\scheme{(u16/be)} - a big-endian short integer}
|
|
;;> \item{\scheme{(fixed-string <length>)} - a fixed-length utf-8 string}
|
|
;;> \item{\scheme{(padded-string <length> (pad <pad-char>))} - a utf-8 string padded to a given length}
|
|
;;> \item{\scheme{(octal <length>)} - an integer in octal string format}
|
|
;;> \item{\scheme{(decimal <length>)} - an integer in decimal string format}
|
|
;;> \item{\scheme{(hexadecimal <length>)} - an integer in hexadecimal string format}
|
|
;;> ]
|
|
;;>
|
|
;;> In addition, the field can be a literal (char, string or
|
|
;;> bytevector), for instance as a file magic sequence or fixed
|
|
;;> separator. The fields (and any constants) are serialized in the
|
|
;;> order they appear in the block. For example, the header of a GIF
|
|
;;> file could be defined as:
|
|
;;>
|
|
;;> \example{
|
|
;;> (define-binary-record-type gif-header
|
|
;;> (make: make-gif-header)
|
|
;;> (pred: gif-header?)
|
|
;;> (read: read-gif-header)
|
|
;;> (write: write-gif-header)
|
|
;;> (block:
|
|
;;> "GIF89a"
|
|
;;> (width (u16/le) gif-header-width)
|
|
;;> (height (u16/le) gif-header-height)
|
|
;;> (gct (u8) gif-header-gct)
|
|
;;> (bgcolor (u8) gif-header-gbcolor)
|
|
;;> (aspect-ratio (u8) gif-header-aspect-ratio)
|
|
;;> ))
|
|
;;> }
|
|
;;>
|
|
;;> For a more complex example see the \scheme{(chibi tar)}
|
|
;;> implementation.
|
|
;;>
|
|
;;> The binary type itself is a macro used to expand to a predicate
|
|
;;> and reader/writer procedures, which can be defined with
|
|
;;> \scheme{define-binary-type}. For example,
|
|
;;>
|
|
;;> \example{
|
|
;;> (define-binary-type (u8)
|
|
;;> (lambda (x) (and (exact-integer? x) (<= 0 x 255)))
|
|
;;> read-u8
|
|
;;> write-u8)
|
|
;;> }
|
|
|
|
(define-syntax define-binary-record-type
|
|
(syntax-rules ()
|
|
((define-binary-record-type name x ...)
|
|
(defrec (x ...) name hidden-make hidden-pred hidden-read hidden-write
|
|
() () ()))))
|
|
|
|
(define-syntax defrec
|
|
(syntax-rules (make: pred: read: write: block:)
|
|
((defrec () n m p r w
|
|
((field-tmp field-read field-read-expr field-write field-write-expr field-get) ...)
|
|
((field getter . s) ...)
|
|
(def-setter ...))
|
|
(begin
|
|
(define-record-type n (m field ...) p
|
|
(field getter . s) ...)
|
|
(define r
|
|
(let ((field-read field-read-expr) ...)
|
|
(lambda (in)
|
|
(let* ((field-tmp (field-read in)) ...)
|
|
(m field ...)))))
|
|
(define w
|
|
(let ((field-write field-write-expr) ...)
|
|
(lambda (x out)
|
|
(field-write (field-get x) out) ...)))
|
|
def-setter ...)
|
|
;; workaround for impls which strip hygiene from top-level defs
|
|
;; for some reason, works in chicken but not across libraries
|
|
;;
|
|
;; (begin
|
|
;; (define-values (n m p getter ... setter ...)
|
|
;; (let ()
|
|
;; (define-record-type n (m field ...) p
|
|
;; (field getter . s) ...)
|
|
;; (def setter val) ...
|
|
;; (values (record-rtd n) m p getter ... setter ...)))
|
|
;; (define r
|
|
;; (let ((field-read field-read-expr) ...)
|
|
;; (lambda (in)
|
|
;; (let* ((field-tmp (field-read in)) ...)
|
|
;; (m field ...)))))
|
|
;; (define w
|
|
;; (let ((field-write field-write-expr) ...)
|
|
;; (lambda (x out)
|
|
;; (field-write (field-get x) out) ...))))
|
|
)
|
|
((defrec ((make: x) . rest) n m p r w b f s)
|
|
(defrec rest n x p r w b f s))
|
|
((defrec ((pred: x) . rest) n m p r w b f s)
|
|
(defrec rest n m x r w b f s))
|
|
((defrec ((read: x) . rest) n m p r w b f s)
|
|
(defrec rest n m p x w b f s))
|
|
((defrec ((write: x) . rest) n m p r w b f s)
|
|
(defrec rest n m p r x b f s))
|
|
((defrec ((block: (field (type . args) getter setter) . fields) . rest) n m p r w b f s)
|
|
(defrec ((block: (field (type . args) getter setter tmp-setter) . fields) . rest) n m p r w b f s))
|
|
((defrec ((block: (field (type . args) getter setter tmp-setter) . fields) . rest) n m p r w
|
|
(b ...) (f ...) (s ...))
|
|
(defrec ((block: . fields) . rest) n m p r w
|
|
(b ...
|
|
(field read-tmp (type read: args) write-tmp (type write: args) getter))
|
|
(f ...
|
|
(field getter tmp-setter))
|
|
(s ...
|
|
(define setter
|
|
(let ((pred? (type pred: args)))
|
|
(lambda (x val)
|
|
(if (not (pred? val))
|
|
(error "invalid val for" 'field val))
|
|
(tmp-setter x val)))))))
|
|
((defrec ((block: (field (type . args) getter) . fields) . rest) n m p r w
|
|
(b ...) (f ...) s)
|
|
(defrec ((block: . fields) . rest) n m p r w
|
|
(b ...
|
|
(field read-tmp (type read: args) write-tmp (type write: args) getter))
|
|
(f ...
|
|
(field getter))
|
|
s))
|
|
((defrec ((block: (field . x)) . rest) n m p r w b f s)
|
|
(syntax-error "invalid field in block" (field . x)))
|
|
((defrec ((block: data . fields) . rest) n m p r w (b ...) f s)
|
|
(defrec ((block: . fields) . rest) n m p r w
|
|
(b ...
|
|
(tmp-data read-tmp (read-literal 'data) write-tmp (write-literal 'data) (lambda (x) x)))
|
|
f
|
|
s))
|
|
((defrec ((block:) . rest) n m p r w b f s)
|
|
(defrec rest n m p r w b f s))
|
|
))
|