From: Numeromancer
Subject: Re: (defX-export ...
Date: 
Message-ID: <A0fGj.2550$p24.1205@nlpi061.nbdc.sbc.com>
D Herring wrote:
> Numeromancer wrote:
>> Usually, the last thing I do when making a package is putting together
>> the
>> :export list.  I find this tedious.  Most of the time, I know which
>> symbols I
>> want to export as I am define them.  Defining the
>>
>> (defmacro def(un|var|paramater|const|macro|...)-export (name ...)...)
>> `(progn
>>    (defun ,name ...)
>>    (export ',name))
>>
>> in a utility package seems like a good idea, (sorta like screamer's
>> defX-compile-time) but I have a nagging fear that the appeal of a neat
>> macro is
>> causing me to overlook something.  I know most readers of the code
>> will expect
>> to find all exported symbols in defpackage, but that can be fixed with a
>> comment.  I wanted to know if there were any technical problems which
>> I might
>> encounter.
>>
>> So I ask: are there?  Or should I just tell my nag to shut up?
> 
> Another approach is to write a function that scans source files and
> creates the export list for you:
> 
> ** begin test.lisp **
> :export
> (defun collect-exports (filename)
>   (let ((*readtable* (copy-readtable nil)))
>     ;; preserve case
>     (setf (readtable-case *readtable*) :preserve)
>     (with-open-file (file filename)
>       (do ((line (read file nil nil)
>                  (read file nil nil))
>            (flag nil)
>            (list nil))
>           ((not line)
>            (sort list #'string<))
>         (if flag
>             ;; check for def-forms
>             (progn
>               (setf flag nil)
>               (when (listp line)
>                 (let ((key (string-upcase (symbol-name (car line)))))
>                   (when (or (string= key "DEFCONSTANT")
>                             (string= key "DEFUN")
>                             (string= key "DEFMACRO")
>                             (string= key "DEFPARAMETER")
>                             (string= key "DEFVAR"))
>                     (push (cadr line) list)))))
>             ;; check for :export flag
>             (setf flag (unless (listp line)
>                          (string= (string-upcase (symbol-name line))
>                                   "EXPORT"))))))))
> 
> :export
> (defun test (x)
>   (+ x 1))
> 
> (defun test2 (x)
>   (+ x 2))
> 
> ;; test
> ;; (format t "(:export ~{:~A~^ ~})~%" (collect-exports "test.lisp"))
> ** end test.lisp **
> 
> I'd recommend calling COLLECT-EXPORTS manually; but you could go crazy
> and try
> ** begin package.lisp **
> (defpackage :package
>   (:use :cl)
>   #.(read-from-string
>      (format nil
>              "(:export ~{:~A~^ ~})~%"
>              (collect-exports "test.lisp"))))
> ** end package.lisp **
> but don't complain when the paths aren't right or you accidentally cause
> an infinite loop.
> 
> - Daniel
> [Breaking from tradition, the above code was actually lightly tested.]


This is interesting, but I don't think I'd do this.  It looks, uh, dodgy.  And
it doesn't really give you back what you've taken away.

Thanks,

Tim S