
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)
176 lines
5.5 KiB
Racket
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
|
|
;; ;; ;; ;; ;; ;; ;; ;;
|