Re: easily embedding html into Lisp

From: Pascal Bourguignon (spam_at_mouse-potato.com)
Date: 09/15/04


Date: 15 Sep 2004 11:53:59 +0000

rpw3@rpw3.org (Rob Warnock) writes:

> Andreas Thiele <nospam6329@nospam.com> wrote:
> +---------------
> | I'd like to introduce a simple approach of embedding html into Lisp.
> +---------------
>
> You should look at <http://www.cliki.net/Lisp%20Markup%20Languages>,
> 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 <http://www.cliki.net/HTML-from-sexpr>.
>
> +---------------
> | 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"))))
> [...]
> +---------------
> | 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].

 
> [...]
> (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)))
> (lfd)
> ((: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 ""))
> "&nbsp;"
> (escape-string v))))
> (htm ((:td :nowrap) vv) (lfd))))))))
> (lfd))))
> [...]
> +---------------
> | When talking about html I must mention Kevin Rosenberg's LML package
> | at http://lml.b9.com
> +---------------
>
> 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. ;-}

My own variation is as follow.

I translated the DTD of HTML 4.01 as:

(DEFELEMENT A () "anchor")
(DEFELEMENT ABBR () "abbreviated form (e.g., WWW, HTTP, etc.)")
(DEFELEMENT ACRONYM ())
(DEFELEMENT ADDRESS () "information on author")
(DEFELEMENT APPLET (:DEPRECATED :LOOSE-DTD) "Java applet")
(DEFELEMENT AREA (:END-FORBIDDEN :EMPTY) "client-side image map area")
(DEFELEMENT B () "bold text style")
(DEFELEMENT BASE (:END-FORBIDDEN :EMPTY) "document base URI")
;; ...

(DEFATTRIBUTE ABBR
  (TD TH)
  (%TEXT) :IMPLIED
  () "abbreviation for header cell"
  );;ABBR

(DEFATTRIBUTE ACCEPT-CHARSET
  (FORM)
  (%CHARSETS) :IMPLIED
  () "list of supported charsets"
  );;ACCEPT-CHARSET

(DEFATTRIBUTE ACCEPT
  (FORM INPUT)
  (%CONTENTTYPES) :IMPLIED
  () "list of MIME types for file upload"
  );;ACCEPT
;; ...

Then I define DEFELEMENT and DEFATTRIBUTE as macros doing what I need
in various cases. I've got a package that define them to generate HTML
as Andreas (attribute validation is not implemented yet, DEFELEMENT
generates a macro named for the tag instead of a function as in
Andreas' solution. (There's only one shadow: MAP, between HTML4.01
tags and COMMON-LISP). The advantage of using macros for tags is that
you can embed a lisp body non functionally:

    (table (:width "80%")
       (dolist (row rows)
          (when (odd (length row))
            (tr ()
              (dolist (item row)
                 (td () (p () (insert-pcdata "Item: ~A" item))))))))
    
In HTOUT it would be written almost the same.

Andreas would have to evaluate all arguments and build lists:

   (table :cellpadding 0 :cellspacing 5
     (mapcan (lambda (row)
               (when (odd (length row))
                 (list (tr (mapcan (lambda (item)
                                    (list (td item))) row))))) rows))

In another package, I defined DEFELEMENT and DEFATTRIBUTE otherwise to
generate an HTML parser. Since the knowledge of the tags and
attribute is embedded inside WITH-HTML-OUTPUT, I feel it's a solution
that's less declarative, ie. lower level than mine and less useful. 

Also, since HTOUT binds locally an output stream, and doesn't take it
as argument to the non function keyword tags, I don't see how you
could split the description of a page over serveral functions. On the
other hand, for now I have to use a global context to generate HTML,
but this allow be to define methods to insert items on pages such as
headers, feets, forms, etc. This could be easily done with Andreas'
solution. Would HTM work outside of the lexical context of
WITH-HTML-OUTPUT?

-- 
__Pascal Bourguignon__                     http://www.informatimago.com/
Our enemies are innovative and resourceful, and so are we. They never
stop thinking about new ways to harm our country and our people, and
neither do we.


Relevant Pages

  • Re: using this IF to forward to page?
    ... includes your <html> tag, ... the web server sends the headers. ... html tags. ... group box lists, like a list where you can drop down and pick your ...
    (comp.lang.php)
  • Re: Keyword abuse (Re: reduced size symbols/keywords)
    ... because I want to use XHTML and you use HTML), there is no way to keep ... With symbols from my own package, ... agnostic about what tags can exist in an HTML/XML document, ... some declarations, at least. ...
    (comp.lang.lisp)
  • Re: Keyword abuse (Re: reduced size symbols/keywords)
    ... packages and it is important that the refer to the same symbol. ... If something unrelated to HTML uses the key like:body no harm is done. ... package they are not the same symbol. ... agnostic about what tags can exist in an HTML/XML document, ...
    (comp.lang.lisp)
  • Re: word webpages
    ... The ther are som tags with no closing tags DreamWeaver would remove what ever was causing these problems. ... Just create a simple document and save as HTML Make sure it has some type of formatting. ... XML all versions ...
    (microsoft.public.mac.office.word)
  • Re: macro and cl-who help
    ... Lisp, but... ... you back into the "walking forms as HTML data" mode, ... This would have been extensible with user-defined tags, ... HTML tags are macros can be functions: ...
    (comp.lang.lisp)