#lang racket/base
(require (only-in racket/port call-with-input-string)
"ssax/ssax.rkt"
"ssax/errors-and-warnings.rkt"
"sxml-tools.rkt")
(provide (all-defined-out))
(define (sxml:string object)
(cond
((string? object) object)
((nodeset? object) (if (null? object)
""
(sxml:string-value (car object))))
((number? object)
(if (and (rational? object) (not (integer? object))) (number->string (exact->inexact object))
(number->string object)))
((boolean? object) (if object "true" "false"))
(else "")))
(define (sxml:boolean object)
(cond
((boolean? object) object)
((number? object) (not (= object 0)))
((string? object) (> (string-length object) 0))
((nodeset? object) (not (null? object)))
(else #f)))
(define (sxml:number obj)
(cond
((number? obj) obj)
((string? obj)
(let ((nmb (call-with-input-string obj read)))
(if (number? nmb)
nmb
0))) ((boolean? obj) (if obj 1 0))
((nodeset? obj) (sxml:number (sxml:string obj)))
(else 0)))
(define (sxml:string-value node)
(cond
((not (pair? node)) (sxml:string node))
((null? (cdr node))
"")
(else
(apply string-append (map
(lambda (node)
(if (sxml:node? node) (sxml:string-value node) ""))
(cdr node))))))
(define (sxml:id id-index)
(lambda(object)
(if (nodeset? object)
(let loop ((str-lst (map sxml:string-value object))
(res '()))
(if (null? str-lst)
(reverse res)
(let ((node (sxml:lookup (car str-lst) id-index)))
(if (not node) (loop (cdr str-lst) res)
(loop (cdr str-lst) (cons node res))))))
(let rpt ((lst (string->list (sxml:string object)))
(tmp '())
(res '()))
(cond
((null? lst)
(if (null? tmp)
(reverse res)
(let ((node (sxml:lookup (list->string (reverse tmp)) id-index)))
(if (not node)
(reverse res)
(reverse (cons node res))))))
((member (car lst) '(#\space #\return #\newline #\tab))
(if (null? tmp)
(rpt (cdr lst) tmp res)
(let ((node (sxml:lookup (list->string (reverse tmp)) id-index)))
(if (not node)
(rpt (cdr lst) '() res)
(rpt (cdr lst) '() (cons node res))))))
(else (rpt (cdr lst) (cons (car lst) tmp) res)))))))
(define (sxml:nested-loop-join string-set1 string-set2 string-op)
(let first ((str-set1 string-set1)
(str-set2 string-set2))
(cond
((null? str-set1) #f)
((let second ((elem (car str-set1))
(set2 str-set2))
(cond
((null? set2) #f)
((string-op elem (car set2)) #t)
(else (second elem (cdr set2))))) #t)
(else
(first (cdr str-set1) str-set2)))))
(define (sxml:list-head lst k)
(if (or (null? lst) (zero? k))
'()
(cons (car lst) (sxml:list-head (cdr lst) (- k 1)))))
(define (sxml:merge-sort less-than?-pred lst)
(letrec
((merge-sorted-lists
(lambda (lst1 lst2)
(cond
((null? lst1) lst2)
((null? lst2) lst1)
((less-than?-pred (car lst1) (car lst2))
(cons (car lst1)
(merge-sorted-lists (cdr lst1) lst2)))
(else
(cons (car lst2)
(merge-sorted-lists lst1 (cdr lst2))))))))
(if
(or (null? lst) (null? (cdr lst))) lst
(let ((middle (inexact->exact (round (/ (length lst) 2)))))
(merge-sorted-lists
(sxml:merge-sort less-than?-pred (sxml:list-head lst middle))
(sxml:merge-sort less-than?-pred (list-tail lst middle)))))))
(define (sxml:merge-sort-join string-set1 string-set2 string-op)
(let loop ((str-set1 (sxml:merge-sort string<? string-set1))
(str-set2 (sxml:merge-sort string<? string-set2)))
(cond
((or (null? str-set1) (null? str-set2))
#f)
((string-op (car str-set1) (car str-set2))
#t)
((string<? (car str-set1) (car str-set2))
(loop (cdr str-set1) str-set2))
(else (loop str-set1 (cdr str-set2))))))
(define (sxml:charlst->branch lst)
(if (null? (cdr lst)) `(,(car lst) #t)
`(,(car lst) #f ,(sxml:charlst->branch (cdr lst)))))
(define (sxml:string->tree str)
(let ((lst (string->list str)))
(if (null? lst) '(*top* #t)
`(*top* #f ,(sxml:charlst->branch lst)))))
(define (sxml:add-string-to-tree str tree)
(letrec
((add-lst-to-tree (lambda (lst tree)
(if
(null? lst) (if
(cadr tree) tree
(cons (car tree)
(cons #t (cddr tree))))
(let ((curr-char (car lst)))
(let iter-alist ((alist (cddr tree))
(res (list (cadr tree) (car tree))))
(cond
((null? alist) (reverse
(cons
(sxml:charlst->branch lst)
res)))
((char=? (caar alist) curr-char) (if
(null? (cdr alist)) (reverse
(cons
(add-lst-to-tree (cdr lst) (car alist))
res))
(append
(reverse
(cons
(add-lst-to-tree (cdr lst) (car alist))
res))
(cdr alist))))
((char>? (caar alist) curr-char)
(if
(null? (cdr alist)) (reverse
(cons (car alist)
(cons (sxml:charlst->branch lst) res)))
(append
(reverse
(cons
(sxml:charlst->branch lst)
res))
alist)))
(else
(iter-alist (cdr alist)
(cons (car alist) res))))))))))
(add-lst-to-tree (string->list str) tree)))
(define (sxml:string-in-tree? str tree)
(let loop ((lst (string->list str))
(tree tree))
(cond
((null? lst) (cadr tree))
((assv (car lst) (cddr tree))
=> (lambda (new-tree)
(loop (cdr lst) new-tree)))
(else #f))))
(define (sxml:radix-sort-join string-set1 string-set2 bool-op)
(if
(null? string-set1) #f
(let ((tree
(let iter-1 ((set1 (cdr string-set1))
(tree (sxml:string->tree (car string-set1))))
(if (null? set1)
tree
(iter-1 (cdr set1)
(sxml:add-string-to-tree (car set1) tree))))))
(let iter-2 ((set2 string-set2))
(cond
((null? set2) #f)
((bool-op (sxml:string-in-tree? (car set2) tree) #t)
#t)
(else
(iter-2 (cdr set2))))))))
(define (sxml:equality-cmp bool-op number-op string-op)
(lambda (obj1 obj2)
(cond
((and (not (nodeset? obj1)) (not (nodeset? obj2)))
(cond
((boolean? obj1) (bool-op obj1 (sxml:boolean obj2)))
((boolean? obj2) (bool-op (sxml:boolean obj1) obj2))
((number? obj1) (number-op obj1 (sxml:number obj2)))
((number? obj2) (number-op (sxml:number obj1) obj2))
(else (string-op obj1 obj2))))
((and (nodeset? obj1) (nodeset? obj2)) (let ((lng1 (length obj1))
(lng2 (length obj2)))
(cond
((and (< lng1 100000) (< lng2 100000))
((if (or (<= lng1 2) (<= lng2 2))
sxml:nested-loop-join
sxml:merge-sort-join)
(map sxml:string-value obj1)
(map sxml:string-value obj2)
string-op))
((< lng1 lng2)
(sxml:radix-sort-join (map sxml:string-value obj1)
(map sxml:string-value obj2)
bool-op))
(else (sxml:radix-sort-join (map sxml:string-value obj2)
(map sxml:string-value obj1)
bool-op)))))
(else (call-with-values
(lambda () (if (nodeset? obj1) (values obj1 obj2) (values obj2 obj1)))
(lambda (nset elem)
(cond
((boolean? elem) (bool-op elem (sxml:boolean nset)))
((number? elem)
(let loop ((nset
(map
(lambda (node) (sxml:number (sxml:string-value node)))
nset)))
(cond
((null? nset) #f)
((number-op elem (car nset)) #t)
(else (loop (cdr nset))))))
((string? elem)
(let loop ((nset (map sxml:string-value nset)))
(cond
((null? nset) #f)
((string-op elem (car nset)) #t)
(else (loop (cdr nset))))))
(else (sxml:warn 'sxml:equality-cmp "unknown datatype: ~e" elem)
#f))))))))
(define sxml:equal? (sxml:equality-cmp eq? = string=?))
(define sxml:not-equal?
(sxml:equality-cmp
(lambda (bool1 bool2) (not (eq? bool1 bool2)))
(lambda (num1 num2) (not (= num1 num2)))
(lambda (str1 str2) (not (string=? str1 str2)))))
(define (sxml:relational-cmp op)
(lambda (obj1 obj2)
(cond
((not (or (nodeset? obj1) (nodeset? obj2))) (op (sxml:number obj1) (sxml:number obj2)))
((boolean? obj1) (op (sxml:number obj1) (sxml:number (sxml:boolean obj2))))
((boolean? obj2) (op (sxml:number (sxml:boolean obj1)) (sxml:number obj2)))
((or (null? obj1) (null? obj2)) #f)
(else (op
(cond
((nodeset? obj1) (let ((nset1 (map
(lambda (node) (sxml:number (sxml:string-value node)))
obj1)))
(let first ((num1 (car nset1))
(nset1 (cdr nset1)))
(cond
((null? nset1) num1)
((op num1 (car nset1)) (first num1 (cdr nset1)))
(else (first (car nset1) (cdr nset1)))))))
((string? obj1) (sxml:number obj1))
(else obj1))
(cond
((nodeset? obj2) (let ((nset2 (map
(lambda (node) (sxml:number (sxml:string-value node)))
obj2)))
(let second ((num2 (car nset2))
(nset2 (cdr nset2)))
(cond
((null? nset2) num2)
((op num2 (car nset2)) (second (car nset2) (cdr nset2)))
(else (second num2 (cdr nset2)))))))
((string? obj2) (sxml:number obj2))
(else obj2)))))))
(define (sxml:ancestor test-pred?)
(lambda (root-node) (lambda (node) (if (nodeset? node)
(map-union ((sxml:ancestor test-pred?) root-node) node)
(let rpt ((paths (if (nodeset? root-node)
(map list root-node)
(list (list root-node)))))
(if (null? paths)
'()
(let ((path (car paths)))
(if (eq? (car path) node)
((sxml:filter test-pred?) (cdr path))
(rpt (append
(map
(lambda (arg) (cons arg path))
(append
((sxml:attribute (ntype?? '*)) (car path))
((sxml:child sxml:node?) (car path))))
(cdr paths)))))))))))
(define (sxml:ancestor-or-self test-pred?)
(lambda (root-node) (lambda (node) (if (nodeset? node)
(map-union ((sxml:ancestor-or-self test-pred?) root-node) node)
(let rpt ((paths (if (nodeset? root-node)
(map list root-node)
(list (list root-node)))))
(if (null? paths)
((sxml:filter test-pred?) (list node))
(let ((path (car paths)))
(if (eq? (car path) node)
((sxml:filter test-pred?) path)
(rpt (append
(map
(lambda (arg) (cons arg path))
(append
((sxml:attribute (ntype?? '*)) (car path))
((sxml:child sxml:node?) (car path))))
(cdr paths)))))))))))
(define (sxml:descendant test-pred?)
(lambda (node) (if (nodeset? node)
(map-union (sxml:descendant test-pred?) node)
(let rpt ((res '())
(more ((sxml:child sxml:node?) node)))
(if (null? more)
(reverse res)
(rpt (if (test-pred? (car more))
(cons (car more) res)
res)
(append ((sxml:child sxml:node?) (car more))
(cdr more))))))))
(define (sxml:descendant-or-self test-pred?)
(lambda (node) (if (nodeset? node)
(map-union (sxml:descendant-or-self test-pred?) node)
(let rpt ((res '())
(more (list node)))
(if (null? more)
(reverse res)
(rpt (if (test-pred? (car more))
(cons (car more) res)
res)
(append ((sxml:child sxml:node?) (car more))
(cdr more))))))))
(define (sxml:following test-pred?)
(lambda (root-node) (lambda (node) (if (nodeset? node)
(map-union ((sxml:following test-pred?) root-node) node)
(let loop ((seq (if (nodeset? root-node)
(list root-node)
(list (list root-node)))))
(cond
((null? seq) '())
((null? (car seq)) (loop (cdr seq)))
((eq? (caar seq) node)
(let rpt ((seq (cdr (apply append seq)))
(res '()))
(if (null? seq)
res
(rpt (cdr seq)
(append
res
((sxml:descendant-or-self test-pred?) (car seq)))))))
((and (sxml:element? (caar seq))
(memq node (sxml:attr-list (caar seq))))
(let rpt ((sq (cdr (apply append seq)))
(res ((sxml:descendant test-pred?) (caar seq))))
(if (null? sq)
res
(rpt (cdr sq)
(append res
((sxml:descendant-or-self test-pred?) (car sq)))))))
(else
(loop (cons
((sxml:child sxml:node?) (caar seq))
(cons (cdar seq) (cdr seq)))))))))))
(define (sxml:following-sibling test-pred?)
(lambda (root-node) (lambda (node) (if (nodeset? node)
(map-union ((sxml:following-sibling test-pred?) root-node) node)
(let loop ((seqs (if (nodeset? root-node)
(list root-node)
(list (list root-node)))))
(if (null? seqs)
'()
(let rpt ((seq (car seqs)))
(cond
((null? seq)
(loop (append
(map (sxml:child sxml:node?)
(car seqs))
(cdr seqs))))
((eq? (car seq) node) ((sxml:filter test-pred?) (cdr seq)))
(else (rpt (cdr seq)))))))))))
(define (sxml:namespace test-pred?)
(lambda (node) ((sxml:filter test-pred?)
(sxml:ns-list node))))
(define (sxml:preceding test-pred?)
(lambda (root-node) (lambda (node) (if (nodeset? node)
(map-union ((sxml:preceding test-pred?) root-node) node)
(let loop ((seq (if (nodeset? root-node)
(list (reverse root-node))
(list (list root-node)))))
(cond
((null? seq) '())
((null? (car seq)) (loop (cdr seq)))
((or (eq? (caar seq) node)
(not (null? ((sxml:attribute
(lambda (n)
(eq? n node)))
(caar seq)))))
(let rpt ((seq (cdr (apply append seq)))
(res '()))
(if (null? seq)
res
(rpt (cdr seq)
(append res
(reverse ((sxml:descendant-or-self test-pred?)
(car seq))))))))
(else (loop (cons (reverse ((sxml:child sxml:node?) (caar seq)))
(cons (cdar seq) (cdr seq)))))))))))
(define (sxml:preceding-sibling test-pred?)
(lambda (root-node) (lambda (node) (if(nodeset? node)
(map-union ((sxml:preceding-sibling test-pred?) root-node) node)
(let loop ((seqs (if (nodeset? root-node)
(list root-node)
(list (list root-node)))))
(if (null? seqs)
'()
(let rpt ((seq (car seqs)))
(cond
((null? seq)
(loop (append
(map
(lambda (n)
(reverse ((sxml:child sxml:node?) n)))
(car seqs))
(cdr seqs))))
((eq? (car seq) node) ((sxml:filter test-pred?) (cdr seq)))
(else (rpt (cdr seq)))))))))))