Subject: Re: html generation API
From: rpw3@rpw3.org (Rob Warnock)
Date: Sun, 27 Jul 2003 04:28:12 -0500
Newsgroups: comp.lang.lisp
Message-ID: <vO2cndvsvqixBr6iXTWc-g@speakeasy.net>
Eduardo Mu�oz  <emufer@terra.es> wrote:
+---------------
| * Marco Antoniotti <marcoxa@cs.nyu.edu>
| | Does it mean that you are generating the full string in memory before
| | writing it out?
| | Do you have ways to control this behavior?
| 
| htout does it too:
| 
| * (with-html-output (*standard-output*) (:html (:h1 "Hi") (:p "foo"
| "bar" "baz")))
| <HTML><H1>Hi</H1><P>foobarbaz</P></HTML>
| NIL
| * (macroexpand '(with-html-output (*standard-output*) (:html (:h1 "Hi")
| (:p "foo" "bar" "baz"))))
| (LET ((*STANDARD-OUTPUT* *STANDARD-OUTPUT*))
|   (WRITE-SEQUENCE "<HTML><H1>Hi</H1><P>foobarbaz</P></HTML>"
|                   *STANDARD-OUTPUT*)
|   NIL)
+---------------

The problem with HTOUT [which I cheerfully continue to use anyway --
thanks, Tim!] is that it only does this aggregation if *ALL* of the
forms of the entire WITH-HTML-OUTPUT call result in constant strings.
Compare this with Edi Weitz's CL-WHO <URL:http://weitz.de/cl-who/>
[*very* similar to HTOUT], which tries to aggregate runs of constant
strings together [I'm using explicit package names here cuz I normally
have a (use-package :htout) in my init file]:

    > (macroexpand
	'(cl-who:with-html-output (*standard-output* *standard-output*
				   :prologue nil)
	   (:html (:h1 "Hi") (:p "foo" "bar" "baz"))))
    (LET ((*STANDARD-OUTPUT* *STANDARD-OUTPUT*))
      (PROGN
       NIL
       (WRITE-STRING "<html><h1>Hi</h1><p>foobarbaz</p></html>"
		     *STANDARD-OUTPUT*)))
    T
    >

The same as HTOUT so far, yes? [Except for the ":prologue nil" to
suppress the "<!DOCTYPE...>" that CL-WHO wants to put in for you
by default every time. Ugh.] Now let's add just one little STR call
to print a free string variable:

    > (macroexpand
        '(cl-who:with-html-output (*standard-output* *standard-output*
				                     :prologue nil)
	   (:html (:h1 "Hi")
		  (:p "foo" (cl-who:str some-string-var) "bar" "baz"))))
    (LET ((*STANDARD-OUTPUT* *STANDARD-OUTPUT*))
      (PROGN
       NIL
       (WRITE-STRING "<html><h1>Hi</h1><p>foo" *STANDARD-OUTPUT*)
       (PRINC SOME-STRING-VAR *STANDARD-OUTPUT*)
       (WRITE-STRING "barbaz</p></html>" *STANDARD-OUTPUT*)))
    T
    >

Again, exactly what one would expect [or at least hope], yes?

Unfortunately, as noted above HTOUT doesn't do any aggregation at all if
it can't aggregate the entire macro call, and so just that one reference
to STR blows the code size up *way* out of sight [oops!]:

    > (macroexpand
        '(with-html-output (*standard-output*)
	   (:html (:h1 "Hi")
		  (:p "foo" (str some-string-var) "bar" "baz"))))
    (LET ((*STANDARD-OUTPUT* *STANDARD-OUTPUT*))
      (MACROLET ((HTM (&BODY ORG.TFEB.TML::FORMS)
		   `(WITH-HTML-OUTPUT (*STANDARD-OUTPUT* *STANDARD-OUTPUT* NIL)
				      ,@ORG.TFEB.TML::FORMS))
		 (FMT (ORG.TFEB.TML::FORMAT-STRING &REST ORG.TFEB.TML::ARGS)
		   `(FORMAT *STANDARD-OUTPUT*
			    ,ORG.TFEB.TML::FORMAT-STRING
			    ,@ORG.TFEB.TML::ARGS))
		 (LFD (&OPTIONAL (ORG.TFEB.TML::N 1))
		   (IF (= ORG.TFEB.TML::N 1)
		       '(TERPRI *STANDARD-OUTPUT*)
		       `(LOOP ORG.TFEB.TML::REPEAT
			      ,ORG.TFEB.TML::N
			      DO
			      (TERPRI *STANDARD-OUTPUT*))))
		 (ESC (STRING &OPTIONAL MAP)
		   (IF MAP
		       `(WRITE-SEQUENCE (ESCAPE-STRING ,STRING ,MAP)
					*STANDARD-OUTPUT*)
		       `(WRITE-SEQUENCE (ESCAPE-STRING ,STRING)
					*STANDARD-OUTPUT*)))
		 (STR (STRING)
		   `(WRITE-SEQUENCE ,STRING *STANDARD-OUTPUT*)))
	(PROGN
	 (EMIT-TAG ':HTML *STANDARD-OUTPUT* :TYPE :OPEN)
	 (PROGN
	  (EMIT-TAG ':H1 *STANDARD-OUTPUT* :TYPE :OPEN)
	  (WRITE-SEQUENCE "Hi" *STANDARD-OUTPUT*)
	  (EMIT-TAG ':H1 *STANDARD-OUTPUT* :TYPE :CLOSE))
	 (PROGN
	  (EMIT-TAG ':P *STANDARD-OUTPUT* :TYPE :OPEN)
	  (WRITE-SEQUENCE "foo" *STANDARD-OUTPUT*)
	  (STR SOME-STRING-VAR)
	  (WRITE-SEQUENCE "bar" *STANDARD-OUTPUT*)
	  (WRITE-SEQUENCE "baz" *STANDARD-OUTPUT*)
	  (EMIT-TAG ':P *STANDARD-OUTPUT* :TYPE :CLOSE))
	 (EMIT-TAG ':HTML *STANDARD-OUTPUT* :TYPE :CLOSE))))
    T
    >

[The EMIT-TAGs are the generic functions HTOUT uses for the general cases.
All well and good, but it would be nice to do some more constant-string
aggregation around their occurences.]

That said, HTOUT is plenty fast for my current applications
(especially when compiled), so I haven't found a need yet to
convert to CL-WHO. (Yet...)


-Rob

-----
Rob Warnock, PP-ASEL-IA		<rpw3@rpw3.org>
627 26th Avenue			<URL:http://rpw3.org/>
San Mateo, CA 94403		(650)572-2607