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