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