this memo introduces a common lisp package which implements a primitive
"processor" for data streams serialized according to the proposed
"extensible
markup language" standard (of which the
08.12.97 version
accompanies this distribution).
this document describes the proposed approach and explains how
to try the processor out.
this version of the processor can parse and process two sorts of documents
XML-ELEMENT.APPEND-ELEMENT
or on (SETF XML-NODE.CONTENT)(t XML-ELEMENT).
DTD and XML streams are, for the most part, processed identically.
a distinction is limited to two interface functions which
read the named respective stream type and enforce content constraints on the results.
READ-DTD-STREAM (generic-function (stream) DTD)
READ-XML-STREAM (generic-function (stream) XML-ELEMENT)
additional interface functions are provided to read individual elements,
read PCDATA, CDATA, etc. look at "interface.lisp" for descriptions.
the reading and construction process is controlled by numerous parameter bindings. there are, for example, bindings to specify the class of each node to be instantiated. see "global-bindings.lisp" for explanations.
this prototype includes support for the following elements. a distinction is made
between parsing and processing. the former means that the element is read
and instantiated. the latter means that the relations or side effects specified
by the standard are implemented.
| element | parsed | processed | comments |
|---|---|---|---|
| <!-- | yes | yes | comments are appended to the parent node. they are bound separate from element children and keyed to the immediately preceeding element. |
| !ELEMENT | yes | yes | element definitions are bound to the parent DTD. the names are required to be of the same package as the DTD. where element references appear in a model, supplementary (non-standard) ELEMENT-REFERENCE nodes are generated to accommodate forward references and to accept instance-specific attributes. |
| !ENTITY | yes | yes | |
| !NOTATION | yes | no | |
| ![CDATA[ | yes | yes | bound as text to the parent node |
| ![INCLUDE[ | yes | yes | read as a string and reparsed. the last element in the stream is returned. |
| ![IGNORE[ | yes | yes | read as string and discarded |
| ?XML | yes | yes | no support for encoding |
| ?NAMESPACE | yes | yes | namespaces are implemented through packages. each DTD is associated
with a package which has the
same name as the DTD. where a DTD has been included by virtue of a namespace reference, the respective package is renamed according to the "AS" attribute. the original name becomes a nickname. |
| !DOCTYPE | yes | yes | in a document type definition, it specifies the name of the DTD.
in an XML document, it references the respective DTD and or provides an internal definition |
| &entity; | yes | yes | named entity references are appended to the content of containing elements. methods to retrieve and parse the referenced entity's content are not yet implemented. if they were PCDATA only, the dtd is available through the containing elements dtd-element-reference and the string content could be reread with READ-PROCESS-PCDATA. this should wait, however, until we understand the meaning and uses of notations, since things like external entities and binary data aren't that simple. |
| %entity; | yes | yes | wherever they are permitted (type names, models, attributes, ... ?), parameter entities are supported by concatenating streams. the entity value is prepended to the active stream in place of the entity reference. this means that syntactic restrictions specified by the standard for entity values are not enforced. |
| element | yes | yes | elements are parsed and processed. the processing step appends an element
to its parent node.
attributes are parsed and bound to the element node. limited attribute normalization is not supported. empty elements are supported. |
the processor performs limited case-folding. all element names
are folded to upper case. attribute names and values are left unmodified
with the exceptions: ID, IDREF, IDREFS, NMTOKEN, NMTOKENS. although element
name folding is not specified by the standard, ID value folding would appear to require
it.
i do not believe this is according to standard, but i do not yet understand the
case handling rules.
it is implemented in terms of three constructs: (take a look at "markup-reader.lisp")
the parsing process can be understood to pass control among several levels. from outermost to innermost they are:
read-dtd-stream read-xml-stream markup-dispatching-reader-macro read-element-stream read-element element-reader-macro read-typed-markup-element read-typed-processing-instruction read-typed-markup-declaration read-typed-section-element read-markup-tag-attributes read-markup-tag-type read-markup-tag-parameters read-process-pcdata read-process-cdata read-process-pcdata read-pcdata read-cdata read
there are also several test / example files
the code is distributed among some two dozen files. they can be divided into four categories:
this prototype is no paradigm of efficiency, it is merely a draft
tool for serializing data for exchange with java "front-end" processes.
it is in flux and future compatibility is not assured.
it should be seen as a base for discussion and possible further implementation only.
please do not REdistribute it. it is available from the cl-http contributed source
archive, from which, for the time being, all distribution should occur.
working with a 2.5M portion of the old testament xml-example file, for instance, the
performance looks like this (mcl 4.1, ppc7300/200):
? (room)
There are at least 13,426,192 bytes of available RAM.
Total Size Free Used
Mac Heap: 1295856 (1265K) 477552 (466K) 818304 (800K)
Lisp Heap: 15453968 (15092K) 12948640 (12645K) 2505328 (2447K)
Stacks: 240368 (235K) 236804 (231K) 3564 (3K)
Static: 5584 (5K) 0 (0K) 5584 (5K)
? (time (let ((*xml-text-class* nil)) ; intern text simply as strings
(defparameter *ot*
(read-xml-stream #P"entwicklung-server:Source:LISP:XML:otxml Folder:ot-abridged.xml"))))
(LET ((*XML-TEXT-CLASS* NIL)) (DEFPARAMETER *OT* (READ-XML-STREAM #P"entwicklung-server:Source:LISP:XML:otxml Folder:ot-abridged.xml"))) took 125,784 milliseconds (125.784 seconds) to run.
Of that, 19,010 milliseconds (19.010 seconds) were spent in The Cooperative Multitasking Experience.
11,065,544 bytes of memory allocated.
*OT*
? (gc)
NIL
? (room)
There are at least 5,853,552 bytes of available RAM.
Total Size Free Used
Mac Heap: 1295840 (1265K) 477552 (466K) 818288 (800K)
Lisp Heap: 15453984 (15092K) 5376000 (5250K) 10077984 (9842K)
Stacks: 240368 (235K) 236804 (231K) 3564 (3K)
Static: 5584 (5K) 0 (0K) 5584 (5K)
?
which is several factors slower than the equivalent c++ parser