Fix asd for cmucl with unicode
[closure-common.git] / hax.lisp
blobc77b4e295a2b68937558e8e2c36edbafa2aa51ab
1 ;;; -*- show-trailing-whitespace: t; indent-tabs: nil -*-
2 ;;; ---------------------------------------------------------------------------
3 ;;; Title: An event API for the HTML parser, inspired by SAX
4 ;;; Created: 2007-10-14
5 ;;; Author: David Lichteblau
6 ;;; License: BSD
7 ;;; ---------------------------------------------------------------------------
8 ;;; (c) copyright 2005,2007 David Lichteblau
10 ;;; Redistribution and use in source and binary forms, with or without
11 ;;; modification, are permitted provided that the following conditions are
12 ;;; met:
13 ;;;
14 ;;; 1. Redistributions of source code must retain the above copyright
15 ;;; notice, this list of conditions and the following disclaimer.
16 ;;;
17 ;;; 2. Redistributions in binary form must reproduce the above copyright
18 ;;; notice, this list of conditions and the following disclaimer in the
19 ;;; documentation and/or other materials provided with the distribution
20 ;;;
21 ;;; THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
22 ;;; WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
23 ;;; MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 ;;; IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
25 ;;; INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26 ;;; (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 ;;; SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 ;;; HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
29 ;;; STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
30 ;;; IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 ;;; POSSIBILITY OF SUCH DAMAGE.
33 (defpackage :hax
34 (:use :common-lisp)
35 (:export #:abstract-handler
36 #:default-handler
38 #:make-attribute
39 #:standard-attribute
40 #:find-attribute
41 #:attribute-name
42 #:attribute-value
43 #:attribute-specified-p
45 #:start-document
46 #:start-element
47 #:characters
48 #:unescaped
49 #:end-element
50 #:end-document
51 #:comment
53 #+rune-is-integer
54 #:%want-strings-p))
56 (in-package :hax)
59 ;;;; ATTRIBUTE
61 (defgeneric attribute-name (attribute))
62 (defgeneric attribute-value (attribute))
63 (defgeneric attribute-specified-p (attribute))
65 (defclass standard-attribute ()
66 ((name :initarg :name :accessor attribute-name)
67 (value :initarg :value :accessor attribute-value)
68 (specified-p :initarg :specified-p :accessor attribute-specified-p)))
70 (defun make-attribute (name value &optional (specified-p t))
71 (make-instance 'standard-attribute
72 :name name
73 :value value
74 :specified-p specified-p))
76 (defun %rod= (x y)
77 ;; allow rods *and* strings *and* null
78 (cond
79 ((zerop (length x)) (zerop (length y)))
80 ((zerop (length y)) nil)
81 ((stringp x) (string= x y))
82 (t (runes:rod= x y))))
84 (defun find-attribute (name attrs)
85 (find name attrs :key #'attribute-name :test #'%rod=))
88 ;;;; ABSTRACT-HANDLER and DEFAULT-HANDLER
90 (defclass abstract-handler () ())
91 (defclass default-handler (abstract-handler) ())
93 #+rune-is-integer
94 (defgeneric %want-strings-p (handler)
95 (:method ((handler null)) nil)
96 (:method ((handler abstract-handler)) t))
98 (defgeneric start-document (handler name public-id system-id)
99 (:method ((handler null) name public-id system-id)
100 (declare (ignore name public-id system-id))
101 nil)
102 (:method ((handler default-handler) name public-id system-id)
103 (declare (ignore name public-id system-id))
104 nil))
106 (defgeneric start-element (handler name attributes)
107 (:method ((handler null) name attributes)
108 (declare (ignore name attributes))
109 nil)
110 (:method ((handler default-handler) name attributes)
111 (declare (ignore name attributes))
112 nil))
114 (defgeneric characters (handler data)
115 (:method ((handler null) data)
116 (declare (ignore data))
117 nil)
118 (:method ((handler default-handler) data)
119 (declare (ignore data))
120 nil))
122 (defgeneric unescaped (handler data)
123 (:method ((handler null) data)
124 (declare (ignore data))
125 nil)
126 (:method ((handler default-handler) data)
127 (declare (ignore data))
128 nil))
130 (defgeneric end-element (handler name)
131 (:method ((handler null) name)
132 (declare (ignore name))
133 nil)
134 (:method ((handler default-handler) name)
135 (declare (ignore name))
136 nil))
138 (defgeneric end-document (handler)
139 (:method ((handler null)) nil)
140 (:method ((handler default-handler)) nil))
142 (defgeneric comment (handler data)
143 (:method ((handler null) data)
144 (declare (ignore data))
145 nil)
146 (:method ((handler default-handler) data)
147 (declare (ignore data))
148 nil))
151 ;;;; documentation
153 (setf (documentation (find-package :hax) t)
154 "An event protocol for HTML serialization, this package is similar
155 to the SAX protocol defined by cxml for XML serialization.
157 (Technically, this package should have been spelled SAH, but HAX
158 sounds better.)
160 Note that Closure HTML is not a streaming parser yet. Documents
161 are always parsed in full before the first HAX event is emitted.
162 In spite of this restriction, the HAX API is useful for HTML
163 serialization and transformation purposes, and for integration
164 with SAX.
166 @begin[HAX handlers]{section}
167 @aboutclass{abstract-handler}
168 @aboutclass{default-handler}
169 @end{section}
170 @begin[The attribute protocol]{section}
171 @aboutclass{standard-attribute}
172 @aboutfun{make-attribute}
173 @aboutfun{attribute-name}
174 @aboutfun{attribute-value}
175 @aboutfun{attribute-specified-p}
176 @end{section}
177 @begin[HAX events]{section}
178 @aboutfun{start-document}
179 @aboutfun{start-element}
180 @aboutfun{end-element}
181 @aboutfun{characters}
182 @aboutfun{unescaped}
183 @aboutfun{comment}
184 @aboutfun{end-document}
185 @end{section}")
187 (setf (documentation 'abstract-handler 'type)
188 "@short{The superclass of all HAX handlers.}
190 Direct subclasses have to implement all event methods, since
191 no default methods are defined on this class.
193 Note that it is permissible to use handlers that are not
194 instances of this class in some circumstances.
196 In particular,
197 @code{nil} is a valid HAX handler and ignores all events.
199 In addition,
200 @a[http://common-lisp.net/project/cxml/sax.html#sax]{SAX handlers}
201 are valid HAX handlers (and vice versa), even though
202 hax:abstract-handler and sax:abstract-handler do not
203 share a specific superclass. HAX events sent to SAX handlers are
204 automatically re-signalled as XHTML SAX events, and SAX events sent
205 to HAX handlers are re-signalled as namespace-less HAX events.
207 However, user code should define subclasses of the documented
208 superclasses to enable the HAX/SAX bridging described above.
210 @see{chtml:parse}
211 @see{chtml:serialize-lhtml}
212 @see{chtml:serialize-pt}
213 @see{start-document}
214 @see{end-document}
215 @see{start-element}
216 @see{end-element}
217 @see{characters}
218 @see{unescaped}
219 @see{comment}")
221 (setf (documentation 'default-handler 'type)
222 "@short{A no-op HAX handler.}
224 This class defines methods for all HAX events that do nothing.
225 It is useful as a superclass when implementing a HAX handler that
226 is interested in only some events and not others.
228 @see{chtml:parse}
229 @see{chtml:serialize-lhtml}
230 @see{chtml:serialize-pt}
231 @see{start-document}
232 @see{end-document}
233 @see{start-element}
234 @see{end-element}
235 @see{characters}
236 @see{unescaped}
237 @see{comment}")
239 (setf (documentation 'standard-attribute 'type)
240 "@short{An implementation of the HAX attribute protocol.}
242 A standard class implementing the generic functions for HAX
243 attributes. Instances of this class can be passed to
244 @fun{hax:start-element} in the list of attributes.
246 @see-slot{attribute-name}
247 @see-slot{attribute-value}
248 @see-slot{attribute-specified-p}
249 @see-constructor{make-instance}")
251 (setf (documentation 'make-attribute 'function)
252 "@arg[name]{a string/rod}
253 @arg[value]{a string/rod}
254 @arg[specified-p]{a boolean, default is @code{t}}
255 @return{an instance of @class{standard-attribute}.}
256 @short{Creates a HAX attribute.}
258 Creates an instance that can be used with the generic functions
259 for HAX attributes. The result can be passed to
260 @fun{hax:start-element} in the list of attributes.
262 @see{attribute-name}
263 @see{attribute-value}
264 @see{attribute-specified-p}")
266 (setf (documentation 'find-attribute 'function)
267 "@arg[name]{a string/rod}
268 @arg[attrs]{a list of attributes}
269 @return{an attribute, or nil}
270 @short{Searches for an attribute by name.}
272 Returns the first attribute in @var{attrs} with the specified name,
273 or @code{nil} if no such attribute was found.
275 @see{attribute-name}")
277 (setf (documentation 'attribute-name 'function)
278 "@arg[instance]{any class implementing this function}
279 @return{a string/rod}
280 @short{Return an attribute's name.}
282 Instances of this classes implementing this function can be passed to
283 @fun{hax:start-element} in the list of attributes.
285 @see{attribute-value}
286 @see{attribute-specified-p}")
288 (setf (documentation 'attribute-value 'function)
289 "@arg[instance]{any class implementing this function}
290 @return{a string/rod}
291 @short{Return an attribute's value.}
293 Instances of this classes implementing this function can be passed to
294 @fun{hax:start-element} in the list of attributes.
296 @see{attribute-name}
297 @see{attribute-specified-p}")
299 (setf (documentation 'attribute-specified-p 'function)
300 "@arg[instance]{any class implementing this function}
301 @return{a string/rod}
302 @short{Return whether the attribute was contained the parsed document.}
304 Attributes return @code{nil} here if they resulted from a default
305 value declaration in a DTD.
307 Instances of this classes implementing this function can be passed to
308 @fun{hax:start-element} in the list of attributes.
310 @see{attribute-name}
311 @see{attribute-value}")
313 (setf (documentation 'start-document 'function)
314 "@arg[handler]{a HAX/SAX handler
315 (see @class{abstract-handler} for details)}
316 @arg[name]{root element name, a rod/string}
317 @arg[public-id]{nil or the Public ID, a rod/string}
318 @arg[system-id]{nil or the System ID/URI, a rod/string}
319 @return{unspecified}
320 @short{Signals the beginning of an HTML document.}
322 This is the first event sent to any handler.
324 If @var{system-id} is non-nil, the document includes a doctype
325 declaration.
327 @see{start-element}
328 @see{end-element}
329 @see{characters}
330 @see{unescaped}
331 @see{comment}
332 @see{end-document}")
334 (setf (documentation 'start-element 'function)
335 "@arg[handler]{a HAX/SAX handler
336 (see @class{abstract-handler} for details)}
337 @arg[name]{root element name, a rod/string}
338 @arg[attributes]{a list of attributes}
339 @return{unspecified}
340 @short{Signals the beginning of an HTML element.}
342 This event corresponds to the opening tag of an element.
344 Elements of the attribute list can have any class, but must implement
345 the generic functions for attributes. See @class{standard-attribute}
346 for the built-in attribute implementation.
348 @see{find-attribute}
349 @see{start-document}
350 @see{end-element}
351 @see{characters}
352 @see{unescaped}
353 @see{comment}
354 @see{end-document}")
356 (setf (documentation 'end-element 'function)
357 "@arg[handler]{a HAX/SAX handler
358 (see @class{abstract-handler} for details)}
359 @arg[name]{root element name, a rod/string}
360 @return{unspecified}
361 @short{Signals the end of an HTML element.}
363 This event corresponds to the closing tag of an element.
365 @see{start-document}
366 @see{start-element}
367 @see{characters}
368 @see{unescaped}
369 @see{comment}
370 @see{end-document}")
372 (setf (documentation 'characters 'function)
373 "@arg[handler]{a HAX/SAX handler
374 (see @class{abstract-handler} for details)}
375 @arg[data]{rod/string}
376 @return{unspecified}
377 @short{Signals character data.}
379 This event represents character data in a document.
381 @see{start-document}
382 @see{start-element}
383 @see{end-element}
384 @see{comment}
385 @see{end-document}")
387 (setf (documentation 'unescaped 'function)
388 "@arg[handler]{a HAX/SAX handler
389 (see @class{abstract-handler} for details)}
390 @arg[data]{rod/string}
391 @return{unspecified}
392 @short{Escaping bypass.}
394 This event writes raw characters into a document.
396 Beware dragons.
398 @see{start-document}
399 @see{start-element}
400 @see{end-element}
401 @see{comment}
402 @see{end-document}")
404 (setf (documentation 'comment 'function)
405 "@arg[handler]{a HAX/SAX handler
406 (see @class{abstract-handler} for details)}
407 @arg[data]{rod/string}
408 @return{unspecified}
409 @short{Signals a comment.}
411 This event represents a comment.
413 @see{start-document}
414 @see{start-element}
415 @see{end-element}
416 @see{characters}
417 @see{unescaped}
418 @see{end-document}")
420 (setf (documentation 'end-document 'function)
421 "@arg[handler]{a HAX/SAX handler
422 (see @class{abstract-handler} for details)}
423 @return{The return value of this function depends on the handler class.}
424 @short{Signals the end of an HTML document.}
426 This is the last event sent to any handler, and signals the end of
427 serialization.
429 The return value of this function is usually returned to user code
430 by higher-level serialization functions and can be considered the
431 result of serialization and \"return value\" of the handler.
433 @see{start-document}
434 @see{start-element}
435 @see{end-element}
436 @see{characters}
437 @see{unescaped}
438 @see{comment}")