diff options
author | Vityok <vityok@example.com> | 2017-12-21 14:29:51 +0200 |
---|---|---|
committer | Vityok <vityok@example.com> | 2017-12-21 14:29:51 +0200 |
commit | bfdd686f9e3769a86c45d192d76ddadbcd520f51 (patch) | |
tree | f359ae2f9632c64c19f68efa56d246c0331c1761 /docs | |
parent | ab4c8ca5244197b02e308406e5a154d2cb938f5a (diff) | |
download | tl-who-bfdd686f9e3769a86c45d192d76ddadbcd520f51.tar.gz tl-who-bfdd686f9e3769a86c45d192d76ddadbcd520f51.tar.bz2 tl-who-bfdd686f9e3769a86c45d192d76ddadbcd520f51.zip |
moved doc/ to docs/
Diffstat (limited to 'docs')
-rw-r--r-- | docs/index.html | 829 |
1 files changed, 829 insertions, 0 deletions
diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 0000000..f64dc10 --- /dev/null +++ b/docs/index.html @@ -0,0 +1,829 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> +<html> + +<head> + <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> + <title>CL-WHO - Yet another Lisp markup language</title> + <style type="text/css"> + pre { padding:5px; background-color:#e0e0e0 } + h3, h4 { text-decoration: underline; } + a { text-decoration: none; padding: 1px 2px 1px 2px; } + a:visited { text-decoration: none; padding: 1px 2px 1px 2px; } + a:hover { text-decoration: none; padding: 1px 1px 1px 1px; border: 1px solid #000000; } + a:focus { text-decoration: none; padding: 1px 2px 1px 2px; border: none; } + a.none { text-decoration: none; padding: 0; } + a.none:visited { text-decoration: none; padding: 0; } + a.none:hover { text-decoration: none; border: none; padding: 0; } + a.none:focus { text-decoration: none; border: none; padding: 0; } + a.noborder { text-decoration: none; padding: 0; } + a.noborder:visited { text-decoration: none; padding: 0; } + a.noborder:hover { text-decoration: none; border: none; padding: 0; } + a.noborder:focus { text-decoration: none; border: none; padding: 0; } + pre.none { padding:5px; background-color:#ffffff } + </style> +</head> + +<body bgcolor=white> + +<h2>CL-WHO - Yet another Lisp markup language</h2> + +<blockquote> +<br> <br><h3><a name=abstract class=none>Abstract</a></h3> + +There are plenty of <a +href="http://www.cliki.net/Lisp%20Markup%20Languages">Lisp Markup +Languages</a> out there - every Lisp programmer seems to write at +least one during his career - and CL-WHO (where <em>WHO</em> means +"with-html-output" for want of a better acronym) is probably +just as good or bad as the next one. They are all more or less similar +in that they provide convenient means to convert S-expressions +intermingled with code into (X)HTML, XML, or whatever but differ with +respect to syntax, implementation, and API. So, if you haven't made a +choice yet, check out the alternatives as well before you begin to use +CL-WHO just because it was the first one you came across. (Was that +repelling enough?) If you're looking for a slightly different approach +you might also want to look at <a +href="http://weitz.de/html-template/">HTML-TEMPLATE</a>. +<p> +I wrote this one in 2002 although at least Tim Bradshaw's <a +href="http://www.cliki.net/htout">htout</a> and <a +href="http://opensource.franz.com/aserve/aserve-dist/doc/htmlgen.html">AllegroServe's +HTML generation facilities</a> by John Foderaro of Franz Inc. were +readily available. Actually, I don't remember why I had to write my +own library - maybe just because it was fun and didn't take very long. The +syntax was obviously inspired by htout although it is slightly +different. +<p> +CL-WHO tries to create efficient code in that it makes constant +strings as long as possible. In other words, the code generated by the +CL-WHO macros will usually be a sequence of <code>WRITE-STRING</code> +forms for constant parts of the output interspersed with arbitrary +code inserted by the user of the macro. CL-WHO will make sure that +there aren't two adjacent <code>WRITE-STRING</code> forms with +constant strings. CL-WHO's output is +either XHTML (default), 'plain' (SGML) HTML or HTML5 (using HTML syntax) — depending on +what you've set <a href="#html-mode"><code>HTML-MODE</code></a> to. +<p> +CL-WHO is intended to be portable and should work with all +conforming Common Lisp implementations. <a +href="#mail">Let us know</a> if you encounter any +problems. +<p> +It comes with a <a +href="http://www.opensource.org/licenses/bsd-license.php">BSD-style +license</a> so you can basically do with it whatever you want. +<p> +CL-WHO is for example used by <a href="http://clutu.com/">clutu</a> and <a href="http://heikestephan.de/">Heike Stephan</a>. + +<p> +<font color=red>Download shortcut:</font> <a href="http://weitz.de/files/cl-who.tar.gz">http://weitz.de/files/cl-who.tar.gz</a>. +</blockquote> + +<br> <br><h3><a class=none name="contents">Contents</a></h3> +<ol> + <li><a href="#example">Example usage</a> + <li><a href="#install">Download and installation</a> + <li><a href="#support">Support</a> + <li><a href="#syntax">Syntax and Semantics</a> + <li><a href="#dictionary">The CL-WHO dictionary</a> + <ol> + <li><a href="#with-html-output"><code>with-html-output</code></a> + <li><a href="#with-html-output-to-string"><code>with-html-output-to-string</code></a> + <li><a href="#*attribute-quote-char*"><code>*attribute-quote-char*</code></a> + <li><a href="#*downcase-tokens-p*"><code>*downcase-tokens-p*</code></a> + <li><a href="#*html-empty-tag-aware-p*"><code>*html-empty-tag-aware-p*</code></a> + <li><a href="#*html-empty-tags*"><code>*html-empty-tags*</code></a> + <li><a href="#*html-no-indent-tags*"><code>*html-no-indent-tags*</code></a> + <li><a href="#*prologue*"><code>*prologue*</code></a> + <li><a href="#esc"><code>esc</code></a> + <li><a href="#fmt"><code>fmt</code></a> + <li><a href="#htm"><code>htm</code></a> + <li><a href="#str"><code>str</code></a> + <li><a href="#html-mode"><code>html-mode</code></a> + <li><a href="#escape-string"><code>escape-string</code></a> + <li><a href="#escape-char"><code>escape-char</code></a> + <li><a href="#*escape-char-p*"><code>*escape-char-p*</code></a> + <li><a href="#escape-string-minimal"><code>escape-string-minimal</code></a> + <li><a href="#escape-string-minimal-plus-quotes"><code>escape-string-minimal-plus-quotes</code></a> + <li><a href="#escape-string-iso-8859-1"><code>escape-string-iso-8859-1</code></a> + <li><a href="#escape-string-all"><code>escape-string-all</code></a> + <li><a href="#escape-char-minimal"><code>escape-char-minimal</code></a> + <li><a href="#escape-char-minimal-plus-quotes"><code>escape-char-minimal-plus-quotes</code></a> + <li><a href="#escape-char-iso-8859-1"><code>escape-char-iso-8859-1</code></a> + <li><a href="#escape-char-all"><code>escape-char-all</code></a> + <li><a href="#conc"><code>conc</code></a> + <li><a href="#convert-tag-to-string-list"><code>convert-tag-to-string-list</code></a> + <li><a href="#convert-attributes"><code>convert-attributes</code></a> + </ol> + <li><a href="#ack">Acknowledgements</a> +</ol> + +<br> <br><h3><a name="example" class=none>Example usage</a></h3> + +Let's assume that <code>*HTTP-STREAM*</code> is the stream your web +application is supposed to write to. Here are some contrived code snippets +together with the Lisp code generated by CL-WHO and the resulting HTML output. + +<table border=0 cellspacing=10 width="100%"> + +<tr> +<td bgcolor="#e0e0e0" valign=top><pre> +(<a class=noborder href="#with-html-output">with-html-output</a> (*http-stream*) + (loop for (link . title) in '(("http://zappa.com/" . "Frank Zappa") + ("http://marcusmiller.com/" . "Marcus Miller") + ("http://www.milesdavis.com/" . "Miles Davis")) + do (<a class=noborder href="#htm">htm</a> (:a :href link + (:b (str title))) + :br))) +</pre></td> + +<td valign=top rowspan=2> +<a href='http://zappa.com/'><b>Frank Zappa</b></a><br /><a href='http://marcusmiller.com/'><b>Marcus Miller</b></a><br /><a href='http://www.milesdavis.com/'><b>Miles Davis</b></a><br /> +</td> +</tr> + +<tr> +<td bgcolor="#e0e0e0" valign=top><pre> +<font color="orange">;; code generated by CL-WHO (simplified)</font> + +(let ((*http-stream* *http-stream*)) + (progn + nil + (loop for (link . title) in '(("http://zappa.com/" . "Frank Zappa") + ("http://marcusmiller.com/" . "Marcus Miller") + ("http://www.milesdavis.com/" . "Miles Davis")) + do (progn + (write-string "<a href='" *http-stream*) + (princ link *http-stream*) + (write-string "'><b>" *http-stream*) + (princ title *http-stream*) + (write-string "</b></a><br />" *http-stream*))))) +</pre></td> +</tr> + +<tr> +<td bgcolor="#e0e0e0" valign=top><pre> +(<a class=noborder href="#with-html-output">with-html-output</a> (*http-stream*) + (:table :border 0 :cellpadding 4 + (loop for i below 25 by 5 + do (<a class=noborder href="#htm">htm</a> + (:tr :align "right" + (loop for j from i below (+ i 5) + do (<a class=noborder href="#htm">htm</a> + (:td :bgcolor (if (oddp j) + "pink" + "green") + (fmt "~@R" (1+ j)))))))))) +</pre></td> + +<td valign=top rowspan=2> +<table border='0' cellpadding='4'><tr align='right'><td bgcolor='green'>I</td><td bgcolor='pink'>II</td><td bgcolor='green'>III</td><td bgcolor='pink'>IV</td><td bgcolor='green'>V</td></tr><tr align='right'><td bgcolor='pink'>VI</td><td bgcolor='green'>VII</td><td bgcolor='pink'>VIII</td><td bgcolor='green'>IX</td><td bgcolor='pink'>X</td></tr><tr align='right'><td bgcolor='green'>XI</td><td bgcolor='pink'>XII</td><td bgcolor='green'>XIII</td><td bgcolor='pink'>XIV</td><td bgcolor='green'>XV</td></tr><tr align='right'><td bgcolor='pink'>XVI</td><td bgcolor='green'>XVII</td><td bgcolor='pink'>XVIII</td><td bgcolor='green'>XIX</td><td bgcolor='pink'>XX</td></tr><tr align='right'><td bgcolor='green'>XXI</td><td bgcolor='pink'>XXII</td><td bgcolor='green'>XXIII</td><td bgcolor='pink'>XXIV</td><td bgcolor='green'>XXV</td></tr></table> +</td> +</tr> + +<tr> +<td bgcolor="#e0e0e0" valign=top><pre> +<font color="orange">;; code generated by CL-WHO (simplified)</font> + +(let ((*http-stream* *http-stream*)) + (progn + nil + (write-string "<table border='0' cellpadding='4'>" *http-stream*) + (loop for i below 25 by 5 + do (progn + (write-string "<tr align='right'>" *http-stream*) + (loop for j from i below (+ i 5) + do (progn + (write-string "<td bgcolor='" *http-stream*) + (princ (if (oddp j) "pink" "green") *http-stream*) + (write-string "'>" *http-stream*) + (format *http-stream* "~@r" (1+ j)) + (write-string "</td>" *http-stream*))) + (write-string "</tr>" *http-stream*))) + (write-string "</table>" *http-stream*))) +</pre></td> +</tr> + +<tr> +<td bgcolor="#e0e0e0" valign=top><pre> +(<a class=noborder href="#with-html-output">with-html-output</a> (*http-stream*) + (:h4 "Look at the character entities generated by this example") + (loop for i from 0 + for string in '("Fête" "Sørensen" "naïve" "Hühner" "Straße") + do (<a class=noborder href="#htm">htm</a> + (:p :style (<a href="#conc">conc</a> "background-color:" (case (mod i 3) + ((0) "red") + ((1) "orange") + ((2) "blue"))) + (<a class=noborder href="#htm">htm</a> (<a href="#esc">esc</a> string)))))) +</pre></td> +<td valign=top rowspan=2> +<h4>Look at the character entities generated by this example</h4><p style='background-color:red'>Fête</p><p style='background-color:orange'>Sørensen</p><p style='background-color:blue'>naïve</p><p style='background-color:red'>Hühner</p><p style='background-color:orange'>Straße</p> +</td> +</tr> + +<tr> +<td bgcolor="#e0e0e0" valign=top><pre> +<font color="orange">;; code generated by CL-WHO (simplified)</font> + +(let ((*http-stream* *http-stream*)) + (progn + nil + (write-string + "<h4>Look at the character entities generated by this example</h4>" + *http-stream*) + (loop for i from 0 for string in '("Fête" "Sørensen" "naïve" "Hühner" "Straße") + do (progn + (write-string "<p style='" *http-stream*) + (princ (<a class=noborder href="#conc">conc</a> "background-color:" + (case (mod i 3) + ((0) "red") + ((1) "orange") + ((2) "blue"))) + *http-stream*) + (write-string "'>" *http-stream*) + (progn (write-string (<a class=noborder href="#escape-string">escape-string</a> string) *http-stream*)) + (write-string "</p>" *http-stream*))))) +</pre></td> +</tr> + + +</table> + +<br> <br><h3><a name="install" class=none>Download and installation</a></h3> + +CL-WHO together with this documentation can be downloaded from <a +href="http://weitz.de/files/cl-who.tar.gz">http://weitz.de/files/cl-who.tar.gz</a>. The +current version is 1.1.3. +<p> +The preferred method to fetch, compile and load CL-WHO is via <a href="http://beta.quicklisp.org/">Quicklisp</a>. Install +Quicklisp, then run +<pre>(ql:quickload :cl-who)</pre> +<p> +The current development version of CL-WHO can be found +at <a href="https://github.com/edicl/cl-who">https://github.com/edicl/cl-who</a>. +This is the one to send <a href="#mail">patches</a> against. Use at +your own risk. +<p> +Luís Oliveira maintains an +unofficial <a href="http://darcs.net/">darcs</a> repository of CL-WHO +at <a href="http://common-lisp.net/~loliveira/ediware/">http://common-lisp.net/~loliveira/ediware/</a>. +<p> +You can run a test suite which tests <em>some</em> (but +not <em>all</em>) aspects of the library with +<pre> +(asdf:oos 'asdf:test-op :cl-who) +</pre> + +<br> <br><h3><a name="mail" class=none>Support and mailing lists</a></h3> + +The development version of cl-who can be +found <a href="https://github.com/edicl/cl-who" target="_new">on +github</a>. Please use the github issue tracking system to submit bug +reports. Patches are welcome, please +use <a href="https://github.com/edicl/cl-who/pulls">GitHub pull +requests</a>. If you want to make a change, +please <a href="http://weitz.de/patches.html" target="_new">read this +first</a>. + +<br> <br><h3><a name="syntax" class=none>Syntax and Semantics</a></h3> + +CL-WHO is essentially just one <a +href="http://cl-cookbook.sourceforge.net/macros.html">macro</a>, <a +href="#with-html-output"><code>WITH-HTML-OUTPUT</code></a>, which +transforms the body of code it encloses into something else obeying the +following rules (which we'll call <em>transformation rules</em>) for the body's forms: + +<ul> + + <li>A string will be printed verbatim. To be +more precise, it is transformed into a form which'll print this +string to the stream the user provides. + +<table border=0 cellpadding=2 cellspacing=3><tr><td><pre>"foo" <font color="red">=></font> (write-string "foo" s)</pre></td></tr></table> + + (Here and for the rest of this document the <em>red arrow</em> means '... will be converted to code equivalent to ...' where <em>equivalent</em> means that all output is sent to the "right" stream.) + + <li>Each list beginning with a <a +href="http://www.lispworks.com/reference/HyperSpec/Body/t_kwd.htm"><em>keyword</em></a> +is transformed into an (X)HTML <b>tag</b> of the same (usually <href="#*downcase-tokens-p*">downcased</a>) name by the following rules: + + <ul> + + <li>If the list contains nothing but the keyword, the resulting tag + will be empty. + + <table border=0 cellpadding=2 cellspacing=3><tr><td><pre>(:br) <font color="red">=></font> (write-string "<br />" s)</pre></td></tr></table> + With <a href="#html-mode"><code>HTML-MODE</code></a> set to <code>:SGML</code> an empty element is written this way: + <table border=0 cellpadding=2 cellspacing=3><tr><td><pre>(:br) <font color="red">=></font> (write-string "<br>" s)</pre></td></tr></table> + + <li>The initial keyword can be followed by another keyword which will be interpreted as the name of an <b>attribute</b>. The next form which will be taken as the attribute's <b>value</b>. (If there's no next form it'll be as if the next form had been <code>NIL</code>.) The form denoting the attribute's value will be treated as follows. (Note that the behaviour with respect to attributes is <em>incompatible</em> with versions earlier than 0.3.0!) + <ul> + <li>If it is a string it will be printed literally. + + <table border=0 cellpadding=2 cellspacing=3><tr><td><pre>(:td :bgcolor "red") <font color="red">=></font> (write-string "<td bgcolor='red' />" s)</pre></td></tr></table> + + <li>If it is <code>T</code> and <a href="#html-mode"><code>HTML-MODE</code></a> is <code>:XML</code> (default) the attribute's value will be the attribute's name (following XHTML convention to denote attributes which don't have a value in HTML). + + <table border=0 cellpadding=2 cellspacing=3><tr><td><pre>(:td :nowrap t) <font color="red">=></font> (write-string "<td nowrap='nowrap' />" s)</pre></td></tr></table> + + With <a href="#html-mode"><code>HTML-MODE</code></a> set to <code>:SGML</code> or <code>:HTML5</code>: + + <table border=0 cellpadding=2 cellspacing=3><tr><td><pre>(:td :nowrap t) <font color="red">=></font> (write-string "<td nowrap>" s)</pre></td></tr></table> + + Attribute minimization is controlled by <a href="#*empty-attribute-syntax*"><code>*EMPTY-ATTRIBUTE-SYNTAX*</code></a><br> + + <li>If it is <code>NIL</code> the attribute will be left out completely. + + <table border=0 cellpadding=2 cellspacing=3><tr><td><pre>(:td :nowrap nil) <font color="red">=></font> (write-string "<td />" s)</pre></td></tr></table> + + <li>If it is a <a + href="http://www.lispworks.com/reference/HyperSpec/Body/26_glo_c.htm#constant_form"><em>constant form</em></a>, the result of evaluating it will be inserted into the resulting string as if printed with the <a + href="http://www.lispworks.com/reference/HyperSpec/Body/26_glo_c.htm#constant_form">format string</a> <code>"~A"</code> at macro expansion time. + + <table border=0 cellpadding=2 cellspacing=3><tr><td><pre>(:table :border 3) <font color="red">=></font> (write-string "<table border='3' />" s)</pre></td></tr></table> + + <li>If it is any other form it will be left as is and later evaluated at run time and printed like with <a + href="http://www.lispworks.com/reference/HyperSpec/Body/f_wr_pr.htm"><code>PRINC</code></a> <em>unless</em> the value is <code>T</code> or <code>NIL</code> which will be treated as above. (It is the application developer's job to provide the correct <a href="http://www.lispworks.com/reference/HyperSpec/Body/26_glo_p.htm#printer_control_variable">printer control variables</a>.) + + <table border=0 cellpadding=2 cellspacing=3><tr><td><pre><font color="orange">;; simplified example, see function CHECKBOX below +;; note that this form is not necessarily CONSTANTP in all Lisps</font> + +(:table :border (+ 1 2)) <font color="red">=></font> (write-string "<table border='" s) + (princ (+ 1 2) s) + (write-string "' />" s)</pre></td></tr></table> + </ul> + + <li>Once an attribute/value pair has been worked up another one can follow, i.e. if the form following an attribute's value is again a keyword it will again be treated as an attribute and so on. + + <table border=0 cellpadding=2 cellspacing=3><tr><td><pre>(:table :border 0 :cellpadding 5 :cellspacing 5) + <font color="red">=></font> (write-string "<table border='0' cellpadding='5' cellspacing='5' />" s)</pre></td></tr></table> + + <li>The first form following either the tag's name itself or an attribute value which is <em>not</em> a keyword determines the beginning of the tag's <b>content</b>. This and all the following forms are subject to the transformation rules we're just describing. + + <table border=0 cellpadding=2 cellspacing=3><tr><td><pre>(:p "Paragraph") <font color="red">=></font> (write-string "<p>Paragraph</p>" s) +(:p :class "foo" "Paragraph") <font color="red">=></font> (write-string "<p class='foo'>Paragraph</p>" s) +(:p :class "foo" "One" " " "long" " " "sentence") <font color="red">=></font> (write-string "<p class='foo'>One long sentence</p>" s) +(:p :class "foo" "Visit " (:a :href "http://www.cliki.net/" "CLiki")) + <font color="red">=></font> (write-string "<p class='foo'>Visit <a href='http://www.cliki.net/'>CLiki</a></p>" s)</pre></td></tr></table> + + <li>Beginning with <a href="#install">version 0.4.0</a> you can also use a syntax like that of <a href="http://opensource.franz.com/xmlutils/xmlutils-dist/phtml.htm">LHTML</a> where the tag and all attribute/value pairs are enclosed in an additional list: + + <table border=0 cellpadding=2 cellspacing=3><tr><td><pre>((:p) "Paragraph") <font color="red">=></font> (write-string "<p>Paragraph</p>" s) +((:p :class "foo") "Paragraph") <font color="red">=></font> (write-string "<p class='foo'>Paragraph</p>" s) +((:p :class "foo" :name "humpty-dumpty") "One" " " "long" " " "sentence") + <font color="red">=></font> (write-string "<p class='foo' name='humpty-dumpty'>One long sentence</p>" s) +((:p :class "foo") "Visit " ((:a :href "http://www.cliki.net/") "CLiki")) + <font color="red">=></font> (write-string "<p class='foo'>Visit <a href='http://www.cliki.net/'>CLiki</a></p>" s)</pre></td></tr></table> + + </ul> + + Here's a slightly more elaborate example: +<pre> +* (defun checkbox (stream name checked &optional value) + (with-html-output (stream) + (:input :type "checkbox" :name name :checked checked :value value))) + +CHECKBOX +* (with-output-to-string (s) (checkbox s "foo" t)) + +"<input type='checkbox' name='foo' checked='checked' />" +* (with-output-to-string (s) (checkbox s "foo" nil)) + +"<input type='checkbox' name='foo' />" +* (with-output-to-string (s) (checkbox s "foo" nil "bar")) + +"<input type='checkbox' name='foo' value='bar' />" +* (with-output-to-string (s) (checkbox s "foo" t "bar")) + +"<input type='checkbox' name='foo' checked='checked' value='bar' />" +</pre> + + <li>A keyword alone will be treated like a list containing only this keyword. + +<table border=0 cellpadding=2 cellspacing=3><tr><td><pre>:hr <font color="red">=></font> (write-string "<hr />" s)</pre></td></tr></table> + + <li>A form which is neither a string nor a keyword nor a list beginning with a keyword will be left as is except for the following <a href="http://www.lispworks.com/documentation/HyperSpec/Body/s_flet_.htm#macrolet">local macros</a>: + <ul> + <li>Forms that look like <code>(<b>str</b> <i>form</i>)</code> will be substituted with + <span style="white-space: nowrap"><code>(let ((result <i>form</i>)) (when result (princ result s)))</code></span>. + +<table border=0 cellpadding=2 cellspacing=3><tr><td><pre>(loop for i below 10 do (str i)) <font color="red">=></font> +(loop for i below 10 do + (let ((#:result i)) + (when #:result (princ #:result *standard-output*))))</pre></td></tr></table> + + <li>Forms that look like <code>(<b>fmt</b> <i>form*</i>)</code> will be substituted with <code>(format s <i>form*</i>)</code>. + +<table border=0 cellpadding=2 cellspacing=3><tr><td><pre>(loop for i below 10 do (fmt "~R" i)) <font color="red">=></font> (loop for i below 10 do (format s "~R" i))</pre></td></tr></table> + <li>Forms that look like <code>(<b>esc</b> <i>form</i>)</code> will be substituted with + <span style="white-space: nowrap"><code>(let ((result <i>form</i>)) (when result (write-string (<a href="#escape-string">escape-string</a> result s))))</code></span>. + + <li>If a form looks like <code>(<b>htm</b> <i>form*</i>)</code> then each of the <code><i>forms</i></code> will be subject to the transformation rules we're just describing, i.e. this is the body is wrapped with another invocation of <a href="#with-html-output"><code>WITH-HTML-OUTPUT</code></a>. + +<table border=0 cellpadding=2 cellspacing=3><tr><td><pre>(loop for i below 100 do (htm (:b "foo") :br)) + <font color="red">=></font> (loop for i below 100 do (progn (write-string "<b>foo</b><br />" s)))</pre></td></tr></table> + + + </ul> + + <li>That's all. Note in particular that CL-WHO knows <em>nothing</em> about HTML or XHTML, i.e. it doesn't check whether you mis-spelled tag names or use attributes which aren't allowed. CL-WHO doesn't care if you use, say, <code>:foobar</code> instead of <code>:hr</code>. +</ul> + +<br> <br><h3><a class=none name="dictionary">The CL-WHO dictionary</a></h3> + +CL-WHO exports the following symbols: + +<p><br>[Macro] +<br><a class=none name="with-html-output"><b>with-html-output</b> <i>(var <tt>&optional</tt> stream <tt>&key</tt> prologue indent) declaration* form*</i> => <i>result*</i></a> + +<blockquote><br> This is the main macro of CL-WHO. It will transform +its body by the transformation rules described +in <a href="#syntax"><em>Syntax and Semantics</em></a> such that the +output generated is sent to the stream denoted +by <code><i>var</i></code> +and <code><i>stream</i></code>. <code><i>var</i></code> must be a +symbol. If <code><i>stream</i></code> is <code>NIL</code> it is +assumed that <code><i>var</i></code> is already bound to a stream, +if <code><i>stream</i></code> is +not <code>NIL</code> <code><i>var</i></code> will be bound to the +form <code><i>stream</i></code> which will be evaluated at run +time. <code><i>prologue</i></code> should be a string +(or <code>NIL</code> for the empty string which is the default) which +is guaranteed to be the first thing sent to the stream from within the +body of this macro. If <code><i>prologue</i></code> is <code>T</code> +the prologue string is the value +of <a href="#*prologue*"><code>*PROLOGUE*</code></a>. +<p> +CL-WHO will usually try not to insert any unnecessary whitespace in +order to save bandwidth. However, if <code><i>indent</i></code> +is <em>true</em> line breaks will be inserted and nested tags will be +indented properly. The value of <code><i>indent</i></code> - if it is +an integer - will be taken as the initial indentation. If it is not an +integer it is assumed to mean <code>0</code>. Value +of <a href="#*html-no-indent-tags*"><code>*HTML-NO-INDENT-TAGS*</code></a> +controls which tag-contents are excempt from indentation: by default +contents of <code>PRE</code> and <code>TEXTAREA</code> tags are not +indented to avoid spurious layout changes. (Note: in certain +situations additional whitespace may change the layout of tables.) +<p> +The <code><i>results</i></code> are the values returned by +the <code><i>forms</i></code>. +<p> +Note that the keyword arguments <code><i>prologue</i></code> +and <code><i>indent</i></code>, and the associated variables are +used <em>at macro expansion time</em>. + +<pre> +* (with-html-output (*standard-output* nil :prologue t) + (:html (:body "Not much there")) + (values)) +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html><body>Not much there</body></html> +* (with-html-output (*standard-output*) + (:html (:body :bgcolor "white" + "Not much there")) + (values)) +<html><body bgcolor='white'>Not much there</body></html> +* (with-html-output (*standard-output* nil :prologue t :indent t) + (:html (:body :bgcolor "white" + "Not much there")) + (values)) +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> + <body bgcolor='white'> + Not much there + </body> +</html> +</pre> +</blockquote> + +<p><br>[Macro] +<br><a class=none name="with-html-output-to-string"><b>with-html-output-to-string</b> <i>(var <tt>&optional</tt> string-form <tt>&key</tt> element-type prologue indent) declaration* form*</i> => <i>result*</i></a> + +<blockquote><br> +This is just a thin wrapper around <a href="#with-html-output"><code>WITH-HTML-OUTPUT</code></a>. Indeed, the wrapper is so thin that the best explanation probably is to show its definition: +<pre> +(defmacro with-html-output-to-string ((var &optional string-form + &key (element-type ''character) + prologue + indent) + &body body) + "Transform the enclosed BODY consisting of HTML as s-expressions +into Lisp code which creates the corresponding HTML as a string." + `(with-output-to-string (,var ,string-form :element-type ,element-type) + (with-html-output (,var nil :prologue ,prologue :indent ,indent) + ,@body))) +</pre> +Note that the <code><i>results</i></code> of this macro are determined by the behaviour of <a href="http://www.lispworks.com/reference/HyperSpec/Body/m_w_out_.htm"><code>WITH-OUTPUT-TO-STRING</code></a>. +</blockquote> + +<p><br>[Special variable] +<br><a class=none name="*attribute-quote-char*"><b>*attribute-quote-char*</b></a> + +<blockquote><br> +This character is used as the quote character when building attributes. Defaults to the single quote <code>#\'</code>. Only other reasonable character is the double quote <code>#\"</code>. +</blockquote> + +<p><br>[Special variable] +<br><a class=none name="*downcase-tokens-p*"><b>*downcase-tokens-p*</b></a> + +<blockquote><br> +If the value of this variable is <code>NIL</code>, keyword symbols representing a tag or attribute name will not be +automatically converted to lowercase. This is useful when one needs to +output case sensitive XML. The default is <code>T</code>. +</blockquote> + +<p><br>[Special variable] +<br><a class=none name="*html-empty-tag-aware-p*"><b>*html-empty-tag-aware-p*</b></a> + +<blockquote><br> +Set this to <code>NIL</code> to if you want to use CL-WHO as a strict XML +generator. Otherwise, CL-WHO will only write empty tags listed in +<a href="#*html-empty-tags*"><code>*HTML-EMPTY-TAGS*</code></a> as <code><tag/></code> (XHTML mode) or <code><tag></code> (SGML mode or HTML mode). For +all other tags, it will always generate <code><tag></tag></code>. The initial value of this variable is <code>T</code>. +</blockquote> + +<p><br>[Special variable] +<br><a class=none name="*html-empty-tags*"><b>*html-empty-tags*</b></a> + +<blockquote><br> +The list of HTML tags that should be output as empty tags. See +<a href="#*html-empty-tag-aware-p*"><code>*HTML-EMPTY-TAG-AWARE-P*</code></a>. +The initial value is the list +<pre> +(:area :atop :audioscope :base :basefont :br :choose :col :command :embed + :frame :hr :img :input :isindex :keygen :left :limittext :link :meta :nextid + :of :over :param :range :right :source :spacer :spot :tab :track :wbr) +</pre> +</blockquote> + +<p><br>[Special variable] +<br><a class=none name="*html-no-indent-tags*"><b>*html-no-indent-tags*</b></a> + +<blockquote><br> +The list of HTML tags that should disable indentation inside them even +when indentation is requested. The initial value is a list containing +only <code>:pre</code> and <code>:texarea</code>. +</blockquote> + +<p><br>[Special variable] +<br><a class=none name="*prologue*"><b>*prologue*</b></a> + +<blockquote><br> +This is the prologue string which will be printed if the <code><i>prologue</i></code> keyword argument to <a href="#with-html-output"><code>WITH-HTML-OUTPUT</code></a> is <code>T</code>. Gets changed when you set <a href="#html-mode"><code>HTML-MODE</code></a>. Its initial value is + +<pre>"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">"</pre> +</blockquote> + +<p><br>[Special variable] + <br><a class=none name="*empty-attribute-syntax*"><b>*empty-attribute-syntax*</b></a> + + <blockquote><br> + This controls the attribute minimization. (also called 'boolean attributes', or 'empty attribute syntax' according to the w3 html standard). Set value to <code>T</code> to enable attribute minimization. + <p>In XHTML attribute minimization is forbidden, and all attributes must have a value. Thus in XHTML boolean attributes must be defined as + <pre> <input disabled='disabled' /></pre> + In HTML5 and SGML HTML boolean attributes can be defined as + <pre><input disabled></pre> + Gets changed when you set <a href="#html-mode"><code>HTML-MODE</code></a>. Its initial value is <code>NIL</code> + </p> + </blockquote> + +<p><br>[Symbol] +<br><a class=none name="esc"><b>esc</b></a> +<br>[Symbol] +<br><a class=none name="fmt"><b>fmt</b></a> +<br>[Symbol] +<br><a class=none name="htm"><b>htm</b></a> +<br>[Symbol] +<br><a class=none name="str"><b>str</b></a> + +<blockquote><br> +These are just symbols with no bindings associated with them. The only reason they are exported is their special meaning during the transformations described in <a href="#syntax"><em>Syntax and Semantics</em></a>. +</blockquote> + +<p><br>[Accessor] +<br><a class=none name="html-mode"><b>html-mode</b></a> <i>=> mode</i> +<br><tt>(setf (</tt><b>html-mode</b>) <i>mode</i><tt>)</tt> +<blockquote><br> +The function <code>HTML-MODE</code> returns the current mode for generating HTML. The default is <code>:XML</code> for XHTML. You can change this by setting it with <code>(SETF (HTML-MODE) :SGML)</code> to pre-XML HTML mode or <code>(SETF (HTML-MODE) :HTML5)</code> to HTML5 mode (using HTML syntax). +<p> +Setting it to SGML HTML sets the <a href="#*prologue*"><code>*prologue*</code></a> to the doctype string for HTML 4.01 transitional: +<pre><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"></pre> +Code generation in HTML5 and SGML HTML is slightly different from XHTML - there's no need to end empty elements with <code>/></code> and empty attributes are allowed. +<p> +Setting it to HTML5 sets the <a href="#*prologue*"><code>*prologue*</code></a> to the following doctype string: +<pre><!DOCTYPE html></pre> +</blockquote> + +<p><br>[Function] +<br><a class=none name="escape-string"><b>escape-string</b></a> <i>string <tt>&key</tt> test</i> => <i>escaped-string</i> + +<blockquote><br> +This function will accept a string <code><i>string</i></code> and will replace every character for which <code><i>test</i></code> returns <em>true</em> with its character entity. The numeric character entities use decimal instead of hexadecimal values when <a href="#html-mode"><code>HTML-MODE</code></a> is set to <code>:SGML</code> because of compatibility reasons with old clients. <code><i>test</i></code> must be a function of one argument which accepts a character and returns a <a href="http://www.lispworks.com/reference/HyperSpec/Body/26_glo_g.htm#generalized_boolean">generalized boolean</a>. The default is the value of <a href="#*escape-char-p*"><code>*ESCAPE-CHAR-P*</code></a>. Note the <a href="#esc"><code>ESC</code></a> shortcut described in <a href="#syntax"><em>Syntax and Semantics</em></a>. + +<pre> +* (escape-string "<Hühner> 'naïve'") +"&lt;H&#xFC;hner&gt; &#x27;na&#xEF;ve&#x27;" +* (with-html-output-to-string (s) + (:b (esc "<Hühner> 'naïve'"))) +"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\"<b>&lt;H&#xFC;hner&gt; &#x27;na&#xEF;ve&#x27;</b>" +</pre> +</blockquote> + +<p><br>[Function] +<br><a class=none name="escape-char"><b>escape-char</b></a> <i>character <tt>&key</tt> test</i> => <i>escaped-string</i> + +<blockquote><br> +This function works identical to <a href="#escape-string"><code>ESCAPE-STRING</code></a>, except that it operates on characters instead of strings. +</blockquote> + +<p><br>[Special variable] +<br><a class=none name="*escape-char-p*"><b>*escape-char-p*</b></a> + +<blockquote><br> +This is the default for the <code><i>test</i></code> keyword argument to <a href="#escape-string"><code>ESCAPE-STRING</code></a> and <a href="#escape-char"><code>ESCAPE-CHAR</code></a>. Its initial value is + +<pre> +#'(lambda (char) + (or (find char "<>&'\"") + (> (char-code char) 127))) +</pre> +</blockquote> + +<p><br>[Function] +<br><a class=none name="escape-string-minimal"><b>escape-string-minimal</b> <i>string</i> => <i>escaped-string</i></a> +<br>[Function] +<br><a class=none name="escape-string-minimal-plus-quotes"><b>escape-string-minimal-plus-quotes</b> <i>string</i> => <i>escaped-string</i></a> +<br>[Function] +<br><a class=none name="escape-string-iso-8859-1"><b>escape-string-iso-8859-1</b> <i>string</i> => <i>escaped-string</i></a> +<br>[Function] +<br><a class=none name="escape-string-all"><b>escape-string-all</b> <i>string</i> => <i>escaped-string</i></a> +<br>[Function] +<br><a class=none name="escape-char-minimal"><b>escape-char-minimal</b> <i>character</i> => <i>escaped-string</i></a> +<br>[Function] +<br><a class=none name="escape-char-minimal-plus-quotes"><b>escape-char-minimal-plus-quotes</b> <i>character</i> => <i>escaped-string</i></a> +<br>[Function] +<br><a class=none name="escape-char-iso-8859-1"><b>escape-char-iso-8859-1</b> <i>character</i> => <i>escaped-string</i></a> +<br>[Function] +<br><a class=none name="escape-char-all"><b>escape-char-all</b> <i>character</i> => <i>escaped-string</i></a> + +<blockquote><br> These are convenience function based +on <a href="#escape-string"><code>ESCAPE-STRING</code></a> +and <a href="#escape-char"><code>ESCAPE-CHAR</code></a>. The string +functions are defined in a way similar to this one: + +<pre> +(defun escape-string-minimal (string) + "Escape only #\<, #\>, and #\& in STRING." + (escape-string string :test #'(lambda (char) (find char "<>&")))) + +(defun escape-string-minimal-plus-quotes (string) + "Like ESCAPE-STRING-MINIMAL but also escapes quotes." + (escape-string string :test #'(lambda (char) (find char "<>&'\"")))) + +(defun escape-string-iso-8859-1 (string) + "Escapes all characters in STRING which aren't defined in ISO-8859-1." + (escape-string string :test #'(lambda (char) + (or (find char "<>&'\"") + (> (char-code char) 255))))) + +(defun escape-string-all (string) + "Escapes all characters in STRING which aren't in the 7-bit ASCII +character set." + (escape-string string :test #'(lambda (char) + (or (find char "<>&'\"") + (> (char-code char) 127))))) +</pre> +The character functions are defined in an analogous manner. +</blockquote> + +<p><br>[Function] +<br><a class=none name="conc"><b>conc</b> <i><tt>&rest</tt> string-list</i> => <i>string</i></a> + +<blockquote><br> +Utility function to concatenate all arguments (which should be strings) into one string. Meant to be used mainly with attribute values. + +<pre> +* (conc "This" " " "is" " " "a" " " "sentence") +"This is a sentence" +* (with-html-output-to-string (s) + (:div :style (conc "padding:" + (format nil "~A" (+ 3 2))) + "Foobar")) +"<div style='padding:5'>Foobar</div>" +</pre> +</blockquote> + +<p><br>[Generic Function] +<br><a class=none name="convert-tag-to-string-list"><b>convert-tag-to-string-list</b></a> <i>tag attr-list body body-fn</i> => <i>strings-or-forms</i> + +<blockquote><br> + +This function exposes some of CL-WHO's internals so users can +customize its behaviour. It is called whenever a tag is processed and +must return a corresponding list of strings or Lisp forms. The idea +is that you can specialize this generic function in order to process +certain tags yourself. +<p> +<code><i>tag</i></code> is a keyword symbol naming the outer tag, +<code><i>attr-list</i></code> is an alist of its attributes (the car +is the attribute's name as a keyword, the cdr is its value), +<code><i>body</i></code> is the tag's body, and +<code><i>body-fn</i></code> is a function which should be applied to +the body to further process it. Of course, if you define your own +methods you can ignore <code><i>body-fn</i></code> if you want. +<p> +Here are some simple examples: +<pre> +* (defmethod convert-tag-to-string-list ((tag (eql :red)) attr-list body body-fn) + (declare (ignore attr-list)) + (nconc (cons "<font color='red'>" (funcall body-fn body)) (list "</font>"))) +; Compiling LAMBDA (PCL::.PV-CELL. PCL::.NEXT-METHOD-CALL. TAG ATTR-LIST BODY BODY-FN): +; Compiling Top-Level Form: + +#<STANDARD-METHOD CONVERT-TAG-TO-STRING-LIST ((EQL :RED) T T T) {582B268D}> +* (with-html-output (*standard-output*) + (:red (:b "Bold and red")) + (values)) +<font color='red'><b>Bold and red</b></font> +* (show-html-expansion (s) + (:red :style "spiffy" (if (foo) (htm "Attributes are ignored")))) + +(LET ((S S)) + (PROGN + NIL + (WRITE-STRING "<font color='red'>" S) + (IF (FOO) (PROGN (WRITE-STRING "Attributes are ignored" S))) + (WRITE-STRING "</font>" S))) +* (defmethod convert-tag-to-string-list ((tag (eql :table)) attr-list body body-fn) + (cond ((cdr (assoc :simple attr-list)) + (nconc (cons "<table" + (<a class=noborder href="#convert-attributes">convert-attributes</a> (remove :simple attr-list :key #'car))) + (list ">") + (loop for row in body + collect "<tr>" + nconc (loop for col in row + collect "<td>" + when (constantp col) + collect (format nil "~A" col) + else + collect col + collect "</td>") + collect "</tr>") + (list "</table>"))) + (t + <font color=orange>;; you could as well invoke CALL-NEXT-METHOD here, of course</font> + (nconc (cons "<table " + (<a class=noborder href="#convert-attributes">convert-attributes</a> attr-list)) + (list ">") + (funcall body-fn body) + (list "</table>"))))) +; Compiling LAMBDA (PCL::.PV-CELL. PCL::.NEXT-METHOD-CALL. TAG ATTR-LIST BODY BODY-FN): +; Compiling Top-Level Form: + +#<STANDARD-METHOD CONVERT-TAG-TO-STRING-LIST ((EQL :TABLE) T T T) {58AFB7CD}> +* (with-html-output (*standard-output*) + (:table :border 0 (:tr (:td "1") (:td "2")) (:tr (:td "3") (:td "4")))) +<table border='0'><tr><td>1</td><td>2</td></tr><tr><td>3</td><td>4</td></tr></table> +"</td></tr></table>" +* (show-html-expansion (s) + (:table :simple t :border 0 + (1 2) (3 (fmt "Result = ~A" (compute-result))))) + +(LET ((S S)) + (PROGN + NIL + (WRITE-STRING + "<table border='0'><tr><td>1</td><td>2</td></tr><tr><td>3</td><td>" + S) + (FORMAT S "Result = ~A" (COMPUTE-RESULT)) + (WRITE-STRING "</td></tr></table>" S))) +</pre> + +</blockquote> + +<p><br>[Function] +<br><a class=none name="convert-attributes"><b>convert-attributes</b></a> <i>attr-list</i> => <i>strings-or-forms</i> + +<blockquote><br> + +This is a helper function which can be called from +<a href="#convert-tag-to-string-list"><code>CONVERT-TAG-TO-STRING-LIST</code></a> to process the list of attributes. + +</blockquote> + +<br> <br><h3><a class=none name="ack">Acknowledgements</a></h3> + +Thanks to Tim Bradshaw and John Foderaro for the inspiration provided +by their libraries mentioned <a href="#abstract">above</a>. Thanks to +Jörg-Cyril Höhle for his suggestions with respect to +attribute values. Thanks to Kevin Rosenberg for the LHTML patch. +Thanks to Stefan Scholl for the 'old school' patch. Thanks to Mac +Chan for several useful additions. + +<p> +$Header: /usr/local/cvsrep/cl-who/doc/index.html,v 1.68 2009/03/09 21:54:11 edi Exp $ +<p><a href="http://weitz.de/index.html">BACK TO MY HOMEPAGE</a> + +</body> +</html> |