summaryrefslogtreecommitdiffstats
path: root/refs.lisp
blob: eed0ee635266cdf81f1fa1294adf6a2bf4e04115 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
;;;
;;; Lisp references: pointer-like place locators that can be passed around.
;;;
;;; Copyright 2012 Kaz Kylheku <kaz@kylheku.com>
;;;
;;; How to use:
;;;
;;; Produce a reference which "lifts" the place designated
;;; by form P:
;;;
;;;   (ref p)
;;;
;;; Dereference a reference R to designate the original place:
;;;
;;;   (deref r)
;;;   (setf (deref r) 42) ;; store new value 42
;;;
;;; Shorthand notation instead of writing a lot of (deref)
;;; Over FORMS, A is a symbol macro which expands to 
;;; (DEREF RA), B expands to (DEREF RB):
;;;
;;;   (with-refs ((a ra) (b rb) ...)
;;;     
;;;     ... forms)
;;; 
(defstruct ref 
  (get-func) 
  (set-func))

(defun deref (ref) 
  (funcall (ref-get-func ref))) 

(defun (setf deref) (val ref) 
  (funcall (ref-set-func ref) val)) 

(defmacro ref (place-expression &environment env)
  (multiple-value-bind (temp-syms val-forms 
                        store-vars store-form access-form)
                        (get-setf-expansion place-expression env)
    (when (cdr store-vars)
      (error "REF: cannot take ref of multiple-value place"))
    `(multiple-value-bind (,@temp-syms) (values ,@val-forms)
       (make-ref
         :get-func (lambda () ,access-form)
         :set-func (lambda (,@store-vars) ,store-form)))))

(defmacro with-refs ((&rest ref-specs) &body forms) 
  `(symbol-macrolet 
     ,(loop for (var ref) in ref-specs 
            collecting (list var `(deref ,ref))) 
     ,@forms))