Subject: Re: easily embedding html into Lisp
From: (Rob Warnock)
Date: Wed, 15 Sep 2004 06:19:31 -0500
Newsgroups: comp.lang.lisp
Message-ID: <>
Andreas Thiele <> wrote:
| I'd like to introduce a simple approach of embedding html into Lisp.

You should look at <>,
which lists several readily-available packages which already do
this (or similar). I tend to use HTOUT, but CL-WHO is also nice.
Also see <>.

| Let's assume we already have three functions 'table 'tr and 'td.
| Our html code to make a table then might look like
|   (table :cellpadding 0 :cellspacing 5
|     (tr (td "hello") (td "world")))
| producing
|   <table cellpadding="0"
|   cellspacing="5"><tr><td>hello</td><td>world</td></tr></table>

In HTOUT, that would be written this way.

    (with-html-output (s stream)
      ((:table :cellpadding 0 :cellspacing 5)
	(:tr (:td "hello") (:td "world"))))

and generates:

    <TABLE CELLPADDING='0' CELLSPACING='5'><TR><TD>hello</TD><TD>world</TD></TR></TABLE>

| I assume html tags can be described liked
| <tag-name attribute1=value1 ... > content </tag-name>

HTOUT uses keywords for tags, so you don't need to define functions
for all of them. Or, if the first element of a form is itself a list
the CAR of which is a keyword, then the CAAR is taken as the tag and
the CDAR is a property list of "attribute value..." (as shown in the
example above).

| Now let's go one step ahead. For each html tag there has to be such a
| function definition.

Packages such as HTOUT & CL-WHO don't require this, using symbols in
the keyword package as tag markers instead. Any list whose CAR is not
a keyword or whose CAR is not a list starting with a keyword is taken
to be pure Lisp code, which is simply passed through to the compiler.
Also, inside a WITH-HTML-OUTPUT, macrolets are automatically defined
for several common operations, including going back into HTML mode
from Lisp code [note the use of HTM in the following example].

[There is an alternate syntax available which puts the attributes
in a required list after the tag, but I won't go into that here.]

For example, instead of just saying "hello" and "world", suppose you
wanted to write a function that took an entire list of lists, and gave
you an HTML table row per element, with a data item per sub-element.
Furthermore, suppose you want the first element to be the column-head
labels (that is, use <TH> instead of <TD> on the first row). Here's
one version [the LFD calls produce newlines in the HTML, only needed
for human readability]:

    (defun list-html-table (lists &key (stream *standard-output*))
      (let ((column-names (car lists))
	    (rows (cdr lists)))
	(with-html-output (s stream)
	  (:h3 (fmt "Results: ~d rows" (length rows)))
	  ((:table :border 1 :cellspacing 0 :cellpadding 1) ; make compact
	    (:tr (lfd)
	      (loop for name in column-names do
		(htm ((:th :nowrap) name) (lfd))))
	    (loop for row in rows do
	      (htm (:tr (lfd)
		     (loop for v in row do
		       (let ((vv (if (or (null v) (string-equal v ""))
				   (escape-string v))))
			 (htm ((:td :nowrap) vv) (lfd))))))))

So this call:

      '(("Name" "Room" "Phone")
	("Joe" "37A" "2345")
	("Bill" "37B" "5432")
	("Sally" "22C" "1234")))

outputs this:

    <H3>Results: 3 rows</H3>
    <TH NOWRAP>Name</TH>
    <TH NOWRAP>Room</TH>
    <TH NOWRAP>Phone</TH>
    <TD NOWRAP>Joe</TD>
    <TD NOWRAP>37A</TD>
    <TD NOWRAP>2345</TD>
    <TD NOWRAP>Bill</TD>
    <TD NOWRAP>37B</TD>
    <TD NOWRAP>5432</TD>
    <TD NOWRAP>Sally</TD>
    <TD NOWRAP>22C</TD>
    <TD NOWRAP>1234</TD>

| When talking about html I must mention Kevin Rosenberg's LML package
| at

That one is also listed on the above-mentioned CLiki pages, as well as
his LML2 (for generating XHTML documents).

| I am interested in your criticism to my approach.

It certainly works. I used something very similar to it [in Scheme]...
before I found HTOUT.  ;-}


Rob Warnock			<>
627 26th Avenue			<URL:>
San Mateo, CA 94403		(650)572-2607