From 90a6079532316ebd63f343242263a3ea3cf58c2c Mon Sep 17 00:00:00 2001 From: dlichteblau Date: Sun, 18 Feb 2007 16:46:32 +0000 Subject: [PATCH] find-element, find-event --- doc/html.xsl | 2 + doc/klacks.xml | 153 ++++++++++++++++++++++++++++++++-------- klacks/klacks-impl.lisp | 6 ++ klacks/klacks.lisp | 64 +++++++++++++++-- klacks/package.lisp | 9 ++- 5 files changed, 198 insertions(+), 36 deletions(-) diff --git a/doc/html.xsl b/doc/html.xsl index f20af8b..cefd6bd 100644 --- a/doc/html.xsl +++ b/doc/html.xsl @@ -58,7 +58,9 @@ Klacks parser
  • diff --git a/doc/klacks.xml b/doc/klacks.xml index 6ca626c..a3de0fd 100644 --- a/doc/klacks.xml +++ b/doc/klacks.xml @@ -16,34 +16,9 @@ support, entity resolution) while offering a more flexible interface than SAX.

    - -

    Example

    - The following example illustrates creation of a klacks source, - use of the consume function to read individual events, - and shows some of the most common event types. + See below for examples.

    -
    * (defparameter *source* (cxml:make-source "<example>text</example>"))
    -*SOURCE*
    -* (klacks:consume *source*)
    -:START-DOCUMENT
    -* (klacks:consume *source*)
    -:START-ELEMENT
    -NIL                      ;namespace URI
    -"example"                ;local name
    -"example"                ;qualified name
    -* (klacks:consume *source*)
    -:CHARACTERS
    -"text"
    -* (klacks:consume *source*)
    -:END-ELEMENT
    -NIL
    -"example"
    -"example"
    -* (klacks:consume *source*)
    -:END-DOCUMENT
    -* (klacks:consume *source*)
    -NIL

    Parsing incrementally using sources

    @@ -161,11 +136,11 @@ NIL peek returns the current event's key and main values.

    -

    Function KLACKS:CONSUME (source) => key, value*
    +
    Function KLACKS:PEEK-NEXT (source) => key, value*

    - Return the same values peek would, and in addition - advance the source forward to the next event. + Advance the source forward to the next event and returns it + like peek would.

    Function KLACKS:PEEK-VALUE (source) => value*
    @@ -173,6 +148,13 @@ NIL

    Like peek, but return only the values, not the key.

    +

    +

    Function KLACKS:CONSUME (source) => key, value*
    +

    +

    + Return the same values peek would, and in addition + advance the source forward to the next event. +

    Function KLACKS:CURRENT-URI (source) => uri
    Function KLACKS:CURRENT-LNAME (source) => string
    @@ -231,11 +213,124 @@ NIL exiting body, whether normally or abnormally.

    +
    +

    Convenience functions

    +

    +

    Function KLACKS:FIND-EVENT (source key)
    + Read events from source and discard them until an event + of type key is found. Return values like peek, or + NIL if no such event was found. +

    +

    +

    Function KLACKS:FIND-ELEMENT (source &optional + lname uri)
    + Read events from source and discard them until an event + of type :start-element is found with matching local name and + namespace uri is found. If lname is nil, any + tag name matches. If uri is nil, any + namespace matches. Return values like peek or NIL if no + such event was found. +

    +

    Bridging Klacks and SAX

    +

    +

    Function KLACKS:SERIALIZE-EVENT (source handler)
    + Send the current klacks events from source as a SAX + events to the SAX handler and consume it. +

    +

    +

    Function KLACKS:SERIALIZE-ELEMENT (source handler + &key document-events)
    + Read all klacks events from the following :start-element to + its :end-element and send them as SAX events + to handler. When this function is called, the current + event must be :start-element, else an error is + signalled. With document-events (the default), + sax:start-document and sax:end-document events + are sent around the element. +

    Function KLACKS:SERIALIZE-SOURCE (source handler)
    Read all klacks events from source and send them as SAX events to the SAX handler.

    + +
    +

    Examples

    +

    + The following example illustrates creation of a klacks source, + use of the peek-next function to read individual events, + and shows some of the most common event types. +

    +
    * (defparameter *source* (cxml:make-source "<example>text</example>"))
    +*SOURCE*
    +
    +* (klacks:peek-next *source*)
    +:START-DOCUMENT
    +
    +* (klacks:peek-next *source*)
    +:START-ELEMENT
    +NIL                      ;namespace URI
    +"example"                ;local name
    +"example"                ;qualified name
    +
    +* (klacks:peek-next *source*)
    +:CHARACTERS
    +"text"
    +
    +* (klacks:peek-next *source*)
    +:END-ELEMENT
    +NIL
    +"example"
    +"example"
    +
    +* (klacks:peek-next *source*)
    +:END-DOCUMENT
    +
    +* (klacks:peek-next *source*)
    +NIL
    + +

    + In this example, find-element is used to skip over the + uninteresting events until the opening child1 tag is + found. Then serialize-element is used to generate SAX + events for the following element, including its children, and an + xmls-compatible list structure is build from those + events. find-element skips over whitespace, + and find-event is used to parse up + to :end-document, ensuring that the source has been + closed. +

    +
    * (defparameter *source*
    +      (cxml:make-source "<example>
    +                           <child1><p>foo</p></child1>
    +                           <child2 bar='baz'/>
    +                         </example>"))
    +*SOURCE*
    +
    +* (klacks:find-element *source* "child1")
    +:START-ELEMENT
    +NIL
    +"child1"
    +"child1"
    +
    +* (klacks:serialize-element *source* (cxml-xmls:make-xmls-builder))
    +("child1" NIL ("p" NIL "foo"))
    +
    +* (klacks:find-element *source*)
    +:START-ELEMENT
    +NIL
    +"child2"
    +"child2"
    +
    +*  (klacks:serialize-element *source* (cxml-xmls:make-xmls-builder))
    +("child2" (("bar" "baz")))
    +
    +* (klacks:find-event *source* :end-document)
    +:END-DOCUMENT
    +NIL
    +NIL
    +NIL
    +
    diff --git a/klacks/klacks-impl.lisp b/klacks/klacks-impl.lisp index 2c5883a..7cd6027 100644 --- a/klacks/klacks-impl.lisp +++ b/klacks/klacks-impl.lisp @@ -79,6 +79,12 @@ (fill-source source) (apply #'values current-values))) +(defmethod klacks:peek-next ((source cxml-source)) + (with-source (source current-key current-values) + (setf current-key nil) + (fill-source source) + (apply #'values current-key current-values))) + (defmethod klacks:consume ((source cxml-source)) (with-source (source current-key current-values) (fill-source source) diff --git a/klacks/klacks.lisp b/klacks/klacks.lisp index 3d646a1..9ff4944 100644 --- a/klacks/klacks.lisp +++ b/klacks/klacks.lisp @@ -69,9 +69,9 @@ (check-type key (member :characters)) characters)) -(defun klacks:serialize-source (source handler) - (loop - (multiple-value-bind (key a b c) (klacks:peek source) +(defun klacks:serialize-event (source handler) + (multiple-value-bind (key a b c) (klacks:peek source) + (let ((result nil)) (case key (:start-document (sax:start-document handler)) @@ -107,12 +107,66 @@ (:end-element (sax:end-element handler a b c)) (:end-document - (return (sax:end-document handler))) + (setf result (sax:end-document handler))) + ((nil) + (error "serialize-event read past end of document")) (t (error "unexpected klacks key: ~A" key))) - (klacks:consume source)))) + (klacks:consume source) + result))) (defun serialize-declaration-kludge (list handler) (loop for (fn . args) in list do (apply fn handler args))) + +(defun klacks:serialize-source (source handler) + (loop + (let ((document (klacks:serialize-event source handler))) + (when document + (return document))))) + +(defun klacks:serialize-element (source handler &key (document-events t)) + (unless (eq (klacks:peek source) :start-element) + (error "not at start of element")) + (when document-events + (sax:start-document handler)) + (labels ((recurse () + (klacks:serialize-event source handler) + (loop + (let ((key (klacks:peek source))) + (ecase key + (:start-element (recurse)) + (:end-element (return)) + ((:characters :comment :processing-instruction) + (klacks:serialize-event source handler))))) + (klacks:serialize-event source handler))) + (recurse)) + (when document-events + (sax:end-document handler))) + +(defun klacks:find-element (source &optional lname uri) + (loop + (multiple-value-bind (key current-uri current-lname current-qname) + (klacks:peek-next source) + (case key + ((nil) + (return nil)) + (:start-element + (when (and (eq key :start-element) + (or (null lname) + (equal lname (klacks:current-lname source))) + (or (null uri) + (equal uri (klacks:current-uri source)))) + (return + (values key current-uri current-lname current-qname)))))))) + +(defun klacks:find-event (source key) + (loop + (multiple-value-bind (this a b c) + (klacks:peek-next source) + (cond + ((null this) + (return nil)) + ((eq this key) + (return (values this a b c))))))) diff --git a/klacks/package.lisp b/klacks/package.lisp index 124071e..276ef13 100644 --- a/klacks/package.lisp +++ b/klacks/package.lisp @@ -24,6 +24,11 @@ #:peek #:peek-value + #:peek-next + #:consume + + #:find-element + #:find-event #:map-attributes #:list-attributes @@ -33,6 +38,6 @@ #:current-characters #:current-cdata-section-p - #:consume - + #:serialize-event + #:serialize-element #:serialize-source))