Abandoned atoms work: hardly any performance benefit.
[sod] / src / utilities.lisp
CommitLineData
dea4d055
MW
1;;; -*-lisp-*-
2;;;
3;;; Various handy utilities
4;;;
5;;; (c) 2009 Straylight/Edgeware
6;;;
7
8;;;----- Licensing notice ---------------------------------------------------
9;;;
e0808c47 10;;; This file is part of the Sensible Object Design, an object system for C.
dea4d055
MW
11;;;
12;;; SOD is free software; you can redistribute it and/or modify
13;;; it under the terms of the GNU General Public License as published by
14;;; the Free Software Foundation; either version 2 of the License, or
15;;; (at your option) any later version.
16;;;
17;;; SOD is distributed in the hope that it will be useful,
18;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20;;; GNU General Public License for more details.
21;;;
22;;; You should have received a copy of the GNU General Public License
23;;; along with SOD; if not, write to the Free Software Foundation,
24;;; Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25
26(cl:defpackage #:sod-utilities
27 (:use #:common-lisp
28
29 ;; MOP from somewhere.
30 #+sbcl #:sb-mop
31 #+(or cmu clisp) #:mop
32 #+ecl #:clos))
33
34(cl:in-package #:sod-utilities)
35
36;;;--------------------------------------------------------------------------
37;;; Macro hacks.
38
39(export 'with-gensyms)
40(defmacro with-gensyms ((&rest binds) &body body)
41 "Evaluate BODY with variables bound to fresh symbols.
42
43 The BINDS are a list of entries (VAR [NAME]), and a singleton list can be
44 replaced by just a symbol; each VAR is bound to a fresh symbol generated
45 by (gensym NAME), where NAME defaults to the symbol-name of VAR."
46 `(let (,@(mapcar (lambda (bind)
47 (multiple-value-bind (var name)
48 (if (atom bind)
49 (values bind (concatenate 'string
50 (symbol-name bind) "-"))
51 (destructuring-bind
52 (var &optional
53 (name (concatenate 'string
54 (symbol-name var) "-")))
55 bind
56 (values var name)))
57 `(,var (gensym ,name))))
58 binds))
59 ,@body))
60
61(eval-when (:compile-toplevel :load-toplevel :execute)
62 (defun strip-quote (form)
63 "If FORM looks like (quote FOO) for self-evaluating FOO, return FOO.
64
65 If FORM is a symbol whose constant value is `nil' then return `nil'.
66 Otherwise return FORM unchanged. This makes it easier to inspect constant
67 things. This is a utility for `once-only'."
68
69 (cond ((and (consp form)
70 (eq (car form) 'quote)
71 (cdr form)
72 (null (cddr form)))
73 (let ((body (cadr form)))
74 (if (or (not (or (consp body) (symbolp body)))
75 (member body '(t nil))
76 (keywordp body))
77 body
78 form)))
79 ((and (symbolp form) (boundp form) (null (symbol-value form)))
80 nil)
81 (t
82 form))))
83
84(export 'once-only)
74ca1bf5 85(defmacro once-only ((&rest binds) &body body)
dea4d055
MW
86 "Macro helper for preventing repeated evaluation.
87
88 The syntax is actually hairier than shown:
89
90 once-only ( [[ :environment ENV ]] { VAR | (VAR [VALUE-FORM]) }* )
91 { FORM }*
92
93 So, the BINDS are a list of entries (VAR [VALUE-FORM]); a singleton list
94 can be replaced by just a symbol VAR, and the VALUE-FORM defaults to VAR.
95 But before them you can have keyword arguments. Only one is defined so
96 far. See below for the crazy things that does.
97
98 The result of evaluating a ONCE-ONLY form is a form with the structure
99
100 (let ((#:GS1 VALUE-FORM1)
101 ...
102 (#:GSn VALUE-FORMn))
103 STUFF)
104
105 where STUFF is the value of the BODY forms, as an implicit progn, in an
106 environment with the VARs bound to the corresponding gensyms.
107
108 As additional magic, if any of the VALUE-FORMs is actually constant (as
109 determined by inspection, and aided by `constantp' if an :environment is
110 supplied, then no gensym is constructed for it, and the VAR is bound
111 directly to the constant form. Moreover, if the constant form looks like
112 (quote FOO) for a self-evaluating FOO then the outer layer of quoting is
113 stripped away."
114
115 ;; We need an extra layer of gensyms in our expansion: we'll want the
116 ;; expansion to examine the various VALUE-FORMs to find out whether they're
117 ;; constant without evaluating them repeatedly. This also helps with
118 ;; another problem: we explicitly encourage the rebinding of a VAR
119 ;; (probably a macro argument) to a gensym which will be bound to the value
120 ;; of the form previously held in VAR itself -- so the gensym and value
121 ;; form must exist at the same time and we need two distinct variables.
122
123 (with-gensyms ((envvar "ENV-") lets sym (bodyfunc "BODY-"))
124 (let ((env nil))
125
126 ;; First things first: let's pick up the keywords.
127 (loop
128 (unless (and binds (keywordp (car binds)))
129 (return))
130 (ecase (pop binds)
131 (:environment (setf env (pop binds)))))
132
133 ;; Now we'll investigate the bindings. Turn each one into a list (VAR
134 ;; VALUE-FORM TEMP) where TEMP is an appropriate gensym -- see the note
135 ;; above.
136 (let ((canon (mapcar (lambda (bind)
137 (multiple-value-bind (var form)
138 (if (atom bind)
139 (values bind bind)
140 (destructuring-bind
141 (var &optional (form var)) bind
142 (values var form)))
143 (list var form
144 (gensym (format nil "T-~A-"
145 (symbol-name var))))))
146 binds)))
147
148 `(let* (,@(and env `((,envvar ,env)))
149 (,lets nil)
150 ,@(mapcar (lambda (bind)
151 (destructuring-bind (var form temp) bind
152 (declare (ignore var))
153 `(,temp ,form)))
154 canon)
155 ,@(mapcar (lambda (bind)
156 (destructuring-bind (var form temp) bind
157 (declare (ignore form))
158 `(,var
159 (cond ((constantp ,temp
160 ,@(and env `(,envvar)))
161 (strip-quote ,temp))
162 ((symbolp ,temp)
163 ,temp)
164 (t
165 (let ((,sym (gensym
166 ,(concatenate 'string
167 (symbol-name var)
168 "-"))))
169 (push (list ,sym ,temp) ,lets)
170 ,sym))))))
171 canon))
172 (flet ((,bodyfunc () ,@body))
173 (if ,lets
174 `(let (,@(nreverse ,lets)) ,(,bodyfunc))
175 (,bodyfunc))))))))
176
177(export 'parse-body)
b8c698ee 178(defun parse-body (body &key (docp t) (declp t))
dea4d055
MW
179 "Parse the BODY into a docstring, declarations and the body forms.
180
181 These are returned as three lists, so that they can be spliced into a
182 macro expansion easily. The declarations are consolidated into a single
b8c698ee
MW
183 `declare' form. If DOCP is nil then a docstring is not permitted; if
184 DECLP is nil, then declarations are not permitted."
dea4d055
MW
185 (let ((decls nil)
186 (doc nil))
187 (loop
188 (cond ((null body) (return))
b8c698ee 189 ((and declp (consp (car body)) (eq (caar body) 'declare))
dea4d055 190 (setf decls (append decls (cdr (pop body)))))
b8c698ee 191 ((and docp (stringp (car body)) (not doc) (cdr body))
dea4d055
MW
192 (setf doc (pop body)))
193 (t (return))))
194 (values (and doc (list doc))
195 (and decls (list (cons 'declare decls)))
196 body)))
197
198;;;--------------------------------------------------------------------------
e8abb286
MW
199;;; Locatives.
200
201(export '(loc locp))
202(defstruct (loc (:predicate locp) (:constructor make-loc (reader writer)))
203 "Locative data type. See `locf' and `ref'."
204 (reader nil :type function)
205 (writer nil :type function))
206
207(export 'locf)
208(defmacro locf (place &environment env)
209 "Slightly cheesy locatives.
210
211 (locf PLACE) returns an object which, using the `ref' function, can be
212 used to read or set the value of PLACE. It's cheesy because it uses
213 closures rather than actually taking the address of something. Also,
214 unlike Zetalisp, we don't overload `car' to do our dirty work."
215 (multiple-value-bind
216 (valtmps valforms newtmps setform getform)
217 (get-setf-expansion place env)
218 `(let* (,@(mapcar #'list valtmps valforms))
219 (make-loc (lambda () ,getform)
220 (lambda (,@newtmps) ,setform)))))
221
222(export 'ref)
223(declaim (inline ref (setf ref)))
224(defun ref (loc)
225 "Fetch the value referred to by a locative."
226 (funcall (loc-reader loc)))
227(defun (setf ref) (new loc)
228 "Store a new value in the place referred to by a locative."
229 (funcall (loc-writer loc) new))
230
231(export 'with-locatives)
232(defmacro with-locatives (locs &body body)
233 "Evaluate BODY with implicit locatives.
234
235 LOCS is a list of items of the form (SYM [LOC-EXPR]), where SYM is a
236 symbol and LOC-EXPR evaluates to a locative. If LOC-EXPR is omitted, it
237 defaults to SYM. As an abbreviation for a common case, LOCS may be a
238 symbol instead of a list.
239
240 The BODY is evaluated in an environment where each SYM is a symbol macro
241 which expands to (ref LOC-EXPR) -- or, in fact, something similar which
242 doesn't break if LOC-EXPR has side-effects. Thus, references, including
243 `setf' forms, fetch or modify the thing referred to by the LOC-EXPR.
244 Useful for covering over where something uses a locative."
245 (setf locs (mapcar (lambda (item)
246 (cond ((atom item) (list item item))
247 ((null (cdr item)) (list (car item) (car item)))
248 (t item)))
249 (if (listp locs) locs (list locs))))
250 (let ((tt (mapcar (lambda (l) (declare (ignore l)) (gensym)) locs))
251 (ll (mapcar #'cadr locs))
252 (ss (mapcar #'car locs)))
253 `(let (,@(mapcar (lambda (tmp loc) `(,tmp ,loc)) tt ll))
254 (symbol-macrolet (,@(mapcar (lambda (sym tmp)
255 `(,sym (ref ,tmp))) ss tt))
256 ,@body))))
257
258;;;--------------------------------------------------------------------------
dea4d055
MW
259;;; Anaphorics.
260
261(export 'it)
262
263(export 'aif)
264(defmacro aif (cond cons &optional (alt nil altp))
265 "If COND is not nil, evaluate CONS with `it' bound to the value of COND.
266
267 Otherwise, if given, evaluate ALT; `it' isn't bound in ALT."
268 (once-only (cond)
269 `(if ,cond (let ((it ,cond)) ,cons) ,@(and altp `(,alt)))))
270
271(export 'awhen)
272(defmacro awhen (cond &body body)
273 "If COND, evaluate BODY as a progn with `it' bound to the value of COND."
274 `(let ((it ,cond)) (when it ,@body)))
275
3e166443
MW
276(export 'aand)
277(defmacro aand (&rest forms)
278 "Like `and', but anaphoric.
279
280 Each FORM except the first is evaluated with `it' bound to the value of
281 the previous one. If there are no forms, then the result it `t'; if there
282 is exactly one, then wrapping it in `aand' is pointless."
283 (labels ((doit (first rest)
284 (if (null rest)
285 first
286 `(let ((it ,first))
287 (if it ,(doit (car rest) (cdr rest)) nil)))))
288 (if (null forms)
289 't
290 (doit (car forms) (cdr forms)))))
291
dea4d055 292(export 'acond)
bf090e02 293(defmacro acond (&body clauses &environment env)
dea4d055
MW
294 "Like COND, but with `it' bound to the value of the condition.
295
296 Each of the CLAUSES has the form (CONDITION FORM*); if a CONDITION is
297 non-nil then evaluate the FORMs with `it' bound to the non-nil value, and
298 return the value of the last FORM; if there are no FORMs, then return `it'
299 itself. If the CONDITION is nil then continue with the next clause; if
300 all clauses evaluate to nil then the result is nil."
301 (labels ((walk (clauses)
302 (if (null clauses)
303 `nil
304 (once-only (:environment env (cond (caar clauses)))
305 (if (and (constantp cond)
306 (if (and (consp cond) (eq (car cond) 'quote))
307 (cadr cond) cond))
308 (if (cdar clauses)
309 `(let ((it ,cond))
310 (declare (ignorable it))
311 ,@(cdar clauses))
312 cond)
313 `(if ,cond
314 ,(if (cdar clauses)
315 `(let ((it ,cond))
316 (declare (ignorable it))
317 ,@(cdar clauses))
318 cond)
319 ,(walk (cdr clauses))))))))
320 (walk clauses)))
321
322(export '(acase aecase atypecase aetypecase))
323(defmacro acase (value &body clauses)
324 `(let ((it ,value)) (case it ,@clauses)))
325(defmacro aecase (value &body clauses)
326 `(let ((it ,value)) (ecase it ,@clauses)))
327(defmacro atypecase (value &body clauses)
328 `(let ((it ,value)) (typecase it ,@clauses)))
329(defmacro aetypecase (value &body clauses)
330 `(let ((it ,value)) (etypecase it ,@clauses)))
331
332(export 'asetf)
333(defmacro asetf (&rest places-and-values &environment env)
334 "Anaphoric update of places.
335
336 The PLACES-AND-VALUES are alternating PLACEs and VALUEs. Each VALUE is
337 evaluated with IT bound to the current value stored in the corresponding
338 PLACE."
339 `(progn ,@(loop for (place value) on places-and-values by #'cddr
340 collect (multiple-value-bind
341 (temps inits newtemps setform getform)
342 (get-setf-expansion place env)
343 `(let* (,@(mapcar #'list temps inits)
344 (it ,getform))
345 (multiple-value-bind ,newtemps ,value
346 ,setform))))))
347
348;;;--------------------------------------------------------------------------
349;;; MOP hacks (not terribly demanding).
350
bf090e02
MW
351(export 'instance-initargs)
352(defgeneric instance-initargs (instance)
353 (:documentation
354 "Return a plausble list of initargs for INSTANCE.
355
356 The idea is that you can make a copy of INSTANCE by invoking
357
358 (apply #'make-instance (class-of INSTANCE)
359 (instance-initargs INSTANCE))
360
361 The default implementation works by inspecting the slot definitions and
362 extracting suitable initargs, so this will only succeed if enough slots
363 actually have initargs specified that `initialize-instance' can fill in
364 the rest correctly.
365
366 The list returned is freshly consed, and you can destroy it if you like.")
367 (:method ((instance standard-object))
368 (mapcan (lambda (slot)
369 (aif (slot-definition-initargs slot)
370 (list (car it)
371 (slot-value instance (slot-definition-name slot)))
372 nil))
373 (class-slots (class-of instance)))))
374
dea4d055
MW
375(export '(copy-instance copy-instance-using-class))
376(defgeneric copy-instance-using-class (class instance &rest initargs)
377 (:documentation
378 "Metaobject protocol hook for `copy-instance'.")
379 (:method ((class standard-class) instance &rest initargs)
380 (let ((copy (allocate-instance class)))
381 (dolist (slot (class-slots class))
382 (let ((name (slot-definition-name slot)))
383 (when (slot-boundp instance name)
384 (setf (slot-value copy name) (slot-value instance name)))))
385 (apply #'shared-initialize copy nil initargs))))
386(defun copy-instance (object &rest initargs)
387 "Construct and return a copy of OBJECT.
388
389 The new object has the same class as OBJECT, and the same slot values
390 except where overridden by INITARGS."
391 (apply #'copy-instance-using-class (class-of object) object initargs))
392
9ec578d9
MW
393(export '(generic-function-methods method-specializers
394 eql-specializer eql-specializer-object))
395
dea4d055
MW
396;;;--------------------------------------------------------------------------
397;;; List utilities.
398
399(export 'make-list-builder)
400(defun make-list-builder (&optional initial)
401 "Return a simple list builder."
402
403 ;; The `builder' is just a cons cell whose cdr will be the list that's
404 ;; wanted. Effectively, then, we have a list that's one item longer than
405 ;; we actually want. The car of this extra initial cons cell is always the
406 ;; last cons in the list -- which is now well defined because there's
407 ;; always at least one.
408
409 (let ((builder (cons nil initial)))
410 (setf (car builder) (last builder))
411 builder))
412
413(export 'lbuild-add)
414(defun lbuild-add (builder item)
415 "Add an ITEM to the end of a list BUILDER."
416 (let ((new (cons item nil)))
417 (setf (cdar builder) new
418 (car builder) new))
419 builder)
420
421(export 'lbuild-add-list)
422(defun lbuild-add-list (builder list)
423 "Add a LIST to the end of a list BUILDER. The LIST will be clobbered."
424 (when list
425 (setf (cdar builder) list
426 (car builder) (last list)))
427 builder)
428
429(export 'lbuild-list)
430(defun lbuild-list (builder)
431 "Return the constructed list."
432 (cdr builder))
433
434(export 'mappend)
435(defun mappend (function list &rest more-lists)
69dda0c9 436 "Like a nondestructive `mapcan'.
dea4d055
MW
437
438 Map FUNCTION over the the corresponding elements of LIST and MORE-LISTS,
439 and return the result of appending all of the resulting lists."
440 (reduce #'append (apply #'mapcar function list more-lists) :from-end t))
441
17c7c784
MW
442(export 'distinguished-point-shortest-paths)
443(defun distinguished-point-shortest-paths (root neighbours-func)
444 "Moderately efficient shortest-paths-from-root computation.
445
446 The ROOT is a distinguished vertex in a graph. The NEIGHBOURS-FUNC
447 accepts a VERTEX as its only argument, and returns a list of conses (V .
448 C) for each of the VERTEX's neighbours, indicating that there is an edge
449 from VERTEX to V, with cost C.
450
451 The return value is a list of entries (COST . REV-PATH) for each vertex
452 reachable from the ROOT; the COST is the total cost of the shortest path,
453 and REV-PATH is the path from the ROOT, in reverse order -- so the first
454 element is the vertex itself and the last element is the ROOT.
455
456 The NEIGHBOURS-FUNC is called at most N times, and may take O(N) time to
457 produce its output list. The computation as a whole takes O(N^2) time,
458 where N is the number of vertices in the graph, assuming there is at most
459 one edge between any pair of vertices."
460
461 ;; This is a listish version of Dijkstra's shortest-path algorithm. It
462 ;; could be made more efficient by using a fancy priority queue rather than
463 ;; a linear search for finding the nearest live element (see below), but it
464 ;; still runs pretty well.
465
466 (let ((map (make-hash-table))
467 (dead nil)
468 (live (list (list 0 root))))
469 (setf (gethash root map) (cons :live (car live)))
470 (loop
471 ;; The dead list contains a record, in output format (COST . PATH), for
472 ;; each vertex whose shortest path has been finally decided. The live
473 ;; list contains a record for the vertices of current interest, also in
474 ;; output format; the COST for a live record shows the best cost for a
475 ;; path using only dead vertices.
476 ;;
477 ;; Each time through here, we pull an item off the live list and
478 ;; push it onto the dead list, so we do at most N iterations total.
479
480 ;; If there are no more live items, then we're done; the remaining
481 ;; vertices, if any, are unreachable from the ROOT.
482 (when (null live) (return))
483
484 ;; Find the closest live vertex to the root. The linear scan through
485 ;; the live list costs at most N time.
486 (let* ((best (reduce (lambda (x y) (if (< (car x) (car y)) x y)) live))
487 (best-cost (car best))
488 (best-path (cdr best))
489 (best-vertex (car best-path)))
490
491 ;; Remove the chosen vertex from the LIVE list, and add the
492 ;; appropriate record to the dead list. We must have the shortest
493 ;; path to this vertex now: we have the shortest path using currently
494 ;; dead vertices; any other path must use at least one live vertex,
495 ;; and, by construction, the path through any such vertex must be
496 ;; further than the path we already have.
497 ;;
498 ;; Removal from the live list uses a linear scan which costs N time.
499 (setf live (delete best live))
500 (push best dead)
501 (setf (car (gethash best-vertex map)) :dead)
502
503 ;; Work through the chosen vertex's neighbours, adding each of them
504 ;; to the live list if they're not already there. If a neighbour is
505 ;; already live, and we find a shorter path to it through our chosen
506 ;; vertex, then update the neighbour's record.
507 ;;
508 ;; The chosen vertex obviously has at most N neighbours. There's no
509 ;; more looping in here, so performance is as claimed.
510 (dolist (neigh (funcall neighbours-func best-vertex))
511 (let* ((neigh-vertex (car neigh))
512 (neigh-cost (+ best-cost (cdr neigh)))
513 (neigh-record (gethash neigh-vertex map)))
514 (cond ((null neigh-record)
515 ;; If the neighbour isn't known, then now's the time to
516 ;; make a fresh live record for it.
517 (let ((new-record (list* :live neigh-cost
518 neigh-vertex best-path)))
519 (push (cdr new-record) live)
520 (setf (gethash neigh-vertex map) new-record)))
521 ((and (eq (car neigh-record) :live)
522 (< neigh-cost (cadr neigh-record)))
523 ;; If the neighbour is live, and we've found a better path
524 ;; to it, then update its record.
525 (setf (cadr neigh-record) neigh-cost
526 (cdddr neigh-record) best-path)))))))
527 dead))
528
38b78e87
MW
529(export '(inconsistent-merge-error
530 merge-error-candidates merge-error-present-function))
dea4d055
MW
531(define-condition inconsistent-merge-error (error)
532 ((candidates :initarg :candidates
38b78e87
MW
533 :reader merge-error-candidates)
534 (present :initarg :present :initform #'identity
535 :reader merge-error-present-function))
dea4d055 536 (:documentation
9fb4a980 537 "Reports an inconsistency in the arguments passed to `merge-lists'.")
dea4d055 538 (:report (lambda (condition stream)
e2838dc5
MW
539 (format stream "Merge inconsistency: failed to decide between ~
540 ~{~#[~;~A~;~A and ~A~:;~
541 ~@{~A, ~#[~;and ~A~]~}~]~}"
38b78e87
MW
542 (mapcar (merge-error-present-function condition)
543 (merge-error-candidates condition))))))
dea4d055
MW
544
545(export 'merge-lists)
e2838dc5 546(defun merge-lists (lists &key pick (test #'eql) (present #'identity))
dea4d055
MW
547 "Return a merge of the given LISTS.
548
e8c5a09e 549 The resulting list contains the items of the given LISTS, with duplicates
dea4d055
MW
550 removed. The order of the resulting list is consistent with the orders of
551 the input LISTS in the sense that if A precedes B in some input list then
552 A will also precede B in the output list. If the lists aren't consistent
553 (e.g., some list contains A followed by B, and another contains B followed
e2838dc5
MW
554 by A) then an error of type `inconsistent-merge-error' is signalled. The
555 offending items are filtered for presentation through the PRESENT function
556 before being attached to the condition, so as to produce a more useful
557 diagnostic message.
dea4d055
MW
558
559 Item equality is determined by TEST.
560
561 If there is an ambiguity at any point -- i.e., a choice between two or
562 more possible next items to emit -- then PICK is called to arbitrate.
563 PICK is called with two arguments: the list of candidate next items, and
e8c5a09e
MW
564 the current output list. It should return one of the candidate items.
565 The order of the candidates in the list given to the PICK function
566 reflects their order in the input LISTS: item A will precede item B in the
567 candidates list if and only if an occurrence of A appears in an earlier
568 input list than any occurrence of item B. (This completely determines the
569 order of the candidates: it is not possible that two candidates appear in
c5ef873a
MW
570 the same input list, since that would resolve the ambiguity between them.)
571 If PICK is omitted then the item chosen is the one appearing in the
572 earliest of the input lists: i.e., effectively, the default PICK function
573 is
e8c5a09e
MW
574
575 (lambda (candidates output-so-far)
576 (declare (ignore output-so-far))
577 (car candidates))
dea4d055
MW
578
579 The primary use of this function is in computing class precedence lists.
580 By building the input lists and selecting the PICK function appropriately,
581 a variety of different CPL algorithms can be implemented."
582
022a3499
MW
583 (do ((lb (make-list-builder)))
584 ((null lists) (lbuild-list lb))
dea4d055
MW
585
586 ;; The candidate items are the ones at the front of the input lists.
587 ;; Gather them up, removing duplicates. If a candidate is somewhere in
588 ;; one of the other lists other than at the front then we reject it. If
589 ;; we've just rejected everything, then we can make no more progress and
590 ;; the input lists were inconsistent.
e8c5a09e
MW
591 (let* ((candidates (delete-duplicates (mapcar #'car lists)
592 :test test :from-end t))
dea4d055
MW
593 (leasts (remove-if (lambda (item)
594 (some (lambda (list)
595 (member item (cdr list) :test test))
596 lists))
597 candidates))
598 (winner (cond ((null leasts)
599 (error 'inconsistent-merge-error
38b78e87
MW
600 :candidates candidates
601 :present present))
dea4d055
MW
602 ((null (cdr leasts))
603 (car leasts))
604 (pick
605 (funcall pick leasts (lbuild-list lb)))
606 (t (car leasts)))))
607
608 ;; Check that the PICK function isn't conning us.
609 (assert (member winner leasts :test test))
610
611 ;; Update the output list and remove the winning item from the input
612 ;; lists. We know that it must be at the front of each input list
613 ;; containing it. At this point, we discard input lists entirely when
614 ;; they run out of entries. The loop ends when there are no more input
615 ;; lists left, i.e., when we've munched all of the input items.
616 (lbuild-add lb winner)
617 (setf lists (delete nil (mapcar (lambda (list)
618 (if (funcall test winner (car list))
619 (cdr list)
620 list))
621 lists))))))
622
623(export 'categorize)
624(defmacro categorize ((itemvar items &key bind) categories &body body)
625 "Categorize ITEMS into lists and invoke BODY.
626
627 The ITEMVAR is a symbol; as the macro iterates over the ITEMS, ITEMVAR
628 will contain the current item. The BIND argument is a list of LET*-like
629 clauses. The CATEGORIES are a list of clauses of the form (SYMBOL
630 PREDICATE).
631
632 The behaviour of the macro is as follows. ITEMVAR is assigned (not
633 bound), in turn, each item in the list ITEMS. The PREDICATEs in the
634 CATEGORIES list are evaluated in turn, in an environment containing
635 ITEMVAR and the BINDings, until one of them evaluates to a non-nil value.
636 At this point, the item is assigned to the category named by the
637 corresponding SYMBOL. If none of the PREDICATEs returns non-nil then an
638 error is signalled; a PREDICATE consisting only of T will (of course)
639 match anything; it is detected specially so as to avoid compiler warnings.
640
641 Once all of the ITEMS have been categorized in this fashion, the BODY is
642 evaluated as an implicit PROGN. For each SYMBOL naming a category, a
643 variable named after that symbol will be bound in the BODY's environment
644 to a list of the items in that category, in the same order in which they
645 were found in the list ITEMS. The final values of the macro are the final
646 values of the BODY."
647
648 (let* ((cat-names (mapcar #'car categories))
649 (cat-match-forms (mapcar #'cadr categories))
650 (cat-vars (mapcar (lambda (name) (gensym (concatenate 'string
651 (symbol-name name) "-")))
652 cat-names))
653 (items-var (gensym "ITEMS-")))
64a7e651
MW
654 `(let (,@(mapcar (lambda (cat-var) (list cat-var nil)) cat-vars))
655 (let ((,items-var ,items))
656 (dolist (,itemvar ,items-var)
657 (let* ,bind
658 (cond ,@(mapcar (lambda (cat-match-form cat-var)
659 `(,cat-match-form
660 (push ,itemvar ,cat-var)))
661 cat-match-forms cat-vars)
662 ,@(and (not (member t cat-match-forms))
663 `((t (error "Failed to categorize ~A"
664 ,itemvar))))))))
dea4d055
MW
665 (let ,(mapcar (lambda (name var)
666 `(,name (nreverse ,var)))
667 cat-names cat-vars)
668 ,@body))))
669
42291726
MW
670(export 'partial-order-minima)
671(defun partial-order-minima (items order)
672 "Return a list of minimal items according to the non-strict partial ORDER.
673
674 The ORDER function describes the partial order: (funcall ORDER X Y) should
675 return true if X precedes or is equal to Y in the order."
676 (reduce (lambda (tops this)
677 (let ((new nil) (keep t))
678 (dolist (top tops)
679 (cond ((funcall order top this)
680 (setf keep nil)
681 (push top new))
682 ((not (funcall order this top))
683 (push top new))))
684 (nreverse (if keep (cons this new) new))))
685 items
686 :initial-value nil))
687
64cbfb65
MW
688(export 'find-duplicates)
689(defun find-duplicates (report sequence &key (key #'identity) (test #'eql))
690 "Call REPORT on each pair of duplicate items in SEQUENCE.
691
692 Duplicates are determined according to the KEY and TEST funcitons."
693 (when (symbolp test) (setf test (symbol-function test)))
694 (cond ((zerop (length sequence)) nil)
695 ((or (eq test #'eq)
696 (eq test #'eql)
697 (eq test #'equal)
698 (eq test #'equalp))
699 (let ((seen (make-hash-table :test test)))
700 (map nil (lambda (item)
701 (let ((k (funcall key item)))
702 (multiple-value-bind (previous matchp)
703 (gethash k seen)
704 (if matchp (funcall report item previous)
705 (setf (gethash k seen) item)))))
706 sequence)))
707 ((listp sequence)
708 (mapl (lambda (tail)
709 (let* ((item (car tail))
710 (rest (cdr tail))
711 (match (member (funcall key item) rest
712 :test test :key key)))
713 (when match (funcall report item (car match)))))
714 sequence))
715 ((vectorp sequence)
716 (dotimes (i (length sequence))
717 (let* ((item (aref sequence i))
718 (pos (position (funcall key item) sequence
719 :key key :test test :start (1+ i))))
720 (when pos (funcall report item (aref sequence pos))))))
721 (t
722 (error 'type-error :datum sequence :expected-type 'sequence))))
723
dea4d055
MW
724;;;--------------------------------------------------------------------------
725;;; Strings and characters.
726
727(export 'frob-identifier)
728(defun frob-identifier (string &key (swap-case t) (swap-hyphen t))
729 "Twiddles the case of STRING.
730
731 If all the letters in STRING are uppercase, and SWAP-CASE is true, then
732 switch them to lowercase; if they're all lowercase then switch them to
733 uppercase. If there's a mix then leave them all alone. At the same time,
734 if there are underscores but no hyphens, and SWAP-HYPHEN is true, then
735 switch them to hyphens, if there are hyphens and no underscores, switch
736 them underscores, and if there are both then leave them alone.
737
738 This is an invertible transformation, which turns vaguely plausible Lisp
739 names into vaguely plausible C names and vice versa. Lisp names with
740 `funny characters' like stars and percent signs won't be any use, of
741 course."
742
743 ;; Work out what kind of a job we've got to do. Gather flags: bit 0 means
744 ;; there are upper-case letters; bit 1 means there are lower-case letters;
745 ;; bit 2 means there are hyphens; bit 3 means there are underscores.
746 ;;
747 ;; Consequently, (logxor flags (ash flags 1)) is interesting: bit 1 is set
748 ;; if we have to frob case; bit 3 is set if we have to swap hyphens and
749 ;; underscores. So use this to select functions which do bits of the
750 ;; mapping, and then compose them together.
751 (let* ((flags (reduce (lambda (state ch)
752 (logior state
753 (cond ((upper-case-p ch) 1)
754 ((lower-case-p ch) 2)
755 ((char= ch #\-) 4)
756 ((char= ch #\_) 8)
757 (t 0))))
758 string
759 :initial-value 0))
760 (mask (logxor flags (ash flags 1)))
761 (letter (cond ((or (not swap-case) (not (logbitp 1 mask)))
762 (constantly nil))
763 ((logbitp 0 flags)
764 (lambda (ch)
765 (and (alpha-char-p ch) (char-downcase ch))))
766 (t
767 (lambda (ch)
768 (and (alpha-char-p ch) (char-upcase ch))))))
769 (uscore-hyphen (cond ((or (not (logbitp 3 mask)) (not swap-hyphen))
770 (constantly nil))
771 ((logbitp 2 flags)
772 (lambda (ch) (and (char= ch #\-) #\_)))
773 (t
774 (lambda (ch) (and (char= ch #\_) #\-))))))
775
776 (if (logbitp 3 (logior mask (ash mask 2)))
777 (map 'string (lambda (ch)
778 (or (funcall letter ch)
779 (funcall uscore-hyphen ch)
780 ch))
781 string)
782 string)))
783
784(export 'whitespace-char-p)
785(declaim (inline whitespace-char-p))
786(defun whitespace-char-p (char)
787 "Returns whether CHAR is a whitespace character.
788
789 Whitespaceness is determined relative to the compile-time readtable, which
790 is probably good enough for most purposes."
791 (case char
792 (#.(loop for i below char-code-limit
793 for ch = (code-char i)
794 unless (with-input-from-string (in (string ch))
795 (peek-char t in nil))
796 collect ch) t)
797 (t nil)))
798
799(export 'update-position)
800(declaim (inline update-position))
801(defun update-position (char line column)
802 "Updates LINE and COLUMN appropriately for having read the character CHAR.
803
804 Returns the new LINE and COLUMN numbers."
805 (case char
806 ((#\newline #\vt #\page)
807 (values (1+ line) 0))
808 ((#\tab)
809 (values line (logandc2 (+ column 8) 7)))
810 (t
811 (values line (1+ column)))))
812
813(export 'backtrack-position)
814(declaim (inline backtrack-position))
815(defun backtrack-position (char line column)
816 "Updates LINE and COLUMN appropriately for having unread CHAR.
817
818 Well, actually an approximation for it; it will likely be wrong if the
819 last character was a tab. But when the character is read again, it will
820 be correct."
821
822 ;; This isn't perfect: if the character doesn't actually match what was
823 ;; really read then it might not actually be possible: for example, if we
824 ;; push back a newline while in the middle of a line, or a tab while not at
825 ;; a tab stop. In that case, we'll just lose, but hopefully not too badly.
826 (case char
827
828 ;; In the absence of better ideas, I'll set the column number to zero.
829 ;; This is almost certainly wrong, but with a little luck nobody will ask
830 ;; and it'll be all right soon.
831 ((#\newline #\vt #\page) (values (1- line) 0))
832
833 ;; Winding back a single space is sufficient. If the position is
834 ;; currently on a tab stop then it'll advance back here next time. If
835 ;; not, we're going to lose anyway because the previous character
836 ;; certainly couldn't have been a tab.
837 (#\tab (values line (1- column)))
838
839 ;; Anything else: just decrement the column and cross fingers.
840 (t (values line (1- column)))))
841
842;;;--------------------------------------------------------------------------
843;;; Functions.
844
845(export 'compose)
846(defun compose (function &rest more-functions)
847 "Composition of functions. Functions are applied left-to-right.
848
849 This is the reverse order of the usual mathematical notation, but I find
bf090e02
MW
850 it easier to read. It's also slightly easier to work with in programs.
851 That is, (compose F1 F2 ... Fn) is what a category theorist might write as
852 F1 ; F2 ; ... ; Fn, rather than F1 o F2 o ... o Fn."
853
dea4d055
MW
854 (labels ((compose1 (func-a func-b)
855 (lambda (&rest args)
856 (multiple-value-call func-b (apply func-a args)))))
857 (reduce #'compose1 more-functions :initial-value function)))
858
859;;;--------------------------------------------------------------------------
c34b237d
MW
860;;; Variables.
861
862(export 'defvar-unbound)
863(defmacro defvar-unbound (var doc)
864 "Make VAR a special variable with documentation DOC, but leave it unbound."
865 `(eval-when (:compile-toplevel :load-toplevel :execute)
866 (defvar ,var)
867 (setf (documentation ',var 'variable) ',doc)
868 ',var))
869
870;;;--------------------------------------------------------------------------
dea4d055
MW
871;;; Symbols.
872
873(export 'symbolicate)
874(defun symbolicate (&rest symbols)
875 "Return a symbol named after the concatenation of the names of the SYMBOLS.
876
3109662a 877 The symbol is interned in the current `*package*'. Trad."
dea4d055
MW
878 (intern (apply #'concatenate 'string (mapcar #'symbol-name symbols))))
879
880;;;--------------------------------------------------------------------------
881;;; Object printing.
882
883(export 'maybe-print-unreadable-object)
884(defmacro maybe-print-unreadable-object
885 ((object stream &rest args) &body body)
886 "Print helper for usually-unreadable objects.
887
3109662a 888 If `*print-escape*' is set then print OBJECT unreadably using BODY.
dea4d055
MW
889 Otherwise just print using BODY."
890 (with-gensyms (print)
891 `(flet ((,print () ,@body))
892 (if *print-escape*
893 (print-unreadable-object (,object ,stream ,@args)
894 (,print))
895 (,print)))))
896
08b6e064
MW
897(export 'print-ugly-stuff)
898(defun print-ugly-stuff (stream func)
899 "Print not-pretty things to the stream underlying STREAM.
900
901 The Lisp pretty-printing machinery, notably `pprint-logical-block', may
902 interpose additional streams between its body and the original target
903 stream. This makes it difficult to make use of the underlying stream's
904 special features, whatever they might be."
905
906 ;; This is unpleasant. Hacky hacky.
907 #.(or #+sbcl '(if (typep stream 'sb-pretty:pretty-stream)
908 (let ((target (sb-pretty::pretty-stream-target stream)))
909 (pprint-newline :mandatory stream)
910 (funcall func target))
911 (funcall func stream))
912 #+cmu '(if (typep stream 'pp:pretty-stream)
913 (let ((target (pp::pretty-stream-target stream)))
914 (pprint-newline :mandatory stream)
915 (funcall func target))
916 (funcall func stream))
917 '(funcall func stream)))
918
dea4d055
MW
919;;;--------------------------------------------------------------------------
920;;; Iteration macros.
921
922(export 'dosequence)
923(defmacro dosequence ((var seq &key (start 0) (end nil) indexvar)
924 &body body
925 &environment env)
926 "Macro for iterating over general sequences.
927
928 Iterates over a (sub)sequence SEQ, delimited by START and END (which are
929 evaluated). For each item of SEQ, BODY is invoked with VAR bound to the
930 item, and INDEXVAR (if requested) bound to the item's index. (Note that
931 this is different from most iteration constructs in Common Lisp, which
932 work by mutating the variable.)
933
934 The loop is surrounded by an anonymous BLOCK and the loop body forms an
935 implicit TAGBODY, as is usual. There is no result-form, however."
936
937 (once-only (:environment env seq start end)
938 (with-gensyms ((ivar "INDEX-") (endvar "END-") (bodyfunc "BODY-"))
b8c698ee
MW
939 (multiple-value-bind (docs decls body) (parse-body body :docp nil)
940 (declare (ignore docs))
941
942 (flet ((loopguts (indexp listp endvar)
943 ;; Build a DO-loop to do what we want.
944 (let* ((do-vars nil)
945 (end-condition (if endvar
946 `(>= ,ivar ,endvar)
947 `(endp ,seq)))
948 (item (if listp
949 `(car ,seq)
950 `(aref ,seq ,ivar)))
951 (body-call `(,bodyfunc ,item)))
952 (when listp
953 (push `(,seq (nthcdr ,start ,seq) (cdr ,seq))
954 do-vars))
955 (when indexp
956 (push `(,ivar ,start (1+ ,ivar)) do-vars))
957 (when indexvar
958 (setf body-call (append body-call (list ivar))))
959 `(do ,do-vars (,end-condition) ,body-call))))
960
961 `(block nil
962 (flet ((,bodyfunc (,var ,@(and indexvar `(,indexvar)))
963 ,@decls
964 (tagbody ,@body)))
dea4d055
MW
965 (etypecase ,seq
966 (vector
967 (let ((,endvar (or ,end (length ,seq))))
968 ,(loopguts t nil endvar)))
969 (list
970 (if ,end
971 ,(loopguts t t end)
b8c698ee 972 ,(loopguts indexvar t nil)))))))))))
dea4d055
MW
973
974;;;--------------------------------------------------------------------------
4b8e5c03
MW
975;;; Structure accessor hacks.
976
977(export 'define-access-wrapper)
978(defmacro define-access-wrapper (from to &key read-only)
979 "Make (FROM THING) work like (TO THING).
980
981 If not READ-ONLY, then also make (setf (FROM THING) VALUE) work like
982 (setf (TO THING) VALUE).
983
984 This is mostly useful for structure slot accessors where the slot has to
985 be given an unpleasant name to avoid it being an external symbol."
986 `(progn
987 (declaim (inline ,from ,@(and (not read-only) `((setf ,from)))))
988 (defun ,from (object)
989 (,to object))
990 ,@(and (not read-only)
991 `((defun (setf ,from) (value object)
992 (setf (,to object) value))))))
993
994;;;--------------------------------------------------------------------------
db6c3279
MW
995;;; Condition and error utilities.
996
997(export 'designated-condition)
998(defun designated-condition (default-type datum arguments
999 &key allow-pointless-arguments)
1000 "Return the condition designated by DATUM and ARGUMENTS.
1001
1002 DATUM and ARGUMENTS together are a `condition designator' of (some
1003 supertype of) DEFAULT-TYPE; return the condition so designated."
1004 (typecase datum
1005 (condition
1006 (unless (or allow-pointless-arguments (null arguments))
1007 (error "Argument list provided with specific condition"))
1008 datum)
1009 (symbol
1010 (apply #'make-condition datum arguments))
1011 ((or string function)
1012 (make-condition default-type
1013 :format-control datum
1014 :format-arguments arguments))
1015 (t
1016 (error "Unexpected condition designator datum ~S" datum))))
1017
f7b60deb
MW
1018(export 'simple-control-error)
1019(define-condition simple-control-error (control-error simple-error)
1020 ())
1021
1022(export 'invoke-associated-restart)
1023(defun invoke-associated-restart (restart condition &rest arguments)
1024 "Invoke the active RESTART associated with CONDITION, with the ARGUMENTS.
1025
1026 Find an active restart designated by RESTART; if CONDITION is not nil,
1027 then restrict the search to restarts associated with CONDITION, and
1028 restarts not associated with any condition. If no such restart is found
1029 then signal an error of type `control-error'; otherwise invoke the restart
1030 with the given ARGUMENTS."
1031 (apply #'invoke-restart
1032 (or (find-restart restart condition)
1033 (error 'simple-control-error
1034 :format-control "~:[Restart ~S is not active~;~
1035 No active `~(~A~)' restart~]~
1036 ~@[ for condition ~S~]"
1037 :format-arguments (list (symbolp restart)
1038 restart
1039 condition)))
1040 arguments))
1041
c884ec24
MW
1042(export '(enclosing-condition enclosed-condition))
1043(define-condition enclosing-condition (condition)
1044 ((%enclosed-condition :initarg :condition :type condition
1045 :reader enclosed-condition))
1046 (:documentation
1047 "A condition which encloses another condition
1048
1049 This is useful if one wants to attach additional information to an
1050 existing condition. The enclosed condition can be obtained using the
1051 `enclosed-condition' function.")
1052 (:report (lambda (condition stream)
1053 (princ (enclosed-condition condition) stream))))
1054
1055(export 'information)
1056(define-condition information (condition)
1057 ())
1058
1059(export 'simple-information)
1060(define-condition simple-information (simple-condition information)
1061 ())
1062
1063(export 'info)
1064(defun info (datum &rest arguments)
1065 "Report some useful diagnostic information.
1066
1067 Establish a simple restart named `noted', and signal the condition of type
1068 `information' designated by DATUM and ARGUMENTS. Return non-nil if the
1069 restart was invoked, otherwise nil."
1070 (restart-case
1071 (signal (designated-condition 'simple-information datum arguments))
1072 (noted () :report "Noted." t)))
1073
1074(export 'noted)
1075(defun noted (&optional condition)
1076 "Invoke the `noted' restart, possibly associated with the given CONDITION."
1077 (invoke-associated-restart 'noted condition))
1078
1079(export 'promiscuous-cerror)
1080(defun promiscuous-cerror (continue-string datum &rest arguments)
1081 "Like standard `cerror', but robust against sneaky changes of conditions.
1082
1083 It seems that `cerror' (well, at least the version in SBCL) is careful
1084 to limit its restart to the specific condition it signalled. But that's
1085 annoying, because `sod-parser:with-default-error-location' substitutes
1086 different conditions carrying the error-location information."
1087 (restart-case (apply #'error datum arguments)
1088 (continue ()
1089 :report (lambda (stream)
1090 (apply #'format stream continue-string datum arguments))
1091 nil)))
1092
1093(export 'cerror*)
1094(defun cerror* (datum &rest arguments)
1095 (apply #'promiscuous-cerror "Continue" datum arguments))
1096
db6c3279 1097;;;--------------------------------------------------------------------------
dea4d055
MW
1098;;; CLOS hacking.
1099
1100(export 'default-slot)
1101(defmacro default-slot ((instance slot &optional (slot-names t))
1102 &body value
1103 &environment env)
1104 "If INSTANCE's slot named SLOT is unbound, set it to VALUE.
1105
1106 Only set SLOT if it's listed in SLOT-NAMES, or SLOT-NAMES is `t' (i.e., we
1107 obey the `shared-initialize' protocol). SLOT-NAMES defaults to `t', so
1108 you can use it in `initialize-instance' or similar without ill effects.
1109 Both INSTANCE and SLOT are evaluated; VALUE is an implicit progn and only
1110 evaluated if it's needed."
1111
1112 (once-only (:environment env instance slot slot-names)
1113 `(when ,(if (eq slot-names t)
1114 `(not (slot-boundp ,instance ,slot))
1115 `(and (not (slot-boundp ,instance ,slot))
1116 (or (eq ,slot-names t)
1117 (member ,slot ,slot-names))))
1118 (setf (slot-value ,instance ,slot)
1119 (progn ,@value)))))
1120
141283ff
MW
1121(export 'define-on-demand-slot)
1122(defmacro define-on-demand-slot (class slot (instance) &body body)
1123 "Defines a slot which computes its initial value on demand.
1124
1125 Sets up the named SLOT of CLASS to establish its value as the implicit
1126 progn BODY, by defining an appropriate method on `slot-unbound'."
b8c698ee
MW
1127 (multiple-value-bind (docs decls body) (parse-body body)
1128 (with-gensyms (classvar slotvar)
1129 `(defmethod slot-unbound
1130 (,classvar (,instance ,class) (,slotvar (eql ',slot)))
1131 ,@docs ,@decls
1132 (declare (ignore ,classvar))
fc09e191 1133 (setf (slot-value ,instance ',slot) (block ,slot ,@body))))))
141283ff 1134
dea4d055 1135;;;----- That's all, folks --------------------------------------------------