226 lines
8.3 KiB
Scheme
226 lines
8.3 KiB
Scheme
|
|
(module xexpr mzscheme
|
|
(require (lib "unitsig.ss")
|
|
(lib "list.ss")
|
|
(lib "etc.ss"))
|
|
|
|
(require "sig.ss")
|
|
|
|
(provide xexpr@)
|
|
|
|
(define xexpr@
|
|
(unit/sig extra-xexpr^
|
|
(import xml-structs^ writer^)
|
|
;; Xexpr ::= String
|
|
;; | (list* Symbol (listof Attribute-srep) (listof Xexpr))
|
|
;; | (cons Symbol (listof Xexpr))
|
|
;; | Symbol
|
|
;; | Nat
|
|
;; | Comment
|
|
;; | Processing-instruction
|
|
;; | Cdata
|
|
;; Attribute-srep ::= (list Symbol String)
|
|
|
|
;; sorting is no longer necessary, since xt3d uses xml->zxexpr, which sorts.
|
|
|
|
;; assoc-sort : (listof (list Symbol a)) -> (listof (list Symbol a))
|
|
(define (assoc-sort to-sort)
|
|
(quicksort to-sort (bcompose string<? (compose symbol->string car))))
|
|
|
|
(define xexpr-drop-empty-attributes (make-parameter #f))
|
|
|
|
#|
|
|
; : tst -> bool
|
|
(define (xexpr? x)
|
|
(or (string? x) (symbol? x) (number? x) (pcdata? x) (comment? x)
|
|
(and (cons? x) (symbol? (car x))
|
|
(or (and (cons? (cdr x)) (listof? xexpr-attribute? (cadr x))
|
|
(listof? xexpr? (cddr x)))
|
|
(listof? xexpr? (cdr x))))))
|
|
|#
|
|
|
|
(define (xexpr? x)
|
|
(correct-xexpr? x (lambda () #t) (lambda (exn) #f)))
|
|
|
|
|
|
(define (validate-xexpr x)
|
|
(correct-xexpr? x (lambda () #t) (lambda (exn) (raise exn))))
|
|
|
|
;; ;; ;; ;; ;; ;; ;
|
|
;; ; xexpr? helpers
|
|
|
|
(define-struct (exn:invalid-xexpr exn) (code))
|
|
|
|
;; correct-xexpr? : any (-> a) (exn -> a) -> a
|
|
(define (correct-xexpr? x true false)
|
|
(cond
|
|
((string? x) (true))
|
|
((symbol? x) (true))
|
|
((number? x) (true))
|
|
((comment? x) (true))
|
|
((pi? x) (true))
|
|
((cdata? x) (true))
|
|
((list? x)
|
|
(or (null? x)
|
|
(if (symbol? (car x))
|
|
(if (has-attribute? x)
|
|
(and (attribute-pairs? (cadr x) true false)
|
|
(andmap (lambda (part)
|
|
(correct-xexpr? part true false))
|
|
(cddr x))
|
|
(true))
|
|
(andmap (lambda (part)
|
|
(correct-xexpr? part true false))
|
|
(cdr x)))
|
|
(false (make-exn:invalid-xexpr
|
|
(string->immutable-string
|
|
(format
|
|
"Expected a symbol as the element name, given ~a"
|
|
(car x)))
|
|
(current-continuation-marks)
|
|
x)))))
|
|
(else (false
|
|
(make-exn:invalid-xexpr
|
|
(string->immutable-string
|
|
(format
|
|
(string-append
|
|
"Expected a string, symbol, number, comment, "
|
|
"processing instruction, or list, given ~a")
|
|
x))
|
|
(current-continuation-marks)
|
|
x)))))
|
|
|
|
;; has-attribute? : List -> Boolean
|
|
;; True if the Xexpr provided has an attribute list.
|
|
(define (has-attribute? x)
|
|
(and (> (length x) 1)
|
|
(list? (cadr x))
|
|
(andmap (lambda (attr)
|
|
(pair? attr))
|
|
(cadr x))))
|
|
|
|
;; attribute-pairs? : List (-> a) (exn -> a) -> a
|
|
;; True if the list is a list of pairs.
|
|
(define (attribute-pairs? attrs true false)
|
|
(if (null? attrs)
|
|
(true)
|
|
(let ((attr (car attrs)))
|
|
(if (pair? attr)
|
|
(and (attribute-symbol-string? attr true false)
|
|
(attribute-pairs? (cdr attrs) true false )
|
|
(true))
|
|
(false
|
|
(make-exn:invalid-xexpr
|
|
(string->immutable-string
|
|
(format "Expected a pair, given ~a" attr))
|
|
(current-continuation-marks)
|
|
attr))))))
|
|
|
|
;; attribute-symbol-string? : List (-> a) (exn -> a) -> a
|
|
;; True if the list is a list of String,Symbol pairs.
|
|
(define (attribute-symbol-string? attr true false)
|
|
(if (symbol? (car attr))
|
|
(if (string? (cadr attr))
|
|
(true)
|
|
(false (make-exn:invalid-xexpr
|
|
(string->immutable-string
|
|
(format "Expected a string, given ~a" (cadr attr)))
|
|
(current-continuation-marks)
|
|
(cadr attr))))
|
|
(false (make-exn:invalid-xexpr
|
|
(string->immutable-string
|
|
(format "Expected a symbol, given ~a" (car attr)))
|
|
(current-continuation-marks)
|
|
(cadr attr)))))
|
|
|
|
;; ; end xexpr? helpers
|
|
;; ;; ;; ;; ;; ;; ;; ;;
|
|
|
|
|
|
; : (a -> bool) tst -> bool
|
|
; To check if l is a (listof p?)
|
|
; Don't use (and (list? l) (andmap p? l)) because l may be improper.
|
|
(define (listof? p? l)
|
|
(let listof-p? ([l l])
|
|
(or (null? l)
|
|
(and (cons? l) (p? (car l)) (listof-p? (cdr l))))))
|
|
|
|
; : tst -> bool
|
|
(define (xexpr-attribute? b)
|
|
(and (pair? b)
|
|
(symbol? (car b))
|
|
(pair? (cdr b))
|
|
(string? (cadr b))
|
|
(null? (cddr b))))
|
|
|
|
;; xml->xexpr : Content -> Xexpr
|
|
;; The contract is loosely enforced.
|
|
(define (xml->xexpr x)
|
|
(let* ([non-dropping-combine
|
|
(lambda (atts body)
|
|
(cons (assoc-sort (map attribute->srep atts))
|
|
body))]
|
|
[combine (if (xexpr-drop-empty-attributes)
|
|
(lambda (atts body)
|
|
(if (null? atts)
|
|
body
|
|
(non-dropping-combine atts body)))
|
|
non-dropping-combine)])
|
|
(let loop ([x x])
|
|
(cond
|
|
[(element? x)
|
|
(let ([body (map loop (element-content x))]
|
|
[atts (element-attributes x)])
|
|
(cons (element-name x) (combine atts body)))]
|
|
[(pcdata? x) (pcdata-string x)]
|
|
[(entity? x) (entity-text x)]
|
|
[(or (comment? x) (pi? x) (cdata? x)) x]
|
|
[(document? x) (error 'xml->xexpr "Expected content, given ~e~nUse document-element to extract the content." x)]
|
|
[else ;(error 'xml->xexpr "Expected content, given ~e" x)
|
|
x]))))
|
|
|
|
;; attribute->srep : Attribute -> Attribute-srep
|
|
(define (attribute->srep a)
|
|
(list (attribute-name a) (attribute-value a)))
|
|
|
|
;; srep->attribute : Attribute-srep -> Attribute
|
|
(define (srep->attribute a)
|
|
(unless (and (pair? a) (pair? (cdr a)) (null? (cddr a)) (symbol? (car a)) (string? (cadr a)))
|
|
(error 'srep->attribute "expected (list Symbol String) given ~e" a))
|
|
(make-attribute 'scheme 'scheme (car a) (cadr a)))
|
|
|
|
;; xexpr->xml : Xexpr -> Content
|
|
;; The contract is enforced.
|
|
(define (xexpr->xml x)
|
|
(cond
|
|
[(pair? x)
|
|
(let ([f (lambda (atts body)
|
|
(unless (list? body)
|
|
(error 'xexpr->xml
|
|
"expected a list of xexprs for the body in ~e"
|
|
x))
|
|
(make-element 'scheme 'scheme (car x)
|
|
atts
|
|
(map xexpr->xml body)))])
|
|
(if (and (pair? (cdr x))
|
|
(or (null? (cadr x))
|
|
(and (pair? (cadr x)) (pair? (caadr x)))))
|
|
(f (map srep->attribute (cadr x)) (cddr x))
|
|
(f null (cdr x))))]
|
|
[(string? x) (make-pcdata 'scheme 'scheme x)]
|
|
[(or (symbol? x) (and (integer? x) (>= x 0)))
|
|
(make-entity 'scheme 'scheme x)]
|
|
[(or (comment? x) (pi? x) (cdata? x)) x]
|
|
[else ;(error 'xexpr->xml "malformed xexpr ~s" x)
|
|
x]))
|
|
|
|
;; xexpr->string : Xexpression -> String
|
|
(define (xexpr->string xexpr)
|
|
(let ([port (open-output-string)])
|
|
(write-xml/content (xexpr->xml xexpr) port)
|
|
(get-output-string port)))
|
|
|
|
;; bcompose : (a a -> c) (b -> a) -> (b b -> c)
|
|
(define (bcompose f g)
|
|
(lambda (x y) (f (g x) (g y)))))))
|