|
|
|
@ -16,7 +16,7 @@
|
|
|
|
|
((hasheq 'foo "bar" 'zim "zam") . update-with . (hasheq 'zim "BANG") (hasheq 'toe "jam") (hasheq 'foo "zay"))
|
|
|
|
|
(make-hasheq '((zim . "BANG") (foo . "zay") (toe . "jam")))))
|
|
|
|
|
|
|
|
|
|
(define (merge-whitespace qs [white-q? (λ (aq) (char-whitespace? (car (get-field elems aq))))])
|
|
|
|
|
(define (merge-whitespace qs [white-q? (λ (aq) (char-whitespace? (car (quad-elems aq))))])
|
|
|
|
|
;; collapse each sequence of whitespace qs to the first one, and make it a space
|
|
|
|
|
;; also drop leading & trailing whitespaces
|
|
|
|
|
;; (same behavior as web browsers)
|
|
|
|
@ -28,57 +28,60 @@
|
|
|
|
|
(loop (list acc bs (if (and (pair? rest) ;; we precede bs (only #t if rest starts with bs, because we took the ws)
|
|
|
|
|
(pair? bs) ;; we follow bs
|
|
|
|
|
(pair? ws)) ;; we have ws
|
|
|
|
|
(quad (get-field attrs (car ws)) #\space)
|
|
|
|
|
(make-quad (quad-attrs (car ws)) '(#\space))
|
|
|
|
|
null)) rest)))))
|
|
|
|
|
|
|
|
|
|
#;(module+ test
|
|
|
|
|
(check-equal? (merge-whitespace (list (q #\space) (q #\newline) (q #\H) (q #\space) (q #\newline) (q #\space) (q #\i) (q #\newline)))
|
|
|
|
|
(list (q #\H) (q #\space) (q #\i))))
|
|
|
|
|
|
|
|
|
|
(module+ test
|
|
|
|
|
(define (qq . xs) (q #f xs))
|
|
|
|
|
(define (qqa attrs . xs) (q attrs xs))
|
|
|
|
|
(check-equal? (merge-whitespace (list (qq #\space) (qq #\newline) (qq #\H) (qq #\space) (qq #\newline) (qq #\space) (qq #\i) (qq #\newline)))
|
|
|
|
|
(list (qq #\H) (qq #\space) (qq #\i))))
|
|
|
|
|
|
|
|
|
|
(define (atomize qx)
|
|
|
|
|
;; normalize a quad by reducing it to one-character quads.
|
|
|
|
|
;; propagate attrs downward.
|
|
|
|
|
(define atomic-quads
|
|
|
|
|
(let loop ([x (if (string? qx) (q qx) qx)][attrs (current-default-attrs)])
|
|
|
|
|
(let loop ([x (if (string? qx) (q #f (list qx)) qx)][attrs (current-default-attrs)])
|
|
|
|
|
(match x
|
|
|
|
|
[(? char? c) (list (q attrs c))]
|
|
|
|
|
[(? char? c) (list (q attrs (list c)))]
|
|
|
|
|
[(? string?) (append* (for/list ([c (in-string x)]) ;; strings are exploded
|
|
|
|
|
(loop c attrs)))]
|
|
|
|
|
[(? quad?) ;; qexprs with attributes are recursed
|
|
|
|
|
(define this-attrs (get-field attrs x))
|
|
|
|
|
(define elems (get-field elems x))
|
|
|
|
|
(define this-attrs (quad-attrs x))
|
|
|
|
|
(define elems (quad-elems x))
|
|
|
|
|
(define merged-attrs (attrs . update-with . this-attrs))
|
|
|
|
|
(append* (for/list ([elem (in-list elems)])
|
|
|
|
|
(loop elem merged-attrs)))]
|
|
|
|
|
[else (raise-argument-error 'atomize "valid item" x)])))
|
|
|
|
|
(merge-whitespace atomic-quads))
|
|
|
|
|
|
|
|
|
|
#;(module+ test
|
|
|
|
|
(require rackunit)
|
|
|
|
|
(check-equal? (atomize (q "Hi")) (list (q #\H) (q #\i)))
|
|
|
|
|
(check-equal? (atomize (q "Hi " (q "You"))) (list (q #\H) (q #\i) (q #\space) (q #\Y) (q #\o) (q #\u)))
|
|
|
|
|
(check-exn exn:fail:contract? (λ () (atomize #t)))
|
|
|
|
|
(check-equal? (atomize (q "H i")) (list (q #\H) (q #\space) (q #\i)))
|
|
|
|
|
(check-equal? (atomize (q "H \n\n i")) (list (q #\H) (q #\space) (q #\i))) ;; collapse whitespace to single
|
|
|
|
|
(module+ test
|
|
|
|
|
(require rackunit)
|
|
|
|
|
(check-equal? (atomize (qq "Hi")) (list (qq #\H) (qq #\i)))
|
|
|
|
|
(check-equal? (atomize (qq "Hi " (qq "You"))) (list (qq #\H) (qq #\i) (qq #\space) (qq #\Y) (qq #\o) (qq #\u)))
|
|
|
|
|
(check-exn exn:fail:contract? (λ () (atomize #t)))
|
|
|
|
|
(check-equal? (atomize (qq "H i")) (list (qq #\H) (qq #\space) (qq #\i)))
|
|
|
|
|
(check-equal? (atomize (qq "H \n\n i")) (list (qq #\H) (qq #\space) (qq #\i))) ;; collapse whitespace to single
|
|
|
|
|
|
|
|
|
|
;; with attributes
|
|
|
|
|
(check-equal? (atomize (q (hasheq 'k "v") "Hi")) (list (q (hasheq 'k "v") #\H) (q (hasheq 'k "v") #\i)))
|
|
|
|
|
(check-equal? (atomize (q (hasheq 'k "v") "Hi " (q "You")))
|
|
|
|
|
(list
|
|
|
|
|
(quad (hasheq 'k "v") #\H)
|
|
|
|
|
(quad (hasheq 'k "v") #\i)
|
|
|
|
|
(quad (hasheq 'k "v") #\space)
|
|
|
|
|
(quad (hasheq 'k "v") #\Y)
|
|
|
|
|
(quad (hasheq 'k "v") #\o)
|
|
|
|
|
(quad (hasheq 'k "v") #\u)))
|
|
|
|
|
(check-equal? (atomize (q (hasheq 'k1 "v1" 'k2 42) "Hi \n\n" (q (hasheq 'k1 "v2" 'k3 "foo") "\n \nYou")))
|
|
|
|
|
(list
|
|
|
|
|
(quad (hasheq 'k1 "v1" 'k2 42) #\H)
|
|
|
|
|
(quad (hasheq 'k1 "v1" 'k2 42) #\i)
|
|
|
|
|
(quad (hasheq 'k1 "v1" 'k2 42) #\space)
|
|
|
|
|
(quad (hasheq 'k1 "v2" 'k2 42 'k3 "foo") #\Y)
|
|
|
|
|
(quad (hasheq 'k1 "v2" 'k2 42 'k3 "foo") #\o)
|
|
|
|
|
(quad (hasheq 'k1 "v2" 'k2 42 'k3 "foo") #\u))))
|
|
|
|
|
;; with attributes
|
|
|
|
|
(check-equal? (atomize (qqa (hasheq 'k "v") "Hi")) (list (qqa (hasheq 'k "v") #\H) (qqa (hasheq 'k "v") #\i)))
|
|
|
|
|
(check-equal? (atomize (qqa (hasheq 'k "v") "Hi " (qq "You")))
|
|
|
|
|
(list
|
|
|
|
|
(qqa (hasheq 'k "v") #\H)
|
|
|
|
|
(qqa (hasheq 'k "v") #\i)
|
|
|
|
|
(qqa (hasheq 'k "v") #\space)
|
|
|
|
|
(qqa (hasheq 'k "v") #\Y)
|
|
|
|
|
(qqa (hasheq 'k "v") #\o)
|
|
|
|
|
(qqa (hasheq 'k "v") #\u)))
|
|
|
|
|
(check-equal? (atomize (qqa (hasheq 'k1 "v1" 'k2 42) "Hi \n\n" (qqa (hasheq 'k1 "v2" 'k3 "foo") "\n \nYou")))
|
|
|
|
|
(list
|
|
|
|
|
(qqa (hasheq 'k1 "v1" 'k2 42) #\H)
|
|
|
|
|
(qqa (hasheq 'k1 "v1" 'k2 42) #\i)
|
|
|
|
|
(qqa (hasheq 'k1 "v1" 'k2 42) #\space)
|
|
|
|
|
(qqa (hasheq 'k1 "v2" 'k2 42 'k3 "foo") #\Y)
|
|
|
|
|
(qqa (hasheq 'k1 "v2" 'k2 42 'k3 "foo") #\o)
|
|
|
|
|
(qqa (hasheq 'k1 "v2" 'k2 42 'k3 "foo") #\u))))
|
|
|
|
|
|
|
|
|
|
(define whitespace-pat #px"\\s+")
|
|
|
|
|
(define (merge-and-isolate-white str)
|
|
|
|
@ -113,19 +116,19 @@
|
|
|
|
|
[key first-key])
|
|
|
|
|
(match x
|
|
|
|
|
[(? quad?) ;; qexprs with attributes are recursed
|
|
|
|
|
(define this-attrs (get-field attrs x))
|
|
|
|
|
(define elems (get-field elems x))
|
|
|
|
|
(define this-attrs (quad-attrs x))
|
|
|
|
|
(define elems (quad-elems x))
|
|
|
|
|
(define next-key (if (hash-empty? this-attrs) key (gensym)))
|
|
|
|
|
(define next-attrs (if (hash-empty? this-attrs) attrs (attrs . update-with . this-attrs)))
|
|
|
|
|
(unless (hash-empty? this-attrs) (hash-set! next-attrs run-key next-key))
|
|
|
|
|
(append* (for/list ([elem (in-list (merge-adjacent-strings elems 'merge-white))])
|
|
|
|
|
(if (string? elem)
|
|
|
|
|
(list (q next-attrs elem))
|
|
|
|
|
(list (make-quad next-attrs (list elem)))
|
|
|
|
|
(loop elem next-attrs next-key))))]))
|
|
|
|
|
(λ (q) (string=? " " (car (get-field elems q))))))
|
|
|
|
|
(λ (q) (string=? " " (car (quad-elems q))))))
|
|
|
|
|
|
|
|
|
|
#;(module+ test
|
|
|
|
|
(check-equal?
|
|
|
|
|
(runify (quad (hasheq 'foo 42) (quad "Hi" " idiot" (quad (hasheq 'bar 84) "There") "Eve" "ry" "one")))
|
|
|
|
|
(list (quad (hasheq 'foo 42) "Hi") (quad (hasheq 'foo 42) " ") (quad (hasheq 'foo 42) "idiot") (quad (hasheq 'foo 42 'bar 84) "There") (quad (hasheq 'foo 42) "Everyone"))))
|
|
|
|
|
(runify (qqa (hasheq 'foo 42) (qq "Hi" " idiot" (qqa (hasheq 'bar 84) "There") "Eve" "ry" "one")))
|
|
|
|
|
(list (qqa (hasheq 'foo 42) "Hi") (qqa (hasheq 'foo 42) " ") (qqa (hasheq 'foo 42) "idiot") (qqa (hasheq 'foo 42 'bar 84) "There") (qqa (hasheq 'foo 42) "Everyone"))))
|
|
|
|
|
|
|
|
|
|