1 ;;;; Jelly -- portable serialisation for network communication.
2 ;;;; by Allen Short <washort@twistedmatrix.com>
3 ;;;; This file is in the public domain.
5 "'Get into a rut early: Do the same processes the same way. Accumulate idioms. Standardize. The only difference(!) between Shakespeare and you was the size of his idiom list -- not the size of his vocabulary.' -- Alan Perlis, Programming Epigram #10"
11 (preserved (make-hash-table))
12 (cooked (make-hash-table))
15 (defun jelly-cook (obj j
)
16 (if jelly-debug
(print (list "cook" obj j
) (get-buffer "*jelly-trace*")))
17 (let* ((sexp (gethash obj
(jelly-preserved j
)))
18 (new-list (copy-list sexp
)))
19 (incf (jelly-ref-id j
))
20 (setf (car sexp
) 'reference
)
21 (setf (cdr sexp
) (list (jelly-ref-id j
) new-list
))
22 (setf (gethash obj
(jelly-cooked j
)) (list 'dereference
(jelly-ref-id j
)))
26 (defun jelly-prepare (obj j
)
27 (if jelly-debug
(print (list "prepare" obj j
) (get-buffer "*jelly-trace*")))
28 (setf (gethash obj
(jelly-preserved j
)) (cons 'empty
'pair
)))
30 (defun jelly-preserve (obj sexp j
)
31 (if jelly-debug
(print (list "preserve" obj sexp j
) (get-buffer "*jelly-trace*")))
32 (if (not (eq (gethash obj
(jelly-cooked j
) 'empty
) 'empty
))
33 (progn (setf (third (gethash obj
(jelly-preserved j
))) sexp
)
34 (gethash obj
(jelly-preserved j
)))
35 (setf (gethash obj
(jelly-preserved j
)) sexp
)
38 (defun jelly-serialize (obj j
)
39 (if jelly-debug
(print (list "jelly-serialize" obj j
) (get-buffer "*jelly-trace*")))
40 "(jelly-serialise OBJECT JAR)
41 Serialises OBJECT with state JAR."
42 (if (not (eq (gethash obj
(jelly-cooked j
) 'empty
) 'empty
))
43 (gethash obj
(jelly-cooked j
))
44 (if (not (eq (gethash obj
(jelly-preserved j
) 'empty
) 'empty
))
45 (progn (jelly-cook obj j
)
46 (gethash obj
(jelly-cooked j
)))
53 ((or (eq obj
'None
) (null obj
))
57 (let ((jhash '(dictionary)))
60 (setf jhash
(append jhash
(list (list (jelly-serialize k j
) (jelly-serialize v j
))))))
62 (jelly-preserve obj jhash j
)))
66 (let ((jlist (cons 'list
69 (jelly-serialize e j
))
71 (jelly-preserve obj jlist j
)))
72 (t (error "Unpersistable object: %s" obj
))))))
74 (defun jelly-serialize-alist (alist j
)
75 "elisp wont tell you when a list is an alist, and it doesn't have keyword args, so i have to do something silly like this. If you use this for anything but a single top-level non-circular alist, you deserve to lose. "
76 (let ((jhash '(dictionary)))
77 (jelly-prepare alist j
)
80 (setf jhash
(append jhash
(list (list (jelly-serialize (car pair
) j
) (jelly-serialize (cdr pair
) j
))))))
82 (jelly-preserve alist jhash j
)))
84 (defun jelly-unserialize (jelly)
85 (let ((refs (make-hash-table)))
86 (jelly-fixup-refs (jelly-unserialize-internal jelly
))))
90 (defun jelly-unserialize-internal (jelly)
91 ;; time to make dynamic scoping work for me, not against me :)
92 (if (or (integerp jelly
) (stringp jelly
) (floatp jelly
))
94 (ecase (intern (car jelly
))
96 (mapcar (lambda (i) (jelly-unserialize-internal i
)) (cdr jelly
)))
98 (let ((ht (make-hash-table)))
101 (let ((k (car pair
)) (v (second pair
))) ; hi glyph!
102 (setf (gethash k ht
) (jelly-unserialize-internal v
))))
105 ((integer string float
)
106 (lambda () (cdr jelly
)))
108 (let ((val (jelly-unserialize-internal (third jelly
))))
109 (setf (gethash (second jelly
) refs
) val
)
112 (lexical-let ((-ref-num- (cadr jelly
)))
113 (lambda () (gethash -ref-num- refs
)))))))
115 (defun jelly-fixup-refs (obj)
128 (puthash k
(funcall v
) obj
)
129 (jelly-fixup-refs v
)))
135 (if (and (functionp (car list
)) (not (compiled-function-p (car list
))) (not (cadar list
)))
136 (setf (car list
) (funcall (car list
)))
137 (unless (eq obj
(car list
)) (jelly-fixup-refs (car list
)))))
140 (t (error "it's broke"))))