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