summaryrefslogtreecommitdiff
path: root/ebus-racket/ebus/xexpr-path/main.rkt
blob: 97e7f81703b9d42f8642456b4f21b2f68c710f22 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
#lang racket/base
;
; XML-Expression Path Lookup
;

(require racket/contract
         racket/string
         racket/match
         racket/dict
         racket/list
         xml)

(provide xexpr-path-first
         xexpr-path-list
         xexpr-path-text
         xexpr-path/c)


(define xexpr-path/c
  (listof (or/c symbol?
                (list/c symbol? string?)
                (list/c symbol?))))


(define (children element)
  (match element
    ((list tag (list (list name value) ...) children ...)
     children)

    ((list tag children ...)
     children)

    (else
     null)))


(define (attr-value?? name value)
  (lambda (v)
    (equal? (dict-ref (attributes v) name #f) value)))


(define (tag-name?? name)
  (lambda (v)
    (or (eq? name '*)
        (and (pair? v)
             (eq? (car v) name)))))


(define (attributes element)
  (match element
    ((list tag (list (list name value) ...) children ...)
     (for/list ((n (in-list name))
                (v (in-list value)))
       (cons n v)))

    (else
     null)))


(define (path-item-procedure item)
  (match item
    ((list attr-name attr-value)
     (lambda (tags)
       (list (filter (attr-value?? attr-name attr-value) tags))))

    ((list attr-name)
     (lambda (tags)
       (list
         (filter values
                 (for/list ((tag (in-list tags)))
                   (dict-ref (attributes tag) attr-name #f))))))

    (tag-name
     (lambda (tags)
       (for/list ((tag (in-list tags)))
         (filter (tag-name?? tag-name) (children tag)))))))


(define/contract (xexpr-path-list path xexpr)
                 (-> xexpr-path/c xexpr/c (listof (or/c xexpr/c string?)))
  (let ((pipeline (append* (for/list ((item (in-list path)))
                             (list (path-item-procedure item) append*)))))
    ((apply compose (reverse pipeline)) (list xexpr))))


(define/contract (xexpr-path-first path xexpr)
                 (-> xexpr-path/c xexpr/c (or/c xexpr/c string? #f))
  (let ((results (xexpr-path-list path xexpr)))
    (and (not (null? results))
         (first results))))


(define/contract (xexpr-path-text path xexpr)
                 (-> xexpr-path/c xexpr/c (or/c #f string?))
  (let ((results (xexpr-path-list path xexpr)))
    (string-append* (map xexpr->string results))))


; vim:set ts=2 sw=2 et: