src/: Enhance `definst' to allow general BVL syntax.
[sod] / doc / list-exports.lisp
CommitLineData
cf268da2
MW
1(cl:defpackage #:sod-exports
2 (:use #:common-lisp))
3
4(cl:in-package #:sod-exports)
5
097d5a3e
MW
6(defun symbolicate (&rest things)
7 (intern (apply #'concatenate 'string (mapcar #'string things))))
8
9(defun incomprehensible-form (head tail)
10 (format *error-output* ";; incomprehensible: ~S~%" (cons head tail)))
11
12(defgeneric form-list-exports (head tail)
13 (:method (head tail)
14 (declare (ignore head tail))
15 nil))
16
ea28678b 17(defmethod form-list-exports ((head (eql 'cl:export)) tail)
097d5a3e
MW
18 (let ((symbols (car tail)))
19 (if (and (consp symbols)
20 (eq (car symbols) 'quote))
21 (let ((thing (cadr symbols)))
22 (if (atom thing) (list thing) thing))
23 (incomprehensible-form head tail))))
24
ea28678b 25(defmethod form-list-exports ((head (eql 'sod:definst)) tail)
097d5a3e 26 (destructuring-bind (code (streamvar &key export) args &body body) tail
34c51b1c 27 (declare (ignore streamvar body))
097d5a3e 28 (and export
34c51b1c
MW
29 (list* (symbolicate code '-inst)
30 (symbolicate 'make- code '-inst)
167524b5
MW
31 (mapcan (lambda (arg)
32 (let ((sym (if (listp arg) (car arg) arg)))
33 (cond ((char= (char (symbol-name sym) 0) #\&)
34 nil)
35 (t
36 (list (symbolicate 'inst- sym))))))
34c51b1c 37 args)))))
097d5a3e 38
ea28678b 39(defmethod form-list-exports ((head (eql 'sod::define-tagged-type)) tail)
097d5a3e
MW
40 (destructuring-bind (kind what) tail
41 (declare (ignore what))
42 (list kind
43 (symbolicate 'c- kind '-type)
44 (symbolicate 'make- kind '-type))))
45
e43d3532
MW
46(defmethod form-list-exports ((head (eql 'sod:defctype)) tail)
47 (destructuring-bind (names value &key export) tail
48 (declare (ignore value))
49 (let ((names (if (listp names) names (list names))))
50 (and export
51 (list* (symbolicate 'c-type- (car names)) names)))))
52
53(defmethod form-list-exports ((head (eql 'sod:define-simple-c-type)) tail)
54 (destructuring-bind (names type &key export) tail
55 (declare (ignore type))
56 (let ((names (if (listp names) names (list names))))
57 (and export
58 (list* (symbolicate 'c-type- (car names)) names)))))
59
ea28678b 60(defmethod form-list-exports ((head (eql 'cl:macrolet)) tail)
097d5a3e
MW
61 (mapcan #'form-exports (cdr tail)))
62
ea28678b 63(defmethod form-list-exports ((head (eql 'cl:eval-when)) tail)
fdc3e506
MW
64 (mapcan #'form-exports (cdr tail)))
65
ea28678b 66(defmethod form-list-exports ((head (eql 'cl:progn)) tail)
097d5a3e
MW
67 (mapcan #'form-exports tail))
68
69(defgeneric form-exports (form)
70 (:method (form) nil)
71 (:method ((form cons)) (form-list-exports (car form) (cdr form))))
72
73(defgeneric list-exports (thing))
74
75(defmethod list-exports ((stream stream))
76 (loop with eof = '#:eof
77 for form = (read stream nil eof)
78 until (eq form eof)
79 when (consp form) nconc (form-exports form)))
80
81(defmethod list-exports ((path pathname))
82 (mapcar (lambda (each)
83 (cons each (with-open-file (stream each) (list-exports stream))))
84 (directory (merge-pathnames path #p"*.lisp"))))
85
86(defmethod list-exports ((path string))
87 (list-exports (pathname path)))
88
89(defun list-exported-symbols (package)
90 (sort (loop for s being the external-symbols of package collect s)
91 #'string< :key #'symbol-name))
92
93(defun find-symbol-homes (paths package)
94 (let* ((symbols (list-exported-symbols package))
ea28678b
MW
95 (exports-alist (let ((*package* package))
96 (mapcan #'list-exports paths)))
097d5a3e
MW
97 (homes (make-hash-table :test #'equal)))
98 (dolist (assoc exports-alist)
99 (let ((home (car assoc)))
100 (dolist (symbol (cdr assoc))
101 (let ((name (symbol-name symbol)))
de8f0794 102 (unless (nth-value 1 (find-symbol name package))
097d5a3e
MW
103 (format *error-output* ";; unexported: ~S~%" symbol))
104 (setf (gethash name homes) home)))))
105 (dolist (symbol symbols)
106 (unless (gethash (symbol-name symbol) homes)
107 (format *error-output* ";; mysterious: ~S~%" symbol)))
108 exports-alist))
109
110(defun boring-setf-expansion-p (symbol)
111 (multiple-value-bind (temps args stores store fetch)
112 (ignore-errors (get-setf-expansion (list symbol)))
113 (declare (ignore temps args stores fetch))
114 (and (consp store)
115 (eq (car store) 'funcall)
116 (consp (cdr store)) (consp (cadr store))
117 (eq (caadr store) 'function)
118 (let ((func (cadadr store)))
119 (and (consp func) (consp (cdr func))
120 (eq (car func) 'setf))))))
121
122(defun specialized-on-p (func arg what)
123 (some (lambda (method)
124 (let ((spec (nth arg (sb-mop:method-specializers method))))
125 (and (typep spec 'sb-mop:eql-specializer)
126 (eql (sb-mop:eql-specializer-object spec) what))))
127 (sb-mop:generic-function-methods func)))
128
129(defun categorize (symbol)
130 (let ((things nil))
131 (when (boundp symbol)
132 (push (if (constantp symbol) :constant :variable) things))
133 (when (fboundp symbol)
134 (push (cond ((macro-function symbol) :macro)
135 ((typep (fdefinition symbol) 'generic-function)
136 :generic)
137 (t :function))
138 things)
139 (when (or ;;(not (boring-setf-expansion-p symbol))
140 (ignore-errors (fdefinition (list 'setf symbol))))
141 (push :setf things)))
142 (when (find-class symbol nil)
143 (push :class things))
144 (when (or (specialized-on-p #'sod:expand-c-type-spec 0 symbol)
145 (specialized-on-p #'sod:expand-c-type-form 0 symbol))
146 (push :c-type things))
147 (when (or (specialized-on-p #'sod-parser:expand-parser-spec 1 symbol)
148 (specialized-on-p #'sod-parser:expand-parser-form 1 symbol))
149 (push :parser things))
61982981
MW
150 (when (get symbol 'optparse::opthandler)
151 (push :opthandler things))
152 (when (get symbol 'optparse::optmacro)
153 (push :optmacro things))
097d5a3e
MW
154 (nreverse things)))
155
156(defun categorize-symbols (paths package)
157 (mapcar (lambda (assoc)
158 (let ((home (car assoc))
8922d110
MW
159 (symbols (delete-duplicates
160 (sort (mapcan (lambda (sym)
161 (multiple-value-bind
162 (symbol foundp)
163 (find-symbol
164 (symbol-name sym)
165 package)
166 (and foundp (list symbol))))
167 (cdr assoc))
168 #'string< :key #'symbol-name))))
097d5a3e
MW
169 (cons home (mapcar (lambda (symbol)
170 (cons symbol (categorize symbol)))
171 symbols))))
172
649798ab 173 (find-symbol-homes paths package)))
097d5a3e
MW
174
175(defun best-package-name (package)
176 (car (sort (cons (package-name package)
177 (copy-list (package-nicknames package)))
178 #'< :key #'length)))
179
180(defvar charbuf-size 0)
181
b9d603a0
MW
182(defun exported-symbol-p (symbol &optional (package (symbol-package symbol)))
183 (and package
184 (multiple-value-bind (sym how)
185 (find-symbol (symbol-name symbol) package)
186 (and (eq sym symbol)
187 (eq how :external)))))
188
097d5a3e 189(defun pretty-symbol-name (symbol package)
b9d603a0
MW
190 (let ((pkg (symbol-package symbol))
191 (exportp (exported-symbol-p symbol)))
097d5a3e 192 (format nil "~(~:[~A:~:[:~;~]~;~2*~]~A~)"
ed006915 193 (and exportp (eq pkg package))
b9d603a0
MW
194 (cond ((keywordp symbol) "")
195 ((eq pkg nil) "#")
196 (t (best-package-name pkg)))
197 (or exportp (null pkg)) (symbol-name symbol))))
097d5a3e
MW
198
199(defun analyse-classes (package)
200 (setf package (find-package package))
201 (let ((classes (mapcan (lambda (symbol)
202 (let ((class (find-class symbol nil)))
203 (and class
204 (typep class '(or standard-class
205 structure-class))
206 (list class))))
207 (list-exported-symbols package)))
208 (subs (make-hash-table)))
209 (let ((done (make-hash-table)))
210 (labels ((walk-up (class)
211 (unless (gethash class done)
212 (dolist (super (sb-mop:class-direct-superclasses class))
213 (push class (gethash super subs))
214 (walk-up super))
215 (setf (gethash class done) t))))
216 (dolist (class classes)
217 (walk-up class))))
218 (labels ((walk-down (this super depth)
219 (format t "~v,0T~A~@[ [~{~A~^ ~}]~]~%"
220 (* 2 depth)
221 (pretty-symbol-name (class-name this) package)
222 (mapcar (lambda (class)
223 (pretty-symbol-name (class-name class)
224 package))
225 (remove super
226 (sb-mop:class-direct-superclasses this))))
7a35400d
MW
227 (dolist (sub (sort (copy-list (gethash this subs))
228 #'string< :key #'class-name))
097d5a3e
MW
229 (walk-down sub this (1+ depth)))))
230 (walk-down (find-class t) nil 0))))
231
b9d603a0
MW
232(defmacro deep-compare ((left right) &body body)
233 (let ((block (gensym "BLOCK-")) (func (gensym "FUNC-"))
234 (l (gensym "LEFT-")) (r (gensym "RIGHT-")))
235 `(macrolet ((focus (expr &body body)
236 `(flet ((,',func (it) ,expr))
237 (let ((,',l (,',func ,',l))
238 (,',r (,',func ,',r)))
239 ,@body)))
240 (update (expr)
241 `(flet ((,',func (it) ,expr))
242 (psetf ,',l (,',func ,',l)
243 ,',r (,',func ,',r))))
244 (compare (expr)
245 `(cond ((let ((left ,',l) (right ,',r)) ,expr)
246 (return-from ,',block t))
247 ((let ((right ,',l) (left ,',r)) ,expr)
248 (return-from ,',block nil))))
249 (typesw (&rest clauses)
250 (labels ((iter (clauses)
251 (if (null clauses)
252 'nil
253 (destructuring-bind (type &rest body)
254 (car clauses)
255 (if (eq type t)
256 `(progn ,@body)
257 `(if (typep ,',l ',type)
258 (if (typep ,',r ',type)
259 (progn ,@body)
260 (return-from ,',block t))
261 (if (typep ,',r ',type)
262 (return-from ,',block nil)
263 ,(iter (cdr clauses)))))))))
264 (iter clauses))))
265 (let ((,l ,left) (,r ,right))
266 (block ,block
267 ,@body)))))
268
269(defun order-specializers (la lb)
270 (deep-compare (la lb)
271 (loop (typesw (null (return nil)))
272 (focus (car it)
273 (typesw (sb-mop:eql-specializer
274 (focus (sb-mop:eql-specializer-object it)
275 (typesw (keyword
276 (compare (string< left right)))
277 (symbol
278 (focus (package-name (symbol-package it))
279 (compare (string< left right)))
280 (compare (string< left right)))
281 (t
282 (focus (with-output-to-string (out)
283 (prin1 it out)
284 (write-char #\nul))
285 (compare (string< left right)))))))
286 (class
287 (focus (class-name it)
288 (focus (package-name (symbol-package it))
289 (compare (string< left right)))
290 (compare (string< left right))))
291 (t
292 (error "unexpected things"))))
293 (update (cdr it)))))
294
a535feed
MW
295(defun analyse-generic-functions (package)
296 (setf package (find-package package))
297 (flet ((function-name-core (name)
298 (etypecase name
299 (symbol name)
300 ((cons (eql setf) t) (cadr name)))))
301 (let ((methods (make-hash-table))
302 (functions (make-hash-table))
303 (externs (make-hash-table)))
304 (dolist (symbol (list-exported-symbols package))
305 (setf (gethash symbol externs) t))
306 (dolist (symbol (list-exported-symbols package))
307 (flet ((dofunc (func)
308 (when (typep func 'generic-function)
309 (setf (gethash func functions) t)
310 (dolist (method (sb-mop:generic-function-methods func))
311 (setf (gethash method methods) t)))))
312 (dofunc (and (fboundp symbol) (fdefinition symbol)))
313 (dofunc (ignore-errors (fdefinition (list 'setf symbol)))))
314 (when (eq (symbol-package symbol) package)
315 (let ((class (find-class symbol nil)))
316 (when class
317 (dolist
318 (func (sb-mop:specializer-direct-generic-functions class))
319 (let ((name (function-name-core
320 (sb-mop:generic-function-name func))))
321 (when (or (not (eq (symbol-package name) package))
322 (gethash name externs))
323 (setf (gethash func functions) t)
324 (dolist (method (sb-mop:specializer-direct-methods class))
325 (setf (gethash method methods) t)))))))))
326 (let ((funclist nil))
327 (maphash (lambda (func value)
328 (declare (ignore value))
329 (push func funclist))
330 functions)
331 (setf funclist (sort funclist
332 (lambda (a b)
333 (let ((core-a (function-name-core a))
334 (core-b (function-name-core b)))
335 (if (eq core-a core-b)
336 (and (atom a) (consp b))
337 (string< core-a core-b))))
338 :key #'sb-mop:generic-function-name))
339 (dolist (function funclist)
340 (let ((name (sb-mop:generic-function-name function)))
341 (etypecase name
342 (symbol
343 (format t "~A~%" (pretty-symbol-name name package)))
344 ((cons (eql setf) t)
345 (format t "(setf ~A)~%"
346 (pretty-symbol-name (cadr name) package)))))
b9d603a0
MW
347 (dolist (method (sort (copy-list
348 (sb-mop:generic-function-methods function))
349 #'order-specializers
350 :key #'sb-mop:method-specializers))
a535feed
MW
351 (when (gethash method methods)
352 (format t "~2T~{~A~^ ~}~%"
353 (mapcar
354 (lambda (spec)
355 (etypecase spec
356 (class
357 (let ((name (class-name spec)))
358 (if (eq name t) "t"
359 (pretty-symbol-name name package))))
360 (sb-mop:eql-specializer
361 (let ((obj (sb-mop:eql-specializer-object spec)))
362 (format nil "(eql ~A)"
363 (if (symbolp obj)
364 (pretty-symbol-name obj package)
365 obj))))))
366 (sb-mop:method-specializers method))))))))))
367
4b8e5c03
MW
368(defun check-slot-names (package)
369 (setf package (find-package package))
370 (let* ((symbols (list-exported-symbols package))
371 (classes (mapcan (lambda (symbol)
372 (when (eq (symbol-package symbol) package)
373 (let ((class (find-class symbol nil)))
374 (and class (list class)))))
375 symbols))
376 (offenders (mapcan
377 (lambda (class)
378 (let* ((slot-names
379 (mapcar #'sb-mop:slot-definition-name
380 (sb-mop:class-direct-slots class)))
b9d603a0 381 (exported (remove-if
4b8e5c03 382 (lambda (sym)
211bfc14
MW
383 (or (not (symbol-package sym))
384 (and (not (exported-symbol-p
385 sym))
386 (eq (symbol-package sym)
387 package))))
4b8e5c03
MW
388 slot-names)))
389 (and exported
390 (list (cons (class-name class)
391 exported)))))
392 classes))
393 (bad-words (remove-duplicates (mapcan (lambda (list)
394 (copy-list (cdr list)))
395 offenders))))
396 (values offenders bad-words)))
397
097d5a3e
MW
398(defun report-symbols (paths package)
399 (setf package (find-package package))
400 (format t "~A~%Package `~(~A~)'~2%"
401 (make-string 77 :initial-element #\-)
402 (package-name package))
b9d603a0
MW
403 (dolist (assoc (sort (categorize-symbols paths package) #'string<
404 :key (lambda (assoc)
405 (file-namestring (car assoc)))))
097d5a3e
MW
406 (when (cdr assoc)
407 (format t "~A~%" (file-namestring (car assoc)))
408 (dolist (def (cdr assoc))
409 (let ((sym (car def)))
410 (format t " ~A~@[~48T~{~(~A~)~^ ~}~]~%"
411 (pretty-symbol-name sym package)
412 (cdr def))))
413 (terpri)))
4b8e5c03
MW
414 (multiple-value-bind (alist names) (check-slot-names package)
415 (when names
416 (format t "Leaked slot names: ~{~A~^, ~}~%"
417 (mapcar (lambda (name) (pretty-symbol-name name package))
418 names))
419 (dolist (assoc alist)
420 (format t "~2T~A: ~{~A~^, ~}~%"
421 (pretty-symbol-name (car assoc) package)
422 (mapcar (lambda (name) (pretty-symbol-name name package))
423 (cdr assoc))))
424 (terpri)))
388caffa 425 (format t "Classes:~%")
097d5a3e 426 (analyse-classes package)
a535feed
MW
427 (terpri)
428 (format t "Methods:~%")
429 (analyse-generic-functions package)
097d5a3e
MW
430 (terpri))
431
cf268da2 432(export 'report-project-symbols)
097d5a3e
MW
433(defun report-project-symbols ()
434 (labels ((components (comp)
435 (slot-value comp 'asdf::components))
436 (files (comp)
7a35400d 437 (sort (remove-if-not (lambda (comp)
b9d603a0 438 (typep comp 'asdf:cl-source-file))
7a35400d
MW
439 (components comp))
440 #'string< :key #'asdf:component-name))
097d5a3e
MW
441 (by-name (comp name)
442 (find name (components comp)
443 :test #'string= :key #'asdf:component-name))
444 (file-name (file)
445 (slot-value file 'asdf::absolute-pathname)))
446 (let* ((sod (asdf:find-system "sod"))
447 (parser-files (files (by-name sod "parser")))
448 (utilities (by-name sod "utilities"))
61982981
MW
449 (sod-frontend (asdf:find-system "sod-frontend"))
450 (optparse (by-name sod-frontend "optparse"))
6ac5b807 451 (frontend (by-name sod-frontend "frontend"))
61982981 452 (sod-files (set-difference (files sod) (list utilities))))
097d5a3e 453 (report-symbols (mapcar #'file-name sod-files) "SOD")
6ac5b807 454 (report-symbols (mapcar #'file-name (list frontend)) "SOD-FRONTEND")
097d5a3e 455 (report-symbols (mapcar #'file-name parser-files) "SOD-PARSER")
61982981 456 (report-symbols (mapcar #'file-name (list optparse)) "OPTPARSE")
097d5a3e 457 (report-symbols (mapcar #'file-name (list utilities)) "SOD-UTILITIES"))))
1c1a9bf1
MW
458
459#+interactive
460(with-open-file (*standard-output* #p"doc/SYMBOLS" :direction :output
461 :if-exists :supersede :if-does-not-exist :create)
462 (report-project-symbols))