r5162: *** empty log message ***
[xmlutils.git] / pxml.htm
diff --git a/pxml.htm b/pxml.htm
new file mode 100644 (file)
index 0000000..2cf26d5
--- /dev/null
+++ b/pxml.htm
@@ -0,0 +1,387 @@
+<html>
+
+<head>
+<title>A Lisp Based XML Parser</title>
+<meta name="GENERATOR" content="Microsoft FrontPage 3.0">
+</head>
+
+<body>
+
+<p><strong><big><big>A Lisp Based XML Parser</big></big></strong></p>
+
+<p><a href="#intro">Introduction/Simple Example</a><br>
+<a href="#lxml">LXML parse output format</a><br>
+<a href="#props">parse-xml non-validating parser properties</a><br>
+<a href="#modern">case and international character support issues</a><br>
+<a href="#keyword">parse-xml and packages</a><br>
+<a href="#namespace">parse-xml, the XML Namespace specification, and packages</a><br>
+<a href="#unicode-scalar">ACL does not support Unicode 4 byte scalar values</a><br>
+<a href="#big-endian">only little-endian Unicode tested in ACL 6.0 beta</a><br>
+<a href="#debug">debugging aids</a><br>
+<a href="#conformance">XML Conformance test results</a><br>
+<a href="#build">Compiling and Loading the parser</a><br>
+<a href="#reference">parse-xml reference</a></p>
+
+<p><a name="intro"></a>The <strong>parse-xml </strong>generic function processes XML
+input, returning a list of XML tags,<br>
+attributes, and text. Here is a simple example:<br>
+<br>
+(parse-xml &quot;&lt;item1&gt;&lt;item2 att1='one'/&gt;this is some
+text&lt;/item1&gt;&quot;)<br>
+<br>
+--&gt;<br>
+<br>
+((item1 ((item2 att1 &quot;one&quot;)) &quot;this is some text&quot;))<br>
+<br>
+The output format is known as LXML format.<br>
+<br>
+<a name="lxml"></a><strong>LXML Format</strong><br>
+<br>
+LXML is a list representation of XML tags and content.<br>
+<br>
+Each list member may be:<br>
+<br>
+a. a string containing text content, such as &quot;Here is some text with a &quot;<br>
+<br>
+b. a list representing a XML tag with associated attributes and/or content,
+such as ('item1 &quot;text&quot;) or (('item1 :att1 &quot;help.html&quot;)
+&quot;link&quot;). If the XML tag
+does not have associated attributes, then the first list member will be a
+symbol representing the XML tag, and the other elements will
+represent the content, which can be a string (text content), a symbol (XML
+tag with no attributes or content), or list (nested XML tag with
+associated attributes and/or content). If there are associated attributes,
+then the first list member will be a list containing a symbol
+followed by two list members for each associated attribute; the first member is a
+symbol representing the attribute, and the next member is a string corresponding
+to the attribute value.<br>
+<br>
+c. XML comments and or processing instructions - see the more detailed example below for
+further information.</p>
+
+<p><a name="props"></a><strong>Non Validating Parser Properties</strong></p>
+
+<p>Parse-xml is a non-validating XML parser. It will detect non-well-formed XML input.
+When<br>
+processing valid XML input, parse-xml will optionally produce the same output as a
+validating <br>
+parser would, including the processing of an external DTD subset and external entity
+declarations.<br>
+<br>
+By default, parse-xml outputs a DTD parse along with the parsed XML contents. The DTD
+parse may<br>
+be optionally suppressed. The following example shows DTD parsed output components:</p>
+
+<p>(defvar *xml-example-external-url*<br>
+&nbsp;&nbsp; &quot;&lt;!ENTITY ext1 'this is some external entity %param1;'&gt;&quot;)<br>
+<br>
+(defun example-callback (var-name token &amp;optional public)<br>
+&nbsp; (declare (ignorable token public))<br>
+&nbsp; (setf var-name (uri-path var-name))<br>
+&nbsp; (if* (equal var-name &quot;null&quot;) then nil<br>
+&nbsp;&nbsp;&nbsp; else<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (let ((string (eval (intern var-name (find-package
+:user)))))<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (make-string-input-stream string))))<br>
+<br>
+(defvar *xml-example-string*<br>
+&quot;&lt;?xml version='1.0' encoding='utf-8'?&gt;<br>
+&lt;!-- the following XML input is well-formed but its validity has not been checked ...
+--&gt;<br>
+&lt;?piexample this is an example processing instruction tag ?&gt;<br>
+&lt;!DOCTYPE example SYSTEM '*xml-example-external-url*' [<br>
+&nbsp;&nbsp; &lt;!ELEMENT item1 (item2* | (item3+ , item4))&gt;<br>
+&nbsp;&nbsp; &lt;!ELEMENT item2 ANY&gt;<br>
+&nbsp;&nbsp; &lt;!ELEMENT item3 (#PCDATA)&gt;<br>
+&nbsp;&nbsp; &lt;!ELEMENT item4 (#PCDATA)&gt;<br>
+&nbsp;&nbsp; &lt;!ATTLIST item1<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; att1 CDATA #FIXED 'att1-default'<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; att2 ID #REQUIRED<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; att3 ( one | two | three ) 'one'<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; att4 NOTATION ( four | five ) 'four' &gt;<br>
+&nbsp;&nbsp; &lt;!ENTITY % param1 'text'&gt;<br>
+&nbsp;&nbsp; &lt;!ENTITY nentity SYSTEM 'null' NDATA somedata&gt;<br>
+&nbsp;&nbsp; &lt;!NOTATION notation SYSTEM 'notation-processor'&gt;<br>
+&nbsp;&nbsp; ]&gt;<br>
+&lt;item1 att2='1'&gt;&lt;item3&gt;&amp;ext1;&lt;/item3&gt;&lt;/item1&gt;&quot;)<br>
+<br>
+(pprint (parse-xml *xml-example-string* :external-callback 'example-callback))<br>
+<br>
+--&gt;<br>
+<br>
+((:xml :version &quot;1.0&quot; :encoding &quot;utf-8&quot;)<br>
+&nbsp; (:comment &quot; the following XML input is well-formed but may or may not be valid
+&quot;)<br>
+&nbsp; (:pi :piexample &quot;this is an example processing instruction tag &quot;)<br>
+&nbsp; (:DOCTYPE :example<br>
+&nbsp;&nbsp;&nbsp; (:[ (:ELEMENT :item1 (:choice (:* :item2) (:seq (:+ :item3) :item4))) <br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (:ELEMENT :item2 :ANY)<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (:ELEMENT :item3 :PCDATA) (:ELEMENT :item4
+:PCDATA)<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (:ATTLIST item1 (att1 :CDATA :FIXED
+&quot;att1-default&quot;) (att2 :ID :REQUIRED)<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (att3
+(:enumeration :one :two :three) &quot;one&quot;) <br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (att4 (:NOTATION
+:four :five) &quot;four&quot;))<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (:ENTITY :param1 :param &quot;text&quot;) <br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (:ENTITY :nentity :SYSTEM &quot;null&quot;
+:NDATA :somedata)<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (:NOTATION :notation :SYSTEM
+&quot;notation-processor&quot;))<br>
+&nbsp;&nbsp;&nbsp; (:external (:ENTITY :ext1 &quot;this is some external entity
+text&quot;)))<br>
+&nbsp;&nbsp; ((item1 att1 &quot;att1-default&quot; att2 &quot;1&quot; att3 &quot;one&quot;
+att4 &quot;four&quot;) <br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (item3 &quot;this is some external entity
+text&quot;)))<br>
+<br>
+<br>
+<strong><big>Usage Notes</big></strong><br>
+<br>
+<ol>
+<li><a name="modern"></a>The parse-xml function has been primarily compiled and tested in a
+modern ACL. However, in an ANSI Lisp with wide character support, it DOES pass the valid
+component of the conformance suite in the same manner as it does in a Modern Lisp. The
+parser's successful operation in all potential situations depends on wide character support.
+<br><br>
+</li>
+<li><a name="keyword"></a>The parser uses the keyword package for DTD tokens and other
+special XML tokens. Since element and attribute token symbols are usually interned
+in the current package, it is not recommended to execute parse-xml
+when the current package is the keyword package.
+<br><br>
+</li>
+<li><a name="namespace"></a>The XML parser supports the XML Namespaces specification. The
+parser recognizes a &quot;xmlns&quot; attribute and attribute names starting with
+&quot;xmlns:&quot;.
+As per the specification, the parser expects that the associated value
+is an URI string. The parser then associates XML Namespace prefixes with a
+Lisp package provided via the parse-xml :uri-to-package option or, if
+necessary, a package created on the fly. The following example demonstrates
+this behavior:<br>
+
+<p>(setf *xml-example-string4*<br>
+&nbsp;&nbsp; &quot;&lt;bibliography<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; xmlns:bib='http://www.bibliography.org/XML/bib.ns'<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; xmlns='urn:com:books-r-us'&gt;<br>
+&nbsp;&nbsp; &lt;bib:book owner='Smith'&gt;<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;bib:title&gt;A Tale of Two Cities&lt;/bib:title&gt;<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;bib:bibliography<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; xmlns:bib='http://www.franz.com/XML/bib.ns'<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; xmlns='urn:com:books-r-us'&gt;<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;bib:library branch='Main'&gt;UK
+Library&lt;/bib:library&gt;<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;bib:date calendar='Julian'&gt;1999&lt;/bib:date&gt;<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/bib:bibliography&gt;<br>
+&nbsp;&nbsp; &lt;bib:date calendar='Julian'&gt;1999&lt;/bib:date&gt;<br>
+&nbsp;&nbsp; &lt;/bib:book&gt;<br>
+&lt;/bibliography&gt;&quot;)<br>
+<br>
+(setf *uri-to-package* nil)<br>
+(setf *uri-to-package*<br>
+&nbsp;&nbsp; (acons (parse-uri <a href="http://www.bibliography.org/XML/bib.ns">&quot;http://www.bibliography.org/XML/bib.ns&quot;</a>)<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (make-package &quot;bib&quot;) *uri-to-package*))<br>
+(setf *uri-to-package*<br>
+&nbsp;&nbsp; (acons (parse-uri <a href="http://www.bibliography.org/XML/bib.ns">&quot;</a>urn:com:books-r-us<a
+href="http://www.bibliography.org/XML/bib.ns">&quot;</a>)<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (make-package &quot;royal&quot;) *uri-to-package*))<br>
+(setf *uri-to-package*<br>
+&nbsp;&nbsp; (acons (parse-uri <a href="http://www.bibliography.org/XML/bib.ns">&quot;</a>http://www.franz.com/XML/bib.ns<a
+href="http://www.bibliography.org/XML/bib.ns">&quot;</a>)<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (make-package &quot;franz-ns&quot;) *uri-to-package*))<br>
+(pprint (multiple-value-list<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (parse-xml
+*xml-example-string4*<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; :uri-to-package
+*uri-to-package*)))<br>
+<br>
+--&gt;<br>
+((((bibliography |xmlns:bib| <a href="http://www.bibliography.org/XML/bib.ns">&quot;http://www.bibliography.org/XML/bib.ns&quot;</a><br>
+&nbsp;&nbsp;&nbsp;&nbsp; xmlns &quot;urn:com:books-r-us&quot;)<br>
+&nbsp;&nbsp;&nbsp; &quot;<br>
+&nbsp;&nbsp;&nbsp; &quot;<br>
+&nbsp;&nbsp; ((bib::book royal::owner &quot;Smith&quot;) &quot;<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot; (bib::title &quot;A Tale of Two
+Cities&quot;) &quot;<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;<br>
+&nbsp;&nbsp;&nbsp; ((bib::bibliography royal::|xmlns:bib|<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;http://www.franz.com/XML/bib.ns&quot; royal::xmlns<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;urn:com:books-r-us&quot;)<br>
+&nbsp;&nbsp;&nbsp;&nbsp; &quot;<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot; ((franz-ns::library royal::branch
+&quot;Main&quot;) &quot;UK Library&quot;) &quot;<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot; ((franz-ns::date royal::calendar
+&quot;Julian&quot;) &quot;1999&quot;) &quot;<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;)<br>
+&nbsp;&nbsp;&nbsp;&nbsp; &quot;<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot; ((bib::date royal::calendar
+&quot;Julian&quot;) &quot;1999&quot;) &quot;<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;)<br>
+&nbsp;&nbsp;&nbsp;&nbsp; &quot;<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;))<br>
+((#&lt;uri http://www.franz.com/XML/bib.ns&gt; . #&lt;The franz-ns package&gt;)<br>
+&nbsp; (#&lt;uri urn:com:books-r-us&gt; . #&lt;The royal package&gt;)<br>
+&nbsp; (#&lt;uri http://www.bibliography.org/XML/bib.ns&gt; . #&lt;The bib package&gt;)))<br>
+<br>
+</li>
+<li>In the absence of XML Namespace attributes, element and attribute symbols are interned
+in the current package. Note that this implies that attributes and elements referenced
+in DTD content will be interned in the current package.
+</li>
+<li>The parse-xml function has been tested using the OASIS conformance test suite (see
+details below). The test suite has wide coverage across possible XML and DTD syntax,
+but there may be some syntax paths that have not yet been tested or completely
+supported. Here is a list of currently known syntax parsing issues:
+<ul>
+<li><a name="unicode-scalar"></a>ACL does not support 4 byte Unicode scalar values, so
+input containing such data
+will not be processed correctly. (Note, however, that parse-xml does correctly detect
+and process wide Unicode input.)
+</li>
+<li><a name="big-endian"></a>The OASIS tests that contain wide Unicode all use a
+little-endian encoded Unicode.
+Changes to the unicode-check function are required to also support big-endian encoded
+Unicode. (Note also that this issue may be resolved by an ACL 6.0 final release change.)
+</li>
+<li>An initial &lt;?xml declaration in external entity files is skipped without a check
+being made to see if the &lt;?xml declaration is itself incorrect.
+</li>
+</ul>
+</li>
+<li><a name="debug"></a>When investigating possible parser errors or examining more closely
+where the parser
+determined that the input was non-well-formed, the net.xml.parser internal symbols
+*debug-xml* and *debug-dtd* are useful. When not bound to nil, these variables cause
+lexical analysis and intermediate parsing results to be output to *standard-output*.
+</li>
+<li><a name="loading"></a>It is necessary to load the <b>pxml</b> module before using it.
+Typically this can be done by evaluating <b>(require&nbsp;:pxml)</b>.
+</li>
+</ol>
+<a name="conformance"></a><strong>XML Conformance Test Suite</strong><br>
+<br>
+Using the OASIS test suite <a href="http://www.oasis-open.org">(http://www.oasis-open.org)</a>,
+here are the current parse-xml results:<br>
+<br>
+xmltest/invalid:&nbsp;&nbsp;&nbsp; Not tested, since parse-xml is a non-validating parser<br>
+<br>
+not-wf/<br>
+<br>
+&nbsp;&nbsp;&nbsp; ext.sa: 3 tests; all pass<br>
+&nbsp;&nbsp;&nbsp; not-sa: 8 tests; all pass<br>
+&nbsp;&nbsp;&nbsp; sa: 186 tests; the following fail:<br>
+<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 170.xml: fails because ACL does not support 4
+byte Unicode scalar values<br>
+<br>
+valid/<br>
+<br>
+&nbsp;&nbsp;&nbsp; ext-sa: 14 tests; all pass<br>
+&nbsp;&nbsp;&nbsp; not-sa: 31 tests; all pass<br>
+&nbsp;&nbsp;&nbsp; sa: 119 tests: the following fail:<br>
+<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 052.xml, 064.xml, 089.xml: fails because ACL
+does not support 4 byte <br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+Unicode scalar values<br>
+<br>
+<a name="build"></a><big><strong>Compiling and Loading</strong></big><br>
+<br>
+Load build.cl into a modern ACL session will result in a pxml.fasl file that can
+subsequently be<br>
+loaded in a modern ACL to provide XML parsing functionality.<br>
+<br>
+-------------------------------------------------------------------------------------------<br>
+<br>
+<a name="reference"></a><big><strong>parse-xml reference</strong></big><br>
+<br>
+parse-xml&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [Generic
+function]<br>
+<br>
+Arguments: input-source &amp;key external-callback content-only <br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; general-entities
+parameter-entities<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; uri-to-package<br>
+<br>
+Returns multiple values:<br>
+<ol>
+<li>LXML and parsed DTD output, as described above.</li>
+<li>An association list containing the uri-to-package argument conses (if any)
+and conses associated with any XML Namespace packages created during the
+parse (see uri-to-package argument description, below).</li>
+</ol>
+The external-callback argument, if specified, is a function object or symbol
+that parse-xml will execute when encountering an external DTD subset
+or external entity DTD declaration. Here is an example which shows that
+arguments the function should expect, and the value it should return:
+<br><pre>
+(defun file-callback (uri-object token &amp;optional public)
+  ;; The uri-object is an ACL URI object created from
+  ;; the XML input. In this example, this function
+  ;; assumes that all uri's will be file specifications.
+  ;;
+  ;; The token argument identifies what token is associated
+  ;; with the external parse (for example :DOCTYPE for external
+  ;; DTD subset
+  ;;
+  ;; The public argument contains the associated PUBLIC string,
+  ;; when present
+  ;;
+  (declare (ignorable token public))
+  ;; An open stream is returned on success,
+  ;; a nil return value indicates that the external
+  ;; parse should not occur.
+  ;; Note that parse-xml will close the open stream before exiting.
+  (ignore-errors (open (uri-path uri-object))))
+</pre>
+<p>
+The general-entities argument is an association list containing general entity symbol
+and replacement text pairs. The entity symbols should be in the keyword package.
+Note that this option may be useful in generating desirable parse results in
+situations where you do not wish to parse external entities or the external DTD subset.
+<p>
+The parameter-entities argument is an association list containing parameter entity symbol
+and replacement text pairs. The entity symbols should be in the keyword package.
+Note that this option may be useful in generating desirable parse results in
+situations where you do not wish to parse external entities or the external DTD subset.
+<p>
+The uri-to-package argument is an association list containing uri objects and package
+objects. Typically, the uri objects correspond to XML Namespace attribute values, and
+the package objects correspond to the desired package for interning symbols associated
+with the uri namespace. If the parser encounters an uri object not contained in this list,
+it will generate a new package. The first generated package will be named
+net.xml.namespace.0,
+the second will be named net.xml.namespace.1, and so on.
+<h3>parse-xml methods</h3>
+<pre>
+(parse-xml (p stream) &amp;key
+                      external-callback content-only
+                      general-entities
+                      parameter-entities
+                      uri-to-package)
+
+(parse-xml (str string) &amp;key
+                        external-callback content-only
+                        general-entities
+                        parameter-entities
+                        uri-to-package)
+</pre>
+An easy way to parse a file containing XML input:
+<pre>
+(with-open-file (p &quot;example.xml&quot;)
+  (parse-xml p :content-only p))
+</pre>
+<h3>net.xml.parser unexported special variables:</h3>
+<p>
+*debug-xml*<br>
+<br>
+When true, parse-xml generates XML lexical state and intermediary
+parse result debugging output.
+<p>
+*debug-dtd*<br>
+<br>
+When true, parse-xml generates DTD lexical state and intermediary
+parse result debugging output.
+</body>
+</html>