HTML generation

Please note that the latest version of this program is available via Tim Bradshaw's Lisp hacks page at http://www.tfeb.org/lisp/hax.html

Lisp's syntax can be used to express the structure of SGML/XML-based documents very concisely:

<doc>
  <heading>Foo</heading>
  <para>this is a paragraph</para>
</doc>

Becomes

(:doc
 (:heading "Foo")
 (:para "This is a paragraph"))
  

Because Lisp is also a programming language it is possible to mingle Lisp code and SGML/XML markup in Lisp syntax to programmatically generate documents.

In order to do this `right' for XML you need to deal with issues of Unicode, case, namespaces and so on. But almost all documents don't need to worry about this complexity. This code provides a simple way to mix HTML markup with Lisp code. It does not enforce anything except the matching of tags, so it knows nothing about document types or anything like that. In fact the only thing you need to tell it is which elements have empty content models, so it generates <br> rather than <br></br>.

The top-level macro is with-html-output.

Here is an example (which is also included with the code) of Lisp source which calls this macro, and following it the generated HTML (edited slightly so it can be spliced into this document).

(defun count-numbers (n w &optional (s *standard-output*))
  (with-html-output (s)
    (:html
     (:head (:title 
	     (fmt "Numbers from zero below ~R" n)))
     (:body
      (:h1  (fmt "Numbers from zero below ~R" n))
      ;; Forms beginning with non-keyword symbols are code to be evaluated.
      (lfd)
      (:p "Table border width "
	  (princ w s))
      ;; isolated keywords are empty tags.
      :br
      (lfd)
      ;; empty tags with attributes need this slightly crufty syntax, 
      ;; and also need to be defined as empty. 
      ((:hr :noshade))
      (:center
       ;; the values of attributes are evaluated (in fact the whole 
       ;; attribute list is, but attribute names are keywords).
       ((:table :border w
		:width "90%")
	(:tbody				;html 4, bah.
	 (:tr
	  ((:th :align :left) "English")
	  ((:th :align :right) "Arabic")
	  ((:th :align :right) "Roman"))
	 ;; you can leap into Lisp...
	 (dotimes (i n)
	   (let ((c (if (evenp i) "blue" "white")))
	     ;; ... and then back into HTML: the local HTML macro is shorthand
	     ;; for WITH-HTML-OUTPUT to the same stream.
	     (htm
	      ((:tr :bgcolor c)
	       ((:td :align :left)
		(fmt "~R" i))
	       ((:td :align :right)
		(fmt "~D" i))
	       ((:td :align :right)
		(if (zerop i)
		    (fmt "")
		    (fmt "~:@R" i))))
	      (lfd)))))))
       ((:hr :noshade))))))

Calling (count-numbers 10 0) yields HTML which is rendered as follows:

Numbers from zero below ten

Table border width 0



EnglishArabicRoman
zero0
one1I
two2II
three3III
four4IIII
five5V
six6VI
seven7VII
eight8VIII
nine9VIIII

Contact information

Copyright htout.lisp is copyright 1999-2000 by me, Tim Bradshaw, and may be used for any purpose whatsoever by anyone. It has no warranty whatsoever. I would appreciate acknowledgement if you use it in anger, and I would also very much appreciate any feedback or bug fixes.
Please mail me with any bugs or comments.

Author: Tim Bradshaw(tfb@tfeb.org)
Copyright 1999–2003 Tim Bradshaw