-
Notifications
You must be signed in to change notification settings - Fork 1
/
core.cljc
189 lines (142 loc) · 3.79 KB
/
core.cljc
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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
(ns zippo.core
(:require
[clojure.zip :as zip]))
(defn loc-seq
"Get a lazy, finite seq of locations."
[loc]
(->> loc
(iterate zip/next)
(take-while (complement zip/end?))))
(defn ->loc-pred
"Turn a node predicate into a location predicate."
[node-pred]
(fn [loc]
(-> loc zip/node node-pred)))
(defn loc-find
"Find the first location matches a predicate."
[loc loc-pred]
(->> loc
(loc-seq)
(filter loc-pred)
(first)))
(defn loc-find-all
"Find all the locations that match a predicate."
[loc loc-pred]
(->> loc
(loc-seq)
(filter loc-pred)))
(defn loc-update
"Update locations that match the `loc-pred` function
with the `loc-fn` functions and the rest arguments.
Returns the last (end) location."
[loc loc-pred loc-fn & args]
(loop [loc loc]
(if (zip/end? loc)
loc
(if (loc-pred loc)
(recur (zip/next (apply loc-fn loc args)))
(recur (zip/next loc))))))
(defn loc-update-all
"Update all the locations with the `loc-fn` and the rest
arguments. Returns the last (end) location."
[loc loc-fn & args]
(loop [loc loc]
(if (zip/end? loc)
loc
(recur (zip/next (apply loc-fn loc args))))))
(defn node-update
"Like `loc-update` but acts on nodes. Updates all the nodes
that match `node-pred` with the `node-fn` function
and the rest arguments."
[loc node-pred node-fn & args]
(apply loc-update
loc
(->loc-pred node-pred)
zip/edit
node-fn
args))
(defn loc-children
"Return all the children locations."
[loc]
(when-let [loc-child (zip/down loc)]
(->> loc-child
(iterate zip/right)
(take-while some?))))
(defn locs-children
"For a seq of locations, return their concatenated children."
[locs]
(mapcat loc-children locs))
(defn loc-layers
"For a given location, return a lazy seq of its 'layers',
e.g. children, the children of children and so on."
[loc]
(->> [loc]
(iterate locs-children)
(take-while seq)))
(defn- -locs-seq-breadth [locs]
(when (seq locs)
(lazy-seq
(concat locs
(-locs-seq-breadth (locs-children locs))))))
(defn loc-seq-breadth
"Return a lazy seq of locations in breadth-first direction
(left to right, down, left to right and so on)."
[loc]
(-locs-seq-breadth [loc]))
(defn- -lookup-until [direction loc loc-pred]
(->> loc
(iterate direction)
(take-while some?)
(rest)
(filter loc-pred)
(first)))
(defn lookup-up
"Go up until a location matches a predicate."
[loc loc-pred]
(-lookup-until zip/up loc loc-pred))
(defn lookup-left
"Go left until a location matches a predicate."
[loc loc-pred]
(-lookup-until zip/left loc loc-pred))
(defn lookup-right
"Go right until a location matches a predicate."
[loc loc-pred]
(-lookup-until zip/left loc loc-pred))
(defn lookup-down
"Go down until a location matches a predicate."
[loc loc-pred]
(-lookup-until zip/left loc loc-pred))
(defn ->map-entry [k v]
#?(:clj (new clojure.lang.MapEntry k v)
:cljs (new cljs.core.MapEntry k v nil)))
(defn coll-make-node
[node children]
(cond
;; MapEntry doesn't support meta
(map-entry? node)
(let [[k v] children]
(->map-entry k v))
:else
(with-meta
(cond
(vector? node)
(vec children)
(set? node)
(set children)
(map? node)
(persistent!
(reduce ;; into {} doesn't work
(fn [acc! [k v]]
(assoc! acc! k v))
(transient {})
children))
:else
children)
(meta node))))
(defn coll-zip
"A zipper to navigate through any (nested) collection."
[root]
(zip/zipper coll?
seq
coll-make-node
root))