Monday, 25 February 2008

Update: Generating DocBook output using Common Lisp and SBCL

Referencing yesterday's post.

Since Jason asked for the source code, I figured I would post it in a blog instead of a comment as it is more annoying to write a medium-sized comment.

Be warned, it will only work in SBCL and on Linux :-) You need to install docbook-utils (which is apparently the old way of doing things.) On Ubuntu, apt-get install docbook-utils should do you. Here are the files in my little demo:


This code is a hackjob so please keep the pointing and laughing to a minimum. Especially docbook-tree-to-xml-string. There are so many things wrong with that function!

The way I use it is I start up Slime, edit ebook.lisp and use C-c C-l which reloads the file into the Lisp image.

Enjoy and leave comments if you have any questions.

Generating DocBook output using Common Lisp and SBCL

I've been a bit busy lately with Sohail 3.0 and an unexpected trip to Toronto (still here for a week) so I haven't had much time for much blogging.

Anyway, one of the things I've always wanted to learn was DocBook. Today I had a bit of down time so I downloaded the DocBook crash course. Oh boy. XML... If Java could be a markup language, it would be XML. If only I could get rid of the redundancy. Long story short, I thought of CL-WHO to generate the XML. Normally, CL-WHO is used to generate HTML, but it actually knows very little about HTML. To get CL-WHO to generate XML, all you need to do is set the prologue and mode to XML. To do this, execute the following at the REPL:


(setf (cl-who:html-mode) :xml)
;;; This is printed before anything else
(setf cl-who:*prologue* "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>")

For sample output, evaluate the following at the REPL:

(cl-who:with-html-output-to-string (s nil :indent t :prologue t)
  (:people
    (:person :id "1" (:firstname "Sohail") (:surname "Somani"))
    (:person :id "2" (:firstname "Heinz") (:surname "Ketchup"))))

Which generates:

<?xml version=\"1.0\" encoding=\"UTF-8\" ?>
<people>
  <person id=\'1\'>
    <firstname>
      Sohail
    </firstname>
    <surname>
      Somani
    </surname>
  </person>
  <person id=\'2\'>
    <firstname>
      Heinz
    </firstname>
    <surname>
      Ketchup
    </surname>
  </person>
</people>

The workflow is pretty straighforward:

  1. Write CL-WHO document

  2. Convert document to XML

  3. Write to file

  4. Run through docbook2<target>


Here is a "real example" where by "real" I mean copied from the above mentioned ebook:

(defparameter *book*
  `(:book :lang "en"
          (:bookinfo 
           (:title "Hello, world!")
           (:authorgroup
            (:author (:firstname "Sohail")
                     (:surname "Somani")))
           (:abstract
            (:para (:application "twiddle")
                   "is an application specially designed to do nothing
                   you would ever want"))
           (:keywordset
            (:keyword "twiddle")
            (:keyword "sample application")))
          ,(docbook-part "introduction.lisp")
          (:chapter 
           (:title "Zomg")
           (:sect1
            (:title "Testing")
            (:para
             (:segmentedlist
              (:segtitle "Name")
              (:segtitle "Occupation")
              (:segtitle "Favourite food")
              (:seglistitem
               (:seg "Tux")
               (:seg "Linux Mascot")
               (:seg "Herring")))
             (:ulink :url "http://www.kde.org"
                     "My favourite website"))))))

(docbook2pdf *book*) ;; actually also launches pdf reader :-D

So now, with Emacs and Slime (C-c C-l specifically!) I have a fully programmable DocBook IDE. Not to mention that I can use Lisp. Leave a comment if you are actually interested in the code :-)