racket/collects/xml/private/xexpr-core.rkt
Danny Yoo 5c6498b355 Fix for PR 13234. xexpr-core: added more test cases to
correct-xexpr?.  Inverted the logic and replaced the
continuation-passing style with simpler test-for-error logic.  Also
corrected typo in attribute symbol checker that could otherwise lead
to a contract error.  (taking the cadr of a non-cadrable value)
2012-11-07 11:46:32 -07:00

176 lines
5.5 KiB
Racket

#lang racket/base
(require racket/list
racket/contract/base
racket/contract/combinator
racket/pretty
"core.rkt")
(provide
(contract-out
[xexpr/c contract?]
[xexpr? (any/c . -> . boolean?)]
[validate-xexpr (any/c . -> . (one-of/c #t))]
[rename correct-xexpr/k? correct-xexpr? (any/c (-> any/c) (exn:invalid-xexpr? . -> . any/c) . -> . any/c)])
(struct-out exn:invalid-xexpr))
;; Xexpr ::= String
;; | (list* Symbol (listof Attribute-srep) (listof Xexpr))
;; | (cons Symbol (listof Xexpr))
;; | Symbol
;; | Nat (WFC: Valid Char)
;; | Comment
;; | Processing-instruction
;; | Cdata
;; Attribute-srep ::= (list Symbol String)
;; sorting is no longer necessary, since xt3d uses xml->zxexpr, which sorts.
(define xexpr-datum/c
(or/c string? symbol? valid-char?
comment? p-i? cdata? pcdata?))
(define (xexpr? x)
(not (incorrect-xexpr? x)))
(define (validate-xexpr x)
(define maybe-exn (incorrect-xexpr? x))
(if maybe-exn
(raise maybe-exn)
#t))
(define xexpr/c
(make-flat-contract
#:name 'xexpr?
#:projection
(lambda (blame)
(lambda (val)
(with-handlers ([exn:invalid-xexpr?
(lambda (exn)
(raise-blame-error
blame
val
"Not an Xexpr. ~a\n\nContext:\n~a"
(exn-message exn)
(pretty-format val)))])
(validate-xexpr val)
val)))
#:first-order xexpr?))
;; ;; ;; ;; ;; ;; ;
;; ; xexpr? helpers
(define-struct (exn:invalid-xexpr exn:fail) (code))
;; correct-xexpr/k? : any (-> a) (exn -> a) -> a
;; Calls true-k if x is a correct-xexpr. Otherwise, calls the
;; failure continuation false-k with an exn:invalid-xexpr instance.
(define (correct-xexpr/k? x true-k false-k)
(define maybe-exn (incorrect-xexpr? x))
(if maybe-exn
(false-k maybe-exn)
(true-k)))
;; incorrect-xexpr?: any -> (or/c #f exn:invalid-xexpr)
;; Returns an exn:invalid-xexpr if the xexpr has incorrect structure.
;; Otherwise, returns #f.
(define (incorrect-xexpr? x)
(cond
[(string? x) #f]
[(symbol? x) #f]
[(valid-char? x) #f]
[(comment? x) #f]
[(p-i? x) #f]
[(cdata? x) #f]
[(pcdata? x) #f]
[(list? x)
(cond [(null? x)
(make-exn:invalid-xexpr
"Expected a symbol as the element name, given nothing"
(current-continuation-marks)
x)]
[else
(if (symbol? (car x))
(cond [(has-attribute-pairs? x)
(define maybe-exn (erroneous-attribute-pairs? (cadr x)))
(cond [maybe-exn maybe-exn]
[else
(for/or ([elt (in-list (cddr x))])
(incorrect-xexpr? elt))])]
[else
(for/or ([elt (in-list (cdr x))])
(incorrect-xexpr? elt))])
(make-exn:invalid-xexpr
(format
"Expected a symbol as the element name, given ~s"
(car x))
(current-continuation-marks)
x))])]
[(permissive-xexprs) #f]
[else (make-exn:invalid-xexpr
(format (string-append
"Expected a string, symbol, valid numeric entity, comment, "
"processing instruction, or list, given ~s")
x)
(current-continuation-marks)
x)]))
;; has-attribute-pairs? : List -> Boolean
;; True if the Xexpr provided has an attribute list. The attribute list is not
;; checked for correct structure here.
(define (has-attribute-pairs? x)
(and (> (length x) 1)
(list? (cadr x))
(for/and ([attr (in-list (cadr x))])
(pair? attr))))
;; erroneous-attribute-pairs? : List -> (or/c #f exn:invalid-xexpr)
;; Returns exn:invalid-expr if the attribute pair list is not correctly structured.
(define (erroneous-attribute-pairs? attrs)
(cond [(null? attrs)
#f]
[else
(define attr (car attrs))
(cond [(pair? attr)
(define maybe-exn (erroneous-attribute-symbol-string? attr))
(cond
[maybe-exn maybe-exn]
[else
(erroneous-attribute-pairs? (cdr attrs))])]
[else
(make-exn:invalid-xexpr
(format "Expected an attribute pair, given ~s" attr)
(current-continuation-marks)
attr)])]))
;; erroneous-attribute-symbol-string? : List -> (or/c #f exn:invalid-xexpr)
;; Returns exn:invalid-expr if the list is not a (String, Symbol) pair.
(define (erroneous-attribute-symbol-string? attr)
(if (symbol? (car attr))
(if (pair? (cdr attr))
(if (or (string? (cadr attr))
(permissive-xexprs))
#f
(make-exn:invalid-xexpr
(format "Expected an attribute value string, given ~v" (cadr attr))
(current-continuation-marks)
(cadr attr)))
(make-exn:invalid-xexpr
(format "Expected an attribute value string for attribute ~s, given nothing" attr)
(current-continuation-marks)
attr))
(make-exn:invalid-xexpr
(format "Expected an attribute symbol, given ~s" (car attr))
(current-continuation-marks)
(car attr))))
;; ; end xexpr? helpers
;; ;; ;; ;; ;; ;; ;; ;;