├── .gitignore ├── LICENSE ├── net-xml-generator.asd ├── Makefile ├── README.md ├── xml-generator-blurb.cl ├── xml-generator-blurb.html └── net-xml-generator.cl /.gitignore: -------------------------------------------------------------------------------- 1 | build.out 2 | build.tmp 3 | xml-generator-blurb-copy.html 4 | *.fasl 5 | *~ 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | ;; This software is Copyright (c) Franz Inc, 2009, 2010, 2011 2 | ;; Franz Inc grants you the rights to distribute 3 | ;; and use this software as governed by the terms 4 | ;; of the Lisp Lesser GNU Public License 5 | ;; (http://opensource.franz.com/preamble.html), 6 | ;; known as the LLGPL. 7 | -------------------------------------------------------------------------------- /net-xml-generator.asd: -------------------------------------------------------------------------------- 1 | (in-package :cl-user) 2 | 3 | (defpackage :net-xml-generator.asdf 4 | (:use :cl :asdf)) 5 | (in-package :net-xml-generator.asdf) 6 | 7 | (unless (find-class 'cl-file nil) 8 | (defclass asdf::cl-file (asdf:cl-source-file) ()) 9 | (defmethod asdf:source-file-type ((c asdf::cl-file) 10 | (s asdf:module)) 11 | "cl")) 12 | 13 | (defsystem :net-xml-generator 14 | :license "LLGPL" 15 | :author "Steve Haflich " 16 | :depends-on () 17 | :components ((:cl-file "net-xml-generator"))) 18 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # user Makefile for the xml generator 2 | 3 | incl = $(shell if test -f ../../makefile.top; then echo yes; else echo no; fi) 4 | 5 | ifeq ($(incl),yes) 6 | include ../../makefile.top 7 | include ../../makefile.defs 8 | endif 9 | 10 | ifeq ($(OS_NAME),windows) 11 | runlisp = ../lisp +s build.tmp +B +cn -I dcl -q -batch 12 | else 13 | runlisp = ../lisp -I dcl +s build.tmp -q -batch 14 | endif 15 | 16 | SOURCES = Makefile net-xml-generator.cl README.md \ 17 | xml-generator-blurb.cl xml-generator-blurb.html 18 | 19 | all: clean 20 | rm -f build.tmp build.out 21 | ifeq ($(OS_NAME),windows) 22 | echo '(dribble "build.out")' >> build.tmp 23 | endif 24 | echo '(compile-file "net-xml-generator.cl" :recompile t)' >> build.tmp 25 | echo '(exit 0)' >> build.tmp 26 | $(runlisp) 27 | ifeq ($(OS_NAME),windows) 28 | cat build.out 29 | endif 30 | 31 | clean: FORCE 32 | rm -f *.fasl *.tmp 33 | 34 | install: FORCE 35 | ifndef DESTDIR 36 | @echo DESTDIR not defined 37 | exit 1 38 | endif 39 | #### do not use -p since it fails on z:/ on thunder 40 | cp net-xml-generator.fasl $(DESTDIR)/code 41 | 42 | FORCE: 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | net-xml-generator - A Pretty Printing XML Generator for Common Lisp 2 | =================================================================== 3 | 4 | Table of contents 5 | ----------------- 6 | 7 | * Description 8 | * Author 9 | * Author comments 10 | * Documentation 11 | * Platforms 12 | * Dependencies 13 | * Installation 14 | * Configuration 15 | * Licence 16 | * Notes 17 | * Examples 18 | * Franz Inc. Open Source Info 19 | 20 | Description 21 | ----------- 22 | 23 | This module consists of a single source file net-xml-generator.cl. 24 | 25 | It provides a modified readtable that allows normal Lisp source code 26 | to contain possibly-nested marked subforms that emit an XML element. 27 | The tree that is Lisp source code integrates transparently with the 28 | tree that is the intended XML, and the entire panoply of Lisp 29 | operators (iteration, special forms, function calls) can be mixed with 30 | XML generation. The Lisp code that emits XML has a structure 31 | paralleling the XML. 32 | 33 | The Common Lisp pretty printer is optionally used to indent the 34 | emitted XML for human readability. See the file 35 | xml-generator-blurb.html distributed along with this module for a 36 | gentle introduction, and xml-generator-blurb.cl which shows how that 37 | html file was itself generated using the module. 38 | 39 | Author 40 | ------ 41 | 42 | Steven Haflich, Franz Inc. 43 | 44 | Author comments 45 | --------------- 46 | 47 | Platforms 48 | ---------- 49 | 50 | All Allegro Common Lisp versions. It mostly should work on other 51 | implementations, but uses the ACL named-readtable facility (which 52 | could trivially be added to any other implementation) and more 53 | importantly may depend on subtle details of the pretty printer. 54 | 55 | Dependencies 56 | ------------ 57 | 58 | Allegro Common Lisp 59 | 60 | The Allegro Common Lisp excl:named-readtable facility. 61 | 62 | Installation 63 | ------------ 64 | 65 | Start your lisp and compile and load net-xml-generator.cl which is 66 | part of this project: 67 | 68 | (load (compile-file "/path/to/your/net-xml-generator.cl") 69 | 70 | To test, 71 | 72 | (load "/path/to/your/xml-generator-blurb.cl") 73 | (generate-this-page :out-path "./xml-generator-blurb-copy.html" ) 74 | 75 | and that named file will be generated which should be an exact copy of 76 | xml-generator-blurb.html 77 | 78 | Configuration 79 | ------------- 80 | 81 | No configuration is necessary, but see the documentation in the source 82 | file how to use the customized readtable in your Lisp code. 83 | 84 | Documentation 85 | ------------- 86 | 87 | The full documentation is contained in block comments in the 88 | net-xml-generator.cl source file itself. 89 | 90 | License 91 | ------- 92 | 93 | The net-xml-generator source code is licensed under the terms of the 94 | [Lisp Lesser GNU Public License](http://opensource.franz.com/preamble.html), 95 | known as the LLGPL. The LLGPL consists of a preamble and the 96 | LGPL. Where these conflict, the preamble takes precedence. This 97 | project is referenced in the preamble as the LIBRARY. 98 | 99 | Notes 100 | ----- 101 | 102 | An earlier version of this code was first released as part of the Ray 103 | Tracing example in the Franz Inc Dynamic Learning Center. 104 | 105 | Examples and Information 106 | ------------------------ 107 | 108 | The xml-generator-blurb.cl is an odd, self-referential example of Lisp 109 | code using the generator. 110 | 111 | Franz Open Source Info 112 | ---------------------- 113 | 114 | This project's homepage is . There is an 115 | informal community support and development mailing list 116 | [opensource@franz.com](http://opensource.franz.com/mailinglist.html) 117 | for these open source projects. We encourage you to take advantage by 118 | subscribing to the list. Once you're subscribed, email to 119 | with your questions, comments, suggestions, 120 | and patches. 121 | -------------------------------------------------------------------------------- /xml-generator-blurb.cl: -------------------------------------------------------------------------------- 1 | ; -*- mode: common-lisp; package: cl-user; readtable: xml -*- 2 | 3 | (in-package :user) 4 | 5 | (eval-when (compile load eval) 6 | ;; This require won't work until xml-generator is included in the ACL code directory. So we'll give a 7 | ;; useful error if it hasn't yet been loaded. 8 | (ignore-errors (require :net-xml-generator)) 9 | (unless (find-package :net.xml.generator) 10 | (error "This file ~a cannot be loaded unless the :net.xml.generator module is loaded first." 11 | (or *compile-file-pathname* *load-pathname*))) 12 | (use-package :net.xml.generator) 13 | (setq *readtable* (named-readtable :xml t))) 14 | 15 | (defparameter *this-source-file* (macrolet ((x () 16 | (or *compile-file-pathname* 17 | (load-time-value *load-pathname*)))) 18 | (x))) 19 | 20 | (defun generate-this-page (&key (out-path "./xml-generator-blurb.html")) 21 | (let ((*print-right-margin* 92)) 22 | (with-open-file (out out-path :direction :output :if-exists :supersede) 23 | (with-xml-generation (out) 24 | (write-xmldecl out "1.0") 25 | ^(html 26 | ^(head 27 | ^(title "The Net-Xml-Generator Blurb") 28 | ^((style @type "text/css") 29 | "pre.example { color: rgb(20, 20, 0); background-color: rgb(200, 200, 255); 30 | font-size:85%; margin-left:50px; margin-right:50px }" 31 | "pre.result { color: rgb(20, 0, 20); background-color: rgb(200, 255, 200); 32 | font-size:85%; margin-left:70px; margin-right:50px }" 33 | "div.rendered { color: rgb(0, 20, 20); background-color: rgb(255, 200, 200); 34 | font-size:85%; margin-left:90px; margin-right:50px }" 35 | )) 36 | ^(body 37 | 38 | ^(h1 "The Net-Xml-Generator Blurb") 39 | 40 | ^(p ^(b "Allegro Common Lisp") " has a new, open-source module named " 41 | ^(b "xml-generator") " available from " 42 | ^((a @href "http://opensource.franz.com/") "opensource.franz.com") ". " 43 | " 44 | 45 | The module uses the Common Lisp pretty printer and a modified readtable to integrate the generation of 46 | pretty-printed XML (intuitive indentation and line breaks) with arbitrary Lisp code. In many dialects of XML 47 | white space is not significant, but when developing applications that emit long XML documents, human 48 | readability is essential. Everyone has had the pain of trying of understand the source for a web page in a 49 | browser where all the (x)html is on a single unreadable looooooong line. Once an application is debugged, of 50 | course, the pretty printer can simply be turned off to reduce the size of the generated output.") 51 | 52 | ^(p " 53 | 54 | Even more important, the module employs a customized readtable so that XML elements are be marked 55 | lexicographically. Both Lisp source code and XML are trees. Using this module the logical structure of 56 | application code that generates XML maps simply and clearly onto the structure of the generated XML, except 57 | that the entire vocabulary of Lisp forms (iteration, conditionals, case, and function calls) can be freely 58 | mixed with XML generation. This is a significant difference from the unrelated htmlgen module. See the 59 | examples below.") 60 | 61 | ^(p " 62 | 63 | The two additional macro characters default to `^' and `@'. The `^' character marks the start of an XML 64 | element. The `@' character inside a element start tag marks the next two subforms as an attribute/value pair, 65 | and inside element content marks the next subform as content. A string as a top-level subform of an XML 66 | element will generate element content, even withoute `@' reader macro.") 67 | 68 | ^(p " 69 | 70 | The detailed documentation for the module is in the net-xml-generator.cl source file itself, but here are some 71 | examples that show use of these syntax extensions and the generated XML. These examples all happen to 72 | generate XHTML, so we'll also show the rendered example:" ) 73 | (let ((examples 74 | '( 75 | ^(p "Hello, world!") 76 | ^(p @"Hello, world!") 77 | ^(p @@"Hello, world!") 78 | ^((p @id 42) "Hello, world!") 79 | ^(center "Above the line." ^hr "Below the line.") 80 | ^((table @rules "all" @frame "box") 81 | ^((tr @bgcolor "Silver") ^(th @"Name") ^(th @"Phone")) ; Use of bgcolor is deprecated. 82 | ^(tr ^(td "Joe") ^(td @"555-1234")) 83 | ^(tr ^(td "Xavier") ^(td @"555-5678"))) 84 | ^(table 85 | ^((tr @bgcolor "Silver") ^(th @"operator") ^(th @"arglist")) 86 | (do-external-symbols (op :net.xml.generator) 87 | (when (fboundp op) 88 | ^(tr ^(td @(symbol-name op)) 89 | ^(td @(format nil "~{~a~^ ~}" (excl:arglist op))))))) 90 | )) 91 | (*print-right-margin* 70) 92 | (*print-miser-width* 20)) 93 | (dolist (example examples) 94 | (pre ^((pre @class "example") 95 | @(with-output-to-string (str) 96 | (pprint example str)))) 97 | (pre ^((pre @class "result") 98 | @(let ((*print-pretty* t)) ; The surrounding pre turns it off! 99 | (with-output-to-string (str) 100 | (with-xml-generation (str) 101 | (eval example)))))) 102 | ^((div @class "rendered") 103 | (eval example)))) 104 | 105 | ^hr 106 | ^(h2 "How this page was generated") 107 | ^(p " 108 | 109 | Through use of a clever self-referential hack, here is the actual Common Lisp source file that generated this 110 | xhtml page." ) 111 | 112 | (pre ^((pre @class "example") 113 | ^(tt @(file-contents *this-source-file*)))) 114 | )))))) 115 | 116 | (eval-when (load eval) 117 | (format t 118 | "~&;;~%;; To generate this xhtml page, execute ~ 119 | (generate-this-page :out-path \"./xml-generator-blurb-copy.html\") .~%;;~%")) 120 | -------------------------------------------------------------------------------- /xml-generator-blurb.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | The Net-Xml-Generator Blurb 6 | 14 | 15 | 16 |

The Net-Xml-Generator Blurb

17 |

18 | Allegro Common Lisp has a new, open-source module named 19 | xml-generator available from 20 | opensource.franz.com. 21 | 22 | The module uses the Common Lisp pretty printer and a modified readtable to integrate the generation of 23 | pretty-printed XML (intuitive indentation and line breaks) with arbitrary Lisp code. In many dialects of XML 24 | white space is not significant, but when developing applications that emit long XML documents, human 25 | readability is essential. Everyone has had the pain of trying of understand the source for a web page in a 26 | browser where all the (x)html is on a single unreadable looooooong line. Once an application is debugged, of 27 | course, the pretty printer can simply be turned off to reduce the size of the generated output. 28 |

29 |

30 | 31 | Even more important, the module employs a customized readtable so that XML elements are be marked 32 | lexicographically. Both Lisp source code and XML are trees. Using this module the logical structure of 33 | application code that generates XML maps simply and clearly onto the structure of the generated XML, except 34 | that the entire vocabulary of Lisp forms (iteration, conditionals, case, and function calls) can be freely 35 | mixed with XML generation. This is a significant difference from the unrelated htmlgen module. See the 36 | examples below. 37 |

38 |

39 | 40 | The two additional macro characters default to `^' and `@'. The `^' character marks the start of an XML 41 | element. The `@' character inside a element start tag marks the next two subforms as an attribute/value pair, 42 | and inside element content marks the next subform as content. A string as a top-level subform of an XML 43 | element will generate element content, even withoute `@' reader macro. 44 |

45 |

46 | 47 | The detailed documentation for the module is in the net-xml-generator.cl source file itself, but here are some 48 | examples that show use of these syntax extensions and the generated XML. These examples all happen to 49 | generate XHTML, so we'll also show the rendered example: 50 |

51 |
 52 | ^(p "Hello, world!")
53 |
<p>Hello, world!</p>
54 |
55 |

Hello, world!

56 |
57 |
 58 | ^(p @"Hello, world!")
59 |
<p>Hello, world!</p>
60 |
61 |

Hello, world!

62 |
63 |
 64 | ^(p @@"Hello,&nbsp;world!")
65 |
<p>Hello,&nbsp;world!</p>
66 |
67 |

Hello, world!

68 |
69 |
 70 | ^((p @id 42) "Hello, world!")
71 |
<p id="42">Hello, world!</p>
72 |
73 |

Hello, world!

74 |
75 |
 76 | ^(center "Above the line." ^hr "Below the line.")
77 |
<center>Above the line.<hr/>Below the line.</center>
78 |
79 |
Above the line.
Below the line.
80 |
81 |
 82 | ^((table @rules "all" @frame "box")
 83 |   ^((tr @bgcolor "Silver") ^(th @"Name") ^(th @"Phone"))
 84 |   ^(tr ^(td "Joe") ^(td @"555-1234"))
 85 |   ^(tr ^(td "Xavier") ^(td @"555-5678")))
86 |
 87 | <table rules="all" frame="box">
 88 |   <tr bgcolor="Silver"><th>Name</th><th>Phone</th></tr>
 89 |   <tr><td>Joe</td><td>555-1234</td></tr>
 90 |   <tr><td>Xavier</td><td>555-5678</td></tr>
 91 | </table>
92 |
93 | 94 | 95 | 96 | 97 |
NamePhone
Joe555-1234
Xavier555-5678
98 |
99 |
100 | ^(table
101 |   ^((tr @bgcolor "Silver") ^(th @"operator") ^(th @"arglist"))
102 |   (do-external-symbols (op :net.xml.generator)
103 |     (when (fboundp op)
104 |       ^(tr
105 |         ^(td @(symbol-name op))
106 |         ^(td @(format nil "~{~a~^ ~}" (arglist op)))))))
107 |
108 | <table>
109 |   <tr bgcolor="Silver"><th>operator</th><th>arglist</th></tr>
110 |   <tr>
111 |     <td>set-xml-generator-macro-chars</td>
112 |     <td>element-char attribute-char &amp;optional rt</td>
113 |   </tr>
114 |   <tr><td>emit-lxml-as-xml</td><td>.xml-stream. lxml</td></tr>
115 |   <tr><td>xml-write</td><td>value</td></tr>
116 |   <tr><td>write-xmldecl</td><td>stream &amp;optional version</td></tr>
117 |   <tr><td>pre</td><td>&amp;body body</td></tr>
118 |   <tr>
119 |     <td>with-xml-generation</td>
120 |     <td>(stream-var &amp;key) &amp;body body</td>
121 |   </tr>
122 |   <tr>
123 |     <td>write-doctype</td>
124 |     <td>stream name system-literal &amp;optional public-literal</td>
125 |   </tr>
126 |   <tr><td>cdata</td><td>&amp;body body</td></tr>
127 | </table>
128 |
129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 |
operatorarglist
set-xml-generator-macro-charselement-char attribute-char &optional rt
emit-lxml-as-xml.xml-stream. lxml
xml-writevalue
write-xmldeclstream &optional version
pre&body body
with-xml-generation(stream-var &key) &body body
write-doctypestream name system-literal &optional public-literal
cdata&body body
146 |
147 |
148 |

How this page was generated

149 |

150 | 151 | Through use of a clever self-referential hack, here is the actual Common Lisp source file that generated this 152 | xhtml page. 153 |

154 |
; -*- mode: common-lisp; package: cl-user; readtable: xml -*-
155 | 
156 | (in-package :user)
157 | 
158 | (eval-when (compile load eval)
159 |   ;; This require won't work until xml-generator is included in the ACL code directory.  So we'll give a
160 |   ;; useful error if it hasn't yet been loaded.
161 |   (ignore-errors (require :net-xml-generator))
162 |   (unless (find-package :net.xml.generator)
163 |     (error "This file ~a cannot be loaded unless the :net.xml.generator module is loaded first."
164 | 	   (or *compile-file-pathname* *load-pathname*)))
165 |   (use-package :net.xml.generator)
166 |   (setq *readtable* (named-readtable :xml t)))
167 | 
168 | (defparameter *this-source-file* (macrolet ((x ()
169 | 					      (or *compile-file-pathname*
170 | 						  (load-time-value *load-pathname*))))
171 | 				   (x)))
172 | 
173 | (defun generate-this-page (&key (out-path "./xml-generator-blurb.html"))
174 |   (let ((*print-right-margin* 92))
175 |     (with-open-file (out out-path :direction :output :if-exists :supersede)
176 |       (with-xml-generation (out)
177 | 	(write-xmldecl out "1.0")
178 | 	^(html
179 | 	  ^(head
180 | 	    ^(title "The Net-Xml-Generator Blurb")
181 | 	    ^((style @type "text/css")
182 | 	      "pre.example  { color: rgb(20, 20, 0); background-color: rgb(200, 200, 255);
183 |  			    font-size:85%; margin-left:50px; margin-right:50px }"
184 | 	      "pre.result   { color: rgb(20, 0, 20); background-color: rgb(200, 255, 200);
185 |  			    font-size:85%; margin-left:70px; margin-right:50px }"
186 | 	      "div.rendered { color: rgb(0, 20, 20); background-color: rgb(255, 200, 200);
187 |  			    font-size:85%; margin-left:90px; margin-right:50px }"
188 | 	      ))
189 | 	  ^(body
190 | 
191 | 	    ^(h1 "The Net-Xml-Generator Blurb")
192 | 
193 | 	    ^(p ^(b "Allegro Common Lisp") " has a new, open-source module named "
194 | 		^(b "xml-generator") " available from "
195 | 		^((a @href "http://opensource.franz.com/") "opensource.franz.com") ".  "
196 | 		"
197 | 
198 | The module uses the Common Lisp pretty printer and a modified readtable to integrate the generation of
199 | pretty-printed XML (intuitive indentation and line breaks) with arbitrary Lisp code.  In many dialects of XML
200 | white space is not significant, but when developing applications that emit long XML documents, human
201 | readability is essential.  Everyone has had the pain of trying of understand the source for a web page in a
202 | browser where all the (x)html is on a single unreadable looooooong line.  Once an application is debugged, of
203 | course, the pretty printer can simply be turned off to reduce the size of the generated output.")
204 | 
205 | 	    ^(p "
206 | 
207 | Even more important, the module employs a customized readtable so that XML elements are be marked
208 | lexicographically.  Both Lisp source code and XML are trees.  Using this module the logical structure of
209 | application code that generates XML maps simply and clearly onto the structure of the generated XML, except
210 | that the entire vocabulary of Lisp forms (iteration, conditionals, case, and function calls) can be freely
211 | mixed with XML generation.  This is a significant difference from the unrelated htmlgen module.  See the
212 | examples below.")
213 | 
214 | 	    ^(p "
215 | 
216 | The two additional macro characters default to `^' and `@'.  The `^' character marks the start of an XML
217 | element.  The `@' character inside a element start tag marks the next two subforms as an attribute/value pair,
218 | and inside element content marks the next subform as content.  A string as a top-level subform of an XML
219 | element will generate element content, even withoute `@' reader macro.")
220 | 
221 | 	    ^(p "
222 | 
223 | The detailed documentation for the module is in the net-xml-generator.cl source file itself, but here are some
224 | examples that show use of these syntax extensions and the generated XML.  These examples all happen to
225 | generate XHTML, so we'll also show the rendered example:" )
226 | 	    (let ((examples
227 | 		   '(
228 | 		     ^(p "Hello, world!")
229 | 		     ^(p @"Hello, world!")
230 | 		     ^(p @@"Hello,&nbsp;world!")
231 | 		     ^((p @id 42) "Hello, world!")
232 | 		     ^(center "Above the line." ^hr "Below the line.")
233 | 		     ^((table @rules "all" @frame "box")
234 | 		       ^((tr @bgcolor "Silver") ^(th @"Name") ^(th @"Phone")) ; Use of bgcolor is deprecated.
235 | 		       ^(tr ^(td "Joe")    ^(td @"555-1234"))
236 | 		       ^(tr ^(td "Xavier") ^(td @"555-5678")))
237 | 		     ^(table
238 | 		       ^((tr @bgcolor "Silver") ^(th @"operator") ^(th @"arglist"))
239 | 		       (do-external-symbols (op :net.xml.generator)
240 | 			 (when (fboundp op)
241 | 			   ^(tr ^(td @(symbol-name op))
242 | 				^(td @(format nil "~{~a~^ ~}" (excl:arglist op)))))))
243 | 		     ))
244 | 		  (*print-right-margin* 70)
245 | 		  (*print-miser-width*  20))
246 | 	      (dolist (example examples)
247 | 		(pre ^((pre @class "example")
248 | 		       @(with-output-to-string (str)
249 | 			  (pprint example str))))
250 | 		(pre ^((pre @class "result")
251 | 		       @(let ((*print-pretty* t)) ; The surrounding pre turns it off!
252 | 			  (with-output-to-string (str)
253 | 			    (with-xml-generation (str)
254 | 			      (eval example))))))
255 | 	       ^((div @class "rendered")
256 | 		 (eval example))))
257 | 
258 | 	    ^hr
259 | 	    ^(h2 "How this page was generated")
260 | 	    ^(p "
261 | 
262 | Through use of a clever self-referential hack, here is the actual Common Lisp source file that generated this
263 | xhtml page."  )
264 | 
265 | 	    (pre ^((pre @class "example")
266 | 		   ^(tt @(file-contents *this-source-file*))))
267 | 	    ))))))
268 | 
269 | (eval-when (load eval)
270 |   (format t
271 | 	  "~&;;~%;; To generate this xhtml page, execute ~
272 |            (generate-this-page :out-path \"./xml-generator-blurb-copy.html\") .~%;;~%"))
273 | 
274 | 275 | -------------------------------------------------------------------------------- /net-xml-generator.cl: -------------------------------------------------------------------------------- 1 | ;; -*- mode: common-lisp; package: net.xml.generator -*- 2 | ;; See the file LICENSE for the full license governing this code. 3 | ;; 4 | ;; generalized pretty-printing xml generator 5 | 6 | ;; There is no warranty provided by Franz Inc either explicitly or implicitly as to the correctness or 7 | ;; servicability of this code. It is provided "as is" in the hope that it may be useful. Comments and 8 | ;; feedback welcome at . 9 | 10 | ;; This code originally written by smh@franz.com 11 | 12 | ;;; 13 | ;;; This single file implements the :net-xml-generator module. 14 | ;;; 15 | 16 | ;; This module is mostly a readtable hack that provides a palatable syntax for XML generation by Lisp code. 17 | ;; In order for Lisp and XML forms to coexist and nest arbitrarily, there must be some kind of syntactic 18 | ;; marker to differentiate Lisp and XML operators/tagnames. The #\^ reader macro marks XML tagnames in source 19 | ;; code. Both Lisp source code and XML are trees. Using this module the logical structure of application 20 | ;; code that generates XML maps simply and clearly onto the structure of the generated XML, except that the 21 | ;; entire vocabulary of Lisp forms (iteration, conditionals, case, and function calls) can be freely mixed 22 | ;; with XML generation. The earlier, unrelated htmlgen module used keyword symbols to denote the fixed set of 23 | ;; html tags. But this technique does not allow arbitrary nesting of html constructs inside Lisp syntactic 24 | ;; constructs, and required adding additional operators to support Lisp conditions, etc. The reader macro 25 | ;; approach allows much cleaner, terser, and Lisp-idoimatic code. 26 | 27 | ;; In Allegro CL the named-readtable facility makes it easy to associate customized readtables for particular 28 | ;; files. Place a top-level form like 29 | 30 | ;; (eval-when (compile eval) (setq *readtable* (excl:named-readtable :xml))) 31 | 32 | ;; somewhere near the top of a source file using this syntax. In addition, put the attribute "readtable: xml" 33 | ;; in the Emacs mode line f the file so Emacs and the ACL IDE will also use this readtable for evaluation or 34 | ;; compilation requests associated with the buffer. Such a line would look something like this 35 | 36 | ;; -*- mode: common-lisp; package: cl-user; readtable :xml -*- 37 | 38 | ;; The #\^ character is a syntactic marker that the following form should emit an XML element. Within a start 39 | ;; element the #\@ character reads the next two subforms and generates an attribute. (The #\@ character idiom 40 | ;; is suggested by both XSLT usage and Lisp backquote usage.) Both of these can appear anywhere and can be 41 | ;; freely interspersed around and inside arbitrary Lisp forms. This use of #\@ is culturally compatible with 42 | ;; the XSL world. It makes no sense for a #\^ to appear inside another tag, but the #\@ character can be used 43 | ;; in element content as a shorthand to princ the result of executing the following form (most often a string 44 | ;; constant) to the XML stream. Some illustrative examples, each of which must be lexically inside a 45 | ;; with-xml-generation form. 46 | 47 | ;; ^foo ==> 48 | ;; ^(foo ^(bar)) ==> 49 | ;; ^((foo @id "31415") ^(bar)) ==> 50 | ;; ^((foo ^bar) ^(bar)) ==> illegal 51 | ;; ^(time @"The current UT is " ^(ut @(get-universal-time))) ==> 52 | ;; 53 | ;; ; The @ before the literal string in the previous example is optional since the 54 | ;; ; string is at top-level of the ^ element body. See below. 55 | ;; ^((foo @name "xy&z") ^(bar)) ==> 56 | 57 | ;; Arbitrary lisp code can appear freely, anywhere inside an XML element form, as all attributes and internal 58 | ;; elements are flagged syntactically with the #\^ and #\@ characters. 59 | 60 | ;; The #\@ read macro inside an element start consumes exactly two successive subforms. The first is the 61 | ;; attribute name and the second is the attribute value. A comma preceding the attribute name causes the name 62 | ;; form to be evaluated. If the #\@ is doubled, the attribute value is printed without the usual double 63 | ;; quotes. This appears to e necessary sometimes in a Javascript