private/quote.ss
(module quote mzscheme
  
  (require "xml-core.ss"
           (lib "list.ss"))
  
  (require-for-syntax "xml-helper.ss"
                      (lib "stx.ss" "syntax"))
  
  (provide define-namespace
           quote-xml
           quasi-xml
           strict-webit-xml)
  
  (define-syntax (strict-webit-xml stx)
    (syntax-case stx ()
      ((_)
       #`(quote #,(strict-type-checking)))
      ((_ #t)
       #'(define-syntaxes () (begin (strict-type-checking! #t)
                                    (values))))
      ((_ #t)
       #'(define-syntaxes () (begin (strict-type-checking! #f)
                                    (values))))))
  
  (define-syntax (define-namespace stx)
    (syntax-case stx ()
      ((_ prefix namespace)
       #`(define-syntax #,(datum->syntax-object
                           (syntax prefix)
                           (string->symbol
                            (string-append
                             (symbol->string 
                              (syntax-object->datum (syntax prefix)))
                             "-nspre")))
           (string->symbol namespace)))))
  
  (define-syntax (quote-xml stx)
    (define (construct-attribute full-tag val)
      #`(make-xml-attribute '#,full-tag #,val))
    (define (get-attributes lst-stx acc)
      (syntax-case lst-stx ()
        (() (values (reverse acc) '()))
        ((key val)
         (keyword-identifier? (syntax-object->datum #'key))
         (begin (unless (string? (syntax-object->datum #'val))
                  (raise-syntax-error #f
                                      "attribute value must be a string"
                                      stx
                                      #'val))
                (values (reverse (cons (construct-attribute (attribute->tag #'key) #'val) acc))
                        '())))
        ((key val item ...)
         (keyword-identifier? (syntax-object->datum #'key))
         (begin (unless (string? (syntax-object->datum #'val))
                  (raise-syntax-error #f
                                      "attribute value must be a string"
                                      stx
                                      #'val))
                (get-attributes #'(item ...)
                                (cons (construct-attribute (attribute->tag #'key) #'val)
                                      acc))))
        ((item ...)
         (values (reverse acc) #'(item ...)))))
    (define (get-contents lst-stx)
      (syntax-case lst-stx ()
        (() '())
        ((node)
         (syntax->list #'node)
         (list (translate-xml #'node)))
        ((node item ...)
         (syntax->list #'node)
         (cons (translate-xml #'node) (get-contents #'(item ...))))
        ((item1)
         (begin (unless (string? (syntax-e #'item1))
                  (raise-syntax-error #f
                                      "content item must be a string or element"
                                      stx
                                      #'item1))
                (list #'item1)))
        ((item1 item ...)
         (begin (unless (string? (syntax-e #'item1))
                  (raise-syntax-error #f
                                      "content item must be a string or element"
                                      stx
                                      #'item1))
                (cons #'item1 (get-contents #'(item ...)))))))
    (define (translate-xml top-stx)
      (syntax-case top-stx ()
        ((tag)
         (begin (unless (identifier? #'tag)
                  (raise-syntax-error #f
                                      "element tag must be an identifier"
                                      stx
                                      #'tag))
                (let ((full-tag (type->tag #'tag)))
                  #`(make-xml-element '#,full-tag '() '()))))
        ((tag item ...)
         (begin (unless (identifier? #'tag)
                  (raise-syntax-error #f
                                      "element tag must be an identifier"
                                      stx
                                      #'tag))
                (let*-values (((attrs rest) (get-attributes #'(item ...) '()))
                              ((contents) (get-contents rest))
                              ((full-tag) (type->tag #'tag)))
                  #`(make-xml-element '#,full-tag
                                  (list #,@attrs) (list #,@contents)))))
        (other
         (raise-syntax-error #f
                             "expected an element syntax"
                             stx
                             #'other))))
    (syntax-case stx ()
      ((_ name)
       (identifier? (syntax name))
       #`(quote #,(type->tag (syntax name))))
      ((_ it)
       (translate-xml (syntax it)))))
  
  (define (attrval-mustbe-string name val)
    (if (string? val)
        val
        (error (format "quasi-xml: value for ~a not a string, given ~s" name val))))
  
  (define (content-mustbe-node name val)
    (cond
      ((xml-element? val) val)
      ((string? val) val)
      (else 
       (error 
        (format "quasi-xml: unquoted expression must be an element, pcdata, or a string; in element ~a given ~s"
                name val)))))
  
  (define (content-mustbe-list name val-lst)
    (if (list? val-lst)
        (map (lambda (val)
               (cond
                 ((xml-element? val) val)
                 ((string? val) val)
                 (else 
                  (error 
                   (format "quasi-xml: node must be an element, pcdata, or a string; in element ~a given ~s"
                           name val)))))
             val-lst)
        (error 
         (format "quasi-xml: unquote-splice expression must evaluate to a list; in element ~a given ~s"
                 name val-lst))))
  
  (define-syntax (quasi-xml stx)
    (define (get-attrval tag-stx val-stx)
      (syntax-case* val-stx (unquote unquote-splicing)
        module-or-top-identifier=?
        ((unquote x) #`(attrval-mustbe-string '#,tag-stx x))
        (val
         (begin (unless (string? (syntax-object->datum #'val))
                  (raise-syntax-error #f
                                      "attribute value must be a string"
                                      stx
                                      #'val))
                #'val))))
    (define (construct-attribute full-tag val)
      #`(make-xml-attribute '#,full-tag #,val))
    (define (get-attributes lst-stx acc)
      (syntax-case lst-stx ()
        (() (values (reverse acc) '()))
        ((key val)
         (keyword-identifier? (syntax-object->datum #'key))
         (begin (values (reverse (cons (construct-attribute (attribute->tag #'key)
                                                            (get-attrval #'key #'val))
                                       acc))
                        '())))
        ((key val item ...)
         (keyword-identifier? (syntax-object->datum #'key))
         (begin (get-attributes #'(item ...)
                                (cons (construct-attribute (attribute->tag #'key)
                                                           (get-attrval #'key #'val))
                                      acc))))
        ((item ...)
         (values (reverse acc) #'(item ...)))))
    (define (get-contents tag-stx lst-stx)
      (syntax-case* lst-stx (unquote unquote-splicing)
        module-or-top-identifier=?
        (() '())
        (((unquote x))
         #`(list (content-mustbe-node '#,tag-stx x)))
        (((unquote x) item ...)
         #`(cons (content-mustbe-node '#,tag-stx x) #,(get-contents tag-stx #'(item ...))))
        (((unquote-splicing x))
         #`(content-mustbe-list '#,tag-stx x))
        (((unquote-splicing x) item ...)
         #`(append (content-mustbe-list '#,tag-stx x) #,(get-contents tag-stx #'(item ...))))
        ((node)
         (syntax->list #'node)
         #`(list #,(translate-xml #'node)))
        ((node item ...)
         (syntax->list #'node)
         #`(cons #,(translate-xml #'node) #,(get-contents tag-stx #'(item ...))))
        ((item1)
         (begin (unless (string? (syntax-e #'item1))
                  (raise-syntax-error #f
                                      "content item must be a string or element"
                                      stx
                                      #'item1))
                #'(list item1)))
        ((item1 item ...)
         (begin (unless (string? (syntax-e #'item1))
                  (raise-syntax-error #f
                                      "content item must be a string or element"
                                      stx
                                      #'item1))
                #`(cons item1 #,(get-contents tag-stx #'(item ...)))))))
    (define (translate-xml top-stx)
      (syntax-case top-stx ()
        ((tag)
         (begin (unless (identifier? #'tag)
                  (raise-syntax-error #f
                                      "element tag must be an identifier"
                                      stx
                                      #'tag))
                (let ((full-tag (type->tag #'tag)))
                  #`(make-xml-element '#,full-tag '() '()))))
        ((tag item ...)
         (begin (unless (identifier? #'tag)
                  (raise-syntax-error #f
                                      "element tag must be an identifier"
                                      stx
                                      #'tag))
                (let*-values (((attrs rest) (get-attributes #'(item ...) '()))
                              ((contents) (get-contents #'tag rest))
                              ((full-tag) (type->tag #'tag)))
                  #`(make-xml-element '#,full-tag
                                      (list #,@attrs)
                                      #,contents))))
        (other
         (raise-syntax-error #f
                             "expected an element syntax"
                             stx
                             #'other))))
    (syntax-case stx ()
      ((_ name)
       (identifier? (syntax name))
       #`(quote #,(type->tag (syntax name))))
      ((_ it)
       (translate-xml #'it))))
  
  )