0.8.9.23
authorRudi Schlatte <rudi@constantly.at>
Tue, 6 Apr 2004 15:17:21 +0000 (15:17 +0000)
committerRudi Schlatte <rudi@constantly.at>
Tue, 6 Apr 2004 15:17:21 +0000 (15:17 +0000)
- Add a documentation string extractor.  Docstrings of exported
  symbols of the packages listed in doc/manual/Makefile can be
  included in the manual like so:

@include macro-sb-ext-define-source-context.texinfo

CREDITS
NEWS
doc/manual/Makefile
doc/manual/docstrings.lisp [new file with mode: 0644]
doc/manual/docstrings.sh [new file with mode: 0644]
doc/manual/intro.texinfo
version.lisp-expr

diff --git a/CREDITS b/CREDITS
index bb87d4b..c923fd7 100644 (file)
--- a/CREDITS
+++ b/CREDITS
@@ -647,6 +647,12 @@ Stig Erik Sandoe:
   He showed how to convince the GNU toolchain to build SBCL in a way
   which supports callbacks from C code into SBCL.
 
+Rudi Schlatte:
+  He ported Paul Foley's simple-streams implementation from cmucl,
+  converted the sbcl manual to Texinfo and wrote a documentation
+  string extractor that keeps function documentation in the manual
+  current.
+
 Nikodemus Siivola:
   He provided build fixes, in particular to tame the SunOS toolchain,
   and has fixed many (stream-related and other) bugs besides.
diff --git a/NEWS b/NEWS
index 6e9cdc2..8de95f7 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -2367,6 +2367,11 @@ changes in sbcl-0.8.10 relative to sbcl-0.8.9:
   * [placeholder for DX summary]
     ** user code with &REST lists declared dynamic-extent, under high
        speed or space and low safety and debug optimization policy.
+  * The manual has been converted to Texinfo format and the debugger
+    chapter from the cmucl manual has been added
+  * A facility has been added to extract documentation strings from
+    sbcl and store them as Texinfo-formatted snippets for inclusion in
+    the manual (via Texinfo's @include directive)
   * bug fix: compiler emitted division in optimized DEREF.  (thanks for
     the test case to Dave Roberts)
   * bug fix: multidimensional simple arrays loaded from FASLs had fill
index f3f5c5a..48c5ec5 100644 (file)
@@ -7,7 +7,10 @@ PSFILE=sbcl.ps
 PDFFILE=sbcl.pdf
 DVIFILE=sbcl.dvi
 INFOFILE=sbcl.info
-HTMLDIR=sbcl
+HTMLDIR=$(basename $(ROOTFILE))
+# The value of DOCSTRINGDIR has to end with a slash or you lose (it's
+# passed to Lisp's `pathname' function).
+DOCSTRINGDIR="docstrings/"
 
 
 ifeq ($(MAKEINFO),)
@@ -30,46 +33,54 @@ dist: html pdf
 
 
 
-
+# html documentation; output in $(HTMLDIR)
 .PHONY: html
 html: html-stamp
 
-html-stamp: $(DOCFILES)
+html-stamp: $(DOCFILES) docstrings
        @rm -rf $(HTMLDIR)
-       $(MAKEINFO) --html $(ROOTFILE)
+       $(MAKEINFO) -I $(DOCSTRINGDIR) --html $(ROOTFILE)
        touch html-stamp
 
-
+# Postscript documentation
 .PHONY: ps
 ps: $(PSFILE)
 
 $(PSFILE): $(DVIFILE)
        dvips -o $@ $<
 
-$(DVIFILE): $(DOCFILES)
-       texi2dvi $(ROOTFILE)
-
+$(DVIFILE): $(DOCFILES) docstrings
+       texi2dvi -I $(DOCSTRINGDIR) $(ROOTFILE)
 
+# PDF documentation
 .PHONY: pdf
 pdf: $(PDFFILE)
 
-$(PDFFILE): $(DOCFILES)
-       texi2pdf $(ROOTFILE)
-
+$(PDFFILE): $(DOCFILES) docstrings
+       texi2pdf -I $(DOCSTRINGDIR) $(ROOTFILE)
 
+# info docfiles
 .PHONY: info
 info: $(INFOFILE)
 
-$(INFOFILE): $(DOCFILES)
-       $(MAKEINFO) $(ROOTFILE)
+$(INFOFILE): $(DOCFILES) docstrings
+       $(MAKEINFO) -I $(DOCSTRINGDIR) $(ROOTFILE)
+
+# Texinfo docstring snippets; output hardcoded in docstrings/ for now.
+.PHONY: docstrings
+docstrings: docstrings-stamp
+
+docstrings-stamp:
+       DOCSTRINGDIR=$(DOCSTRINGDIR) sh docstrings.sh
+       touch docstrings-stamp
 
 
 
 .PHONY: clean
 clean: 
        rm -f *~ *.bak *.orig \#*\# .\#* texput.log
-       rm -rf $(HTMLDIR)
-       rm -f $(PSFILE) $(PDFFILE) $(DVIFILE) html-stamp
+       rm -rf $(HTMLDIR) $(DOCSTRINGDIR)
+       rm -f $(PSFILE) $(PDFFILE) $(DVIFILE) html-stamp docstrings-stamp
        rm -f $(TMPFILES)
        rm -f sbcl.info sbcl.info-*
 
diff --git a/doc/manual/docstrings.lisp b/doc/manual/docstrings.lisp
new file mode 100644 (file)
index 0000000..44de495
--- /dev/null
@@ -0,0 +1,244 @@
+;;;; -*- lisp -*-
+
+;;;; (c) 2004 Rudi Schlatte <rudi@constantly.at>
+;;;; Use it as you wish, send changes back to me if you like.
+
+#+sbcl
+(eval-when (:compile-toplevel :load-toplevel :execute)
+  (require 'sb-introspect)
+  )
+
+(defparameter *documentation-types*
+  '(compiler-macro
+    function
+    method-combination
+    setf
+    ;;structure  ; also handled by `type'
+    type
+    variable)
+  "A list of symbols accepted as second argument of `documentation'")
+
+;;; Collecting info from package
+
+(defun documentation-for-symbol (symbol)
+  "Collects all doc for a symbol, returns a list of the
+  form (symbol doc-type docstring).  See `*documentation-types*'
+  for the possible values of doc-type."
+  (loop for kind in *documentation-types*
+       for doc = (documentation symbol kind)
+       when doc
+       collect (list symbol kind doc)))
+
+(defun collect-documentation (package)
+  "Collects all documentation for all external symbols of the
+  given package, as well as for the package itself."
+  (let* ((package (find-package package))
+         (package-doc (documentation package t))
+         (result nil))
+    (do-external-symbols (symbol package)
+      (let ((docs (documentation-for-symbol symbol)))
+        (when docs (setf result (nconc docs result)))))
+    (when package-doc
+      (setf result (nconc (list (list (intern (package-name package) :keyword)
+                                      'package package-doc)) result)))))
+
+;;; Helpers for texinfo output
+
+(defvar *texinfo-escaped-chars* "@{}"
+  "Characters that must be escaped with #\@ for Texinfo.")
+
+(defun texinfoify (string-designator)
+  "Return 'string-designator' with characters in
+  *texinfo-escaped-chars* escaped with #\@"
+  (let ((name (string string-designator)))
+    (nstring-downcase
+     (with-output-to-string (s)
+       (loop for char across name
+          when (find char *texinfo-escaped-chars*)
+          do (write-char #\@ s)
+          do (write-char char s))))))
+
+;;; Begin, rest and end of definition.
+
+(defun argument-list (fname)
+  (prog1
+      ;; arglist accessors looked up in slime; FIXME: can we depend on
+      ;; swank instead?  Some of the arglist accessors (not found
+      ;; here) are hairy ...
+      #+clisp (ext:arglist fname)
+      #+sbcl (sb-introspect:function-arglist fname)
+      #+openmcl (ccl:arglist fname)
+      '(arglist not supported in this implementation)
+  ))
+
+(defvar *character-replacements*
+  '((#\* . "star") (#\/ . "slash"))
+  "Characters and their replacement names that `alphanumize'
+  uses.  If the replacements contain any of the chars they're
+  supposed to replace, you deserve to lose.")
+
+(defvar *characters-to-drop* '(#\\ #\` #\')
+  "Characters that should be removed by `alphanumize'.")
+
+
+(defun alphanumize (symbol)
+  "Construct a string without characters like *`' that will
+  f-star-ck up filename handling.  See `*character-replacements*'
+  and `*characters-to-drop*' for customization."
+  (let ((name (remove-if #'(lambda (x) (member x *characters-to-drop*))
+                         (string symbol)))
+        (chars-to-replace (mapcar #'car *character-replacements*)))
+    (flet ((replacement-delimiter (index)
+             (cond ((or (< index 0) (>= index (length name))) "")
+                   ((alphanumericp (char name index)) "-")
+                   (t ""))))
+      (loop for index = (position-if #'(lambda (x) (member x chars-to-replace))
+                                     name)
+         while index
+         do (setf name (concatenate 'string (subseq name 0 index)
+                                    (replacement-delimiter (1- index))
+                                    (cdr (assoc (aref name index)
+                                                *character-replacements*))
+                                    (replacement-delimiter (1+ index))
+                                    (subseq name (1+ index))))))
+    name))
+
+(defun unique-name (symbol kind)
+  (nstring-downcase
+   (format nil "~A-~A-~A"
+           (ecase kind
+             (compiler-macro "compiler-macro")
+             (function (if (macro-function symbol)
+                           "macro"
+                           "fun"))
+             (method-combination "method-combination")
+             (package "package")
+             (setf "setf-expander")
+             (structure "struct")
+             (type (if (find-class symbol)
+                       (if (documentation symbol 'structure) ; cheesy structness check
+                           "struct"
+                           "class")
+                       "type"))
+             (variable (if (constantp symbol)
+                           "constant"
+                           "var")))
+           (package-name (symbol-package symbol))
+           (alphanumize symbol)
+           )))
+
+(defun def-begin (symbol kind)
+  (ecase kind
+    (compiler-macro "@deffn {Compiler Macro}")
+    (function (if (macro-function symbol)
+                  "@defmac"
+                  "@defun"))
+    (method-combination "@deffn {Method Combination}")
+    (package "@deffn Package")
+    (setf "@deffn {Setf Expander}")
+    (structure "@deftp Structure")
+    (type (if (find-class symbol)
+              (if (documentation symbol 'structure) ; cheesy structness check
+                  "@deftp Structure"
+                  "@deftp Class")
+              "@deftp Type"))
+    (variable (if (constantp symbol)
+                  "@defvr Constant"
+                  "@defvar"))))
+
+(defparameter *arglist-keywords*
+  '(&allow-other-keys &aux &body &environment &key &optional &rest &whole))
+
+(defun texinfoify-arglist-part (part)
+  (with-output-to-string (s)
+    (etypecase part
+      (string (prin1 (texinfoify part) s))
+      (number (prin1 part s))
+      (symbol
+       (if (member part *arglist-keywords*)
+           (princ (texinfoify part) s)
+           (format s "@var{~A}" (texinfoify part))))
+      (list
+       (format s "(~{~A~^ ~})" (mapcar #'texinfoify-arglist-part part))))))
+
+(defun def-rest (symbol kind)
+  (case kind
+    (function
+     (format nil "~{~A~^ ~}" (mapcar #'texinfoify-arglist-part
+                                     (argument-list symbol))))))
+
+(defun def-end (symbol kind)
+  (ecase kind
+    (compiler-macro "@end deffn")
+    (function (if (macro-function symbol)
+                  "@end defmac"
+                  "@end defun"))
+    (method-combination "@end deffn")
+    (package "@end deffn")
+    (setf "@end deffn")
+    ;;(structure "@end deftp") ; caught by `type'
+    (type (if (find-class symbol)
+              (if (documentation symbol 'structure) ; cheesy structness check
+                  "@end deftp"
+                  "@end deftp")
+              "@end deftp"))
+    (variable (if (constantp symbol)
+                  "@end defvr"
+                  "@defvar"))))
+
+(defun make-info-file (package &optional filename)
+  "Create a file containing all available documentation for the
+  exported symbols of `package' in Texinfo format.  If `filename'
+  is not supplied, a file \"<packagename>.texinfo\" is generated.
+
+  The definitions can be referenced using Texinfo statements like
+  @ref{<doc-type>_<packagename>_<symbol-name>.texinfo}.  Texinfo
+  syntax-significant characters are escaped in symbol names, but
+  if a docstring contains invalid Texinfo markup, you lose."
+  (let* ((package (find-package package))
+         (filename (or filename (make-pathname
+                                 :name (string-downcase (package-name package))
+                                 :type "texinfo")))
+         (docs (sort (collect-documentation package) #'string< :key #'first)))
+    (with-open-file (out filename :direction :output
+                         :if-does-not-exist :create :if-exists :supersede)
+      (loop for (symbol kind docstring) in docs
+           do (format out "~&@anchor{~A}~%~A ~A~@[ ~A~]~%~A~%~A~%~%"
+                      (unique-name symbol kind)
+                      (def-begin symbol kind)
+                      (texinfoify symbol)
+                      (def-rest symbol kind)
+                      docstring
+                      (def-end symbol kind))))
+    filename))
+
+(defun docstrings-to-texinfo (directory &rest packages)
+  "Create files in `directory' containing Texinfo markup of all
+  docstrings of each exported symbol in `packages'.  `directory'
+  is created if necessary.  If you supply a namestring that
+  doesn't end in a slash, you lose.  The generated files are of
+  the form \"<doc-type>_<packagename>_<symbol-name>.texinfo\" and
+  can be included via @include statements.  Texinfo
+  syntax-significant characters are escaped in symbol names, but
+  if a docstring contains invalid Texinfo markup, you lose."
+  (let ((directory (merge-pathnames (pathname directory))))
+    (ensure-directories-exist directory)
+    (dolist (package packages)
+      (loop
+         with docs = (collect-documentation (find-package package))
+         for (symbol kind docstring) in docs
+         for doc-identifier = (unique-name symbol kind)
+         do (with-open-file (out
+                             (merge-pathnames
+                              (make-pathname :name doc-identifier :type "texinfo")
+                              directory)
+                             :direction :output
+                             :if-does-not-exist :create :if-exists :supersede)
+              (format out "~&@anchor{~A}~%~A ~A~@[ ~A~]~%~A~%~A~%~%"
+                      (unique-name symbol kind)
+                      (def-begin symbol kind)
+                      (texinfoify symbol)
+                      (def-rest symbol kind)
+                      docstring
+                      (def-end symbol kind)))))
+    directory))
diff --git a/doc/manual/docstrings.sh b/doc/manual/docstrings.sh
new file mode 100644 (file)
index 0000000..51a971d
--- /dev/null
@@ -0,0 +1,39 @@
+#!/bin/sh
+
+# Create Texinfo snippets from the documentation of exported symbols.
+
+# This software is part of the SBCL system. See the README file for
+# more information.
+#
+# This software is in the public domain and is provided with
+# absolutely no warranty. See the COPYING and CREDITS files for
+# more information.
+
+# how we invoke SBCL
+#
+# Until sbcl-0.6.12.8, the shell variable SBCL was bound to a relative
+# pathname, but now we take care to bind it to an absolute pathname (still
+# generated relative to `pwd` in the tests/ directory) so that tests
+# can chdir before invoking SBCL and still work.
+
+# We create the documentation from the in-tree sbcl if it is found,
+# else an installed sbcl is used.
+sbclsystem=`pwd`/../../src/runtime/sbcl
+if [ -e $sbclsystem ] 
+then
+SBCL="${1:-$sbclsystem --core `pwd`/../output/sbcl.core}"
+else
+SBCL="${1:-`which sbcl`}"
+fi
+
+# List of package names that documentation will be created for.
+PACKAGES=":SB-EXT"
+
+# Output directory.  This has to end with a slash (it's interpreted by
+# Lisp's `pathname' function) or you lose.
+DOCSTRINGDIR="${DOCSTRINGDIR:-docstrings/}"
+
+
+echo /creating docstring snippets from SBCL=\'$SBCL\' for packages \'$PACKAGES\'
+
+echo "(progn (load \"docstrings.lisp\") (docstrings-to-texinfo \"$DOCSTRINGDIR\" $PACKAGES) (sb-ext:quit))" | $SBCL --noinform --sysinit /dev/null --userinit /dev/null --noprint --disable-debugger
index 10297f6..8a98d73 100644 (file)
@@ -229,6 +229,3 @@ have been deleted or are no longer used in the implementation of SBCL
 itself.
 
 @end itemize
-  
-
-
index 86fcfc8..0915947 100644 (file)
@@ -17,4 +17,4 @@
 ;;; checkins which aren't released. (And occasionally for internal
 ;;; versions, especially for internal versions off the main CVS
 ;;; branch, it gets hairier, e.g. "0.pre7.14.flaky4.13".)
-"0.8.9.22"
+"0.8.9.23"