chiark / gitweb /
src/method-impl.lisp: Initialize `suppliedp' flags properly.
[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)
85(defmacro once-only (binds &body body)
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
e8abb286
MW
198;;;--------------------------------------------------------------------------
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
dea4d055
MW
258;;;--------------------------------------------------------------------------
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
276(export 'acond)
bf090e02 277(defmacro acond (&body clauses &environment env)
dea4d055
MW
278 "Like COND, but with `it' bound to the value of the condition.
279
280 Each of the CLAUSES has the form (CONDITION FORM*); if a CONDITION is
281 non-nil then evaluate the FORMs with `it' bound to the non-nil value, and
282 return the value of the last FORM; if there are no FORMs, then return `it'
283 itself. If the CONDITION is nil then continue with the next clause; if
284 all clauses evaluate to nil then the result is nil."
285 (labels ((walk (clauses)
286 (if (null clauses)
287 `nil
288 (once-only (:environment env (cond (caar clauses)))
289 (if (and (constantp cond)
290 (if (and (consp cond) (eq (car cond) 'quote))
291 (cadr cond) cond))
292 (if (cdar clauses)
293 `(let ((it ,cond))
294 (declare (ignorable it))
295 ,@(cdar clauses))
296 cond)
297 `(if ,cond
298 ,(if (cdar clauses)
299 `(let ((it ,cond))
300 (declare (ignorable it))
301 ,@(cdar clauses))
302 cond)
303 ,(walk (cdr clauses))))))))
304 (walk clauses)))
305
306(export '(acase aecase atypecase aetypecase))
307(defmacro acase (value &body clauses)
308 `(let ((it ,value)) (case it ,@clauses)))
309(defmacro aecase (value &body clauses)
310 `(let ((it ,value)) (ecase it ,@clauses)))
311(defmacro atypecase (value &body clauses)
312 `(let ((it ,value)) (typecase it ,@clauses)))
313(defmacro aetypecase (value &body clauses)
314 `(let ((it ,value)) (etypecase it ,@clauses)))
315
316(export 'asetf)
317(defmacro asetf (&rest places-and-values &environment env)
318 "Anaphoric update of places.
319
320 The PLACES-AND-VALUES are alternating PLACEs and VALUEs. Each VALUE is
321 evaluated with IT bound to the current value stored in the corresponding
322 PLACE."
323 `(progn ,@(loop for (place value) on places-and-values by #'cddr
324 collect (multiple-value-bind
325 (temps inits newtemps setform getform)
326 (get-setf-expansion place env)
327 `(let* (,@(mapcar #'list temps inits)
328 (it ,getform))
329 (multiple-value-bind ,newtemps ,value
330 ,setform))))))
331
332;;;--------------------------------------------------------------------------
333;;; MOP hacks (not terribly demanding).
334
bf090e02
MW
335(export 'instance-initargs)
336(defgeneric instance-initargs (instance)
337 (:documentation
338 "Return a plausble list of initargs for INSTANCE.
339
340 The idea is that you can make a copy of INSTANCE by invoking
341
342 (apply #'make-instance (class-of INSTANCE)
343 (instance-initargs INSTANCE))
344
345 The default implementation works by inspecting the slot definitions and
346 extracting suitable initargs, so this will only succeed if enough slots
347 actually have initargs specified that `initialize-instance' can fill in
348 the rest correctly.
349
350 The list returned is freshly consed, and you can destroy it if you like.")
351 (:method ((instance standard-object))
352 (mapcan (lambda (slot)
353 (aif (slot-definition-initargs slot)
354 (list (car it)
355 (slot-value instance (slot-definition-name slot)))
356 nil))
357 (class-slots (class-of instance)))))
358
dea4d055
MW
359(export '(copy-instance copy-instance-using-class))
360(defgeneric copy-instance-using-class (class instance &rest initargs)
361 (:documentation
362 "Metaobject protocol hook for `copy-instance'.")
363 (:method ((class standard-class) instance &rest initargs)
364 (let ((copy (allocate-instance class)))
365 (dolist (slot (class-slots class))
366 (let ((name (slot-definition-name slot)))
367 (when (slot-boundp instance name)
368 (setf (slot-value copy name) (slot-value instance name)))))
369 (apply #'shared-initialize copy nil initargs))))
370(defun copy-instance (object &rest initargs)
371 "Construct and return a copy of OBJECT.
372
373 The new object has the same class as OBJECT, and the same slot values
374 except where overridden by INITARGS."
375 (apply #'copy-instance-using-class (class-of object) object initargs))
376
9ec578d9
MW
377(export '(generic-function-methods method-specializers
378 eql-specializer eql-specializer-object))
379
dea4d055
MW
380;;;--------------------------------------------------------------------------
381;;; List utilities.
382
383(export 'make-list-builder)
384(defun make-list-builder (&optional initial)
385 "Return a simple list builder."
386
387 ;; The `builder' is just a cons cell whose cdr will be the list that's
388 ;; wanted. Effectively, then, we have a list that's one item longer than
389 ;; we actually want. The car of this extra initial cons cell is always the
390 ;; last cons in the list -- which is now well defined because there's
391 ;; always at least one.
392
393 (let ((builder (cons nil initial)))
394 (setf (car builder) (last builder))
395 builder))
396
397(export 'lbuild-add)
398(defun lbuild-add (builder item)
399 "Add an ITEM to the end of a list BUILDER."
400 (let ((new (cons item nil)))
401 (setf (cdar builder) new
402 (car builder) new))
403 builder)
404
405(export 'lbuild-add-list)
406(defun lbuild-add-list (builder list)
407 "Add a LIST to the end of a list BUILDER. The LIST will be clobbered."
408 (when list
409 (setf (cdar builder) list
410 (car builder) (last list)))
411 builder)
412
413(export 'lbuild-list)
414(defun lbuild-list (builder)
415 "Return the constructed list."
416 (cdr builder))
417
418(export 'mappend)
419(defun mappend (function list &rest more-lists)
420 "Like a nondestructive MAPCAN.
421
422 Map FUNCTION over the the corresponding elements of LIST and MORE-LISTS,
423 and return the result of appending all of the resulting lists."
424 (reduce #'append (apply #'mapcar function list more-lists) :from-end t))
425
426(export '(inconsistent-merge-error merge-error-candidates))
427(define-condition inconsistent-merge-error (error)
428 ((candidates :initarg :candidates
429 :reader merge-error-candidates))
430 (:documentation
9fb4a980 431 "Reports an inconsistency in the arguments passed to `merge-lists'.")
dea4d055
MW
432 (:report (lambda (condition stream)
433 (format stream "Merge inconsistency: failed to decide among ~A."
434 (merge-error-candidates condition)))))
435
436(export 'merge-lists)
437(defun merge-lists (lists &key pick (test #'eql))
438 "Return a merge of the given LISTS.
439
e8c5a09e 440 The resulting list contains the items of the given LISTS, with duplicates
dea4d055
MW
441 removed. The order of the resulting list is consistent with the orders of
442 the input LISTS in the sense that if A precedes B in some input list then
443 A will also precede B in the output list. If the lists aren't consistent
444 (e.g., some list contains A followed by B, and another contains B followed
3109662a 445 by A) then an error of type `inconsistent-merge-error' is signalled.
dea4d055
MW
446
447 Item equality is determined by TEST.
448
449 If there is an ambiguity at any point -- i.e., a choice between two or
450 more possible next items to emit -- then PICK is called to arbitrate.
451 PICK is called with two arguments: the list of candidate next items, and
e8c5a09e
MW
452 the current output list. It should return one of the candidate items.
453 The order of the candidates in the list given to the PICK function
454 reflects their order in the input LISTS: item A will precede item B in the
455 candidates list if and only if an occurrence of A appears in an earlier
456 input list than any occurrence of item B. (This completely determines the
457 order of the candidates: it is not possible that two candidates appear in
458 the same input list would resolve the ambiguity between them.) If PICK is
459 omitted then the item chosen is the one appearing in the earliest of the
460 input lists: i.e., effectively, the default PICK function is
461
462 (lambda (candidates output-so-far)
463 (declare (ignore output-so-far))
464 (car candidates))
dea4d055
MW
465
466 The primary use of this function is in computing class precedence lists.
467 By building the input lists and selecting the PICK function appropriately,
468 a variety of different CPL algorithms can be implemented."
469
022a3499
MW
470 (do ((lb (make-list-builder)))
471 ((null lists) (lbuild-list lb))
dea4d055
MW
472
473 ;; The candidate items are the ones at the front of the input lists.
474 ;; Gather them up, removing duplicates. If a candidate is somewhere in
475 ;; one of the other lists other than at the front then we reject it. If
476 ;; we've just rejected everything, then we can make no more progress and
477 ;; the input lists were inconsistent.
e8c5a09e
MW
478 (let* ((candidates (delete-duplicates (mapcar #'car lists)
479 :test test :from-end t))
dea4d055
MW
480 (leasts (remove-if (lambda (item)
481 (some (lambda (list)
482 (member item (cdr list) :test test))
483 lists))
484 candidates))
485 (winner (cond ((null leasts)
486 (error 'inconsistent-merge-error
487 :candidates candidates))
488 ((null (cdr leasts))
489 (car leasts))
490 (pick
491 (funcall pick leasts (lbuild-list lb)))
492 (t (car leasts)))))
493
494 ;; Check that the PICK function isn't conning us.
495 (assert (member winner leasts :test test))
496
497 ;; Update the output list and remove the winning item from the input
498 ;; lists. We know that it must be at the front of each input list
499 ;; containing it. At this point, we discard input lists entirely when
500 ;; they run out of entries. The loop ends when there are no more input
501 ;; lists left, i.e., when we've munched all of the input items.
502 (lbuild-add lb winner)
503 (setf lists (delete nil (mapcar (lambda (list)
504 (if (funcall test winner (car list))
505 (cdr list)
506 list))
507 lists))))))
508
509(export 'categorize)
510(defmacro categorize ((itemvar items &key bind) categories &body body)
511 "Categorize ITEMS into lists and invoke BODY.
512
513 The ITEMVAR is a symbol; as the macro iterates over the ITEMS, ITEMVAR
514 will contain the current item. The BIND argument is a list of LET*-like
515 clauses. The CATEGORIES are a list of clauses of the form (SYMBOL
516 PREDICATE).
517
518 The behaviour of the macro is as follows. ITEMVAR is assigned (not
519 bound), in turn, each item in the list ITEMS. The PREDICATEs in the
520 CATEGORIES list are evaluated in turn, in an environment containing
521 ITEMVAR and the BINDings, until one of them evaluates to a non-nil value.
522 At this point, the item is assigned to the category named by the
523 corresponding SYMBOL. If none of the PREDICATEs returns non-nil then an
524 error is signalled; a PREDICATE consisting only of T will (of course)
525 match anything; it is detected specially so as to avoid compiler warnings.
526
527 Once all of the ITEMS have been categorized in this fashion, the BODY is
528 evaluated as an implicit PROGN. For each SYMBOL naming a category, a
529 variable named after that symbol will be bound in the BODY's environment
530 to a list of the items in that category, in the same order in which they
531 were found in the list ITEMS. The final values of the macro are the final
532 values of the BODY."
533
534 (let* ((cat-names (mapcar #'car categories))
535 (cat-match-forms (mapcar #'cadr categories))
536 (cat-vars (mapcar (lambda (name) (gensym (concatenate 'string
537 (symbol-name name) "-")))
538 cat-names))
539 (items-var (gensym "ITEMS-")))
540 `(let ((,items-var ,items)
541 ,@(mapcar (lambda (cat-var) (list cat-var nil)) cat-vars))
542 (dolist (,itemvar ,items-var)
543 (let* ,bind
544 (cond ,@(mapcar (lambda (cat-match-form cat-var)
545 `(,cat-match-form
546 (push ,itemvar ,cat-var)))
547 cat-match-forms cat-vars)
548 ,@(and (not (member t cat-match-forms))
549 `((t (error "Failed to categorize ~A" ,itemvar)))))))
550 (let ,(mapcar (lambda (name var)
551 `(,name (nreverse ,var)))
552 cat-names cat-vars)
553 ,@body))))
554
555;;;--------------------------------------------------------------------------
556;;; Strings and characters.
557
558(export 'frob-identifier)
559(defun frob-identifier (string &key (swap-case t) (swap-hyphen t))
560 "Twiddles the case of STRING.
561
562 If all the letters in STRING are uppercase, and SWAP-CASE is true, then
563 switch them to lowercase; if they're all lowercase then switch them to
564 uppercase. If there's a mix then leave them all alone. At the same time,
565 if there are underscores but no hyphens, and SWAP-HYPHEN is true, then
566 switch them to hyphens, if there are hyphens and no underscores, switch
567 them underscores, and if there are both then leave them alone.
568
569 This is an invertible transformation, which turns vaguely plausible Lisp
570 names into vaguely plausible C names and vice versa. Lisp names with
571 `funny characters' like stars and percent signs won't be any use, of
572 course."
573
574 ;; Work out what kind of a job we've got to do. Gather flags: bit 0 means
575 ;; there are upper-case letters; bit 1 means there are lower-case letters;
576 ;; bit 2 means there are hyphens; bit 3 means there are underscores.
577 ;;
578 ;; Consequently, (logxor flags (ash flags 1)) is interesting: bit 1 is set
579 ;; if we have to frob case; bit 3 is set if we have to swap hyphens and
580 ;; underscores. So use this to select functions which do bits of the
581 ;; mapping, and then compose them together.
582 (let* ((flags (reduce (lambda (state ch)
583 (logior state
584 (cond ((upper-case-p ch) 1)
585 ((lower-case-p ch) 2)
586 ((char= ch #\-) 4)
587 ((char= ch #\_) 8)
588 (t 0))))
589 string
590 :initial-value 0))
591 (mask (logxor flags (ash flags 1)))
592 (letter (cond ((or (not swap-case) (not (logbitp 1 mask)))
593 (constantly nil))
594 ((logbitp 0 flags)
595 (lambda (ch)
596 (and (alpha-char-p ch) (char-downcase ch))))
597 (t
598 (lambda (ch)
599 (and (alpha-char-p ch) (char-upcase ch))))))
600 (uscore-hyphen (cond ((or (not (logbitp 3 mask)) (not swap-hyphen))
601 (constantly nil))
602 ((logbitp 2 flags)
603 (lambda (ch) (and (char= ch #\-) #\_)))
604 (t
605 (lambda (ch) (and (char= ch #\_) #\-))))))
606
607 (if (logbitp 3 (logior mask (ash mask 2)))
608 (map 'string (lambda (ch)
609 (or (funcall letter ch)
610 (funcall uscore-hyphen ch)
611 ch))
612 string)
613 string)))
614
615(export 'whitespace-char-p)
616(declaim (inline whitespace-char-p))
617(defun whitespace-char-p (char)
618 "Returns whether CHAR is a whitespace character.
619
620 Whitespaceness is determined relative to the compile-time readtable, which
621 is probably good enough for most purposes."
622 (case char
623 (#.(loop for i below char-code-limit
624 for ch = (code-char i)
625 unless (with-input-from-string (in (string ch))
626 (peek-char t in nil))
627 collect ch) t)
628 (t nil)))
629
630(export 'update-position)
631(declaim (inline update-position))
632(defun update-position (char line column)
633 "Updates LINE and COLUMN appropriately for having read the character CHAR.
634
635 Returns the new LINE and COLUMN numbers."
636 (case char
637 ((#\newline #\vt #\page)
638 (values (1+ line) 0))
639 ((#\tab)
640 (values line (logandc2 (+ column 8) 7)))
641 (t
642 (values line (1+ column)))))
643
644(export 'backtrack-position)
645(declaim (inline backtrack-position))
646(defun backtrack-position (char line column)
647 "Updates LINE and COLUMN appropriately for having unread CHAR.
648
649 Well, actually an approximation for it; it will likely be wrong if the
650 last character was a tab. But when the character is read again, it will
651 be correct."
652
653 ;; This isn't perfect: if the character doesn't actually match what was
654 ;; really read then it might not actually be possible: for example, if we
655 ;; push back a newline while in the middle of a line, or a tab while not at
656 ;; a tab stop. In that case, we'll just lose, but hopefully not too badly.
657 (case char
658
659 ;; In the absence of better ideas, I'll set the column number to zero.
660 ;; This is almost certainly wrong, but with a little luck nobody will ask
661 ;; and it'll be all right soon.
662 ((#\newline #\vt #\page) (values (1- line) 0))
663
664 ;; Winding back a single space is sufficient. If the position is
665 ;; currently on a tab stop then it'll advance back here next time. If
666 ;; not, we're going to lose anyway because the previous character
667 ;; certainly couldn't have been a tab.
668 (#\tab (values line (1- column)))
669
670 ;; Anything else: just decrement the column and cross fingers.
671 (t (values line (1- column)))))
672
673;;;--------------------------------------------------------------------------
674;;; Functions.
675
676(export 'compose)
677(defun compose (function &rest more-functions)
678 "Composition of functions. Functions are applied left-to-right.
679
680 This is the reverse order of the usual mathematical notation, but I find
bf090e02
MW
681 it easier to read. It's also slightly easier to work with in programs.
682 That is, (compose F1 F2 ... Fn) is what a category theorist might write as
683 F1 ; F2 ; ... ; Fn, rather than F1 o F2 o ... o Fn."
684
dea4d055
MW
685 (labels ((compose1 (func-a func-b)
686 (lambda (&rest args)
687 (multiple-value-call func-b (apply func-a args)))))
688 (reduce #'compose1 more-functions :initial-value function)))
689
690;;;--------------------------------------------------------------------------
691;;; Symbols.
692
693(export 'symbolicate)
694(defun symbolicate (&rest symbols)
695 "Return a symbol named after the concatenation of the names of the SYMBOLS.
696
3109662a 697 The symbol is interned in the current `*package*'. Trad."
dea4d055
MW
698 (intern (apply #'concatenate 'string (mapcar #'symbol-name symbols))))
699
700;;;--------------------------------------------------------------------------
701;;; Object printing.
702
703(export 'maybe-print-unreadable-object)
704(defmacro maybe-print-unreadable-object
705 ((object stream &rest args) &body body)
706 "Print helper for usually-unreadable objects.
707
3109662a 708 If `*print-escape*' is set then print OBJECT unreadably using BODY.
dea4d055
MW
709 Otherwise just print using BODY."
710 (with-gensyms (print)
711 `(flet ((,print () ,@body))
712 (if *print-escape*
713 (print-unreadable-object (,object ,stream ,@args)
714 (,print))
715 (,print)))))
716
08b6e064
MW
717(export 'print-ugly-stuff)
718(defun print-ugly-stuff (stream func)
719 "Print not-pretty things to the stream underlying STREAM.
720
721 The Lisp pretty-printing machinery, notably `pprint-logical-block', may
722 interpose additional streams between its body and the original target
723 stream. This makes it difficult to make use of the underlying stream's
724 special features, whatever they might be."
725
726 ;; This is unpleasant. Hacky hacky.
727 #.(or #+sbcl '(if (typep stream 'sb-pretty:pretty-stream)
728 (let ((target (sb-pretty::pretty-stream-target stream)))
729 (pprint-newline :mandatory stream)
730 (funcall func target))
731 (funcall func stream))
732 #+cmu '(if (typep stream 'pp:pretty-stream)
733 (let ((target (pp::pretty-stream-target stream)))
734 (pprint-newline :mandatory stream)
735 (funcall func target))
736 (funcall func stream))
737 '(funcall func stream)))
738
dea4d055
MW
739;;;--------------------------------------------------------------------------
740;;; Iteration macros.
741
742(export 'dosequence)
743(defmacro dosequence ((var seq &key (start 0) (end nil) indexvar)
744 &body body
745 &environment env)
746 "Macro for iterating over general sequences.
747
748 Iterates over a (sub)sequence SEQ, delimited by START and END (which are
749 evaluated). For each item of SEQ, BODY is invoked with VAR bound to the
750 item, and INDEXVAR (if requested) bound to the item's index. (Note that
751 this is different from most iteration constructs in Common Lisp, which
752 work by mutating the variable.)
753
754 The loop is surrounded by an anonymous BLOCK and the loop body forms an
755 implicit TAGBODY, as is usual. There is no result-form, however."
756
757 (once-only (:environment env seq start end)
758 (with-gensyms ((ivar "INDEX-") (endvar "END-") (bodyfunc "BODY-"))
b8c698ee
MW
759 (multiple-value-bind (docs decls body) (parse-body body :docp nil)
760 (declare (ignore docs))
761
762 (flet ((loopguts (indexp listp endvar)
763 ;; Build a DO-loop to do what we want.
764 (let* ((do-vars nil)
765 (end-condition (if endvar
766 `(>= ,ivar ,endvar)
767 `(endp ,seq)))
768 (item (if listp
769 `(car ,seq)
770 `(aref ,seq ,ivar)))
771 (body-call `(,bodyfunc ,item)))
772 (when listp
773 (push `(,seq (nthcdr ,start ,seq) (cdr ,seq))
774 do-vars))
775 (when indexp
776 (push `(,ivar ,start (1+ ,ivar)) do-vars))
777 (when indexvar
778 (setf body-call (append body-call (list ivar))))
779 `(do ,do-vars (,end-condition) ,body-call))))
780
781 `(block nil
782 (flet ((,bodyfunc (,var ,@(and indexvar `(,indexvar)))
783 ,@decls
784 (tagbody ,@body)))
dea4d055
MW
785 (etypecase ,seq
786 (vector
787 (let ((,endvar (or ,end (length ,seq))))
788 ,(loopguts t nil endvar)))
789 (list
790 (if ,end
791 ,(loopguts t t end)
b8c698ee 792 ,(loopguts indexvar t nil)))))))))))
dea4d055 793
4b8e5c03
MW
794;;;--------------------------------------------------------------------------
795;;; Structure accessor hacks.
796
797(export 'define-access-wrapper)
798(defmacro define-access-wrapper (from to &key read-only)
799 "Make (FROM THING) work like (TO THING).
800
801 If not READ-ONLY, then also make (setf (FROM THING) VALUE) work like
802 (setf (TO THING) VALUE).
803
804 This is mostly useful for structure slot accessors where the slot has to
805 be given an unpleasant name to avoid it being an external symbol."
806 `(progn
807 (declaim (inline ,from ,@(and (not read-only) `((setf ,from)))))
808 (defun ,from (object)
809 (,to object))
810 ,@(and (not read-only)
811 `((defun (setf ,from) (value object)
812 (setf (,to object) value))))))
813
dea4d055
MW
814;;;--------------------------------------------------------------------------
815;;; CLOS hacking.
816
817(export 'default-slot)
818(defmacro default-slot ((instance slot &optional (slot-names t))
819 &body value
820 &environment env)
821 "If INSTANCE's slot named SLOT is unbound, set it to VALUE.
822
823 Only set SLOT if it's listed in SLOT-NAMES, or SLOT-NAMES is `t' (i.e., we
824 obey the `shared-initialize' protocol). SLOT-NAMES defaults to `t', so
825 you can use it in `initialize-instance' or similar without ill effects.
826 Both INSTANCE and SLOT are evaluated; VALUE is an implicit progn and only
827 evaluated if it's needed."
828
829 (once-only (:environment env instance slot slot-names)
830 `(when ,(if (eq slot-names t)
831 `(not (slot-boundp ,instance ,slot))
832 `(and (not (slot-boundp ,instance ,slot))
833 (or (eq ,slot-names t)
834 (member ,slot ,slot-names))))
835 (setf (slot-value ,instance ,slot)
836 (progn ,@value)))))
837
141283ff
MW
838(export 'define-on-demand-slot)
839(defmacro define-on-demand-slot (class slot (instance) &body body)
840 "Defines a slot which computes its initial value on demand.
841
842 Sets up the named SLOT of CLASS to establish its value as the implicit
843 progn BODY, by defining an appropriate method on `slot-unbound'."
b8c698ee
MW
844 (multiple-value-bind (docs decls body) (parse-body body)
845 (with-gensyms (classvar slotvar)
846 `(defmethod slot-unbound
847 (,classvar (,instance ,class) (,slotvar (eql ',slot)))
848 ,@docs ,@decls
849 (declare (ignore ,classvar))
fc09e191 850 (setf (slot-value ,instance ',slot) (block ,slot ,@body))))))
141283ff 851
dea4d055 852;;;----- That's all, folks --------------------------------------------------