Newer
Older
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
52
53
54
55
56
57
58
59
60
61
62
;;; updatef: a pure alternative to setf.
;;; generic macro to update places in an extensible way
(in-package :pure)
#|
An updatef expansion is an ordered collection of five objects:
TEMP-VARS
a list of symbols naming temporary variables to be bound sequentially,
as if by let*, to values resulting from value forms.
TEMP-VALS
a list of forms (typically, subforms of the place) which when evaluated
yield the values to which the corresponding temporary variables
should be bound.
BIND-VARS
a list of symbols naming temporary store variables which are to hold
the new values that will be assigned to the place in the updated state
BINDER-FORM
a form which can reference both the temporary and the store variables, and
which returns an updated state in which the place has been assigned
the updated values, which is the correct value for updatef to return.
READER-FORM
a form which can reference the temporary variables, and which returns
the former value of the place in the state before the update.
|#
(eval-when (:compile-toplevel :load-toplevel :execute)
(defgeneric updatef-expansion (expander &key op args place environment))
(defun get-updatef-expansion (place &optional environment)
"pure analogue to (GET-SETF-EXPANSION PLACE ENVIRONMENT)"
(check-type place cons)
(destructuring-bind (op &rest args) place
(check-type op symbol)
(let ((expansion (get op 'updatef-expansion)))
(unless expansion
(error "No updatef expansion for ~S" op))
(updatef-expansion expansion :op op :args args :place place :environment environment))))
(defmacro %define-updatef-expansion (access-fn value)
`(eval-when (:compile-toplevel :load-toplevel :execute)
(setf (get ',access-fn 'updatef-expansion) ,value)
',access-fn))
(defclass updatef-expander ()
((expander :reader updatef-expander :initarg :expander)))
(defmethod updatef-expansion ((u updatef-expander) &key op args place environment)
(declare (ignore op))
(apply (updatef-expander u) environment place args))
(defmacro define-updatef-expander (access-fn lambda-list &body body)
"pure analogue to (DEFINE-SETF-EXPANDER ACCESS-FN LAMBDA-LIST . BODY)"
(check-type access-fn symbol)
(with-gensyms (args)
(multiple-value-bind (destructuring-lambda-list wholevar wholep envvar envp)
(parse-macro-lambda-list lambda-list)
`(%define-updatef-expansion
,access-fn
(make-instance
'updatef-expander :expander
#'(lambda (,envvar ,wholevar &rest ,args)
,@(unless wholep `((declare (ignore ,wholevar))))
,@(unless envp `((declare (ignore ,envvar))))
(destructuring-bind (,@destructuring-lambda-list) ,args
,@body)))))))
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
(defun get-updatef-expansion-tmpvars (environment args)
(loop
:for arg :in args :for tmpvar = (gensym "ARG")
:when (constantp arg environment)
:collect arg :into actual-args
:else
:collect tmpvar :into actual-args :and
:collect tmpvar :into tmpvars :and
:collect arg :into inits
:finally (return (values tmpvars inits actual-args))))
(defun simple-updatef-expansion (environment op args updater updatef-fun-p)
(check-type updater symbol)
(multiple-value-bind (tmpvars inits actual-args)
(get-updatef-expansion-tmpvars environment args)
(let ((newvalvar (gensym "VAL")))
(values tmpvars inits newvalvar
(if updatef-fun-p
`(,updater ,newvalvar ,@actual-args)
`(,updater ,@actual-args ,newvalvar))
`(,op ,@actual-args)))))
(defclass defupdatef-short-expander (updatef-expander) ())
(defmethod updatef-expansion ((u defupdatef-short-expander) &key op args place environment)
(declare (ignore place))
(simple-updatef-expansion environment op args (updatef-expander u) nil))
(defclass defupdatef-function-expander (updatef-expander) ())
(defmethod updatef-expansion ((u defupdatef-function-expander) &key op args place environment)
(declare (ignore place))
(simple-updatef-expansion environment op args (updatef-expander u) t))
(defclass defupdatef-long-expander (updatef-expander)
((n-bind-vars :initarg :n-bind-vars :reader n-bind-vars)))
(defmethod updatef-expansion ((u defupdatef-long-expander) &key op args place environment)
(declare (ignore place))
(multiple-value-bind (tmpvars inits actual-args)
(get-updatef-expansion-tmpvars environment args)
(let* ((n (n-bind-vars u))
(bind-vars (loop :repeat n :collect (gensym "VAL"))))
(assert (= n (length args)))
(values tmpvars inits bind-vars
(funcall (updatef-expander u) environment (append bind-vars actual-args))
`(,op ,@actual-args)))))
(defmacro defupdatef (access-fn &rest more)
"pure analogue to defsetf"
(etypecase (car more)
(symbol ; short form
(destructuring-bind (update-fn &optional docstring) more
(declare (ignore docstring))
`(%define-updatef-expansion
,access-fn
(make-instance 'defupdatef-short-expander :expander ',update-fn))))
(list ; long form
(destructuring-bind (defsetf-lambda-list bind-vars &body body) more
(assert (every 'identifierp bind-vars))
(multiple-value-bind (lambda-list environment envp)
(parse-defsetf-lambda-list defsetf-lambda-list)
`(%define-updatef-expansion
,access-fn
(make-instance
'defupdatef-long-expander :n-bind-vars (length bind-vars) :expander
#'(lambda (,environment ,@bind-vars ,@lambda-list)
,@(unless envp `((declare (ignore ,environment))))
,@body))))))))
(defmacro define-updatef-function (access-fn lambda-list &body body)
"pure analogue to `(DEFUN (SETF ,FUNCTION) ,LAMBDA-LIST ,@BODY)"
(multiple-value-bind (body decls doc) (parse-body body :documentation t)
(declare (ignore doc))
`(%define-updatef-expansion
,access-fn
(make-instance
'defupdatef-function-expander :expander
#'(lambda ,lambda-list
,decls
(block ,access-fn ,@body))))))
(defun updatef-function (sym)
(assert (symbolp sym))
(let ((u (get sym 'updatef-expansion)))
(typecase u
(defupdatef-function-expander
(updatef-expander u))
(null
(error "No updatef function for symbol ~S" sym))
(defupdatef-short-expander
(let ((i (updatef-expander u)))
(if (and (fboundp i) (not (macro-function i)))
#'(lambda (v &rest args)
(apply i (append args (list v))))
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
(error "updatef inverse for ~S is not a function" sym))))
(t
(error "Updater for symbol ~S is not a function" sym)))))
(defmacro updatef (&rest uargs &environment env)
"pure analogue to SETF"
(let ((nargs (length uargs)))
(cond
((= nargs 2)
(let ((place (first uargs))
(value-form (second uargs)))
(when (atom place)
(error "A variable is not a suitable place for UPDATEF"))
(let* ((op (first place))
(args (rest place))
(expansion (get op 'updatef-expansion)))
(typecase expansion
(null
`(call-updatef-function ',op ,value-form ,args))
(defupdatef-short-expander
`(,(updatef-expander expansion) ,args ,value-form))
(defupdatef-function-expander
`(funcall (load-time-value (updatef-function ',op)) ,value-form ,args))
(updatef-expander
(multiple-value-bind (dummies vals newval binder getter)
(updatef-expansion expansion :op op :args args :place place :environment env)
(declare (ignore getter))
`(let* (,@(mapcar #'list dummies vals))
(multiple-value-bind ,newval ,value-form
,binder))))))))
((oddp nargs)
(error "odd number of args to UPDATEF"))
(t
`(values (loop :for (place value) :on uargs :by #'cddr :collect
`(updatef ,place ,value)))))))
);eval-when