From: Garbenn
Subject: HTML generating
Date: 
Message-ID: <200211250913.gAP9DAS1059244@cryptofortress.com>
Hello!

I'm working on functions to generate HTML, using Kevin M. Rosenberg's
LML as a base.  (You don't have to know anything about LML to understand
my problem.)  As you'll see I don't necessary need actual code, but
principle, how it should be done (according to different view points?).
I see my mistake as a good chance to learn more about programming and
lisp from you.

I made macros to generate "complex" HTML objects, for example:

,----
| (html-fancy-table :width 400 
|                   :title "foo"
|                   (p "item-1")
|                   (p "item-2")
|                   (p "item-n")
|                   :title "bar"
|                   (p "baz"))
`----

(Note: (p "foo") as a side effect writes <p>foo</p> into a stream.)

This macro generates a HTML table like this (complete source of
HTML-FANCY-TABLE macro is at the very end of this article):

   +---------------------+
   |############### foo #|
   +---------------------+
   | item-1              |
   +---------------------+
   | item-2              |
   +---------------------+
   | item-n              |
   +---------------------+
   |############### bar #|
   +---------------------+
   | baz                 |
   +---------------------+

It works *great* when I use it *instead* of HTML to write my pages by
hand (actually that was my first goal), but I have serious problems if I
try to use this solution as a part of program (and I didn't see back
then, that this implementation will lead to this problem).  I have
runtime generated "item-1", "item-2", etc in a list for instance:

,----
| (let ((items (list "item-1" "item-2")))
|   (html-fancy-table :title "foo"
|                     ??,@items??))     ; wrong!
`----

Or I have separate sets of items, which I want to include conditionally:

,----
| (let ((set-1 (list "1" "2" "3"))
|       (set-2 nil))
|   (html-fancy-table (when set-1       ; obviously !wrong! code
|                       :title "set-1"
|                       ??set-1??)
|                     (when set-2
|                       :title "set-2"
|                       ??set-2??)))
`----

My first thought was that HTML-FANCY-TABLE could be a function (instead
of a macro), then I could do:

,----
| (let [...]
|   (html-fancy-table :take-apart (when set-1
|                                   (list :title "set-1"
|                                         :take-apart (loop for ele in set-1
|                                                       collect 
|                                                       (with-html-to-string
|                                                         (p ele)))))
|                     :take-apart (when set-2 ...)))
`----

The :take-apart operator would make sure that the list following it
[:take-apart] is processed element by element (recursively), this
solution looks and feels a little brain dead.

I would appreciate your comments, thank you in advance.

,---- [ The code of HTML-FANCY-TABLE ]
| (defmacro html-content-table (&rest args)
|   (let ((n (length args))
|         body
|         (width "98%"))
|     (do ((i 0 (1+ i)))
|         ((> i (1- n)))
|       (let ((arg (nth i args))
|             (value (when (< (1+ i) n)
|                      (nth (1+ i) args))))
|         (if (lml-keyword-symbol? arg)
|             (cond
|               ;; :title
|               ((eq arg :title)
|                (push `(html-content-table-title ,value) body)
|                (incf i))
|               ;; :width
|               ((eq arg :width)
|                (setq width value)
|                (incf i)))
|             (push `(tr (td :bgcolor "#FFFFFF" ,arg)) body))))
|     `(html-content-table-2 width ,@(nreverse body))))
| 
| (defmacro html-content-table-2 (width &rest body)
|   `(progn
|     (table :width ,width :cellspacing 1 :cellpadding 3 :align "center"
|      :bgcolor "#000000"
|      ,@body)))
| 
| (defmacro html-content-table-title (title)
|   `(tr
|     (td :align "right" :bgcolor "#EFDADA" :class "p"
|      (b ,title))))
`----

-- 
 ============
// Garbenn //
============
From: John Williams
Subject: Re: HTML generating
Date: 
Message-ID: <874ra6ylzk.fsf@heisenberg.aston.ac.uk>
I think the normal solution is to process the markup in s-expressions
and then use a simple tree walker which processes this. See the html
function in the Araneida web server for an example of this. I enclose
a more general example below which use to call preprocessing functions
associated with html elements (e.g. to generate the images of
equations as a side effect) - recursing the html as required. In this
case the s-exp format used is like '((tag attributes-p-list) content)
e.g. '((table :border 1) (tr (td "title"))(tr (td "data"))) etc.

(defun process-html (things func)
  "This is a preprocessor for markup used in content. It is called at
load time. It walks the structure, and will call func with the arguments
tag attributes symbol. This should  return new markup which will be 
substituted in place and may carry out side effects. 
If the function returns nil the tag and attribues are left unchanged
 and its elements are processed in place"
  (declare (optimize (speed 2) (debug 3)))
  (declare (function func))
  (if (and (consp things) (not (stringp (car things))))
      (let* ((tag (if (consp (car things)) (caar things) (car things)))
	     (attrs (if (consp (car things)) (cdar things) ()))
	     (content (cdr things)))
	(or
	 (funcall func tag attrs content)
	 (cons (if attrs (cons tag attrs) tag)
	       (mapcar #'(lambda(content-item)
			   (process-html content-item func))
		       content))))
    things))

>>>>> "Garbenn" == Garbenn  <··································@[127.1]> writes:

    Garbenn> Hello!  I'm working on functions to generate HTML, using
    Garbenn> Kevin M. Rosenberg's LML as a base.  (You don't have to
    Garbenn> know anything about LML to understand my problem.)  As
    Garbenn> you'll see I don't necessary need actual code, but
    Garbenn> principle, how it should be done (according to different
    Garbenn> view points?).  I see my mistake as a good chance to
    Garbenn> learn more about programming and lisp from you.

    Garbenn> I made macros to generate "complex" HTML objects, for
    Garbenn> example:

    Garbenn> ,---- | (html-fancy-table :width 400 | :title "foo" | (p
    Garbenn> "item-1") | (p "item-2") | (p "item-n") | :title "bar" |
    Garbenn> (p "baz")) `----

    Garbenn> (Note: (p "foo") as a side effect writes <p>foo</p> into
    Garbenn> a stream.)

    Garbenn> This macro generates a HTML table like this (complete
    Garbenn> source of HTML-FANCY-TABLE macro is at the very end of
    Garbenn> this article):

    Garbenn>    +---------------------+ |############### foo #|
    Garbenn> +---------------------+ | item-1 |
    Garbenn> +---------------------+ | item-2 |
    Garbenn> +---------------------+ | item-n |
    Garbenn> +---------------------+ |############### bar #|
    Garbenn> +---------------------+ | baz | +---------------------+

    Garbenn> ....


-- 
John  Williams