changelog shortlog graph tags branches changeset files revisions annotate raw help

Mercurial > core / lisp/std/defpkg.lisp

changeset 384: 8fe057887c17
parent: 00d1c8afcdbb
child: d876b572b5b9
author: Richard Westhaver <ellis@rwest.io>
date: Wed, 29 May 2024 23:29:40 -0400
permissions: -rw-r--r--
description: skel refactor1
1 ;;; defpkg.lisp --- defpackage extension macro
2 
3 ;;; Commentary:
4 
5 ;;
6 
7 ;;; Code:
8 (defpackage :std/defpkg
9  (:use :cl)
10  (:nicknames :pkg)
11  (:export :defpkg
12  :find-package* :find-symbol* :symbol-call
13  :intern* :export* :import* :shadowing-import*
14  :shadow* :make-symbol* :unintern*
15  :symbol-shadowing-p :home-package-p
16  :symbol-package-name :standard-common-lisp-symbol-p
17  :reify-package :unreify-package :reify-symbol :unreify-symbol
18  :nuke-symbol-in-package :nuke-symbol :rehome-symbol
19  :ensure-package-unused :delete-package*
20  :package-names :packages-from-names :fresh-package-name
21  :rename-package-away :package-definition-form :parse-defpkg-form
22  :ensure-package))
23 
24 (in-package :std/defpkg)
25 
26 (eval-when (:load-toplevel :compile-toplevel :execute)
27  (defun find-package* (package-designator &optional (error t))
28  (let ((package (find-package package-designator)))
29  (cond
30  (package package)
31  (error (error "No package named ~S" (string package-designator)))
32  (t nil))))
33  (defun find-symbol* (name package-designator &optional (error t))
34  "Find a symbol in a package of given string'ified NAME;
35 unlike CL:FIND-SYMBOL, work well with 'modern' case sensitive syntax
36 by letting you supply a symbol or keyword for the name;
37 also works well when the package is not present.
38 If optional ERROR argument is NIL, return NIL instead of an error
39 when the symbol is not found."
40  (block nil
41  (let ((package (find-package* package-designator error)))
42  (when package ;; package error handled by find-package* already
43  (multiple-value-bind (symbol status) (find-symbol (string name) package)
44  (cond
45  (status (return (values symbol status)))
46  (error (error "There is no symbol ~S in package ~S" name (package-name package))))))
47  (values nil nil))))
48  (defun symbol-call (package name &rest args)
49  "Call a function associated with symbol of given name in given package,
50 with given ARGS. Useful when the call is read before the package is loaded,
51 or when loading the package is optional."
52  (apply (find-symbol* name package) args))
53  (defun intern* (name package-designator &optional (error t))
54  (intern (string name) (find-package* package-designator error)))
55  (defun export* (name package-designator)
56  (let* ((package (find-package* package-designator))
57  (symbol (intern* name package)))
58  (export (or symbol (list symbol)) package)))
59  (defun import* (symbol package-designator)
60  (import (or symbol (list symbol)) (find-package* package-designator)))
61  (defun shadowing-import* (symbol package-designator)
62  (shadowing-import (or symbol (list symbol)) (find-package* package-designator)))
63  (defun shadow* (name package-designator)
64  (shadow (list (string name)) (find-package* package-designator)))
65  (defun make-symbol* (name)
66  (etypecase name
67  (string (make-symbol name))
68  (symbol (copy-symbol name))))
69  (defun unintern* (name package-designator &optional (error t))
70  (block nil
71  (let ((package (find-package* package-designator error)))
72  (when package
73  (multiple-value-bind (symbol status) (find-symbol* name package error)
74  (cond
75  (status (unintern symbol package)
76  (return (values symbol status)))
77  (error (error "symbol ~A not present in package ~A"
78  (string symbol) (package-name package))))))
79  (values nil nil))))
80  (defun symbol-shadowing-p (symbol package)
81  (and (member symbol (package-shadowing-symbols package)) t))
82  (defun home-package-p (symbol package)
83  (and package (let ((sp (symbol-package symbol)))
84  (and sp (let ((pp (find-package* package)))
85  (and pp (eq sp pp))))))))
86 
87 
88 (eval-when (:load-toplevel :compile-toplevel :execute)
89  (defun symbol-package-name (symbol)
90  (let ((package (symbol-package symbol)))
91  (and package (package-name package))))
92  (defun standard-common-lisp-symbol-p (symbol)
93  (multiple-value-bind (sym status) (find-symbol* symbol :common-lisp nil)
94  (and (eq sym symbol) (eq status :external))))
95  (defun reify-package (package &optional package-context)
96  (if (eq package package-context) t
97  (etypecase package
98  (null nil)
99  ((eql (find-package :cl)) :cl)
100  (package (package-name package)))))
101  (defun unreify-package (package &optional package-context)
102  (etypecase package
103  (null nil)
104  ((eql t) package-context)
105  ((or symbol string) (find-package package))))
106  (defun reify-symbol (symbol &optional package-context)
107  (etypecase symbol
108  ((or keyword (satisfies standard-common-lisp-symbol-p)) symbol)
109  (symbol (vector (symbol-name symbol)
110  (reify-package (symbol-package symbol) package-context)))))
111  (defun unreify-symbol (symbol &optional package-context)
112  (etypecase symbol
113  (symbol symbol)
114  ((simple-vector 2)
115  (let* ((symbol-name (svref symbol 0))
116  (package-foo (svref symbol 1))
117  (package (unreify-package package-foo package-context)))
118  (if package (intern* symbol-name package)
119  (make-symbol* symbol-name)))))))
120 
121 (eval-when (:load-toplevel :compile-toplevel :execute)
122  (defvar *all-package-happiness* '())
123  (defvar *all-package-fishiness* (list t))
124  (defun record-fishy (info)
125  ;;(format t "~&FISHY: ~S~%" info)
126  (push info *all-package-fishiness*))
127  (defmacro when-package-fishiness (&body body)
128  `(when *all-package-fishiness* ,@body))
129  (defmacro note-package-fishiness (&rest info)
130  `(when-package-fishiness (record-fishy (list ,@info)))))
131 
132 (eval-when (:load-toplevel :compile-toplevel :execute)
133  #+(or clisp clozure)
134  (defun get-setf-function-symbol (symbol)
135  #+clisp (let ((sym (get symbol 'system::setf-function)))
136  (if sym (values sym :setf-function)
137  (let ((sym (get symbol 'system::setf-expander)))
138  (if sym (values sym :setf-expander)
139  (values nil nil)))))
140  #+clozure (gethash symbol ccl::%setf-function-names%))
141  #+(or clisp clozure)
142  (defun set-setf-function-symbol (new-setf-symbol symbol &optional kind)
143  #+clisp (assert (member kind '(:setf-function :setf-expander)))
144  #+clozure (assert (eq kind t))
145  #+clisp
146  (cond
147  ((null new-setf-symbol)
148  (remprop symbol 'system::setf-function)
149  (remprop symbol 'system::setf-expander))
150  ((eq kind :setf-function)
151  (setf (get symbol 'system::setf-function) new-setf-symbol))
152  ((eq kind :setf-expander)
153  (setf (get symbol 'system::setf-expander) new-setf-symbol))
154  (t (error "invalid kind of setf-function ~S for ~S to be set to ~S"
155  kind symbol new-setf-symbol)))
156  #+clozure
157  (progn
158  (gethash symbol ccl::%setf-function-names%) new-setf-symbol
159  (gethash new-setf-symbol ccl::%setf-function-name-inverses%) symbol))
160  #+(or clisp clozure)
161  (defun create-setf-function-symbol (symbol)
162  #+clisp (system::setf-symbol symbol)
163  #+clozure (ccl::construct-setf-function-name symbol))
164  (defun set-dummy-symbol (symbol reason other-symbol)
165  (setf (get symbol 'dummy-symbol) (cons reason other-symbol)))
166  (defun make-dummy-symbol (symbol)
167  (let ((dummy (copy-symbol symbol)))
168  (set-dummy-symbol dummy 'replacing symbol)
169  (set-dummy-symbol symbol 'replaced-by dummy)
170  dummy))
171  (defun dummy-symbol (symbol)
172  (get symbol 'dummy-symbol))
173  (defun get-dummy-symbol (symbol)
174  (let ((existing (dummy-symbol symbol)))
175  (if existing (values (cdr existing) (car existing))
176  (make-dummy-symbol symbol))))
177  (defun nuke-symbol-in-package (symbol package-designator)
178  (let ((package (find-package* package-designator))
179  (name (symbol-name symbol)))
180  (multiple-value-bind (sym stat) (find-symbol name package)
181  (when (and (member stat '(:internal :external)) (eq symbol sym))
182  (if (symbol-shadowing-p symbol package)
183  (shadowing-import* (get-dummy-symbol symbol) package)
184  (unintern* symbol package))))))
185  (defun nuke-symbol (symbol &optional (packages (list-all-packages)))
186  #+(or clisp clozure)
187  (multiple-value-bind (setf-symbol kind)
188  (get-setf-function-symbol symbol)
189  (when kind (nuke-symbol setf-symbol)))
190  (loop :for p :in packages :do (nuke-symbol-in-package symbol p)))
191  (defun rehome-symbol (symbol package-designator)
192  "Changes the home package of a symbol, also leaving it present in its old home if any"
193  (let* ((name (symbol-name symbol))
194  (package (find-package* package-designator))
195  (old-package (symbol-package symbol))
196  (old-status (and old-package (nth-value 1 (find-symbol name old-package))))
197  (shadowing (and old-package (symbol-shadowing-p symbol old-package) (make-symbol name))))
198  (multiple-value-bind (overwritten-symbol overwritten-symbol-status) (find-symbol name package)
199  (unless (eq package old-package)
200  (let ((overwritten-symbol-shadowing-p
201  (and overwritten-symbol-status
202  (symbol-shadowing-p overwritten-symbol package))))
203  (note-package-fishiness
204  :rehome-symbol name
205  (when old-package (package-name old-package)) old-status (and shadowing t)
206  (package-name package) overwritten-symbol-status overwritten-symbol-shadowing-p)
207  (when old-package
208  (if shadowing
209  (shadowing-import* shadowing old-package))
210  (unintern* symbol old-package))
211  (cond
212  (overwritten-symbol-shadowing-p
213  (shadowing-import* symbol package))
214  (t
215  (when overwritten-symbol-status
216  (unintern* overwritten-symbol package))
217  (import* symbol package)))
218  (if shadowing
219  (shadowing-import* symbol old-package)
220  (import* symbol old-package))
221  #+(or clisp clozure)
222  (multiple-value-bind (setf-symbol kind)
223  (get-setf-function-symbol symbol)
224  (when kind
225  (let* ((setf-function (fdefinition setf-symbol))
226  (new-setf-symbol (create-setf-function-symbol symbol)))
227  (note-package-fishiness
228  :setf-function
229  name (package-name package)
230  (symbol-name setf-symbol) (symbol-package-name setf-symbol)
231  (symbol-name new-setf-symbol) (symbol-package-name new-setf-symbol))
232  (when (symbol-package setf-symbol)
233  (unintern* setf-symbol (symbol-package setf-symbol)))
234  (setf (fdefinition new-setf-symbol) setf-function)
235  (set-setf-function-symbol new-setf-symbol symbol kind))))
236  #+(or clisp clozure)
237  (multiple-value-bind (overwritten-setf foundp)
238  (get-setf-function-symbol overwritten-symbol)
239  (when foundp
240  (unintern overwritten-setf)))
241  (when (eq old-status :external)
242  (export* symbol old-package))
243  (when (eq overwritten-symbol-status :external)
244  (export* symbol package))))
245  (values overwritten-symbol overwritten-symbol-status))))
246  (defun ensure-package-unused (package)
247  (loop :for p :in (package-used-by-list package) :do
248  (unuse-package package p)))
249  (defun delete-package* (package &key nuke)
250  (let ((p (find-package package)))
251  (when p
252  (when nuke (do-symbols (s p) (when (home-package-p s p) (nuke-symbol s))))
253  (ensure-package-unused p)
254  (delete-package package))))
255  (defun package-names (package)
256  (cons (package-name package) (package-nicknames package)))
257  (defun packages-from-names (names)
258  (remove-duplicates (remove nil (mapcar #'find-package names)) :from-end t))
259  (defun fresh-package-name (&key (prefix :%TO-BE-DELETED)
260  separator
261  (index (random most-positive-fixnum)))
262  (loop :for i :from index
263  :for n = (format nil "~A~@[~A~D~]" prefix (and (plusp i) (or separator "")) i)
264  :thereis (and (not (find-package n)) n)))
265  (defun rename-package-away (p &rest keys &key prefix &allow-other-keys)
266  (let ((new-name
267  (apply 'fresh-package-name
268  :prefix (or prefix (format nil "__~A__" (package-name p))) keys)))
269  (record-fishy (list :rename-away (package-names p) new-name))
270  (rename-package p new-name))))
271 
272 
273 ;;; Communicable representation of symbol and package information
274 
275 (eval-when (:load-toplevel :compile-toplevel :execute)
276  (defun package-definition-form (package-designator
277  &key (nicknamesp t) (usep t)
278  (shadowp t) (shadowing-import-p t)
279  (exportp t) (importp t) internp (error t))
280  (let* ((package (or (find-package* package-designator error)
281  (return-from package-definition-form nil)))
282  (name (package-name package))
283  (nicknames (package-nicknames package))
284  (use (mapcar #'package-name (package-use-list package)))
285  (shadow ())
286  (shadowing-import (make-hash-table :test 'equal))
287  (import (make-hash-table :test 'equal))
288  (export ())
289  (intern ()))
290  (when package
291  (loop :for sym :being :the :symbols :in package
292  :for status = (nth-value 1 (find-symbol* sym package)) :do
293  (ecase status
294  ((nil :inherited))
295  ((:internal :external)
296  (let* ((name (symbol-name sym))
297  (external (eq status :external))
298  (home (symbol-package sym))
299  (home-name (package-name home))
300  (imported (not (eq home package)))
301  (shadowing (symbol-shadowing-p sym package)))
302  (cond
303  ((and shadowing imported)
304  (push name (gethash home-name shadowing-import)))
305  (shadowing
306  (push name shadow))
307  (imported
308  (push name (gethash home-name import))))
309  (cond
310  (external
311  (push name export))
312  (imported)
313  (t (push name intern)))))))
314  (labels ((sort-names (names)
315  (sort (copy-list names) #'string<))
316  (table-keys (table)
317  (loop :for k :being :the :hash-keys :of table :collect k))
318  (when-relevant (key value)
319  (when value (list (cons key value))))
320  (import-options (key table)
321  (loop :for i :in (sort-names (table-keys table))
322  :collect `(,key ,i ,@(sort-names (gethash i table))))))
323  `(defpackage ,name
324  ,@(when-relevant :nicknames (and nicknamesp (sort-names nicknames)))
325  (:use ,@(and usep (sort-names use)))
326  ,@(when-relevant :shadow (and shadowp (sort-names shadow)))
327  ,@(import-options :shadowing-import-from (and shadowing-import-p shadowing-import))
328  ,@(import-options :import-from (and importp import))
329  ,@(when-relevant :export (and exportp (sort-names export)))
330  ,@(when-relevant :intern (and internp (sort-names intern)))))))))
331 
332 (eval-when (:load-toplevel :compile-toplevel :execute)
333  (defun ensure-shadowing-import (name to-package from-package shadowed imported)
334  (check-type name string)
335  (check-type to-package package)
336  (check-type from-package package)
337  (check-type shadowed hash-table)
338  (check-type imported hash-table)
339  (let ((import-me (find-symbol* name from-package)))
340  (multiple-value-bind (existing status) (find-symbol name to-package)
341  (cond
342  ((gethash name shadowed)
343  (unless (eq import-me existing)
344  (error "Conflicting shadowings for ~A" name)))
345  (t
346  (setf (gethash name shadowed) t)
347  (setf (gethash name imported) t)
348  (unless (or (null status)
349  (and (member status '(:internal :external))
350  (eq existing import-me)
351  (symbol-shadowing-p existing to-package)))
352  (note-package-fishiness
353  :shadowing-import name
354  (package-name from-package)
355  (or (home-package-p import-me from-package) (symbol-package-name import-me))
356  (package-name to-package) status
357  (and status (or (home-package-p existing to-package) (symbol-package-name existing)))))
358  (shadowing-import* import-me to-package))))))
359  (defun ensure-imported (import-me into-package &optional from-package)
360  (check-type import-me symbol)
361  (check-type into-package package)
362  (check-type from-package (or null package))
363  (let ((name (symbol-name import-me)))
364  (multiple-value-bind (existing status) (find-symbol name into-package)
365  (cond
366  ((not status)
367  (import* import-me into-package))
368  ((eq import-me existing))
369  (t
370  (let ((shadowing-p (symbol-shadowing-p existing into-package)))
371  (note-package-fishiness
372  :ensure-imported name
373  (and from-package (package-name from-package))
374  (or (home-package-p import-me from-package) (symbol-package-name import-me))
375  (package-name into-package)
376  status
377  (and status (or (home-package-p existing into-package) (symbol-package-name existing)))
378  shadowing-p)
379  (cond
380  ((or shadowing-p (eq status :inherited))
381  (shadowing-import* import-me into-package))
382  (t
383  (unintern* existing into-package)
384  (import* import-me into-package))))))))
385  (values))
386  (defun ensure-import (name to-package from-package shadowed imported)
387  (check-type name string)
388  (check-type to-package package)
389  (check-type from-package package)
390  (check-type shadowed hash-table)
391  (check-type imported hash-table)
392  (multiple-value-bind (import-me import-status) (find-symbol name from-package)
393  (when (null import-status)
394  (note-package-fishiness
395  :import-uninterned name (package-name from-package) (package-name to-package))
396  (setf import-me (intern* name from-package)))
397  (multiple-value-bind (existing status) (find-symbol name to-package)
398  (cond
399  ((and imported (gethash name imported))
400  (unless (and status (eq import-me existing))
401  (error "Can't import ~S from both ~S and ~S"
402  name (package-name (symbol-package existing)) (package-name from-package))))
403  ((gethash name shadowed)
404  (error "Can't both shadow ~S and import it from ~S" name (package-name from-package)))
405  (t
406  (setf (gethash name imported) t))))
407  (ensure-imported import-me to-package from-package)))
408  (defun ensure-inherited (name symbol to-package from-package mixp shadowed imported inherited)
409  (check-type name string)
410  (check-type symbol symbol)
411  (check-type to-package package)
412  (check-type from-package package)
413  (check-type mixp (member nil t)) ; no cl:boolean on Genera
414  (check-type shadowed hash-table)
415  (check-type imported hash-table)
416  (check-type inherited hash-table)
417  (multiple-value-bind (existing status) (find-symbol name to-package)
418  (let* ((sp (symbol-package symbol))
419  (in (gethash name inherited))
420  (xp (and status (symbol-package existing))))
421  (when (null sp)
422  (note-package-fishiness
423  :import-uninterned name
424  (package-name from-package) (package-name to-package) mixp)
425  (import* symbol from-package)
426  (setf sp (package-name from-package)))
427  (cond
428  ((gethash name shadowed))
429  (in
430  (unless (equal sp (first in))
431  (if mixp
432  (ensure-shadowing-import name to-package (second in) shadowed imported)
433  (error "Can't inherit ~S from ~S, it is inherited from ~S"
434  name (package-name sp) (package-name (first in))))))
435  ((gethash name imported)
436  (unless (eq symbol existing)
437  (error "Can't inherit ~S from ~S, it is imported from ~S"
438  name (package-name sp) (package-name xp))))
439  (t
440  (setf (gethash name inherited) (list sp from-package))
441  (when (and status (not (eq sp xp)))
442  (let ((shadowing (symbol-shadowing-p existing to-package)))
443  (note-package-fishiness
444  :inherited name
445  (package-name from-package)
446  (or (home-package-p symbol from-package) (symbol-package-name symbol))
447  (package-name to-package)
448  (or (home-package-p existing to-package) (symbol-package-name existing)))
449  (if shadowing (ensure-shadowing-import name to-package from-package shadowed imported)
450  (unintern* existing to-package)))))))))
451  (defun ensure-mix (name symbol to-package from-package shadowed imported inherited)
452  (check-type name string)
453  (check-type symbol symbol)
454  (check-type to-package package)
455  (check-type from-package package)
456  (check-type shadowed hash-table)
457  (check-type imported hash-table)
458  (check-type inherited hash-table)
459  (unless (gethash name shadowed)
460  (multiple-value-bind (existing status) (find-symbol name to-package)
461  (let* ((sp (symbol-package symbol))
462  (im (gethash name imported))
463  (in (gethash name inherited)))
464  (cond
465  ((or (null status)
466  (and status (eq symbol existing))
467  (and in (eq sp (first in))))
468  (ensure-inherited name symbol to-package from-package t shadowed imported inherited))
469  (in
470  (remhash name inherited)
471  (ensure-shadowing-import name to-package (second in) shadowed imported))
472  (im
473  (error "Symbol ~S import from ~S~:[~; actually ~:[uninterned~;~:*from ~S~]~] conflicts with existing symbol in ~S~:[~; actually ~:[uninterned~;from ~:*~S~]~]"
474  name (package-name from-package)
475  (home-package-p symbol from-package) (symbol-package-name symbol)
476  (package-name to-package)
477  (home-package-p existing to-package) (symbol-package-name existing)))
478  (t
479  (ensure-inherited name symbol to-package from-package t shadowed imported inherited)))))))
480 
481  (defun recycle-symbol (name recycle exported)
482  ;; Takes a symbol NAME (a string), a list of package designators for RECYCLE
483  ;; packages, and a hash-table of names (strings) of symbols scheduled to be
484  ;; EXPORTED from the package being defined. It returns two values, the
485  ;; symbol found (if any, or else NIL), and a boolean flag indicating whether
486  ;; a symbol was found. The caller (DEFPKG) will then do the
487  ;; re-homing of the symbol, etc.
488  (check-type name string)
489  (check-type recycle list)
490  (check-type exported hash-table)
491  (when (gethash name exported) ;; don't bother recycling private symbols
492  (let (recycled foundp)
493  (dolist (r recycle (values recycled foundp))
494  (multiple-value-bind (symbol status) (find-symbol name r)
495  (when (and status (home-package-p symbol r))
496  (cond
497  (foundp
498  ;; (nuke-symbol symbol)) -- even simple variable names like O or C will do that.
499  (note-package-fishiness :recycled-duplicate name (package-name foundp) (package-name r)))
500  (t
501  (setf recycled symbol foundp r)))))))))
502  (defun symbol-recycled-p (sym recycle)
503  (check-type sym symbol)
504  (check-type recycle list)
505  (and (member (symbol-package sym) recycle) t))
506  (defun ensure-symbol (name package intern recycle shadowed imported inherited exported)
507  (check-type name string)
508  (check-type package package)
509  (check-type intern (member nil t)) ; no cl:boolean on Genera
510  (check-type shadowed hash-table)
511  (check-type imported hash-table)
512  (check-type inherited hash-table)
513  (unless (or (gethash name shadowed)
514  (gethash name imported)
515  (gethash name inherited))
516  (multiple-value-bind (existing status)
517  (find-symbol name package)
518  (multiple-value-bind (recycled previous) (recycle-symbol name recycle exported)
519  (cond
520  ((and status (eq existing recycled) (eq previous package)))
521  (previous
522  (rehome-symbol recycled package))
523  ((and status (eq package (symbol-package existing))))
524  (t
525  (when status
526  (note-package-fishiness
527  :ensure-symbol name
528  (reify-package (symbol-package existing) package)
529  status intern)
530  (unintern existing))
531  (when intern
532  (intern* name package))))))))
533  (declaim (ftype (function (t t t &optional t) t) ensure-exported))
534  (defun ensure-exported-to-user (name symbol to-package &optional recycle)
535  (check-type name string)
536  (check-type symbol symbol)
537  (check-type to-package package)
538  (check-type recycle list)
539  (assert (equal name (symbol-name symbol)))
540  (multiple-value-bind (existing status) (find-symbol name to-package)
541  (unless (and status (eq symbol existing))
542  (let ((accessible
543  (or (null status)
544  (let ((shadowing (symbol-shadowing-p existing to-package))
545  (recycled (symbol-recycled-p existing recycle)))
546  (unless (and shadowing (not recycled))
547  (note-package-fishiness
548  :ensure-export name (symbol-package-name symbol)
549  (package-name to-package)
550  (or (home-package-p existing to-package) (symbol-package-name existing))
551  status shadowing)
552  (if (or (eq status :inherited) shadowing)
553  (shadowing-import* symbol to-package)
554  (unintern existing to-package))
555  t)))))
556  (when (and accessible (eq status :external))
557  (ensure-exported name symbol to-package recycle))))))
558  (defun ensure-exported (name symbol from-package &optional recycle)
559  (dolist (to-package (package-used-by-list from-package))
560  (ensure-exported-to-user name symbol to-package recycle))
561  (unless (eq from-package (symbol-package symbol))
562  (ensure-imported symbol from-package))
563  (export* name from-package))
564  (defun ensure-export (name from-package &optional recycle)
565  (multiple-value-bind (symbol status) (find-symbol* name from-package)
566  (unless (eq status :external)
567  (ensure-exported name symbol from-package recycle))))
568 
569  (defun ensure-package (name &key
570  nicknames documentation use
571  shadow shadowing-import-from
572  import-from export intern
573  recycle mix reexport
574  unintern)
575  #+genera (declare (ignore documentation))
576  (let* ((package-name (string name))
577  (nicknames (mapcar #'string nicknames))
578  (names (cons package-name nicknames))
579  (previous (packages-from-names names))
580  (discarded (cdr previous))
581  (to-delete ())
582  (package (or (first previous) (make-package package-name :nicknames nicknames)))
583  (recycle (packages-from-names recycle))
584  (use (mapcar 'find-package* use))
585  (mix (mapcar 'find-package* mix))
586  (reexport (mapcar 'find-package* reexport))
587  (shadow (mapcar 'string shadow))
588  (export (mapcar 'string export))
589  (intern (mapcar 'string intern))
590  (unintern (mapcar 'string unintern))
591  (shadowed (make-hash-table :test 'equal)) ; string to bool
592  (imported (make-hash-table :test 'equal)) ; string to bool
593  (exported (make-hash-table :test 'equal)) ; string to bool
594  ;; string to list home package and use package:
595  (inherited (make-hash-table :test 'equal)))
596  (when-package-fishiness (record-fishy package-name))
597  #-genera
598  (when documentation (setf (documentation package t) documentation))
599  (loop :for p :in (set-difference (package-use-list package) (append mix use))
600  :do (note-package-fishiness :over-use name (package-names p))
601  (unuse-package p package))
602  (loop :for p :in discarded
603  :for n = (remove-if #'(lambda (x) (member x names :test 'equal))
604  (package-names p))
605  :do (note-package-fishiness :nickname name (package-names p))
606  (cond (n (rename-package p (first n) (rest n)))
607  (t (rename-package-away p)
608  (push p to-delete))))
609  (rename-package package package-name nicknames)
610  (dolist (name unintern)
611  (multiple-value-bind (existing status) (find-symbol name package)
612  (when status
613  (unless (eq status :inherited)
614  (note-package-fishiness
615  :unintern (package-name package) name (symbol-package-name existing) status)
616  (unintern* name package nil)))))
617  (dolist (name export)
618  (setf (gethash name exported) t))
619  (dolist (p reexport)
620  (do-external-symbols (sym p)
621  (setf (gethash (string sym) exported) t)))
622  (do-external-symbols (sym package)
623  (let ((name (symbol-name sym)))
624  (unless (gethash name exported)
625  (note-package-fishiness
626  :over-export (package-name package) name
627  (or (home-package-p sym package) (symbol-package-name sym)))
628  (unexport sym package))))
629  (dolist (name shadow)
630  (setf (gethash name shadowed) t)
631  (multiple-value-bind (existing status) (find-symbol name package)
632  (multiple-value-bind (recycled previous) (recycle-symbol name recycle exported)
633  (let ((shadowing (and status (symbol-shadowing-p existing package))))
634  (cond
635  ((eq previous package))
636  (previous
637  (rehome-symbol recycled package))
638  ((or (member status '(nil :inherited))
639  (home-package-p existing package)))
640  (t
641  (let ((dummy (make-symbol name)))
642  (note-package-fishiness
643  :shadow-imported (package-name package) name
644  (symbol-package-name existing) status shadowing)
645  (shadowing-import* dummy package)
646  (import* dummy package)))))))
647  (shadow* name package))
648  (loop :for (p . syms) :in shadowing-import-from
649  :for pp = (find-package* p) :do
650  (dolist (sym syms) (ensure-shadowing-import (string sym) package pp shadowed imported)))
651  (loop :for p :in mix
652  :for pp = (find-package* p) :do
653  (do-external-symbols (sym pp) (ensure-mix (symbol-name sym) sym package pp shadowed imported inherited)))
654  (loop :for (p . syms) :in import-from
655  :for pp = (find-package p) :do
656  (dolist (sym syms) (ensure-import (symbol-name sym) package pp shadowed imported)))
657  (dolist (p (append use mix))
658  (do-external-symbols (sym p) (ensure-inherited (string sym) sym package p nil shadowed imported inherited))
659  (use-package p package))
660  (loop :for name :being :the :hash-keys :of exported :do
661  (ensure-symbol name package t recycle shadowed imported inherited exported)
662  (ensure-export name package recycle))
663  (dolist (name intern)
664  (ensure-symbol name package t recycle shadowed imported inherited exported))
665  (do-symbols (sym package)
666  (ensure-symbol (symbol-name sym) package nil recycle shadowed imported inherited exported))
667  (map () 'delete-package* to-delete)
668  package)))
669 
670 (eval-when (:load-toplevel :compile-toplevel :execute)
671  (defun parse-defpkg-form (package clauses)
672  (loop
673  :with use-p = nil :with recycle-p = nil
674  :with documentation = nil
675  :for (kw . args) :in clauses
676  :when (eq kw :nicknames) :append args :into nicknames :else
677  :when (eq kw :documentation)
678  :do (cond
679  (documentation (error "defpkg: can't define documentation twice"))
680  ((or (atom args) (cdr args)) (error "defpkg: bad documentation"))
681  (t (setf documentation (car args)))) :else
682  :when (eq kw :use) :append args :into use :and :do (setf use-p t) :else
683  :when (eq kw :shadow) :append args :into shadow :else
684  :when (eq kw :shadowing-import-from) :collect args :into shadowing-import-from :else
685  :when (eq kw :import-from) :collect args :into import-from :else
686  :when (eq kw :export) :append args :into export :else
687  :when (eq kw :intern) :append args :into intern :else
688  :when (eq kw :recycle) :append args :into recycle :and :do (setf recycle-p t) :else
689  :when (eq kw :mix) :append args :into mix :else
690  :when (eq kw :reexport) :append args :into reexport :else
691  :when (eq kw :use-reexport) :append args :into use :and :append args :into reexport
692  :and :do (setf use-p t) :else
693  :when (eq kw :mix-reexport) :append args :into mix :and :append args :into reexport
694  :and :do (setf use-p t) :else
695  :when (eq kw :unintern) :append args :into unintern :else
696  :do (error "unrecognized defpkg keyword ~S" kw)
697  :finally (return `(,package
698  :nicknames ,nicknames :documentation ,documentation
699  :use ,(if use-p use '(:common-lisp))
700  :shadow ,shadow :shadowing-import-from ,shadowing-import-from
701  :import-from ,import-from :export ,export :intern ,intern
702  :recycle ,(if recycle-p recycle (cons package nicknames))
703  :mix ,mix :reexport ,reexport :unintern ,unintern)))))
704 
705 (defmacro defpkg (package &rest clauses)
706  "Richard's Robust DEFPACKAGE macro. Based on UIOP:DEFINE-PACKAGE ymmv.
707 
708 DEFPKG takes a PACKAGE and a number of CLAUSES, of the form (KEYWORD . ARGS).
709 
710 DEFPKG supports the following keywords:
711 USE, SHADOW, SHADOWING-IMPORT-FROM, IMPORT-FROM, EXPORT, INTERN -- as per CL:DEFPACKAGE.
712 
713 DEFPKG also redefines the following extensions:
714 RECYCLE, MIX, REEXPORT, UNINTERN -- as per UIOP/PACKAGE:DEFINE-PACKAGE
715 
716 REEXPORT -- Takes a list of package designators. For each package in
717 the list, export symbols with the same name as those exported from
718 that package. In the case of shadowing, etc. They may not be EQL."
719  (let ((ensure-form
720  `(apply 'ensure-package ',(parse-defpkg-form package clauses))))
721  `(progn
722  #+(or clasp ecl gcl mkcl) (defpackage ,package (:use))
723  (eval-when (:compile-toplevel :load-toplevel :execute)
724  ,ensure-form))))
725 
726 ;; This macro is courtesy of Paul Werkowski. A very nice idea. (From LISA)
727 
728 (defmacro define-lisp-package (pkg-name)
729  (flet ((externals-of (pkg)
730  (loop for s being each external-symbol in pkg collect s)))
731  (let* ((pkg-externs (externals-of pkg-name))
732  (pkg-shadows (intersection (package-shadowing-symbols pkg-name)
733  pkg-externs))
734  (cl-externs (externals-of "COMMON-LISP")))
735  `(defpackage ,(sb-int:symbolicate pkg-name "-LISP")
736  (:use "COMMON-LISP")
737  (:shadowing-import-from ,pkg-name ,@pkg-shadows)
738  (:import-from ,pkg-name ,@(set-difference pkg-externs pkg-shadows))
739  (:export ,@cl-externs)
740  (:export ,@pkg-externs)))))