-
Notifications
You must be signed in to change notification settings - Fork 14
/
tcons.lisp
305 lines (230 loc) · 8.54 KB
/
tcons.lisp
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
63
64
65
66
67
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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
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
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
;; -*- lisp -*-
;; This file is part of STMX.
;; Copyright (c) 2013-2016 Massimiliano Ghilardi
;;
;; This library is free software: you can redistribute it and/or
;; modify it under the terms of the Lisp Lesser General Public License
;; (http://opensource.franz.com/preamble.html), known as the LLGPL.
;;
;; This library is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty
;; of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
;; See the Lisp Lesser General Public License for more details.
(in-package :stmx.util)
(enable-#?-syntax)
;;;; ** TCONS: a transactional cell holding two values. It is the STM equivalent of CONS cells.
(declaim (notinline tcons-first (setf tcons-first)
tcons-rest (setf tcons-rest)
tconsp)
(inline make-tcons))
;; transactional objects are a little slow...
;; use a transactional struct instead
#-(and)
(transactional
(defclass tcons ()
((first :initarg :first :accessor first-of)
(rest :initarg :rest :accessor rest-of))
(:documentation "Transactional cell holding two values. It is the STM equivalent of CONS cells.
To use TCONS cells, see the functions TCONS, TLIST, TFIRST and TREST.")))
#+(and)
(transactional
(defstruct (tcons (:predicate tconsp) (:copier copy-tcons))
"Transactional cell holding two values. It is the STM equivalent of CONS cells.
To use TCONS cells, prepend T to the name of most list-manipulating functions. Examples:
(CONS a b) -> (TCONS a b)
(LIST ...) -> (TLIST ...)
(FIRST c) -> (TFIRST c)
(REST c) -> (TREST c) and so on"
(first nil)
(rest nil)))
(deftype tlist () '(or tcons null))
(declaim (ftype (function (t t) (values tcons &optional)) tcons)
(ftype (function (#-ecl tlist #+ecl t) (values t &optional)) tfirst trest)
(notinline tcons))
(defun tcons (first rest)
"Create and return a new TCONS."
(make-tcons :first first :rest rest))
(optimize-for-transaction*
(:inline t)
(defun tfirst (tlist)
"Return the first element in a TCONS or TLIST."
(when tlist (tcons-first tlist))))
(optimize-for-transaction*
(:inline t)
(defun trest (tlist)
"Return the rest element in a TCONS or TLIST."
(when tlist (tcons-rest tlist))))
(optimize-for-transaction*
(:inline t)
(defun (setf tfirst) (value cons)
"Set VALUE as the first element in a TCONS or non-null TLIST.
This function should always be executed inside an STMX atomic block."
(declare (type tcons cons))
(the (values t &optional)
(setf (tcons-first cons) value))))
(optimize-for-transaction*
(:inline t)
(defun (setf trest) (value cons)
"Set VALUE as the rest element in a TCONS or non-null TLIST.
This function should always be executed inside an STMX atomic block."
(declare (type tcons cons))
(the (values t &optional)
(setf (tcons-rest cons) value))))
;; defined automatically by (defstruct tcons ...) above
#-(and)
(defun copy-tcons (cons)
(declare (type tcons cons))
(tcons (tcons-first cons) (tcons-rest cons)))
;; defined automatically by (defstruct tcons ...) above
#-(and)
(defun tconsp (object)
"Return T if OBJECT is a TCONS, and NIL otherwise."
(not (tconsp object)))
(declaim (inline tatom))
(defun tatom (object)
"Return NIL if OBJECT is a TCONS, and T otherwise."
(not (tconsp object)))
(defprint-object (obj tcons :type nil :identity nil)
(write-string "(")
(loop for value = (tfirst obj)
for rest = (trest obj)
do
(format t "~A" value)
(unless (typep rest 'tcons)
(unless (null rest)
(format t " . ~A" rest))
(return))
(write-string " ")
(setf obj rest))
(write-string ")"))
(optimize-for-transaction*
(:inline t)
(defun trplaca (tcons x)
"Change the TCAR of TCONS to X and return the TCONS."
(declare (type tcons tcons))
(setf (tcons-first tcons) x)
tcons))
(optimize-for-transaction*
(:inline t)
(defun trplacd (tcons x)
"Change the TCDR of TCONS to X and return the TCONS."
(declare (type tcons tcons))
(setf (tcons-rest tcons) x)
tcons))
(defmacro tpush (value place)
"Equivalent to PUSH, but for TCONS transactional cells.
Inserts VALUE as the first element in PLACE.
Return the modified PLACE."
(multiple-value-bind (temps vals stores store-form get-form)
(get-setf-expansion place)
(with-gensym var
`(let* ((,var ,value)
,@(loop for temp in temps
for val in vals
collect `(,temp ,val))
(,(first stores) (tcons ,var ,get-form)))
,store-form))))
(defmacro tpop (place)
"Equivalent to POP, but for TCONS transactional cells.
Removes and returns the first element in PLACE."
(multiple-value-bind (temps vals stores store-form get-form)
(get-setf-expansion place)
(with-gensym var
`(let* (,@(loop for temp in temps
for val in vals
collect `(,temp ,val))
(,var ,get-form)
(,(first stores) (trest ,var)))
,store-form
(tfirst ,var)))))
(declaim (inline tlistp))
(defun tlistp (object)
"Return true if OBJECT is a TLIST, and NIL otherwise."
(or (null object) (tconsp object)))
(declaim (ftype (function (&rest t) (values tlist &optional)) tlist))
(defun tlist (&rest list)
"Create and return a new TLIST, whose cells are TCONS."
(when list
(let ((list #?+&rest-is-fresh-list (nreverse list)
#?-&rest-is-fresh-list (reverse list))
(result nil))
(dolist (e list result)
(setf result (tcons e result))))))
(define-compiler-macro tlist (&whole form &rest list)
(cond
((null list) nil)
((null (rest list)) `(tcons ,(first list) nil))
((null (rest (rest list))) `(tcons ,(first list) (tcons ,(second list) nil)))
(t form)))
(defun tlist* (arg0 &rest args)
"Return a TLIST of the arguments with last TCONS a dotted pair."
;; We know the &REST is a proper list.
(cond ((null args) arg0)
((atom (rest args)) (tcons arg0 (first args)))
(t (let* ((args #?+&rest-is-fresh-list (nreverse args)
#?-&rest-is-fresh-list (reverse args))
(argn (pop args))
(list (tcons (pop args) argn)))
(dolist (argx args)
(setf list (tcons argx list)))
(tcons arg0 list)))))
(define-compiler-macro tlist* (&whole form arg0 &rest args)
(cond
((null args) arg0)
((null (rest args)) `(tcons ,arg0 ,(first args)))
((null (rest (rest args))) `(tcons ,arg0 (tcons ,(first args) ,(second args))))
(t form)))
(defun make-tlist (size &key initial-element)
"Constructs a tlist with SIZE elements each set to INITIAL-ELEMENT"
(declare (type fixnum size))
(do ((count size (1- count))
(result nil (tcons initial-element result)))
((<= count 0) result)
(declare (type fixnum count))))
(defmacro do-tlist ((var tlist &optional result) &body body)
"Analogous to DOLIST, iterates on transactional list TLIST.
On each iteration, sets VAR to the element and executes BODY inside a tagbody.
Returns RESULT. Note: when RESULT is executed, VAR is set to NIL.
An implicit block named NIL surrounds DO-TLIST, so RETURN can be used
to terminate immediately the iterations and return zero or more values."
(with-gensyms (l start)
`(block nil
(let ((,l ,tlist))
(tagbody
,start
(unless (tendp ,l)
(let ((,var (tcons-first ,l)))
(setf ,l (tcons-rest ,l))
(tagbody
,@body))
(go ,start))))
(let ((,var nil))
(declare (ignorable ,var))
,result))))
;;; basic tlist operations.
(optimize-for-transaction*
(:inline t)
(defun tcar (list)
"Return the 1st object in a TLIST."
(declare (type tlist list))
(tfirst list)))
(optimize-for-transaction*
(:inline t)
(defun tcdr (list)
"Return all but the first object in a TLIST."
(declare (type tlist list))
(trest list)))
(optimize-for-transaction*
(:inline t)
(defun (setf tcar) (value cons)
"Set VALUE as the first element in a TCONS or non-null TLIST.
This function should always be executed inside an STMX atomic block."
(declare (type tcons cons))
(setf (tcons-first cons) value)))
(optimize-for-transaction*
(:inline t)
(defun (setf tcdr) (value cons)
"Set VALUE as the rest element in a TCONS or non-null TLIST.
This function should always be executed inside an STMX atomic block."
(declare (type tcons cons))
(setf (tcons-rest cons) value)))