2015-02-26 15:55:20 +00:00
|
|
|
(ns com.rpl.specter
|
2015-10-09 16:33:24 +00:00
|
|
|
#+cljs (:require-macros
|
|
|
|
|
[com.rpl.specter.macros
|
|
|
|
|
:refer
|
2015-09-11 20:13:03 +00:00
|
|
|
[pathed-collector
|
2016-05-21 19:54:07 +00:00
|
|
|
variable-pathed-nav
|
|
|
|
|
fixed-pathed-nav
|
2015-12-12 17:03:59 +00:00
|
|
|
defcollector
|
2016-05-21 19:54:07 +00:00
|
|
|
defnav
|
2016-05-20 20:57:53 +00:00
|
|
|
defpathedfn
|
2015-10-09 16:33:24 +00:00
|
|
|
]]
|
|
|
|
|
)
|
2016-05-21 19:54:07 +00:00
|
|
|
(:use [com.rpl.specter.protocols :only [Navigator]]
|
2015-10-09 16:33:24 +00:00
|
|
|
#+clj [com.rpl.specter.macros :only
|
|
|
|
|
[pathed-collector
|
2016-05-21 19:54:07 +00:00
|
|
|
variable-pathed-nav
|
|
|
|
|
fixed-pathed-nav
|
2015-12-12 17:03:59 +00:00
|
|
|
defcollector
|
2016-05-21 19:54:07 +00:00
|
|
|
defnav
|
2016-05-20 20:57:53 +00:00
|
|
|
defpathedfn]]
|
2015-09-11 20:06:31 +00:00
|
|
|
)
|
2015-10-10 15:52:50 +00:00
|
|
|
(:require [com.rpl.specter.impl :as i]
|
|
|
|
|
[clojure.set :as set])
|
2015-02-26 15:55:20 +00:00
|
|
|
)
|
|
|
|
|
|
2015-04-19 17:45:20 +00:00
|
|
|
(defn comp-paths [& paths]
|
2015-09-09 19:20:58 +00:00
|
|
|
(i/comp-paths* (vec paths)))
|
2015-02-26 15:55:20 +00:00
|
|
|
|
2016-05-21 13:44:20 +00:00
|
|
|
(def must-cache-paths! i/must-cache-paths!)
|
|
|
|
|
|
2016-04-21 14:59:35 +00:00
|
|
|
;; Selection functions
|
2015-02-26 15:55:20 +00:00
|
|
|
|
2016-04-21 14:59:35 +00:00
|
|
|
(def ^{:doc "Version of select that takes in a path pre-compiled with comp-paths"}
|
2015-06-24 18:28:33 +00:00
|
|
|
compiled-select i/compiled-select*)
|
2015-05-10 06:12:06 +00:00
|
|
|
|
2015-05-10 22:47:57 +00:00
|
|
|
(defn select
|
2016-04-21 14:59:35 +00:00
|
|
|
"Navigates to and returns a sequence of all the elements specified by the path."
|
|
|
|
|
[path structure]
|
|
|
|
|
(compiled-select (i/comp-paths* path)
|
2015-05-10 22:47:57 +00:00
|
|
|
structure))
|
|
|
|
|
|
|
|
|
|
(defn compiled-select-one
|
2016-04-21 14:59:35 +00:00
|
|
|
"Version of select-one that takes in a path pre-compiled with comp-paths"
|
|
|
|
|
[path structure]
|
|
|
|
|
(let [res (compiled-select path structure)]
|
2015-02-26 15:55:20 +00:00
|
|
|
(when (> (count res) 1)
|
2016-04-21 14:59:35 +00:00
|
|
|
(i/throw-illegal "More than one element found for params: " path structure))
|
2015-02-26 15:55:20 +00:00
|
|
|
(first res)
|
|
|
|
|
))
|
|
|
|
|
|
2015-05-10 22:47:57 +00:00
|
|
|
(defn select-one
|
|
|
|
|
"Like select, but returns either one element or nil. Throws exception if multiple elements found"
|
2016-04-21 14:59:35 +00:00
|
|
|
[path structure]
|
|
|
|
|
(compiled-select-one (i/comp-paths* path) structure))
|
2015-05-10 22:47:57 +00:00
|
|
|
|
|
|
|
|
(defn compiled-select-one!
|
2016-04-21 14:59:35 +00:00
|
|
|
"Version of select-one! that takes in a path pre-compiled with comp-paths"
|
|
|
|
|
[path structure]
|
|
|
|
|
(let [res (compiled-select path structure)]
|
|
|
|
|
(when (not= 1 (count res)) (i/throw-illegal "Expected exactly one element for params: " path structure))
|
2015-06-30 03:43:15 +00:00
|
|
|
(first res)
|
2015-02-26 15:55:20 +00:00
|
|
|
))
|
|
|
|
|
|
2015-05-10 22:47:57 +00:00
|
|
|
(defn select-one!
|
|
|
|
|
"Returns exactly one element, throws exception if zero or multiple elements found"
|
2016-04-21 14:59:35 +00:00
|
|
|
[path structure]
|
|
|
|
|
(compiled-select-one! (i/comp-paths* path) structure))
|
2015-05-10 22:47:57 +00:00
|
|
|
|
|
|
|
|
(defn compiled-select-first
|
2016-04-21 14:59:35 +00:00
|
|
|
"Version of select-first that takes in a path pre-compiled with comp-paths"
|
|
|
|
|
[path structure]
|
|
|
|
|
(first (compiled-select path structure)))
|
2015-05-10 22:47:57 +00:00
|
|
|
|
2015-02-26 15:55:20 +00:00
|
|
|
(defn select-first
|
2015-04-22 15:46:13 +00:00
|
|
|
"Returns first element found. Not any more efficient than select, just a convenience"
|
2016-04-21 14:59:35 +00:00
|
|
|
[path structure]
|
|
|
|
|
(compiled-select-first (i/comp-paths* path) structure))
|
2015-02-26 15:55:20 +00:00
|
|
|
|
|
|
|
|
|
2016-04-21 14:59:35 +00:00
|
|
|
;; Transformation functions
|
2015-06-18 04:56:03 +00:00
|
|
|
|
2016-04-21 14:59:35 +00:00
|
|
|
(def ^{:doc "Version of transform that takes in a path pre-compiled with comp-paths"}
|
2015-06-24 18:28:33 +00:00
|
|
|
compiled-transform i/compiled-transform*)
|
2015-05-10 22:47:57 +00:00
|
|
|
|
2015-06-24 15:42:50 +00:00
|
|
|
(defn transform
|
2016-04-21 14:59:35 +00:00
|
|
|
"Navigates to each value specified by the path and replaces it by the result of running
|
2015-06-24 15:42:50 +00:00
|
|
|
the transform-fn on it"
|
2016-04-21 14:59:35 +00:00
|
|
|
[path transform-fn structure]
|
|
|
|
|
(compiled-transform (i/comp-paths* path) transform-fn structure))
|
2015-05-10 22:47:57 +00:00
|
|
|
|
|
|
|
|
(defn compiled-setval
|
2016-04-21 14:59:35 +00:00
|
|
|
"Version of setval that takes in a path pre-compiled with comp-paths"
|
|
|
|
|
[path val structure]
|
|
|
|
|
(compiled-transform path (fn [_] val) structure))
|
2015-05-10 12:09:48 +00:00
|
|
|
|
2015-04-22 15:46:13 +00:00
|
|
|
(defn setval
|
2016-04-21 14:59:35 +00:00
|
|
|
"Navigates to each value specified by the path and replaces it by val"
|
|
|
|
|
[path val structure]
|
|
|
|
|
(compiled-setval (i/comp-paths* path) val structure))
|
2015-02-26 15:55:20 +00:00
|
|
|
|
2015-05-10 22:47:57 +00:00
|
|
|
(defn compiled-replace-in
|
2016-04-21 14:59:35 +00:00
|
|
|
"Version of replace-in that takes in a path pre-compiled with comp-paths"
|
|
|
|
|
[path transform-fn structure & {:keys [merge-fn] :or {merge-fn concat}}]
|
2015-06-24 18:28:33 +00:00
|
|
|
(let [state (i/mutable-cell nil)]
|
2016-04-21 14:59:35 +00:00
|
|
|
[(compiled-transform path
|
2016-01-12 21:25:02 +00:00
|
|
|
(fn [& args]
|
|
|
|
|
(let [res (apply transform-fn args)]
|
2015-02-26 15:55:20 +00:00
|
|
|
(if res
|
|
|
|
|
(let [[ret user-ret] res]
|
|
|
|
|
(->> user-ret
|
2015-06-24 18:28:33 +00:00
|
|
|
(merge-fn (i/get-cell state))
|
|
|
|
|
(i/set-cell! state))
|
2015-02-26 15:55:20 +00:00
|
|
|
ret)
|
2016-01-12 21:25:02 +00:00
|
|
|
(last args)
|
2015-02-26 15:55:20 +00:00
|
|
|
)))
|
|
|
|
|
structure)
|
2015-06-24 18:28:33 +00:00
|
|
|
(i/get-cell state)]
|
2015-02-26 15:55:20 +00:00
|
|
|
))
|
|
|
|
|
|
2015-05-10 22:47:57 +00:00
|
|
|
(defn replace-in
|
2015-09-18 19:01:01 +00:00
|
|
|
"Similar to transform, except returns a pair of [transformed-structure sequence-of-user-ret].
|
2016-05-21 19:54:07 +00:00
|
|
|
The transform-fn in this case is expected to return [ret user-ret]. ret is
|
2015-06-24 15:42:50 +00:00
|
|
|
what's used to transform the data structure, while user-ret will be added to the user-ret sequence
|
2015-05-10 22:47:57 +00:00
|
|
|
in the final return. replace-in is useful for situations where you need to know the specific values
|
2016-01-12 20:40:37 +00:00
|
|
|
of what was transformed in the data structure."
|
2016-04-21 14:59:35 +00:00
|
|
|
[path transform-fn structure & {:keys [merge-fn] :or {merge-fn concat}}]
|
|
|
|
|
(compiled-replace-in (i/comp-paths* path) transform-fn structure :merge-fn merge-fn))
|
2015-05-10 22:47:57 +00:00
|
|
|
|
2015-09-11 05:42:29 +00:00
|
|
|
;; Helpers for defining selectors and collectors with late-bound params
|
2015-09-10 17:56:33 +00:00
|
|
|
|
2016-04-21 14:59:35 +00:00
|
|
|
(def ^{:doc "Takes a compiled path that needs late-bound params and supplies it with
|
2015-09-11 15:21:21 +00:00
|
|
|
an array of params and a position in the array from which to begin reading
|
|
|
|
|
params. The return value is an executable selector."}
|
|
|
|
|
bind-params* i/bind-params*)
|
|
|
|
|
|
2016-01-30 20:03:46 +00:00
|
|
|
(defn params-reset [params-path]
|
|
|
|
|
;; TODO: error if not paramsneededpath
|
|
|
|
|
(let [s (i/params-needed-selector params-path)
|
2016-01-30 20:41:29 +00:00
|
|
|
t (i/params-needed-transformer params-path)
|
|
|
|
|
needed (i/num-needed-params params-path)]
|
2016-01-30 20:03:46 +00:00
|
|
|
(i/->ParamsNeededPath
|
|
|
|
|
(i/->TransformFunctions
|
|
|
|
|
i/RichPathExecutor
|
|
|
|
|
(fn [params params-idx vals structure next-fn]
|
2016-01-30 20:41:29 +00:00
|
|
|
(s params (- params-idx needed) vals structure next-fn)
|
2016-01-30 20:03:46 +00:00
|
|
|
)
|
|
|
|
|
(fn [params params-idx vals structure next-fn]
|
2016-01-30 20:41:29 +00:00
|
|
|
(t params (- params-idx needed) vals structure next-fn)
|
2016-01-30 20:03:46 +00:00
|
|
|
))
|
|
|
|
|
0)))
|
|
|
|
|
|
2015-02-26 15:55:20 +00:00
|
|
|
;; Built-in pathing and context operations
|
|
|
|
|
|
2016-05-21 19:54:07 +00:00
|
|
|
(defnav
|
2016-01-10 15:35:18 +00:00
|
|
|
^{:doc "Stops navigation at this point. For selection returns nothing and for
|
|
|
|
|
transformation returns the structure unchanged"}
|
2016-02-13 23:56:35 +00:00
|
|
|
STOP
|
2016-01-10 15:35:18 +00:00
|
|
|
[]
|
|
|
|
|
(select* [this structure next-fn]
|
|
|
|
|
nil )
|
|
|
|
|
(transform* [this structure next-fn]
|
|
|
|
|
structure
|
|
|
|
|
))
|
|
|
|
|
|
2016-05-21 19:54:07 +00:00
|
|
|
(defnav
|
2016-04-21 14:59:35 +00:00
|
|
|
^{:doc "Stays navigated at the current point. Essentially a no-op navigator."}
|
2016-01-14 18:15:19 +00:00
|
|
|
STAY
|
|
|
|
|
[]
|
|
|
|
|
(select* [this structure next-fn]
|
|
|
|
|
(next-fn structure))
|
|
|
|
|
(transform* [this structure next-fn]
|
|
|
|
|
(next-fn structure)))
|
|
|
|
|
|
2016-05-21 19:54:07 +00:00
|
|
|
(def ALL (comp-paths (i/->AllNavigator)))
|
2015-02-26 15:55:20 +00:00
|
|
|
|
2015-06-24 18:28:33 +00:00
|
|
|
(def VAL (i/->ValCollect))
|
2015-02-26 15:55:20 +00:00
|
|
|
|
2016-05-21 19:54:07 +00:00
|
|
|
(def LAST (comp-paths (i/->PosNavigator last i/set-last)))
|
2015-02-26 15:55:20 +00:00
|
|
|
|
2016-05-21 19:54:07 +00:00
|
|
|
(def FIRST (comp-paths (i/->PosNavigator first i/set-first)))
|
2015-02-26 15:55:20 +00:00
|
|
|
|
2016-05-21 19:54:07 +00:00
|
|
|
(defnav
|
2015-09-11 15:21:21 +00:00
|
|
|
^{:doc "Uses start-fn and end-fn to determine the bounds of the subsequence
|
|
|
|
|
to select when navigating. Each function takes in the structure as input."}
|
|
|
|
|
srange-dynamic
|
|
|
|
|
[start-fn end-fn]
|
2015-09-11 02:26:32 +00:00
|
|
|
(select* [this structure next-fn]
|
|
|
|
|
(i/srange-select structure (start-fn structure) (end-fn structure) next-fn))
|
|
|
|
|
(transform* [this structure next-fn]
|
|
|
|
|
(i/srange-transform structure (start-fn structure) (end-fn structure) next-fn)
|
|
|
|
|
))
|
2015-04-15 03:49:32 +00:00
|
|
|
|
2016-05-21 19:54:07 +00:00
|
|
|
(defnav
|
2015-09-11 15:21:21 +00:00
|
|
|
^{:doc "Navigates to the subsequence bound by the indexes start (inclusive)
|
|
|
|
|
and end (exclusive)"}
|
|
|
|
|
srange
|
|
|
|
|
[start end]
|
2015-09-11 02:26:32 +00:00
|
|
|
(select* [this structure next-fn]
|
|
|
|
|
(i/srange-select structure start end next-fn))
|
|
|
|
|
(transform* [this structure next-fn]
|
|
|
|
|
(i/srange-transform structure start end next-fn)
|
|
|
|
|
))
|
2015-04-15 03:49:32 +00:00
|
|
|
|
2015-06-24 18:28:33 +00:00
|
|
|
(def BEGINNING (srange 0 0))
|
2015-04-15 03:49:32 +00:00
|
|
|
|
|
|
|
|
(def END (srange-dynamic count count))
|
|
|
|
|
|
2016-05-21 19:54:07 +00:00
|
|
|
(defnav
|
2015-10-10 15:52:50 +00:00
|
|
|
^{:doc "Navigates to the specified subset (by taking an intersection).
|
|
|
|
|
In a transform, that subset in the original set is changed to the
|
|
|
|
|
new value of the subset."}
|
|
|
|
|
subset
|
|
|
|
|
[aset]
|
|
|
|
|
(select* [this structure next-fn]
|
|
|
|
|
(next-fn (set/intersection structure aset)))
|
|
|
|
|
(transform* [this structure next-fn]
|
2016-01-30 20:41:29 +00:00
|
|
|
(let [subset (set/intersection structure aset)
|
2015-10-10 15:52:50 +00:00
|
|
|
newset (next-fn subset)]
|
|
|
|
|
(-> structure
|
|
|
|
|
(set/difference subset)
|
|
|
|
|
(set/union newset))
|
|
|
|
|
)))
|
|
|
|
|
|
2016-05-21 19:54:07 +00:00
|
|
|
(defnav
|
2016-04-20 00:24:00 +00:00
|
|
|
^{:doc "Navigates to the specified submap (using select-keys).
|
|
|
|
|
In a transform, that submap in the original map is changed to the new
|
|
|
|
|
value of the submap."}
|
|
|
|
|
submap
|
2016-04-19 22:15:06 +00:00
|
|
|
[m-keys]
|
|
|
|
|
(select* [this structure next-fn]
|
2016-04-20 00:24:00 +00:00
|
|
|
(next-fn (select-keys structure m-keys)))
|
2016-04-19 22:15:06 +00:00
|
|
|
|
|
|
|
|
(transform* [this structure next-fn]
|
2016-04-20 00:24:00 +00:00
|
|
|
(let [submap (select-keys structure m-keys)
|
2016-04-19 22:15:06 +00:00
|
|
|
newmap (next-fn submap)]
|
2016-04-20 00:24:00 +00:00
|
|
|
(merge (reduce dissoc structure m-keys)
|
2016-04-19 22:15:06 +00:00
|
|
|
newmap))))
|
|
|
|
|
|
2016-05-21 19:54:07 +00:00
|
|
|
(defnav
|
2015-09-24 16:55:24 +00:00
|
|
|
walker
|
|
|
|
|
[afn]
|
|
|
|
|
(select* [this structure next-fn]
|
|
|
|
|
(i/walk-select afn next-fn structure))
|
|
|
|
|
(transform* [this structure next-fn]
|
|
|
|
|
(i/walk-until afn next-fn structure)))
|
2015-02-26 15:55:20 +00:00
|
|
|
|
2016-05-21 19:54:07 +00:00
|
|
|
(defnav
|
2015-09-24 16:55:24 +00:00
|
|
|
codewalker
|
|
|
|
|
[afn]
|
|
|
|
|
(select* [this structure next-fn]
|
|
|
|
|
(i/walk-select afn next-fn structure))
|
|
|
|
|
(transform* [this structure next-fn]
|
|
|
|
|
(i/codewalk-until afn next-fn structure)))
|
2015-02-26 15:55:20 +00:00
|
|
|
|
2016-05-20 20:57:53 +00:00
|
|
|
(defpathedfn subselect
|
2016-04-19 07:47:56 +00:00
|
|
|
"Navigates to a sequence that contains the results of (select ...),
|
2016-04-19 19:03:21 +00:00
|
|
|
but is a view to the original structure that can be transformed.
|
|
|
|
|
|
|
|
|
|
Requires that the input navigators will walk the structure's
|
|
|
|
|
children in the same order when executed on \"select\" and then
|
|
|
|
|
\"transform\"."
|
2016-04-19 07:47:56 +00:00
|
|
|
[& path]
|
2016-05-21 19:54:07 +00:00
|
|
|
(fixed-pathed-nav [late path]
|
2016-04-19 07:47:56 +00:00
|
|
|
(select* [this structure next-fn]
|
|
|
|
|
(next-fn (compiled-select late structure)))
|
|
|
|
|
(transform* [this structure next-fn]
|
|
|
|
|
(let [select-result (compiled-select late structure)
|
|
|
|
|
transformed (next-fn select-result)
|
2016-04-19 18:55:32 +00:00
|
|
|
values-to-insert (i/mutable-cell transformed)]
|
2016-04-19 07:47:56 +00:00
|
|
|
(compiled-transform late
|
2016-04-19 18:55:32 +00:00
|
|
|
(fn [_] (let [next-val (first (i/get-cell values-to-insert))]
|
|
|
|
|
(i/update-cell! values-to-insert rest)
|
2016-04-19 07:47:56 +00:00
|
|
|
next-val))
|
|
|
|
|
structure)))))
|
|
|
|
|
|
2016-05-21 19:54:07 +00:00
|
|
|
(defnav
|
2016-05-05 18:24:57 +00:00
|
|
|
^{:doc "Navigates to the specified key, navigating to nil if it does not exist."}
|
|
|
|
|
keypath
|
|
|
|
|
[key]
|
2015-09-10 17:56:33 +00:00
|
|
|
(select* [this structure next-fn]
|
|
|
|
|
(next-fn (get structure key)))
|
|
|
|
|
(transform* [this structure next-fn]
|
|
|
|
|
(assoc structure key (next-fn (get structure key)))
|
|
|
|
|
))
|
2015-02-26 15:55:20 +00:00
|
|
|
|
2016-05-21 19:54:07 +00:00
|
|
|
(defnav
|
2016-05-05 18:24:57 +00:00
|
|
|
^{:doc "Navigates to the key only if it exists in the map."}
|
|
|
|
|
must
|
|
|
|
|
[k]
|
|
|
|
|
(select* [this structure next-fn]
|
2016-05-05 18:59:25 +00:00
|
|
|
(if (contains? structure k)
|
|
|
|
|
(next-fn (get structure k))))
|
2016-05-05 18:24:57 +00:00
|
|
|
(transform* [this structure next-fn]
|
|
|
|
|
(if (contains? structure k)
|
|
|
|
|
(assoc structure k (next-fn (get structure k)))
|
|
|
|
|
structure
|
|
|
|
|
)))
|
|
|
|
|
|
2016-05-21 19:54:07 +00:00
|
|
|
(defnav
|
2016-05-05 18:29:20 +00:00
|
|
|
^{:doc "Navigates to result of running `afn` on the currently navigated value."}
|
|
|
|
|
view
|
|
|
|
|
[afn]
|
2015-09-12 18:15:35 +00:00
|
|
|
(select* [this structure next-fn]
|
|
|
|
|
(next-fn (afn structure)))
|
|
|
|
|
(transform* [this structure next-fn]
|
|
|
|
|
(next-fn (afn structure))
|
|
|
|
|
))
|
2015-04-18 16:16:51 +00:00
|
|
|
|
2016-05-21 19:54:07 +00:00
|
|
|
(defnav parser [parse-fn unparse-fn]
|
2016-04-16 16:16:23 +00:00
|
|
|
(select* [this structure next-fn]
|
2016-04-18 16:21:33 +00:00
|
|
|
(next-fn (parse-fn structure)))
|
2016-04-16 16:16:23 +00:00
|
|
|
(transform* [this structure next-fn]
|
2016-04-18 16:21:33 +00:00
|
|
|
(unparse-fn (next-fn (parse-fn structure)))
|
2016-04-16 16:16:23 +00:00
|
|
|
))
|
|
|
|
|
|
2016-05-21 19:54:07 +00:00
|
|
|
(defnav
|
2016-05-08 19:55:45 +00:00
|
|
|
^{:doc "Navigates to atom value."}
|
|
|
|
|
ATOM
|
|
|
|
|
[]
|
|
|
|
|
(select* [this structure next-fn]
|
|
|
|
|
(next-fn @structure))
|
|
|
|
|
(transform* [this structure next-fn]
|
|
|
|
|
(do
|
|
|
|
|
(swap! structure next-fn)
|
|
|
|
|
structure)))
|
|
|
|
|
|
2016-05-20 20:57:53 +00:00
|
|
|
(defpathedfn selected?
|
2016-04-21 14:59:35 +00:00
|
|
|
"Filters the current value based on whether a path finds anything.
|
2015-04-19 17:45:20 +00:00
|
|
|
e.g. (selected? :vals ALL even?) keeps the current element only if an
|
2015-09-11 15:21:21 +00:00
|
|
|
even number exists for the :vals key.
|
|
|
|
|
|
|
|
|
|
The input path may be parameterized, in which case the result of selected?
|
2016-04-21 14:59:35 +00:00
|
|
|
will be parameterized in the order of which the parameterized navigators
|
2015-09-11 15:21:21 +00:00
|
|
|
were declared."
|
2015-09-11 00:47:46 +00:00
|
|
|
[& path]
|
2016-05-21 19:54:07 +00:00
|
|
|
(fixed-pathed-nav [late path]
|
2015-09-11 00:47:46 +00:00
|
|
|
(select* [this structure next-fn]
|
|
|
|
|
(i/filter-select
|
|
|
|
|
#(i/selected?* late %)
|
|
|
|
|
structure
|
|
|
|
|
next-fn))
|
|
|
|
|
(transform* [this structure next-fn]
|
|
|
|
|
(i/filter-transform
|
|
|
|
|
#(i/selected?* late %)
|
|
|
|
|
structure
|
|
|
|
|
next-fn))))
|
2015-04-19 17:45:20 +00:00
|
|
|
|
2016-05-20 20:57:53 +00:00
|
|
|
(defpathedfn not-selected? [& path]
|
2016-05-21 19:54:07 +00:00
|
|
|
(fixed-pathed-nav [late path]
|
2015-09-11 00:47:46 +00:00
|
|
|
(select* [this structure next-fn]
|
|
|
|
|
(i/filter-select
|
|
|
|
|
#(i/not-selected?* late %)
|
|
|
|
|
structure
|
|
|
|
|
next-fn))
|
|
|
|
|
(transform* [this structure next-fn]
|
|
|
|
|
(i/filter-transform
|
|
|
|
|
#(i/not-selected?* late %)
|
|
|
|
|
structure
|
|
|
|
|
next-fn))))
|
2015-07-02 21:23:18 +00:00
|
|
|
|
2016-05-20 20:57:53 +00:00
|
|
|
(defpathedfn filterer
|
2016-04-21 02:09:02 +00:00
|
|
|
"Navigates to a view of the current sequence that only contains elements that
|
2016-04-21 14:59:35 +00:00
|
|
|
match the given path. An element matches the selector path if calling select
|
|
|
|
|
on that element with the path yields anything other than an empty sequence.
|
2016-04-21 02:09:02 +00:00
|
|
|
|
|
|
|
|
The input path may be parameterized, in which case the result of filterer
|
|
|
|
|
will be parameterized in the order of which the parameterized selectors
|
|
|
|
|
were declared."
|
|
|
|
|
[& path]
|
|
|
|
|
(subselect ALL (selected? path)))
|
|
|
|
|
|
2016-05-20 20:57:53 +00:00
|
|
|
(defpathedfn transformed
|
2015-07-02 21:23:18 +00:00
|
|
|
"Navigates to a view of the current value by transforming it with the
|
2016-04-21 14:59:35 +00:00
|
|
|
specified path and update-fn.
|
2015-09-11 15:21:21 +00:00
|
|
|
|
|
|
|
|
The input path may be parameterized, in which case the result of transformed
|
2016-04-21 14:59:35 +00:00
|
|
|
will be parameterized in the order of which the parameterized navigators
|
2015-09-11 15:21:21 +00:00
|
|
|
were declared."
|
2015-09-11 00:47:46 +00:00
|
|
|
[path update-fn]
|
2016-05-21 19:54:07 +00:00
|
|
|
(fixed-pathed-nav [late path]
|
2015-09-11 00:47:46 +00:00
|
|
|
(select* [this structure next-fn]
|
|
|
|
|
(next-fn (compiled-transform late update-fn structure)))
|
|
|
|
|
(transform* [this structure next-fn]
|
|
|
|
|
(next-fn (compiled-transform late update-fn structure)))))
|
2015-07-02 21:23:18 +00:00
|
|
|
|
2015-10-09 16:33:24 +00:00
|
|
|
(extend-type #+clj clojure.lang.Keyword #+cljs cljs.core/Keyword
|
2016-05-21 19:54:07 +00:00
|
|
|
Navigator
|
2015-05-11 18:02:08 +00:00
|
|
|
(select* [kw structure next-fn]
|
|
|
|
|
(next-fn (get structure kw)))
|
2015-06-24 15:42:50 +00:00
|
|
|
(transform* [kw structure next-fn]
|
2015-05-11 18:02:08 +00:00
|
|
|
(assoc structure kw (next-fn (get structure kw)))
|
2015-02-26 15:55:20 +00:00
|
|
|
))
|
|
|
|
|
|
2015-10-09 16:33:24 +00:00
|
|
|
(extend-type #+clj clojure.lang.AFn #+cljs function
|
2016-05-21 19:54:07 +00:00
|
|
|
Navigator
|
2015-04-15 17:43:19 +00:00
|
|
|
(select* [afn structure next-fn]
|
2015-06-30 18:31:07 +00:00
|
|
|
(i/filter-select afn structure next-fn))
|
2015-06-24 15:42:50 +00:00
|
|
|
(transform* [afn structure next-fn]
|
2015-06-30 18:31:07 +00:00
|
|
|
(i/filter-transform afn structure next-fn)))
|
2015-06-29 22:30:30 +00:00
|
|
|
|
2015-10-09 16:33:24 +00:00
|
|
|
(extend-type #+clj clojure.lang.PersistentHashSet #+cljs cljs.core/PersistentHashSet
|
2016-05-21 19:54:07 +00:00
|
|
|
Navigator
|
2015-06-29 22:30:30 +00:00
|
|
|
(select* [aset structure next-fn]
|
2015-06-30 18:31:07 +00:00
|
|
|
(i/filter-select aset structure next-fn))
|
2015-06-29 22:30:30 +00:00
|
|
|
(transform* [aset structure next-fn]
|
2015-06-30 18:31:07 +00:00
|
|
|
(i/filter-transform aset structure next-fn)))
|
2015-02-26 15:55:20 +00:00
|
|
|
|
2016-05-21 13:44:20 +00:00
|
|
|
(def
|
2015-09-12 18:15:35 +00:00
|
|
|
^{:doc "Keeps the element only if it matches the supplied predicate. This is the
|
|
|
|
|
late-bound parameterized version of using a function directly in a path."}
|
|
|
|
|
pred
|
2016-05-21 13:44:20 +00:00
|
|
|
i/pred*
|
|
|
|
|
)
|
2015-09-12 18:15:35 +00:00
|
|
|
|
2016-05-21 19:54:07 +00:00
|
|
|
(defnav
|
2015-10-10 15:52:50 +00:00
|
|
|
^{:doc "Navigates to the provided val if the structure is nil. Otherwise it stays
|
|
|
|
|
navigated at the structure."}
|
|
|
|
|
nil->val
|
|
|
|
|
[v]
|
|
|
|
|
(select* [this structure next-fn]
|
|
|
|
|
(next-fn (if structure structure v)))
|
|
|
|
|
(transform* [this structure next-fn]
|
|
|
|
|
(next-fn (if structure structure v))))
|
|
|
|
|
|
|
|
|
|
(def NIL->SET (nil->val #{}))
|
|
|
|
|
(def NIL->LIST (nil->val '()))
|
|
|
|
|
(def NIL->VECTOR (nil->val []))
|
|
|
|
|
|
2016-05-20 20:57:53 +00:00
|
|
|
(defpathedfn collect [& path]
|
2015-09-11 03:09:19 +00:00
|
|
|
(pathed-collector [late path]
|
2015-09-11 05:42:29 +00:00
|
|
|
(collect-val [this structure]
|
2015-09-11 03:09:19 +00:00
|
|
|
(compiled-select late structure)
|
|
|
|
|
)))
|
2015-02-26 15:55:20 +00:00
|
|
|
|
2016-05-20 20:57:53 +00:00
|
|
|
(defpathedfn collect-one [& path]
|
2015-09-11 03:09:19 +00:00
|
|
|
(pathed-collector [late path]
|
2015-09-11 05:42:29 +00:00
|
|
|
(collect-val [this structure]
|
2015-09-11 03:09:19 +00:00
|
|
|
(compiled-select-one late structure)
|
|
|
|
|
)))
|
2015-05-27 05:02:19 +00:00
|
|
|
|
2015-12-12 17:03:59 +00:00
|
|
|
(defcollector
|
2015-09-11 15:23:51 +00:00
|
|
|
^{:doc
|
|
|
|
|
"Adds an external value to the collected vals. Useful when additional arguments
|
|
|
|
|
are required to the transform function that would otherwise require partial
|
|
|
|
|
application or a wrapper function.
|
2015-09-18 19:01:01 +00:00
|
|
|
|
2015-09-11 15:23:51 +00:00
|
|
|
e.g., incrementing val at path [:a :b] by 3:
|
|
|
|
|
(transform [:a :b (putval 3)] + some-map)"}
|
|
|
|
|
putval
|
|
|
|
|
[val]
|
2015-09-11 05:42:29 +00:00
|
|
|
(collect-val [this structure]
|
2015-09-11 02:55:43 +00:00
|
|
|
val ))
|
2015-09-11 00:47:46 +00:00
|
|
|
|
2016-05-20 20:57:53 +00:00
|
|
|
(defpathedfn cond-path
|
2016-04-21 14:59:35 +00:00
|
|
|
"Takes in alternating cond-path path cond-path path...
|
2015-06-19 18:27:22 +00:00
|
|
|
Tests the structure if selecting with cond-path returns anything.
|
2016-04-21 14:59:35 +00:00
|
|
|
If so, it uses the following path for this portion of the navigation.
|
2015-06-19 18:27:22 +00:00
|
|
|
Otherwise, it tries the next cond-path. If nothing matches, then the structure
|
2015-09-11 15:21:21 +00:00
|
|
|
is not selected.
|
|
|
|
|
|
|
|
|
|
The input paths may be parameterized, in which case the result of cond-path
|
2016-04-21 14:59:35 +00:00
|
|
|
will be parameterized in the order of which the parameterized navigators
|
2015-09-11 15:21:21 +00:00
|
|
|
were declared."
|
2015-06-18 04:56:03 +00:00
|
|
|
[& conds]
|
2016-05-21 19:54:07 +00:00
|
|
|
(variable-pathed-nav [compiled-paths conds]
|
2015-09-11 00:47:46 +00:00
|
|
|
(select* [this structure next-fn]
|
|
|
|
|
(if-let [selector (i/retrieve-cond-selector compiled-paths structure)]
|
|
|
|
|
(->> (compiled-select selector structure)
|
|
|
|
|
(mapcat next-fn)
|
|
|
|
|
doall)))
|
|
|
|
|
(transform* [this structure next-fn]
|
|
|
|
|
(if-let [selector (i/retrieve-cond-selector compiled-paths structure)]
|
|
|
|
|
(compiled-transform selector next-fn structure)
|
|
|
|
|
structure
|
|
|
|
|
))))
|
2015-06-18 04:56:03 +00:00
|
|
|
|
2016-05-20 20:57:53 +00:00
|
|
|
(defpathedfn if-path
|
2015-06-18 04:56:03 +00:00
|
|
|
"Like cond-path, but with if semantics."
|
2015-09-11 00:47:46 +00:00
|
|
|
([cond-p if-path] (cond-path cond-p if-path))
|
|
|
|
|
([cond-p if-path else-path]
|
|
|
|
|
(cond-path cond-p if-path nil else-path)))
|
2015-06-25 20:30:27 +00:00
|
|
|
|
2016-05-20 20:57:53 +00:00
|
|
|
(defpathedfn multi-path
|
2015-06-25 20:30:27 +00:00
|
|
|
"A path that branches on multiple paths. For updates,
|
|
|
|
|
applies updates to the paths in order."
|
|
|
|
|
[& paths]
|
2016-05-21 19:54:07 +00:00
|
|
|
(variable-pathed-nav [compiled-paths paths]
|
2015-09-11 00:47:46 +00:00
|
|
|
(select* [this structure next-fn]
|
|
|
|
|
(->> compiled-paths
|
|
|
|
|
(mapcat #(compiled-select % structure))
|
|
|
|
|
(mapcat next-fn)
|
|
|
|
|
doall
|
|
|
|
|
))
|
|
|
|
|
(transform* [this structure next-fn]
|
|
|
|
|
(reduce
|
2016-04-21 14:59:35 +00:00
|
|
|
(fn [structure path]
|
|
|
|
|
(compiled-transform path next-fn structure))
|
2015-09-11 00:47:46 +00:00
|
|
|
structure
|
|
|
|
|
compiled-paths
|
|
|
|
|
))))
|
2016-01-23 03:45:20 +00:00
|
|
|
|
2016-05-20 20:57:53 +00:00
|
|
|
(defpathedfn stay-then-continue
|
2016-01-23 03:45:20 +00:00
|
|
|
"Navigates to the current element and then navigates via the provided path.
|
|
|
|
|
This can be used to implement pre-order traversal."
|
|
|
|
|
[& path]
|
|
|
|
|
(multi-path STAY path))
|
|
|
|
|
|
2016-05-20 20:57:53 +00:00
|
|
|
(defpathedfn continue-then-stay
|
2016-01-23 03:45:20 +00:00
|
|
|
"Navigates to the provided path and then to the current element. This can be used
|
|
|
|
|
to implement post-order traversal."
|
|
|
|
|
[& path]
|
|
|
|
|
(multi-path path STAY))
|