restructured in terms of CompiledPath and implemented composition for ParamsNeededPath
This commit is contained in:
parent
d693ad29ae
commit
6e3f79dd53
2 changed files with 134 additions and 99 deletions
|
|
@ -18,7 +18,7 @@
|
||||||
(defn select
|
(defn select
|
||||||
"Navigates to and returns a sequence of all the elements specified by the selector."
|
"Navigates to and returns a sequence of all the elements specified by the selector."
|
||||||
[selector structure]
|
[selector structure]
|
||||||
(compiled-select (i/comp-unoptimal selector)
|
(compiled-select (i/comp-paths* selector)
|
||||||
structure))
|
structure))
|
||||||
|
|
||||||
(defn compiled-select-one
|
(defn compiled-select-one
|
||||||
|
|
@ -33,7 +33,7 @@
|
||||||
(defn select-one
|
(defn select-one
|
||||||
"Like select, but returns either one element or nil. Throws exception if multiple elements found"
|
"Like select, but returns either one element or nil. Throws exception if multiple elements found"
|
||||||
[selector structure]
|
[selector structure]
|
||||||
(compiled-select-one (i/comp-unoptimal selector) structure))
|
(compiled-select-one (i/comp-paths* selector) structure))
|
||||||
|
|
||||||
(defn compiled-select-one!
|
(defn compiled-select-one!
|
||||||
"Version of select-one! that takes in a selector pre-compiled with comp-paths"
|
"Version of select-one! that takes in a selector pre-compiled with comp-paths"
|
||||||
|
|
@ -46,7 +46,7 @@
|
||||||
(defn select-one!
|
(defn select-one!
|
||||||
"Returns exactly one element, throws exception if zero or multiple elements found"
|
"Returns exactly one element, throws exception if zero or multiple elements found"
|
||||||
[selector structure]
|
[selector structure]
|
||||||
(compiled-select-one! (i/comp-unoptimal selector) structure))
|
(compiled-select-one! (i/comp-paths* selector) structure))
|
||||||
|
|
||||||
(defn compiled-select-first
|
(defn compiled-select-first
|
||||||
"Version of select-first that takes in a selector pre-compiled with comp-paths"
|
"Version of select-first that takes in a selector pre-compiled with comp-paths"
|
||||||
|
|
@ -56,7 +56,7 @@
|
||||||
(defn select-first
|
(defn select-first
|
||||||
"Returns first element found. Not any more efficient than select, just a convenience"
|
"Returns first element found. Not any more efficient than select, just a convenience"
|
||||||
[selector structure]
|
[selector structure]
|
||||||
(compiled-select-first (i/comp-unoptimal selector) structure))
|
(compiled-select-first (i/comp-paths* selector) structure))
|
||||||
|
|
||||||
;; Transformfunctions
|
;; Transformfunctions
|
||||||
|
|
||||||
|
|
@ -68,7 +68,7 @@
|
||||||
"Navigates to each value specified by the selector and replaces it by the result of running
|
"Navigates to each value specified by the selector and replaces it by the result of running
|
||||||
the transform-fn on it"
|
the transform-fn on it"
|
||||||
[selector transform-fn structure]
|
[selector transform-fn structure]
|
||||||
(compiled-transform (i/comp-unoptimal selector) transform-fn structure))
|
(compiled-transform (i/comp-paths* selector) transform-fn structure))
|
||||||
|
|
||||||
(defn compiled-setval
|
(defn compiled-setval
|
||||||
"Version of setval that takes in a selector pre-compiled with comp-paths"
|
"Version of setval that takes in a selector pre-compiled with comp-paths"
|
||||||
|
|
@ -78,7 +78,7 @@
|
||||||
(defn setval
|
(defn setval
|
||||||
"Navigates to each value specified by the selector and replaces it by val"
|
"Navigates to each value specified by the selector and replaces it by val"
|
||||||
[selector val structure]
|
[selector val structure]
|
||||||
(compiled-setval (i/comp-unoptimal selector) val structure))
|
(compiled-setval (i/comp-paths* selector) val structure))
|
||||||
|
|
||||||
(defn compiled-replace-in
|
(defn compiled-replace-in
|
||||||
"Version of replace-in that takes in a selector pre-compiled with comp-paths"
|
"Version of replace-in that takes in a selector pre-compiled with comp-paths"
|
||||||
|
|
@ -106,7 +106,7 @@
|
||||||
in the final return. replace-in is useful for situations where you need to know the specific values
|
in the final return. replace-in is useful for situations where you need to know the specific values
|
||||||
of what was transformd in the data structure."
|
of what was transformd in the data structure."
|
||||||
[selector transform-fn structure & {:keys [merge-fn] :or {merge-fn concat}}]
|
[selector transform-fn structure & {:keys [merge-fn] :or {merge-fn concat}}]
|
||||||
(compiled-replace-in (i/comp-unoptimal selector) transform-fn structure :merge-fn merge-fn))
|
(compiled-replace-in (i/comp-paths* selector) transform-fn structure :merge-fn merge-fn))
|
||||||
|
|
||||||
;; Built-in pathing and context operations
|
;; Built-in pathing and context operations
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -38,13 +38,13 @@
|
||||||
(def RichPathExecutor
|
(def RichPathExecutor
|
||||||
(->ExecutorFunctions
|
(->ExecutorFunctions
|
||||||
:richpath
|
:richpath
|
||||||
(fn [selector structure]
|
(fn [params selector structure]
|
||||||
(selector [] structure
|
(selector params 0 [] structure
|
||||||
(fn [vals structure]
|
(fn [params params-idx vals structure]
|
||||||
(if-not (empty? vals) [(conj vals structure)] [structure]))))
|
(if-not (empty? vals) [(conj vals structure)] [structure]))))
|
||||||
(fn [transformer transform-fn structure]
|
(fn [params transformer transform-fn structure]
|
||||||
(transformer [] structure
|
(transformer params 0 [] structure
|
||||||
(fn [vals structure]
|
(fn [params params-idx vals structure]
|
||||||
(if (empty? vals)
|
(if (empty? vals)
|
||||||
(transform-fn structure)
|
(transform-fn structure)
|
||||||
(apply transform-fn (conj vals structure))))))
|
(apply transform-fn (conj vals structure))))))
|
||||||
|
|
@ -53,17 +53,18 @@
|
||||||
(def StructurePathExecutor
|
(def StructurePathExecutor
|
||||||
(->ExecutorFunctions
|
(->ExecutorFunctions
|
||||||
:spath
|
:spath
|
||||||
(fn [selector structure]
|
(fn [params selector structure]
|
||||||
(selector structure (fn [structure] [structure])))
|
(selector structure (fn [structure] [structure])))
|
||||||
(fn [transformer transform-fn structure]
|
(fn [params transformer transform-fn structure]
|
||||||
(transformer structure transform-fn))
|
(transformer structure transform-fn))
|
||||||
))
|
))
|
||||||
|
|
||||||
(deftype TransformFunctions [executors selector transformer])
|
(defrecord TransformFunctions [executors selector transformer])
|
||||||
|
|
||||||
|
(defrecord CompiledPath [transform-fns params])
|
||||||
|
|
||||||
(defprotocol CoerceTransformFunctions
|
;;TODO: this must implement IFn so it can be transformed to CompiledPath
|
||||||
(coerce-path [this]))
|
(defrecord ParamsNeededPath [transform-fns num-needed-params])
|
||||||
|
|
||||||
(defn- seq-contains? [aseq val]
|
(defn- seq-contains? [aseq val]
|
||||||
(->> aseq
|
(->> aseq
|
||||||
|
|
@ -119,48 +120,61 @@
|
||||||
collector-impl
|
collector-impl
|
||||||
:collect-val
|
:collect-val
|
||||||
)
|
)
|
||||||
afn (fn [vals structure next-fn]
|
afn (fn [params params-idx vals structure next-fn]
|
||||||
(next-fn (conj vals (cfn this structure)) structure)
|
(next-fn params params-idx (conj vals (cfn this structure)) structure)
|
||||||
)]
|
)]
|
||||||
(->TransformFunctions RichPathExecutor afn afn)))
|
(->CompiledPath
|
||||||
|
(->TransformFunctions RichPathExecutor afn afn)
|
||||||
|
nil
|
||||||
|
)))
|
||||||
|
|
||||||
|
|
||||||
(defn coerce-structure-path [this]
|
(defn coerce-structure-path [this]
|
||||||
(let [pimpl (structure-path-impl this)
|
(let [pimpl (structure-path-impl this)
|
||||||
selector (:select* pimpl)
|
selector (:select* pimpl)
|
||||||
transformer (:transform* pimpl)]
|
transformer (:transform* pimpl)]
|
||||||
(->TransformFunctions
|
(->CompiledPath
|
||||||
StructurePathExecutor
|
(->TransformFunctions
|
||||||
(fn [structure next-fn]
|
StructurePathExecutor
|
||||||
(selector this structure next-fn))
|
(fn [structure next-fn]
|
||||||
(fn [structure next-fn]
|
(selector this structure next-fn))
|
||||||
(transformer this structure next-fn))
|
(fn [structure next-fn]
|
||||||
)))
|
(transformer this structure next-fn)))
|
||||||
|
nil
|
||||||
|
)))
|
||||||
|
|
||||||
(defn coerce-structure-path-direct [this]
|
(defn coerce-structure-path-rich [this]
|
||||||
(let [pimpl (structure-path-impl this)
|
(let [pimpl (structure-path-impl this)
|
||||||
selector (:select* pimpl)
|
selector (:select* pimpl)
|
||||||
transformer (:transform* pimpl)]
|
transformer (:transform* pimpl)]
|
||||||
(->TransformFunctions
|
(->CompiledPath
|
||||||
RichPathExecutor
|
(->TransformFunctions
|
||||||
(fn [vals structure next-fn]
|
RichPathExecutor
|
||||||
(selector this structure (fn [structure] (next-fn vals structure))))
|
(fn [params params-idx vals structure next-fn]
|
||||||
(fn [vals structure next-fn]
|
(selector this structure (fn [structure] (next-fn params params-idx vals structure))))
|
||||||
(transformer this structure (fn [structure] (next-fn vals structure))))
|
(fn [params params-idx vals structure next-fn]
|
||||||
)))
|
(transformer this structure (fn [structure] (next-fn params params-idx vals structure)))))
|
||||||
|
nil
|
||||||
|
)))
|
||||||
|
|
||||||
(defn structure-path? [obj]
|
(defn structure-path? [obj]
|
||||||
(or (fn? obj) (satisfies? p/StructurePath obj)))
|
(or (fn? obj) (satisfies? p/StructurePath obj)))
|
||||||
|
|
||||||
(extend-protocol CoerceTransformFunctions
|
(defprotocol CoercePath
|
||||||
|
(coerce-path [this]))
|
||||||
|
|
||||||
|
(extend-protocol CoercePath
|
||||||
nil ; needs its own path because it doesn't count as an Object
|
nil ; needs its own path because it doesn't count as an Object
|
||||||
(coerce-path [this]
|
(coerce-path [this]
|
||||||
(coerce-structure-path nil))
|
(coerce-structure-path nil))
|
||||||
|
|
||||||
TransformFunctions
|
CompiledPath
|
||||||
(coerce-path [this]
|
(coerce-path [this]
|
||||||
this)
|
this)
|
||||||
|
|
||||||
|
ParamsNeededPath
|
||||||
|
(coerce-path [this]
|
||||||
|
this)
|
||||||
|
|
||||||
#?(:clj java.util.List :cljs cljs.core/PersistentVector)
|
#?(:clj java.util.List :cljs cljs.core/PersistentVector)
|
||||||
(coerce-path [this]
|
(coerce-path [this]
|
||||||
|
|
@ -192,46 +206,67 @@
|
||||||
))
|
))
|
||||||
|
|
||||||
(defn- combine-same-types [[^TransformFunctions f & _ :as all]]
|
(defn- combine-same-types [[^TransformFunctions f & _ :as all]]
|
||||||
(if (empty? all)
|
(let [^ExecutorFunctions exs (.-executors f)
|
||||||
(coerce-path nil)
|
|
||||||
(let [^ExecutorFunctions exs (.-executors f)
|
|
||||||
|
|
||||||
t (.-type exs)
|
t (.-type exs)
|
||||||
|
|
||||||
combiner
|
combiner
|
||||||
(if (= t :richpath)
|
(if (= t :richpath)
|
||||||
(fn [curr next]
|
(fn [curr next]
|
||||||
(fn [vals structure next-fn]
|
(fn [params params-idx vals structure next-fn]
|
||||||
(curr vals structure
|
(curr params params-idx vals structure
|
||||||
(fn [vals-next structure-next]
|
(fn [params-next params-idx-next vals-next structure-next]
|
||||||
(next vals-next structure-next next-fn)
|
(next params-next params-idx-next vals-next structure-next next-fn)
|
||||||
))))
|
))))
|
||||||
(fn [curr next]
|
(fn [curr next]
|
||||||
(fn [structure next-fn]
|
(fn [structure next-fn]
|
||||||
(curr structure (fn [structure] (next structure next-fn)))))
|
(curr structure (fn [structure] (next structure next-fn)))))
|
||||||
)]
|
)]
|
||||||
|
|
||||||
(reduce (fn [^TransformFunctions curr ^TransformFunctions next]
|
(reduce (fn [^TransformFunctions curr ^TransformFunctions next]
|
||||||
(->TransformFunctions
|
(->TransformFunctions
|
||||||
exs
|
exs
|
||||||
(combiner (.-selector curr) (.-selector next))
|
(combiner (.-selector curr) (.-selector next))
|
||||||
(combiner (.-transformer curr) (.-transformer next))
|
(combiner (.-transformer curr) (.-transformer next))
|
||||||
))
|
))
|
||||||
all))))
|
all)))
|
||||||
|
|
||||||
(defn coerce-structure-vals [^TransformFunctions tfns]
|
(defn coerce-tfns-rich [^TransformFunctions tfns]
|
||||||
(if (= (extype tfns) :richpath)
|
(if (= (extype tfns) :richpath)
|
||||||
tfns
|
tfns
|
||||||
(let [selector (.-selector tfns)
|
(let [selector (.-selector tfns)
|
||||||
transformer (.-transformer tfns)]
|
transformer (.-transformer tfns)]
|
||||||
(->TransformFunctions
|
(->TransformFunctions
|
||||||
RichPathExecutor
|
RichPathExecutor
|
||||||
(fn [vals structure next-fn]
|
(fn [params params-idx vals structure next-fn]
|
||||||
(selector structure (fn [structure] (next-fn vals structure))))
|
(selector structure (fn [structure] (next-fn params params-idx vals structure))))
|
||||||
(fn [vals structure next-fn]
|
(fn [params params-idx vals structure next-fn]
|
||||||
(transformer structure (fn [structure] (next-fn vals structure))))
|
(transformer structure (fn [structure] (next-fn params params-idx vals structure))))
|
||||||
))))
|
))))
|
||||||
|
|
||||||
|
(defn capture-params-internally [path]
|
||||||
|
(if-not (instance? CompiledPath path)
|
||||||
|
path
|
||||||
|
(let [params (:params path)
|
||||||
|
selector (-> path :transform-fns :selector)
|
||||||
|
transformer (-> path :transform-fns :transformer)]
|
||||||
|
(if (empty? params)
|
||||||
|
path
|
||||||
|
(->CompiledPath
|
||||||
|
(->TransformFunctions
|
||||||
|
RichPathExecutor
|
||||||
|
(fn [x-params params-idx vals structure next-fn]
|
||||||
|
(selector params 0 vals structure
|
||||||
|
(fn [_ _ vals-next structure-next]
|
||||||
|
(next-fn x-params params-idx vals-next structure-next)
|
||||||
|
)))
|
||||||
|
(fn [x-params params-idx vals structure next-fn]
|
||||||
|
(transformer params 0 vals structure
|
||||||
|
(fn [_ _ vals-next structure-next]
|
||||||
|
(next-fn x-params params-idx vals-next structure-next)
|
||||||
|
)))
|
||||||
|
))))))
|
||||||
|
|
||||||
(extend-protocol PathComposer
|
(extend-protocol PathComposer
|
||||||
nil
|
nil
|
||||||
(comp-paths* [sp]
|
(comp-paths* [sp]
|
||||||
|
|
@ -241,35 +276,33 @@
|
||||||
(coerce-path sp))
|
(coerce-path sp))
|
||||||
#?(:clj java.util.List :cljs cljs.core/PersistentVector)
|
#?(:clj java.util.List :cljs cljs.core/PersistentVector)
|
||||||
(comp-paths* [structure-paths]
|
(comp-paths* [structure-paths]
|
||||||
(let [combined (->> structure-paths
|
(if (empty? structure-paths)
|
||||||
(map coerce-path)
|
(coerce-path nil)
|
||||||
(partition-by extype)
|
(let [coerced (->> structure-paths
|
||||||
(map combine-same-types)
|
(map coerce-path)
|
||||||
)]
|
(map capture-params-internally))
|
||||||
(if (= 1 (count combined))
|
combined (->> coerced
|
||||||
(first combined)
|
(map :transform-fns)
|
||||||
(->> combined
|
(partition-by extype)
|
||||||
(map coerce-structure-vals)
|
(map combine-same-types)
|
||||||
combine-same-types)
|
)
|
||||||
|
result-tfn (if (= 1 (count combined))
|
||||||
|
(first combined)
|
||||||
|
(->> combined
|
||||||
|
(map coerce-tfns-rich)
|
||||||
|
combine-same-types)
|
||||||
|
)
|
||||||
|
needs-params-paths (filter #(instance? ParamsNeededPath %) coerced)]
|
||||||
|
(if (empty? needs-params-paths)
|
||||||
|
(->CompiledPath result-tfn nil)
|
||||||
|
(->ParamsNeededPath
|
||||||
|
(coerce-tfns-rich result-tfn)
|
||||||
|
(->> needs-params-paths
|
||||||
|
(map :num-needed-params)
|
||||||
|
(reduce +))
|
||||||
|
))
|
||||||
))))
|
))))
|
||||||
|
|
||||||
(defn coerce-structure-vals-direct [this]
|
|
||||||
(cond (structure-path? this) (coerce-structure-path-direct this)
|
|
||||||
(satisfies? p/Collector this) (coerce-collector this)
|
|
||||||
(instance? TransformFunctions this) (coerce-structure-vals this)
|
|
||||||
:else (throw-illegal (no-prot-error-str this))
|
|
||||||
))
|
|
||||||
|
|
||||||
;;this composes paths together much faster than comp-paths* but the resulting composition
|
|
||||||
;;won't execute as fast. Useful for when select/transform are used without pre-compiled paths
|
|
||||||
;;(where cost of compiling dominates execution time)
|
|
||||||
(defn comp-unoptimal [sp]
|
|
||||||
(if (instance? #?(:clj java.util.List :cljs cljs.core/PersistentVector) sp)
|
|
||||||
(->> sp
|
|
||||||
(map coerce-structure-vals-direct)
|
|
||||||
combine-same-types)
|
|
||||||
(coerce-path sp)))
|
|
||||||
|
|
||||||
;; cell implementation idea taken from prismatic schema library
|
;; cell implementation idea taken from prismatic schema library
|
||||||
(defprotocol PMutableCell
|
(defprotocol PMutableCell
|
||||||
#?(:clj (get_cell [cell]))
|
#?(:clj (get_cell [cell]))
|
||||||
|
|
@ -346,15 +379,17 @@
|
||||||
(set-cell! cell (concat (get-cell cell) elems)))
|
(set-cell! cell (concat (get-cell cell) elems)))
|
||||||
|
|
||||||
(defn compiled-select*
|
(defn compiled-select*
|
||||||
[^com.rpl.specter.impl.TransformFunctions tfns structure]
|
[^com.rpl.specter.impl.CompiledPath path structure]
|
||||||
(let [^com.rpl.specter.impl.ExecutorFunctions ex (.-executors tfns)]
|
(let [^com.rpl.specter.impl.TransformFunctions tfns (.-transform-fns path)
|
||||||
((.-select-executor ex) (.-selector tfns) structure)
|
^com.rpl.specter.impl.ExecutorFunctions ex (.-executors tfns)]
|
||||||
|
((.-select-executor ex) (.-params path) (.-selector tfns) structure)
|
||||||
))
|
))
|
||||||
|
|
||||||
(defn compiled-transform*
|
(defn compiled-transform*
|
||||||
[^com.rpl.specter.impl.TransformFunctions tfns transform-fn structure]
|
[^com.rpl.specter.impl.CompiledPath path transform-fn structure]
|
||||||
(let [^com.rpl.specter.impl.ExecutorFunctions ex (.-executors tfns)]
|
(let [^com.rpl.specter.impl.TransformFunctions tfns (.-transform-fns path)
|
||||||
((.-transform-executor ex) (.-transformer tfns) transform-fn structure)
|
^com.rpl.specter.impl.ExecutorFunctions ex (.-executors tfns)]
|
||||||
|
((.-transform-executor ex) (.-params path) (.-transformer tfns) transform-fn structure)
|
||||||
))
|
))
|
||||||
|
|
||||||
(defn selected?*
|
(defn selected?*
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue