170 lines
6.5 KiB
Scheme
170 lines
6.5 KiB
Scheme
|
|
(module writer mzscheme
|
|
(require (lib "unitsig.ss")
|
|
(lib "list.ss")
|
|
(lib "string.ss")
|
|
(lib "etc.ss"))
|
|
|
|
(require "sig.ss")
|
|
|
|
(provide writer@)
|
|
|
|
(define writer@
|
|
(unit/sig writer^
|
|
(import xml-structs^)
|
|
|
|
;; (empty-tag-shorthand) : (U 'always 'never (listof Symbol))
|
|
(define empty-tag-shorthand
|
|
(make-parameter 'always
|
|
(lambda (x)
|
|
(if (or (eq? x 'always) (eq? x 'never) (and (list? x) (andmap symbol? x)))
|
|
x
|
|
(error 'empty-tag-shorthand "expected 'always, 'never, or a list of symbols: received ~a" x)))))
|
|
|
|
(define html-empty-tags '(param meta link isindex input img hr frame col br basefont base area))
|
|
|
|
;; gen-write/display-xml/content : (Nat Output-port -> Void) -> Content [Output-Port]-> Void
|
|
(define (gen-write/display-xml/content dent)
|
|
(opt-lambda (c [out (current-output-port)]) (write-xml-content c 0 dent out)))
|
|
|
|
;; indent : Nat Output-port -> Void
|
|
(define (indent n out)
|
|
(newline out)
|
|
(let loop ([n n])
|
|
(unless (zero? n)
|
|
(display #\space out)
|
|
(loop (sub1 n)))))
|
|
|
|
;; write-xml/content : Content [Output-port] -> Void
|
|
(define write-xml/content (gen-write/display-xml/content void))
|
|
|
|
;; display-xml/content : Content [Output-port] -> Void
|
|
(define display-xml/content (gen-write/display-xml/content indent))
|
|
|
|
;; gen-write/display-xml : (Content [Output-port] -> Void) -> Document [Output-port] -> Void
|
|
(define (gen-write/display-xml output-content)
|
|
(opt-lambda (doc [out (current-output-port)])
|
|
(let ([prolog (document-prolog doc)])
|
|
(display-outside-misc (prolog-misc prolog) out)
|
|
(display-dtd (prolog-dtd prolog) out)
|
|
(display-outside-misc (prolog-misc2 prolog) out))
|
|
(output-content (document-element doc) out)
|
|
(display-outside-misc (document-misc doc) out)))
|
|
|
|
; display-dtd : document-type oport -> void
|
|
(define (display-dtd dtd out)
|
|
(when dtd
|
|
(fprintf out "<!DOCTYPE ~a" (document-type-name dtd))
|
|
(let ([external (document-type-external dtd)])
|
|
(cond
|
|
[(external-dtd/public? external)
|
|
(fprintf out " PUBLIC \"~a\" \"~a\""
|
|
(external-dtd/public-public external)
|
|
(external-dtd-system external))]
|
|
[(external-dtd/system? external)
|
|
(fprintf out " SYSTEM \"~a\"" (external-dtd-system external))]
|
|
[(not external) (void)]))
|
|
(display ">" out)
|
|
(newline out)))
|
|
|
|
;; write-xml : Document [Output-port] -> Void
|
|
(define write-xml (gen-write/display-xml write-xml/content))
|
|
|
|
;; display-xml : Document [Output-port] -> Void
|
|
(define display-xml (gen-write/display-xml display-xml/content))
|
|
|
|
;; display-outside-misc : (listof Misc) Output-port -> Void
|
|
(define (display-outside-misc misc out)
|
|
(for-each (lambda (x)
|
|
((cond
|
|
[(comment? x) write-xml-comment]
|
|
[(pi? x) write-xml-pi]) x 0 void out)
|
|
(newline out))
|
|
misc))
|
|
|
|
;; write-xml-content : Content Nat (Nat Output-Stream -> Void) Output-Stream -> Void
|
|
(define (write-xml-content el over dent out)
|
|
((cond
|
|
[(element? el) write-xml-element]
|
|
[(pcdata? el) write-xml-pcdata]
|
|
[(cdata? el) write-xml-cdata]
|
|
[(entity? el) write-xml-entity]
|
|
[(comment? el) write-xml-comment]
|
|
[(pi? el) write-xml-pi]
|
|
[else (error 'write-xml-content "received ~a" el)])
|
|
el over dent out))
|
|
|
|
;; write-xml-element : Element Nat (Nat Output-Stream -> Void) Output-Stream -> Void
|
|
(define (write-xml-element el over dent out)
|
|
(let* ([name (element-name el)]
|
|
[start (lambda (f) (write-xml-base (format f name) over dent out))]
|
|
[content (element-content el)])
|
|
(start "<~a")
|
|
(for-each (lambda (att)
|
|
(fprintf out " ~a=\"~a\"" (attribute-name att)
|
|
(escape (attribute-value att) escape-attribute-table)))
|
|
(element-attributes el))
|
|
(if (and (null? content)
|
|
(let ([short (empty-tag-shorthand)])
|
|
(case short
|
|
[(always) #t]
|
|
[(never) #f]
|
|
[else (memq (lowercase-symbol name) short)])))
|
|
(fprintf out " />")
|
|
(begin
|
|
(fprintf out ">")
|
|
(for-each (lambda (c) (write-xml-content c (incr over) dent out)) content)
|
|
(start "</~a")
|
|
(fprintf out ">")))))
|
|
|
|
; : sym -> sym
|
|
(define (lowercase-symbol x)
|
|
(let ([s (symbol->string x)])
|
|
(string-lowercase! s)
|
|
(string->symbol s)))
|
|
|
|
;; write-xml-base : (U String Char Symbol) Nat (Nat Output-Stream -> Void) Output-Stream -> Void
|
|
(define (write-xml-base el over dent out)
|
|
(dent over out)
|
|
(display el out))
|
|
|
|
;; write-xml-pcdata : Pcdata Nat (Nat Output-Stream -> Void) Output-Stream -> Void
|
|
(define (write-xml-pcdata str over dent out)
|
|
(write-xml-base (escape (pcdata-string str) escape-table) over dent out))
|
|
|
|
;; write-xml-cdata : Cdata Nat (Nat Output-Stream -> Void) Output-Stream -> Void
|
|
(define (write-xml-cdata cdata over dent out)
|
|
;; XXX: Different kind of quote is needed, for assume the user includes the <![CDATA[...]]> with proper quoting
|
|
(write-xml-base (format "~a" (cdata-string cdata)) over dent out))
|
|
|
|
;; write-xml-pi : Processing-instruction Nat (Nat Output-Stream -> Void) Output-Stream -> Void
|
|
(define (write-xml-pi pi over dent out)
|
|
(write-xml-base (format "<?~a ~a?>" (pi-target-name pi) (pi-instruction pi)) over dent out))
|
|
|
|
;; write-xml-comment : Comment Nat (Nat Output-Stream -> Void) Output-Stream -> Void
|
|
(define (write-xml-comment comment over dent out)
|
|
(write-xml-base (format "<!--~a-->" (comment-text comment)) over dent out))
|
|
|
|
;; write-xml-entity : Entity Nat (Nat Output-stream -> Void) Output-stream -> Void
|
|
(define (write-xml-entity entity over dent out)
|
|
(let ([n (entity-text entity)])
|
|
(fprintf out (if (number? n) "&#~a;" "&~a;") n)))
|
|
|
|
(define escape-table
|
|
(map (lambda (x y) (cons (regexp (symbol->string x)) y))
|
|
'(< > &)
|
|
'("\\<" "\\>" "\\&")))
|
|
|
|
(define escape-attribute-table
|
|
(list* (cons (regexp "\"") "\\"") escape-table))
|
|
|
|
;; escape : String -> String
|
|
;; more here - this could be much more efficient
|
|
(define (escape x table)
|
|
(foldr (lambda (esc str) (regexp-replace* (car esc) str (cdr esc)))
|
|
x
|
|
table))
|
|
|
|
;; incr : Nat -> Nat
|
|
(define (incr n) (+ n 2)))))
|