#lang racket/base
(require "sxml-tools.rkt"
"ssax/ssax.rkt"
"ssax/errors-and-warnings.rkt"
"sxpath-ext.rkt"
"txpath.rkt")
(provide (all-defined-out))
(define (sxpath path . ns-binding)
(let ((ns-binding (if (null? ns-binding) ns-binding (car ns-binding))))
(let loop ((converters '())
(root-vars '()) (path (if (string? path) (list path) path)))
(cond
((null? path) (lambda (node . var-binding)
(let ((var-binding
(if (null? var-binding) var-binding (car var-binding))))
(let rpt ((nodeset (as-nodeset node))
(conv (reverse converters))
(r-v (reverse root-vars)))
(if
(null? conv) nodeset
(rpt
(if (car r-v) ((car conv) nodeset var-binding)
((car conv) nodeset))
(cdr conv)
(cdr r-v)))))))
((and (pair? (car path))
(not (null? (car path)))
(eq? '*or* (caar path)))
(loop (cons (select-kids (ntype-names?? (cdar path))) converters)
(cons #f root-vars)
(cdr path)))
((and (pair? (car path))
(not (null? (car path)))
(eq? '*not* (caar path)))
(loop (cons
(select-kids (sxml:complement (ntype-names?? (cdar path))))
converters)
(cons #f root-vars)
(cdr path)))
((procedure? (car path))
(loop (cons (car path) converters)
(cons #t root-vars)
(cdr path)))
((eq? '// (car path))
(if (or (null? (cdr path))
(not (symbol? (cadr path)))
(eq? (cadr path) '@))
(loop (cons (sxml:descendant-or-self sxml:node?)
converters)
(cons #f root-vars)
(cdr path))
(loop (cons (sxml:descendant (ntype?? (cadr path)))
converters)
(cons #f root-vars)
(cddr path))))
((symbol? (car path))
(loop (cons (select-kids (ntype?? (car path))) converters)
(cons #f root-vars)
(cdr path)))
((string? (car path))
(and-let*
((f (sxml:xpath-expr (car path) ns-binding))) (loop (cons f converters)
(cons #t root-vars)
(cdr path))))
((and (pair? (car path)) (eq? 'equal? (caar path)))
(loop (cons (select-kids (apply node-equal? (cdar path))) converters)
(cons #f root-vars)
(cdr path)))
((and (pair? (car path)) (eq? 'ns-id:* (caar path)))
(loop
(cons (select-kids (ntype-namespace-id?? (cadar path))) converters)
(cons #f root-vars)
(cdr path)))
((and (pair? (car path)) (eq? 'eq? (caar path)))
(loop (cons (select-kids (apply node-eq? (cdar path))) converters)
(cons #f root-vars)
(cdr path)))
((pair? (car path))
(and-let*
((select
(if
(symbol? (caar path))
(lambda (node . var-binding)
((select-kids (ntype?? (caar path))) node))
(sxpath (caar path) ns-binding))))
(let reducer ((reducing-path (cdar path))
(filters '()))
(cond
((null? reducing-path)
(loop
(cons
(lambda (node var-binding)
(map-union
(lambda (node)
(let label ((nodeset (select node var-binding))
(fs (reverse filters)))
(if
(null? fs)
nodeset
(label
((car fs) nodeset var-binding)
(cdr fs)))))
(if (nodeset? node) node (list node))))
converters)
(cons #t root-vars)
(cdr path)))
((number? (car reducing-path))
(reducer
(cdr reducing-path)
(cons
(lambda (node var-binding)
((node-pos (car reducing-path)) node))
filters)))
(else
(and-let*
((func (sxpath (car reducing-path) ns-binding)))
(reducer
(cdr reducing-path)
(cons
(lambda (node var-binding)
((sxml:filter
(lambda (n) (func n var-binding)))
node))
filters))))))))
(else
(sxml:warn 'sxpath "invalid path step: ~e" (car path))
#f)))))
(define (if-sxpath path)
(lambda (obj)
(let ((x ((sxpath path) obj)))
(if (null? x) #f x))))
(define (if-car-sxpath path)
(lambda (obj)
(let ((x ((sxpath path) obj)))
(if (null? x) #f (car x)))))
(define (car-sxpath path)
(lambda (obj)
(let ((x ((sxpath path) obj)))
(if (null? x) '() (car x)))))
(define (sxml:id-alist node . lpaths)
(apply
append
(map
(lambda(lp)
(let ((lpr (reverse lp)))
(map
(lambda (nd)
(cons (sxml:attr nd (car lpr))
nd))
((sxpath (reverse (cons
(lambda(n r+v)
((node-self (sxpath `(@ ,(car lpr)))) n))
(cddr lpr)))) node))
))
lpaths)))