source: version/1.0/binary-class.lisp

Last change on this file was version,1.0,2, checked in by David Owen <dsowen@…>, 19 years ago

Switched to keywords for provided types.

File size: 2.8 KB
Line 
1(defpackage #:binary-class
2  (:use #:cl #:cl-ppcre)
3  (:export #:lisp-type-for #:default-for #:read-binary #:define-file-struct))
4
5(in-package #:binary-class)
6
7
8
9(defstruct member-def
10  name
11  type
12  format)
13
14(defun parse-member-def (def)
15  (destructuring-bind (name type format) def
16    (make-member-def :name name :type type :format format)))
17
18(defgeneric lisp-type-for (binary-type)
19  (:method ((type (eql :unsigned)))
20    '(integer 0))
21  (:method ((type (eql :string)))
22    'string)
23  (:method ((type (eql :date)))
24    't))
25
26(defgeneric default-for (binary-type)
27  (:method ((type (eql :unsigned)))
28    0)
29  (:method ((type (eql :string)))
30    "")
31  (:method ((type (eql :date)))
32    (encode-universal-time 0 0 0 01 01 1970)))
33
34(defgeneric read-binary (type stream &optional format)
35  (:method ((type (eql :unsigned)) stream &optional (format 1))
36    (let ((r 0))
37      (dotimes (i format r)
38        (setf (ldb (byte 8 (* i 8)) r) (read-byte stream)))))
39  (:method ((type (eql :string)) stream &optional format)
40    (let ((v (make-sequence '(vector (unsigned-byte 8)) format
41                            :initial-element 0)))
42      (read-sequence v stream)
43      (string-trim '(#\Nul #\Space) (map 'string #'code-char v))))
44  (:method ((type (eql :date)) stream &optional format)
45    (assert (stringp format))
46    (cond
47      ((string= format "YYMMDD")
48       (let* ((year (read-binary :unsigned stream))
49              (month (read-binary :unsigned stream))
50              (day (read-binary :unsigned stream)))
51         (encode-universal-time 0 0 0 day month year)))
52      ((string= format "YYYYMMDD")
53       (let* ((year (read-binary :unsigned stream 2))
54              (month (read-binary :unsigned stream))
55              (day (read-binary :unsigned stream)))
56         (encode-universal-time 0 0 0 day month year)))
57      (t
58       (error "Date-format must be either \"YYMMDD\" or \"YYYYMMDD\".")))))
59
60(defmacro define-file-struct (name &body defs)
61  (let ((defs (mapcar #'parse-member-def defs)))
62    `(progn
63       (defstruct ,name
64         ,@(mapcar (lambda (def)
65                     (with-slots (name type) def
66                       `(,name ,(default-for type)
67                               :type ,(lisp-type-for type))))
68                   (remove-if #'null defs :key #'member-def-type)))
69       (defmethod read-binary ((type (eql ',name)) stream &optional format)
70         (declare (ignore format))
71         (let ((rec (make-instance ',name)))
72           ,@(mapcar (lambda (def)
73                       (with-slots (name type format) def
74                         (if name
75                             `(setf (slot-value rec ',name)
76                                    (read-binary ',type stream ,format))
77                             `(read-binary :unsigned stream ,format))))
78                     defs)
79           rec)))))
Note: See TracBrowser for help on using the repository browser.