xexp.rkt
#lang typed/racket/base/no-check
;;; @Package     xexp
;;; @Subtitle    SXML/xexp Representation of XML and HTML in Racket
;;; @HomePage    http://www.neilvandyke.org/racket-xexp/
;;; @Author      Neil Van Dyke
;;; @Version     0.1
;;; @Date        2011-08-21
;;; @PLaneT      neil/xexp:1:=0

;; $Id: xexp.rkt,v 1.27 2011/08/22 04:52:47 neilpair Exp $

;;; @legal
;;; Copyright @copyright{} 2011 Neil Van Dyke.  This program is Software;
;;; you can redistribute it and/or modify it under the terms of the GNU Lesser
;;; General Public License as published by the Free Software Foundation; either
;;; version 3 of the License (LGPL 3), or (at your option) any later version.
;;; This program is distributed in the hope that it will be useful, but without
;;; any warranty; without even the implied warranty of merchantability or
;;; fitness for a particular purpose.  See
;;; @indicateurl{http://www.gnu.org/licenses/} for details.  For other licenses
;;; and consulting, please contact the author.
;;; @end legal

;;; @section Introduction

;;; @i{Note: This package is in a state of active development, and some
;;; interface changes, perhaps not backward-compatible, are expected.
;;; Documentation is gravely lacking.}

;;; @uref{http://pobox.com/~oleg/ftp/Scheme/SXML.html, SXML} is a
;;; representation for XML in Scheme, defined by Oleg Kiselyov.
;;; ``SXML/@i{xexp}'' is the temporary name for a format for Racket that's
;;; based on SXML and is mostly compatible with it.  The current plan is,
;;; hopefully, for the ``/@i{xexp}'' part of the name to go away, and for SXML
;;; and @i{xexp} to merge.  For now, Racket language identifiers based on
;;; SXML/@i{xexp} will have ``@code{xexp}'' instead of ``@code{sxml}'', because
;;; we do not want to call something ``SXML'' if it is not strictly SXML.
;;; (And, historically, @i{xexp} was much more different from SXML, while we
;;; experimented with unifying SXML, SHTML, and PLT @i{xexpr}, but we have
;;; decided to move back to as compatible with SXML as practical.)  This
;;; documentation will be fleshed out in a later version of the SXML/@i{xexpr}
;;; tools.

;; TODO: !!! Incorporate bits of this documentation for SHTML, as appropriate.
;;
;; @subsection Differences with SXML
;; @i{xexp} can be defined as differences from SXML:
;;
;; @itemize
;;
;; @item
;; @i{xexp} is intended for representing HTML, including XHTML, but not XML in
;; general.
;;
;; @item
;; When used to represent HTML, element names are all lower-case, even when
;; used to represent a version of HTML that is not case-sensitive.
;;
;; @item
;; @i{xexp} syntax must be ordered as in SXML first normal form (1NF).  For
;; example, any attributes list must precede child elements.  @i{xexp} tools
;; @i{may} be permissive about accepting other orderings, but @i{must not} emit
;; any ordering but 1NF ordering.
;;
;; @item
;; @i{xexp} elements do not have namespace qualifiers.  @i{xexp} tools @i{may} accept
;; qualifiers, but are not required to, and should not emit @i{xexp} with
;; qualifiers.
;;
;; @item
;; The SXML keyword symbols, such as @code{*TOP*}, are defined to be in
;; all-uppercase, regardless of the case-sensitivity of the reader of the
;; hosting Scheme implementation in any context.
;;
;; @item
;; @i{xexp} adds a special @code{&} syntax for non-ASCII characters.  This is
;; because not all character entity references used in HTML can be converted
;; to Scheme characters in all R5RS Scheme implementations, nor represented in
;; conventional text files or other common external text formats to which one
;; might wish to write @i{xexp}.  The syntax is @code{(& @var{val})}, where
;; @var{val} is the symbolic name of the character as a symbol or string, or
;; an integer with the numeric value of the character.
;;
;; @end itemize
;;
;; @i{xexp} was defined originally as SHTML in
;; @uref{http:/www.neilvandyke.org/htmlprag/, HtmlPrag} 0.11 (2004-05-13).
;; HtmlPrag used an SXML subset since its first public release on 2003-01-31.
;; Earlier, unreleased versions of HtmlPrag used a representation closer to
;; that currently used by some PLT Scheme standard libraries.
;;
;; @subsection Differences of xexp from SHTML
;;
;; !!! #t is no longer a valid attribute value

;;; @section xexp and SXML Tools
;;;
;;; Libraries using @i{xexp} include:
;;;
;;; @table @asis
;;;
;;; @item @uref{http://www.neilvandyke.org/racket-html-parsing/, html-parsing}
;;; Permissively parsing HTML to @i{xexp}.
;;;
;;; @item @uref{http://www.neilvandyke.org/racket-html-writing/, html-writing}
;;; Writing HTML from @i{xexp}.
;;;
;;; @item @uref{http://www.neilvandyke.org/racket-html-template/, html-template}
;;; Writing HTML from @i{xexp} templates.
;;;
;;; @item @uref{http://www.neilvandyke.org/webscraperhelper/, WebScraperHelper}
;;; Example-based SXPath query generation for @i{xexp}.
;;;
;;; @end table
;;;
;;; @noindent
;;; There are also libraries for SXML, which generally can be used for @i{xexp}:
;;;
;;; @table @asis
;;;
;;; @item @uref{http://okmij.org/ftp/Scheme/xml.html#SXPath, SXPath}
;;; XPath query language implementation for SXML, by Oleg Kiselyov.
;;;
;;; @item @uref{http://celtic.benderweb.net/sxml-match/, sxml-match}
;;; Pattern-matching of SXML, by Jim Bender.
;;;
;;; @item @uref{http://www196.pair.com/lisovsky/xml/ssax/, SSAX}
;;; Parsing of XML to SXML, by Oleg Kiselyov, and maintained by Kirill Lisovsky.
;;;
;;; @end table

;;; @section Definitions

;;; Some definitions used by many SXML/@i{xexp} packages...

;;; @subsection Exceptions

;; Note: These are here for future expansion.  For example, we might want to
;; give positional information from metadata.

(struct: exn:fail:invalid-xexp exn:fail
         ((expected     : String)
          (context-xexp : Any)
          (invalid-xexp : Any))
         #:transparent)

;;; @defproc make-invalid xexp-exc
;;;
;;; !!!

(define (make-invalid-xexp-exc
         sym
         #:continuation-marks continuation-marks
         #:expected           expected
         #:invalid-xexp       invalid-xexp
         #:context-xexp       (context-xexp (void)))
  (exn:fail:invalid-xexp
   (if (void? context-xexp)
       (format "~A: invalid xexp: expected ~A; got ~S"
               sym
               expected
               invalid-xexp)
       (format "~A: invalid xexp: expected ~A; got ~S in ~S"
               sym
               expected
               invalid-xexp
               context-xexp))
   continuation-marks
   expected
   context-xexp
   invalid-xexp))

;;; @defproc raise-invalid-xexp-error
;;;
;;; !!!

(define-syntax raise-invalid-xexp-error
  (syntax-rules ()
    ((_ SYM
        #:expected     EXPECTED
        #:invalid-xexp INVALID-XEXP
        #:context-xexp CONTEXT-XEXP)
     (raise (make-invalid-xexp-exc
             SYM
             #:continuation-marks (current-continuation-marks)
             #:expected           EXPECTED
             #:invalid-xexp       INVALID-XEXP
             #:context-xexp       CONTEXT-XEXP)))
    ((_ SYM
        #:expected     EXPECTED
        #:invalid-xexp INVALID-XEXP)
     (raise-invalid-xexp-error
      SYM
      #:expected           EXPECTED
      #:invalid-xexp       INVALID-XEXP
      #:context-xexp       (void)))))

;;; @subsection Misc.

;;; The following definitions are used by some @i{xexp}-related libraries.

(define-type Xexp Any)

;; @defvar  xexp-comment-symbol
;; @defvarx xexp-pi-symbol
;; @defvarx xexp-top-symbol
;; @defvarx xexp-decl-symbol
;;
;; These variables are bound to the following case-sensitive symbols used in
;; @i{xexp}, respectively: @code{*COMMENT*}, @code{*DECL*}, @code{*EMPTY*},
;; @code{*END*}, @code{*ENTITY*}, @code{*PI*}, @code{*START*}, @code{*TEXT*},
;; and @code{*TOP*}.  These can be used in lieu of the literal symbols in
;; programs read by a case-insensitive Scheme reader.  (Note: Scheme
;; implementators who have not yet made @code{read} case-sensitive by default
;; are encouraged to do so.)

;;(define xexp-comment-symbol '*COMMENT*)
;;(define xexp-decl-symbol    '*DECL*)
;;(define xexp-pi-symbol      '*PI*)
;;(define xexp-top-symbol     '*TOP*)
;;(define xexp-splice-symbol  '*SPLICE*)

;; (define-type XexpNamedCharRef (Pair '& (Pair Any Any)))

;; @defvar  xexp-named-char-id
;; @defvarx xexp-numeric-char-id
;;
;; These variables are bound to the @i{xexp} entity public identifier strings
;; used in SXML @code{*ENTITY*} named and numeric character entity
;; references.

;; (define xexp-named-char-id   "xexp-named-char")
;; (define xexp-numeric-char-id "xexp-numeric-char")

;;; @defproc make-xexp-char-ref val
;;;
;;; Yields an @i{xexp} character entity reference for @var{val}.  For example:
;;;
;;; @lisp
;;; (make-xexp-char-ref "rArr")                  @result{} (& rArr)
;;; (make-xexp-char-ref (string->symbol "rArr")) @result{} (& rArr)
;;; (make-xexp-char-ref 151)                     @result{} (& 151)
;;; @end lisp

(: make-xexp-char-ref (Symbol -> XexpNamedCharRef))

(define (make-xexp-char-ref val)
  (list '&
        (cond ((symbol?  val) val)
              ;; ((integer? val) val)
              ;; ((string?  val) (string->symbol val))
              (else (error 'make-xexp-char-ref
                           "invalid xexp reference value: ~S"
                           val)))))

;; TODO:
;;
;; (define (xexp-entity? x)
;;   (and (xexp-char-ref-value entity) #t))

;;; @defproc xexp-char-ref-value obj
;;;
;;; Yields the value for the @i{xexp} entity @var{obj}, or @code{#f} if @var{obj}
;;; is not a recognized entity.  Values of named entities are symbols, and
;;; values of numeric entities are numbers.  An error may raised if @var{obj}
;;; is an entity with system ID inconsistent with its public ID.  For example:
;;;
;;; @lisp
;;; (define (f s) (xexp-char-ref-value (cadr (html->xexp s))))
;;; (f " ")  @result{} nbsp
;;; (f "ߐ") @result{} 2000
;;; @end lisp

;; TODO: !!! rename to xexp-reference-SOMETHING (where SOMETHING is idealy
;; whatever the XML grammar calls it)

(: %find-first-xexp-non-extraneous-list (Any -> Any))
(define (%find-first-xexp-non-extraneous-list lst)
  (let loop ((lst lst))
    (: loop (Any -> Any))
    (cond ((null? lst) #f)
          ((pair? lst)
           (loop (car lst))
           (loop (cdr lst)))
          (else lst))))

(: %assert-only-xexp-extraneous-lists (Any -> Void))
(define (%assert-only-xexp-extraneous-lists lst)
  (cond ((%find-first-xexp-non-extraneous-list lst)
         => (lambda (x)
              (raise-invalid-xexp-error
               '%assert-only-xexp-extraneous-lists
               #:expected "nothing except extraneous lists and nulls"
               #:invalid-xexp x)))))

(: xexp-char-ref-value (XexpNamedCharRef -> Symbol))
(define (xexp-char-ref-value char-ref)
  (or (let: loop-find-symbol : (U Symbol False)
            ((lst : Any (cdr char-ref)))
        (cond
         ((null? lst) #f)
         ((pair? lst)
          (let ((head (car lst)))
            (cond ((symbol? head)
                   (%assert-only-xexp-extraneous-lists (cdr lst))
                   head)
                  ((pair? head)
                   (cond ((loop-find-symbol head)
                          => (lambda (found)
                               (%assert-only-xexp-extraneous-lists (cdr head))
                               (%assert-only-xexp-extraneous-lists (cdr lst))
                               found))
                         ((loop-find-symbol (cdr head))
                          => (lambda (found)
                               (%assert-only-xexp-extraneous-lists (cdr lst))
                               found))
                         (else (loop-find-symbol (cdr lst)))))
                  ((null? head) (loop-find-symbol (cdr lst)))
                  (else (raise-invalid-xexp-error
                         'xexp-char-ref-value
                         #:expected     "char-ref body"
                         #:invalid-xexp head
                         #:context-xexp char-ref)))))
         (else (raise-invalid-xexp-error
                'xexp-char-ref-value
                #:expected     "proper list in char-ref body"
                #:invalid-xexp lst
                #:context-xexp char-ref))))
      (raise-invalid-xexp-error 'xexp-char-ref-value
                                #:expected     "char-ref"
                                #:invalid-xexp char-ref)))

;; TODO: !!! CLEAN UP THIS LOGIC, AND ALSO DECIDE WHETHER THE PUBLIC/SYSTEM
;; STUFF IS NEEDED
;; (cond ;; ((not (pair? entity)) #f)
;; ((null? (cdr entity)) #f)
;; ((eqv? (car entity) '&)
;; TODO: Error-check for extraneous list members?
;; (let ((val (cadr entity)))
;;   (cond ((symbol?  val) val)
;; ((integer? val) val)
;; !!! ((string?  val) (string->symbol val))
;;          (else           #f))))
;;((eqv? (car entity) '*ENTITY*)
;; (if (null? (cddr entity))
;;     #f
;;     (let ((public-id (list-ref entity 1))
;;           (system-id (list-ref entity 2)))
;;       ;; TODO: Error-check for extraneous list members?
;;       (cond ((equal? public-id xexp-named-char-id)
;;              (string->symbol system-id))
;;             ((equal? public-id xexp-numeric-char-id)
;;              (string->number system-id))
;;             (else #f)))))
;;         (else #f)))

;; @defvar always-empty-html-elements
;;
;; List of symbols for names of HTML elements that can never have content.
;; For example, the @code{br} element.

;; TODO: !!! move this to html-specific, shared by "html-parsing" and
;; "html-writing".

(define always-empty-html-elements
  '(area base br frame hr img input isindex keygen link meta object param
         spacer wbr))

;; @section !!! DETAILED GRAMMAR

;; Requirements of Scheme implementation:
;; * R5RS
;; * Unicode character support
;; * case-sensitive reader (recommended for examples)

;; !!! hexp will be superset of xexp.

;; !!! sxml: too many normal forms.  too many variations.  doesn't cover
;; everything.  verbose.

;; [XML] W3C, ``Extensible Markup Language (XML) 1.1 (Second Edition),''
;; 2006-08-16, @url{http://www.w3.org/TR/2006/REC-xml11-20060816/}

;; TODO: Find XML test suites, and use them.

;; | 2.1 Well-Formed XML Documents

;; | Document
;; | [1]         document           ::=          ( prolog element Misc* ) - ( Char* RestrictedChar Char* )

;; !!!

;; | 2.2 Characters

;; | [Definition: A parsed entity contains text, a sequence of characters,
;; | which may represent markup or character data.] [Definition: A character is
;; | an atomic unit of text as specified by ISO/IEC 10646 [ISO/IEC
;; | 10646]. Legal characters are tab, carriage return, line feed, and the
;; | legal characters of Unicode and ISO/IEC 10646. The versions of these
;; | standards cited in A.1 Normative References were current at the time this
;; | document was prepared. New characters may be added to these standards by
;; | amendments or new editions. Consequently, XML processors MUST accept any
;; | character in the range specified for Char.]

;; | Character Range
;; | [2]         Char       ::=          [#x1-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]     /* any Unicode character, excluding the surrogate blocks, FFFE, and FFFF. */
;; | [2a]        RestrictedChar     ::=          [#x1-#x8] | [#xB-#xC] | [#xE-#x1F] | [#x7F-#x84] | [#x86-#x9F]

;; | The mechanism for encoding character code points into bit patterns may
;; | vary from entity to entity. All XML processors MUST accept the UTF-8 and
;; | UTF-16 encodings of Unicode [Unicode]; the mechanisms for signaling which
;; | of the two is in use, or for bringing other encodings into play, are
;; | discussed later, in 4.3.3 Character Encoding in Entities.

;; | Note:

;; | Document authors are encouraged to avoid "compatibility characters", as
;; | defined in Unicode [Unicode]. The characters defined in the following
;; | ranges are also discouraged. They are either control characters or
;; | permanently undefined Unicode characters:

;; | [#x1-#x8], [#xB-#xC], [#xE-#x1F], [#x7F-#x84], [#x86-#x9F], [#xFDD0-#xFDDF],
;; | [#x1FFFE-#x1FFFF], [#x2FFFE-#x2FFFF], [#x3FFFE-#x3FFFF],
;; | [#x4FFFE-#x4FFFF], [#x5FFFE-#x5FFFF], [#x6FFFE-#x6FFFF],
;; | [#x7FFFE-#x7FFFF], [#x8FFFE-#x8FFFF], [#x9FFFE-#x9FFFF],
;; | [#xAFFFE-#xAFFFF], [#xBFFFE-#xBFFFF], [#xCFFFE-#xCFFFF],
;; | [#xDFFFE-#xDFFFF], [#xEFFFE-#xEFFFF], [#xFFFFE-#xFFFFF],
;; | [#x10FFFE-#x10FFFF].

;; | 2.3 Common Syntactic Constructs

;; | White Space

;; | [3]         S          ::=          (#x20 | #x9 | #xD | #xA)+

;; TODO: Make this right for XML 1.0 too.

(define %xexp:char-x20 (integer->char #x20))
(define %xexp:char-x9  (integer->char #x9))
(define %xexp:char-xa  (integer->char #xa))
(define %xexp:char-xd  (integer->char #xd))

(define %xexp:whitespace-char-list
  (list %xexp:char-x20
        %xexp:char-x9
        %xexp:char-xa
        %xexp:char-xd))

(define (%xexp:whitespace-char? c)
  (memv c %xexp:whitespace-char-list))

;; PI ::= (? PI-TARGET [* [| STRING (SYMBOL [+ STRING ] ) ] ] )
;;
;; PI-TARGET ::= SYMBOL

;;CDSECT ::= (!cdata [* STRING ] )

;;(!cdata "<greeting>Hello, world!</greeting>")

;;(? xml (version "1.1"))
;;(greeting "Hello, world!")

;;PROLOG ::= XML-DECL [* MISC ] [? doctypedecl [* MISC ] ]

;;XML-DECL ::= (? xml VERSION-INFO [? ENCODINGDECL ] [? SD-DECL ] )

;;VERSION-INFO ::= (version [| "1.0" "1.1" ] )

;;MISC ::= [| COMMENT PI ]

;;DOCTYPEDECL ::= (!doctype NAME [? EXTERNAL-ID ] [? INT-SUBSET ] )

;;EXTERNAL-ID ::= [| (system SYSTEM-LITERAL)
;;                   (public PUBID-LITERAL SYSTEM-LITERAL) ]

;;INT-SUBSET ::= (internal [* [| MARKUP-DECL DECL-SEP ] ])

;;DECL-SEP ::= PE-REFERENCE
;;
;;MARKUP-DECL ::= [| ELEMENT-DECL ATTLIST-DECL ENTITY-DECL NOTATION-DECL PI COMMENT ]

;;EXT-SUBSET ::= [? TEXT-DECL ] EXT-SUBSET-DECL

;;EXT-SUBSET-DECL ::= [* [| MARKUP-DECL CONDITIONAL-SECT DECL-SEP ] ]

;;(? xml (version "1.1"))
;;(!doctype greeting SYSTEM "hello.dtd")
;;(greeting "Hello, world!")

;;(? xml (version "1.1") (encoding "UTF-8"))
;;(!doctype greeting ((!ELEMENT greeting (PCDATA))))
;;(greeting "Hello, world!")

;; SD-DECL ::= (standalone [| "yes" "no" ] )

;;(? xml (version "1.1") (standalone "yes"))

;;(!attlist poem xml:space (default preserve) "preserve")
;;(!attlist pre xml:space (preserve) fixed "preserve")

;;(p ((xml:lang "en")) "The quick brown fox jumps over the lazy dog.")
;;(p ((xml:lang "en-GB")) "What colour is it?")
;;(p ((xml:lang "en-US")) "What color is it?")
;;(sp ((who "Faust") (desc "leide") (xml:lang "de"))
;;    (l "Habe nun, ach! Philosophie,")
;;    (l "Juristerei, und Medizin")
;;    (l "und leider auch Theologie")
;;    (l "durchaus studiert mid hei" #xdf "em Bem" #xfc "h'n."))

;;(!attlist poem  xml:lang CDATA "fr")
;;(!attlist gloss xml:lang CDATA "en")
;;(!attlist note  xml:lang CDATA "en")

;; ELEMENT ::= ( NAME [? ( [* ATTRIBUTE ] ) ] )

;; ATTRIBUTE ::= (NAME [* ATTR-VALUE-PART ] )

;;(!attlist termdef
;;          (id      id      #REQUIRED)
;;          (name    cdata   #IMPLIED))
;;(!attlist list
;;          (type    (| bullets ordered glossary) ordered))
;;(!attlist form
;;          (method  cdata   #FIXED "POST"))

;;(!-- "declare the parameter entity \"ISOLat2\"...")
;;(!entity % ISOLat2 SYSTEM "http://www.xml.com/iso/isolat2-xml.entities")
;;(!-- "... now reference it.")
;;(% ISOLat2)

;; | 4.2 Entity Declarations

;; (!entity Pub-Status "This is a pre-release of the specification.")

;; | 4.2.2 External Entities

;;(!entity open-hatch
;;         SYSTEM
;;         "http://www.textuality.com/boilerplate/OpenHatch.xml")
;;(!entity open-hatch
;;         PUBLIC
;;         "-//Textuality//TEXT Standard open-hatch boilerplate//EN"
;;         "http://www.textuality.com/boilerplate/OpenHatch.xml")
;;(!entity hatch-pic SYSTEM "../grafix/OpenHatch.gif" NDATA gif)

;; | 4.3 Parsed Entities

;; | 4.3.1 The Text Declaration

;; (? xml VERSION-INFO ENCODING-DECL)

;; ---------------------

;; document
;;
;; prolog
;; element
;; misc*
;;
;; ;; http://www.w3.org/TR/2006/REC-xml11-20060816/#sec-prolog-dtd
;;
;; (?xml version STRING [ encoding STRING ] [ standalone BOOL ] )
;;
;; (!doctype NAME [ EXTERNAL-ID ] [ ( INT-SUBSET ) ] )
;;
;; EXTERNAL-ID ::= system SYSTEM-LITERAL
;;                 | public PUBID-LITERAL SYSTEM-LITERAL
;;
;; SYSTEM-LITERAL ::= STRING
;;
;; PUBID-LITERAL ::= STRING
;;
;; ;; http://www.w3.org/TR/2006/REC-xml11-20060816/#sec-cdata-sect
;;
;; (!cdata [ STRING ]+ )
;;
;; (!-- [ STRING ]+ )
;;
;;
;; (!entity % draft 'INCLUDE')
;; (!entity % final 'IGNORE')
;;
;; ;; http://www.w3.org/TR/2006/REC-xml11-20060816/#sec-condition-sect
;;
;; (!% draft [ XEXP ]+ )
;; (!% final [ XEXP ]+ )
;;
;; (!% draft
;;     (!element book ((* comments) title body (? supplements))))
;;
;; (!% final
;;     (!element book (title body (? supplements))))
;;
;;
;; ;; 4.1 Character and Entity References
;; ;; http://www.w3.org/TR/2006/REC-xml11-20060816/#sec-references
;;
;; CharRef
;; 42 #x42
;;
;;
;; EntityRef
;; nbsp
;; copy
;;
;; PEReference
;; (% !!!)
;;
;; (!entity NAME ENTITYDEF)
;; (!entity % NAME PEDEF)
;;
;; (!entity Pub-Status "This is a pre-release of the specification.")
;;
;; ;; 4.5 Construction of Entity Replacement Text
;;
;; (!entity % pub #xC9 "ditions Gallimard")
;; (!entity rights "All rights reserved")
;; (!entity book "La Peste: Albert Camus, " #xA9 " 1947 " (% pub) ". " rights)
;;
;;
;; ;; 4.6 Predefined Entities
;;
;; (!entity lt #x38 #x60)
;; (!entity gt #x62)
;;
;; ;; 4.7 Notation Declarations
;;
;; (!notation NAME EXTERNAL-ID-OR-PUBLIC-ID)
;;
;; PUBLIC-ID ::= public PUBID-LITERAL
;;
;;
;;
;; ;; (?splice ...) used to write multiple elements at once when only one xexp is
;; ;; expected.  and to efficiently incorporate large content lists.
;;
;; ;;; @section Introduction
;;
;;
;;
;; (define (xexp-elem-name elem)
;;
;;   )
;;
;; (define (xexp-elem-attrs elem)
;;
;;   )
;;
;; (define (xexp-elem-content elem)
;;
;;   )
;;
;; (define (sxml->xexp sxml)
;;   '!!!)
;;
;; (define (xexp->xexp xexp)
;;   '!!!)

;;; @unnumberedsec History

;;; @table @asis
;;;
;;; @item Version 0.1 --- 2011-08-21 --- PLaneT @code{(1 0)}
;;; Part of forked development from HtmlPrag.
;;;
;;; @end table

(provide
 ;; types
 ;; Xexp
 ;; variables and syntax
 raise-invalid-xexp-error
 exn:fail:invalid-xexp?
 always-empty-html-elements
 make-xexp-char-ref
 xexp-char-ref-value
 raise-invalid-xexp-error)