Merge branch 'prot-redesign'

This commit is contained in:
Nathan Marz 2016-08-06 00:48:01 -04:00
commit 23a501f4ac
9 changed files with 1385 additions and 1342 deletions

View file

@ -2,8 +2,7 @@
#+cljs (:require-macros #+cljs (:require-macros
[com.rpl.specter.macros [com.rpl.specter.macros
:refer :refer
[pathed-collector [fixed-pathed-collector
variable-pathed-nav
fixed-pathed-nav fixed-pathed-nav
defcollector defcollector
defnav defnav
@ -14,10 +13,9 @@
[com.rpl.specter.util-macros :refer [com.rpl.specter.util-macros :refer
[doseqres]] [doseqres]]
) )
(:use [com.rpl.specter.protocols :only [Navigator]] (:use [com.rpl.specter.protocols :only [ImplicitNav]]
#+clj [com.rpl.specter.macros :only #+clj [com.rpl.specter.macros :only
[pathed-collector [fixed-pathed-collector
variable-pathed-nav
fixed-pathed-nav fixed-pathed-nav
defcollector defcollector
defnav defnav
@ -27,6 +25,7 @@
#+clj [com.rpl.specter.util-macros :only [doseqres]] #+clj [com.rpl.specter.util-macros :only [doseqres]]
) )
(:require [com.rpl.specter.impl :as i] (:require [com.rpl.specter.impl :as i]
[com.rpl.specter.navs :as n]
[clojure.set :as set]) [clojure.set :as set])
) )
@ -40,7 +39,7 @@
(i/comp-paths* (vec apath))) (i/comp-paths* (vec apath)))
(def ^{:doc "Mandate that operations that do inline path factoring and compilation (def ^{:doc "Mandate that operations that do inline path factoring and compilation
(select/transform/setval/replace-in/path/etc.) must succeed in (select/transform/setval/replace-in/path/etc.) must succeed in
factoring the path into static and dynamic portions. If not, an factoring the path into static and dynamic portions. If not, an
error will be thrown and the reasons for not being able to factor error will be thrown and the reasons for not being able to factor
will be printed. Defaults to false, and `(must-cache-paths! false)` will be printed. Defaults to false, and `(must-cache-paths! false)`
@ -137,7 +136,7 @@
(defn multi-transform* (defn multi-transform*
"Just like `transform` but expects transform functions to be specified "Just like `transform` but expects transform functions to be specified
inline in the path using `terminal`. Error is thrown if navigation finishes inline in the path using `terminal`. Error is thrown if navigation finishes
at a non-`terminal` navigator. `terminal-val` is a wrapper around `terminal` and is at a non-`terminal` navigator. `terminal-val` is a wrapper around `terminal` and is
the `multi-transform` equivalent of `setval`." the `multi-transform` equivalent of `setval`."
[path structure] [path structure]
(compiled-multi-transform (i/comp-paths* path) structure)) (compiled-multi-transform (i/comp-paths* path) structure))
@ -172,24 +171,19 @@
(defn params-reset [params-path] (defn params-reset [params-path]
;; TODO: error if not paramsneededpath ;; TODO: error if not paramsneededpath
(let [s (i/params-needed-selector params-path) (let [nav (i/params-needed-nav params-path)
t (i/params-needed-transformer params-path)
needed (i/num-needed-params params-path)] needed (i/num-needed-params params-path)]
(i/->ParamsNeededPath (richnav 0
(i/->TransformFunctions (select* [this params params-idx vals structure next-fn]
i/RichPathExecutor (i/exec-rich-select* nav params (- params-idx needed) vals structure next-fn))
(fn [params params-idx vals structure next-fn] (transform* [this params params-idx vals structure next-fn]
(s params (- params-idx needed) vals structure next-fn) (i/exec-rich-transform* nav params (- params-idx needed) vals structure next-fn)
) ))))
(fn [params params-idx vals structure next-fn]
(t params (- params-idx needed) vals structure next-fn)
))
0)))
;; Built-in pathing and context operations ;; Built-in pathing and context operations
(defnav (defnav
^{:doc "Stops navigation at this point. For selection returns nothing and for ^{:doc "Stops navigation at this point. For selection returns nothing and for
transformation returns the structure unchanged"} transformation returns the structure unchanged"}
STOP STOP
[] []
@ -199,6 +193,7 @@
structure structure
)) ))
(defnav (defnav
^{:doc "Stays navigated at the current point. Essentially a no-op navigator."} ^{:doc "Stays navigated at the current point. Essentially a no-op navigator."}
STAY STAY
@ -208,7 +203,6 @@
(transform* [this structure next-fn] (transform* [this structure next-fn]
(next-fn structure))) (next-fn structure)))
(def (def
^{:doc "For usage with `multi-transform`, defines an endpoint in the navigation ^{:doc "For usage with `multi-transform`, defines an endpoint in the navigation
that will have the parameterized transform function run. The transform that will have the parameterized transform function run. The transform
@ -216,10 +210,10 @@
given as the first arguments"} given as the first arguments"}
terminal terminal
(richnav 1 (richnav 1
(select* [params params-idx vals structure next-fn] (select* [this params params-idx vals structure next-fn]
(i/throw-illegal "'terminal' should only be used in multi-transform")) (i/throw-illegal "'terminal' should only be used in multi-transform"))
(transform* [params params-idx vals structure next-fn] (transform* [this params params-idx vals structure next-fn]
(i/terminal* params params-idx vals structure) (n/terminal* params params-idx vals structure)
))) )))
(defnavconstructor terminal-val (defnavconstructor terminal-val
@ -229,14 +223,18 @@
[v] [v]
(p (i/fast-constantly v))) (p (i/fast-constantly v)))
(def (defnav
^{:doc "Navigate to every element of the collection. For maps navigates to ^{:doc "Navigate to every element of the collection. For maps navigates to
a vector of `[key value]`."} a vector of `[key value]`."}
ALL ALL
(comp-paths (i/->AllNavigator))) []
(select* [this structure next-fn]
(n/all-select structure next-fn))
(transform* [this structure next-fn]
(n/all-transform structure next-fn)))
(defnav (defnav
^{:doc "Navigate to each value of the map. This is more efficient than ^{:doc "Navigate to each value of the map. This is more efficient than
navigating via [ALL LAST]"} navigating via [ALL LAST]"}
MAP-VALS MAP-VALS
[] []
@ -245,23 +243,25 @@
(next-fn v) (next-fn v)
)) ))
(transform* [this structure next-fn] (transform* [this structure next-fn]
(i/map-vals-transform structure next-fn) (n/map-vals-transform structure next-fn)
)) ))
(def VAL (i/->ValCollect)) (defcollector VAL []
(collect-val [this structure]
structure))
(def (def
^{:doc "Navigate to the last element of the collection. If the collection is ^{:doc "Navigate to the last element of the collection. If the collection is
empty navigation is stopped at this point."} empty navigation is stopped at this point."}
LAST LAST
(comp-paths (i/->PosNavigator i/get-last i/update-last))) (n/PosNavigator n/get-last n/update-last))
(def (def
^{:doc "Navigate to the first element of the collection. If the collection is ^{:doc "Navigate to the first element of the collection. If the collection is
empty navigation is stopped at this point."} empty navigation is stopped at this point."}
FIRST FIRST
(comp-paths (i/->PosNavigator i/get-first i/update-first))) (n/PosNavigator n/get-first n/update-first))
(defnav (defnav
^{:doc "Uses start-fn and end-fn to determine the bounds of the subsequence ^{:doc "Uses start-fn and end-fn to determine the bounds of the subsequence
@ -269,9 +269,9 @@
srange-dynamic srange-dynamic
[start-fn end-fn] [start-fn end-fn]
(select* [this structure next-fn] (select* [this structure next-fn]
(i/srange-select structure (start-fn structure) (end-fn structure) next-fn)) (n/srange-select structure (start-fn structure) (end-fn structure) next-fn))
(transform* [this structure next-fn] (transform* [this structure next-fn]
(i/srange-transform structure (start-fn structure) (end-fn structure) next-fn) (n/srange-transform structure (start-fn structure) (end-fn structure) next-fn)
)) ))
(defnav (defnav
@ -280,9 +280,9 @@
srange srange
[start end] [start end]
(select* [this structure next-fn] (select* [this structure next-fn]
(i/srange-select structure start end next-fn)) (n/srange-select structure start end next-fn))
(transform* [this structure next-fn] (transform* [this structure next-fn]
(i/srange-transform structure start end next-fn) (n/srange-transform structure start end next-fn)
)) ))
(defnav (defnav
@ -290,15 +290,15 @@
continuous-subseqs continuous-subseqs
[pred] [pred]
(select* [this structure next-fn] (select* [this structure next-fn]
(doseqres NONE [[s e] (i/matching-ranges structure pred)] (doseqres NONE [[s e] (n/matching-ranges structure pred)]
(i/srange-select structure s e next-fn) (n/srange-select structure s e next-fn)
)) ))
(transform* [this structure next-fn] (transform* [this structure next-fn]
(reduce (reduce
(fn [structure [s e]] (fn [structure [s e]]
(i/srange-transform structure s e next-fn)) (n/srange-transform structure s e next-fn))
structure structure
(reverse (i/matching-ranges structure pred)) (reverse (n/matching-ranges structure pred))
))) )))
(defnav (defnav
@ -309,7 +309,7 @@
(next-fn [])) (next-fn []))
(transform* [this structure next-fn] (transform* [this structure next-fn]
(let [to-prepend (next-fn [])] (let [to-prepend (next-fn [])]
(i/prepend-all structure to-prepend) (n/prepend-all structure to-prepend)
))) )))
(defnav (defnav
@ -320,9 +320,9 @@
(next-fn [])) (next-fn []))
(transform* [this structure next-fn] (transform* [this structure next-fn]
(let [to-append (next-fn [])] (let [to-append (next-fn [])]
(i/append-all structure to-append) (n/append-all structure to-append)
))) )))
(defnav (defnav
^{:doc "Navigates to the specified subset (by taking an intersection). ^{:doc "Navigates to the specified subset (by taking an intersection).
In a transform, that subset in the original set is changed to the In a transform, that subset in the original set is changed to the
@ -360,18 +360,18 @@
walker walker
[afn] [afn]
(select* [this structure next-fn] (select* [this structure next-fn]
(i/walk-select afn next-fn structure)) (n/walk-select afn next-fn structure))
(transform* [this structure next-fn] (transform* [this structure next-fn]
(i/walk-until afn next-fn structure))) (n/walk-until afn next-fn structure)))
(defnav (defnav
^{:doc "Like `walker` but maintains metadata of any forms traversed."} ^{:doc "Like `walker` but maintains metadata of any forms traversed."}
codewalker codewalker
[afn] [afn]
(select* [this structure next-fn] (select* [this structure next-fn]
(i/walk-select afn next-fn structure)) (n/walk-select afn next-fn structure))
(transform* [this structure next-fn] (transform* [this structure next-fn]
(i/codewalk-until afn next-fn structure))) (n/codewalk-until afn next-fn structure)))
(defpathedfn subselect (defpathedfn subselect
"Navigates to a sequence that contains the results of (select ...), "Navigates to a sequence that contains the results of (select ...),
@ -429,8 +429,8 @@
)) ))
(defnav (defnav
^{:doc "Navigate to the result of running `parse-fn` on the value. For ^{:doc "Navigate to the result of running `parse-fn` on the value. For
transforms, the transformed value then has `unparse-fn` run on transforms, the transformed value then has `unparse-fn` run on
it to get the final value at this point."} it to get the final value at this point."}
parser parser
[parse-fn unparse-fn] [parse-fn unparse-fn]
@ -460,32 +460,32 @@
will be parameterized in the order of which the parameterized navigators will be parameterized in the order of which the parameterized navigators
were declared." were declared."
[& path] [& path]
(if-let [afn (i/extract-basic-filter-fn path)] (if-let [afn (n/extract-basic-filter-fn path)]
afn afn
(fixed-pathed-nav [late path] (fixed-pathed-nav [late path]
(select* [this structure next-fn] (select* [this structure next-fn]
(i/filter-select (i/filter-select
#(i/selected?* late %) #(n/selected?* late %)
structure structure
next-fn)) next-fn))
(transform* [this structure next-fn] (transform* [this structure next-fn]
(i/filter-transform (i/filter-transform
#(i/selected?* late %) #(n/selected?* late %)
structure structure
next-fn))))) next-fn)))))
(defpathedfn not-selected? [& path] (defpathedfn not-selected? [& path]
(if-let [afn (i/extract-basic-filter-fn path)] (if-let [afn (n/extract-basic-filter-fn path)]
(fn [s] (not (afn s))) (fn [s] (not (afn s)))
(fixed-pathed-nav [late path] (fixed-pathed-nav [late path]
(select* [this structure next-fn] (select* [this structure next-fn]
(i/filter-select (i/filter-select
#(i/not-selected?* late %) #(n/not-selected?* late %)
structure structure
next-fn)) next-fn))
(transform* [this structure next-fn] (transform* [this structure next-fn]
(i/filter-transform (i/filter-transform
#(i/not-selected?* late %) #(n/not-selected?* late %)
structure structure
next-fn))))) next-fn)))))
@ -514,28 +514,6 @@
(transform* [this structure next-fn] (transform* [this structure next-fn]
(next-fn (compiled-transform late update-fn structure))))) (next-fn (compiled-transform late update-fn structure)))))
(extend-type #+clj clojure.lang.Keyword #+cljs cljs.core/Keyword
Navigator
(select* [kw structure next-fn]
(next-fn (get structure kw)))
(transform* [kw structure next-fn]
(assoc structure kw (next-fn (get structure kw)))
))
(extend-type #+clj clojure.lang.AFn #+cljs function
Navigator
(select* [afn structure next-fn]
(i/filter-select afn structure next-fn))
(transform* [afn structure next-fn]
(i/filter-transform afn structure next-fn)))
(extend-type #+clj clojure.lang.PersistentHashSet #+cljs cljs.core/PersistentHashSet
Navigator
(select* [aset structure next-fn]
(i/filter-select aset structure next-fn))
(transform* [aset structure next-fn]
(i/filter-transform aset structure next-fn)))
(def (def
^{:doc "Keeps the element only if it matches the supplied predicate. This is the ^{: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."} late-bound parameterized version of using a function directly in a path."}
@ -543,6 +521,23 @@
i/pred* i/pred*
) )
(extend-type nil
ImplicitNav
(implicit-nav [this] STAY))
(extend-type #+clj clojure.lang.Keyword #+cljs cljs.core/Keyword
ImplicitNav
(implicit-nav [this] (keypath this))
)
(extend-type #+clj clojure.lang.AFn #+cljs function
ImplicitNav
(implicit-nav [this] (pred this)))
(extend-type #+clj clojure.lang.PersistentHashSet #+cljs cljs.core/PersistentHashSet
ImplicitNav
(implicit-nav [this] (pred this)))
(defnav (defnav
^{:doc "Navigates to the provided val if the structure is nil. Otherwise it stays ^{:doc "Navigates to the provided val if the structure is nil. Otherwise it stays
navigated at the structure."} navigated at the structure."}
@ -585,7 +580,7 @@
current value to the collected vals."} current value to the collected vals."}
collect collect
[& path] [& path]
(pathed-collector [late path] (fixed-pathed-collector [late path]
(collect-val [this structure] (collect-val [this structure]
(compiled-select late structure) (compiled-select late structure)
))) )))
@ -595,7 +590,7 @@
current value to the collected vals."} current value to the collected vals."}
collect-one collect-one
[& path] [& path]
(pathed-collector [late path] (fixed-pathed-collector [late path]
(collect-val [this structure] (collect-val [this structure]
(compiled-select-one late structure) (compiled-select-one late structure)
))) )))
@ -615,7 +610,7 @@
(def (def
^{:doc "Drops all collected values for subsequent navigation."} ^{:doc "Drops all collected values for subsequent navigation."}
DISPENSE i/DISPENSE*) DISPENSE n/DISPENSE*)
(defpathedfn if-path (defpathedfn if-path
@ -623,66 +618,66 @@
([cond-p then-path] ([cond-p then-path]
(if-path cond-p then-path STOP)) (if-path cond-p then-path STOP))
([cond-p then-path else-path] ([cond-p then-path else-path]
(let [then-comp (i/comp-paths* then-path) (let [then-comp (i/comp-paths-internalized then-path)
else-comp (i/comp-paths* else-path) else-comp (i/comp-paths-internalized else-path)
then-needed (i/num-needed-params then-comp) then-needed (i/num-needed-params then-comp)
else-needed (i/num-needed-params else-comp) else-needed (i/num-needed-params else-comp)
[then-s then-t] (i/extract-rich-tfns then-comp) then-nav (i/extract-rich-nav then-comp)
[else-s else-t] (i/extract-rich-tfns else-comp)] else-nav (i/extract-rich-nav else-comp)]
(if-let [afn (i/extract-basic-filter-fn cond-p)] (if-let [afn (n/extract-basic-filter-fn cond-p)]
(richnav (+ then-needed else-needed) (richnav (+ then-needed else-needed)
(select* [params params-idx vals structure next-fn] (select* [this params params-idx vals structure next-fn]
(i/if-select (n/if-select
params params
params-idx params-idx
vals vals
structure structure
next-fn next-fn
afn afn
then-s then-nav
then-needed then-needed
else-s else-nav
)) ))
(transform* [params params-idx vals structure next-fn] (transform* [this params params-idx vals structure next-fn]
(i/if-transform (n/if-transform
params params
params-idx params-idx
vals vals
structure structure
next-fn next-fn
afn afn
then-t then-nav
then-needed then-needed
else-t else-nav
)))) ))))
(let [cond-comp (i/comp-paths* cond-p) (let [cond-comp (i/comp-paths-internalized cond-p)
cond-needed (i/num-needed-params cond-comp)] cond-needed (i/num-needed-params cond-comp)]
(richnav (+ then-needed else-needed cond-needed) (richnav (+ then-needed else-needed cond-needed)
(select* [params params-idx vals structure next-fn] (select* [this params params-idx vals structure next-fn]
(let [late-cond (i/parameterize-path cond-comp params params-idx)] (let [late-cond (i/parameterize-path cond-comp params params-idx)]
(i/if-select (n/if-select
params params
(+ params-idx cond-needed) (+ params-idx cond-needed)
vals vals
structure structure
next-fn next-fn
#(i/selected?* late-cond %) #(n/selected?* late-cond %)
then-s then-nav
then-needed then-needed
else-s else-nav
))) )))
(transform* [params params-idx vals structure next-fn] (transform* [this params params-idx vals structure next-fn]
(let [late-cond (i/parameterize-path cond-comp params params-idx)] (let [late-cond (i/parameterize-path cond-comp params params-idx)]
(i/if-transform (n/if-transform
params params
(+ params-idx cond-needed) (+ params-idx cond-needed)
vals vals
structure structure
next-fn next-fn
#(i/selected?* late-cond %) #(n/selected?* late-cond %)
then-t then-nav
then-needed then-needed
else-t else-nav
)))))))) ))))))))
(defpathedfn cond-path (defpathedfn cond-path
@ -710,23 +705,23 @@
([] STAY) ([] STAY)
([path] (i/comp-paths* path)) ([path] (i/comp-paths* path))
([path1 path2] ([path1 path2]
(let [comp1 (i/comp-paths* path1) (let [comp1 (i/comp-paths-internalized path1)
comp2 (i/comp-paths* path2) comp2 (i/comp-paths-internalized path2)
comp1-needed (i/num-needed-params comp1) comp1-needed (i/num-needed-params comp1)
[s1 t1] (i/extract-rich-tfns comp1) nav1 (i/extract-rich-nav comp1)
[s2 t2] (i/extract-rich-tfns comp2) nav2 (i/extract-rich-nav comp2)
] ]
(richnav (+ comp1-needed (i/num-needed-params comp2)) (richnav (+ comp1-needed (i/num-needed-params comp2))
(select* [params params-idx vals structure next-fn] (select* [this params params-idx vals structure next-fn]
(let [res1 (s1 params params-idx vals structure next-fn) (let [res1 (i/exec-rich-select* nav1 params params-idx vals structure next-fn)
res2 (s2 params (+ params-idx comp1-needed) vals structure next-fn)] res2 (i/exec-rich-select* nav2 params (+ params-idx comp1-needed) vals structure next-fn)]
(if (identical? NONE res2) (if (identical? NONE res2)
res1 res1
res2 res2
))) )))
(transform* [params params-idx vals structure next-fn] (transform* [this params params-idx vals structure next-fn]
(let [s1 (t1 params params-idx vals structure next-fn)] (let [s1 (i/exec-rich-transform* nav1 params params-idx vals structure next-fn)]
(t2 params (+ params-idx comp1-needed) vals s1 next-fn) (i/exec-rich-transform* nav2 params (+ params-idx comp1-needed) vals s1 next-fn)
))))) )))))
([path1 path2 & paths] ([path1 path2 & paths]
(reduce multi-path (multi-path path1 path2) paths) (reduce multi-path (multi-path path1 path2) paths)

View file

@ -13,7 +13,7 @@
~@setters ~@setters
(com.rpl.specter.impl/bind-params* this# ~a 0) (com.rpl.specter.impl/bind-params* this# ~a 0)
)))] )))]
`(defrecord ~'ParamsNeededPath [~'transform-fns ~'num-needed-params] `(defrecord ~'ParamsNeededPath [~'rich-nav ~'num-needed-params]
~fn-type ~fn-type
~@impls ~@impls
~var-arity-impl ~var-arity-impl

View file

@ -0,0 +1,14 @@
(ns com.rpl.specter.defnavhelpers
(:require [com.rpl.specter.impl :as i]))
(defn param-delta [i]
(fn [^objects params params-idx]
(aget params (+ params-idx i))
))
(defn bound-params [path start-delta]
(fn [^objects params params-idx]
(if (i/params-needed-path? path)
(i/bind-params* path params (+ params-idx start-delta))
path
)))

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,471 @@
(ns com.rpl.specter.navs
#+cljs (:require-macros
[com.rpl.specter.macros
:refer
[fixed-pathed-collector
fixed-pathed-nav
defcollector
defnav
defpathedfn
richnav
defnavconstructor
]]
[com.rpl.specter.util-macros :refer
[doseqres]]
)
(:use #+clj [com.rpl.specter macros]
#+clj [com.rpl.specter.util-macros :only [doseqres]])
(:require [com.rpl.specter.impl :as i]
[clojure.walk :as walk]
#+clj [clojure.core.reducers :as r]
[com.rpl.specter.defnavhelpers] ; so that for cljs it's loaded as macros expand to this
)
)
(defn- append [coll elem]
(-> coll vec (conj elem)))
(defn not-selected?*
[compiled-path structure]
(->> structure
(i/compiled-select-any* compiled-path)
(identical? i/NONE)))
(defn selected?*
[compiled-path structure]
(not (not-selected?* compiled-path structure)))
(defn walk-select [pred continue-fn structure]
(let [ret (i/mutable-cell i/NONE)
walker (fn this [structure]
(if (pred structure)
(let [r (continue-fn structure)]
(if-not (identical? r i/NONE)
(i/set-cell! ret r))
r
)
(walk/walk this identity structure)
))]
(walker structure)
(i/get-cell ret)
))
(defn key-select [akey structure next-fn]
(next-fn (get structure akey)))
(defn key-transform [akey structure next-fn]
(assoc structure akey (next-fn (get structure akey))
))
(defn all-select [structure next-fn]
(doseqres i/NONE [e structure]
(next-fn e)))
#+cljs
(defn queue? [coll]
(= (type coll) (type #queue [])))
#+clj
(defn queue? [coll]
(instance? clojure.lang.PersistentQueue coll))
(defprotocol AllTransformProtocol
(all-transform [structure next-fn]))
(defn- non-transient-map-all-transform [structure next-fn empty-map]
(reduce-kv
(fn [m k v]
(let [[newk newv] (next-fn [k v])]
(assoc m newk newv)
))
empty-map
structure
))
(extend-protocol AllTransformProtocol
nil
(all-transform [structure next-fn]
nil
)
;; in cljs they're PersistentVector so don't need a special case
#+clj clojure.lang.MapEntry
#+clj
(all-transform [structure next-fn]
(let [newk (next-fn (key structure))
newv (next-fn (val structure))]
(clojure.lang.MapEntry. newk newv)
))
#+clj clojure.lang.PersistentVector #+cljs cljs.core/PersistentVector
(all-transform [structure next-fn]
(mapv next-fn structure))
#+clj
clojure.lang.PersistentArrayMap
#+clj
(all-transform [structure next-fn]
(let [k-it (.keyIterator structure)
v-it (.valIterator structure)]
(loop [ret {}]
(if (.hasNext k-it)
(let [k (.next k-it)
v (.next v-it)
[newk newv] (next-fn [k v])]
(recur (assoc ret newk newv)))
ret
))))
#+cljs
cljs.core/PersistentArrayMap
#+cljs
(all-transform [structure next-fn]
(non-transient-map-all-transform structure next-fn {})
)
#+clj clojure.lang.PersistentTreeMap #+cljs cljs.core/PersistentTreeMap
(all-transform [structure next-fn]
(non-transient-map-all-transform structure next-fn (empty structure))
)
#+clj clojure.lang.PersistentHashMap #+cljs cljs.core/PersistentHashMap
(all-transform [structure next-fn]
(persistent!
(reduce-kv
(fn [m k v]
(let [[newk newv] (next-fn [k v])]
(assoc! m newk newv)
))
(transient
#+clj clojure.lang.PersistentHashMap/EMPTY #+cljs cljs.core.PersistentHashMap.EMPTY
)
structure
)))
#+clj
Object
#+clj
(all-transform [structure next-fn]
(let [empty-structure (empty structure)]
(cond (and (list? empty-structure) (not (queue? empty-structure)))
;; this is done to maintain order, otherwise lists get reversed
(doall (map next-fn structure))
(map? structure)
;; reduce-kv is much faster than doing r/map through call to (into ...)
(reduce-kv
(fn [m k v]
(let [[newk newv] (next-fn [k v])]
(assoc m newk newv)
))
empty-structure
structure
)
:else
(->> structure (r/map next-fn) (into empty-structure))
)))
#+cljs
default
#+cljs
(all-transform [structure next-fn]
(let [empty-structure (empty structure)]
(if (and (list? empty-structure) (not (queue? empty-structure)))
;; this is done to maintain order, otherwise lists get reversed
(doall (map next-fn structure))
(into empty-structure (map #(next-fn %)) structure)
)))
)
(defprotocol MapValsTransformProtocol
(map-vals-transform [structure next-fn]))
(defn map-vals-non-transient-transform [structure empty-map next-fn]
(reduce-kv
(fn [m k v]
(assoc m k (next-fn v)))
empty-map
structure))
(extend-protocol MapValsTransformProtocol
nil
(map-vals-transform [structure next-fn]
nil
)
#+clj
clojure.lang.PersistentArrayMap
#+clj
(map-vals-transform [structure next-fn]
(let [k-it (.keyIterator structure)
v-it (.valIterator structure)]
(loop [ret {}]
(if (.hasNext k-it)
(let [k (.next k-it)
v (.next v-it)]
(recur (assoc ret k (next-fn v))))
ret
))))
#+cljs
cljs.core/PersistentArrayMap
#+cljs
(map-vals-transform [structure next-fn]
(map-vals-non-transient-transform structure {} next-fn)
)
#+clj clojure.lang.PersistentTreeMap #+cljs cljs.core/PersistentTreeMap
(map-vals-transform [structure next-fn]
(map-vals-non-transient-transform structure (empty structure) next-fn)
)
#+clj clojure.lang.PersistentHashMap #+cljs cljs.core/PersistentHashMap
(map-vals-transform [structure next-fn]
(persistent!
(reduce-kv
(fn [m k v]
(assoc! m k (next-fn v)))
(transient
#+clj clojure.lang.PersistentHashMap/EMPTY #+cljs cljs.core.PersistentHashMap.EMPTY
)
structure
)))
#+clj Object #+cljs default
(map-vals-transform [structure next-fn]
(reduce-kv
(fn [m k v]
(assoc m k (next-fn v)))
(empty structure)
structure))
)
(defn srange-select [structure start end next-fn]
(next-fn (-> structure vec (subvec start end))))
(def srange-transform i/srange-transform*)
(defn- matching-indices [aseq p]
(keep-indexed (fn [i e] (if (p e) i)) aseq))
(defn matching-ranges [aseq p]
(first
(reduce
(fn [[ranges curr-start curr-last :as curr] i]
(cond
(nil? curr-start)
[ranges i i]
(= i (inc curr-last))
[ranges curr-start i]
:else
[(conj ranges [curr-start (inc curr-last)]) i i]
))
[[] nil nil]
(concat (matching-indices aseq p) [-1])
)))
(defn extract-basic-filter-fn [path]
(cond (fn? path)
path
(and (coll? path)
(every? fn? path))
(reduce
(fn [combined afn]
(fn [structure]
(and (combined structure) (afn structure))
))
path
)))
(defn if-select [params params-idx vals structure next-fn then-tester then-nav then-params else-nav]
(let [test? (then-tester structure)
sel (if test?
then-nav
else-nav)
idx (if test? params-idx (+ params-idx then-params))]
(i/exec-rich-select*
sel
params
idx
vals
structure
next-fn
)))
(defn if-transform [params params-idx vals structure next-fn then-tester then-nav then-params else-nav]
(let [test? (then-tester structure)
tran (if test?
then-nav
else-nav)
idx (if test? params-idx (+ params-idx then-params))]
(i/exec-rich-transform*
tran
params
idx
vals
structure
next-fn
)))
(defn terminal* [params params-idx vals structure]
(let [afn (aget ^objects params params-idx)]
(if (identical? vals [])
(afn structure)
(apply afn (conj vals structure)))
))
(defprotocol AddExtremes
(append-all [structure elements])
(prepend-all [structure elements]))
(extend-protocol AddExtremes
nil
(append-all [_ elements]
elements)
(prepend-all [_ elements]
elements)
#+clj clojure.lang.PersistentVector #+cljs cljs.core/PersistentVector
(append-all [structure elements]
(reduce conj structure elements))
(prepend-all [structure elements]
(let [ret (transient [])]
(as-> ret <>
(reduce conj! <> elements)
(reduce conj! <> structure)
(persistent! <>)
)))
#+clj Object #+cljs default
(append-all [structure elements]
(concat structure elements))
(prepend-all [structure elements]
(concat elements structure))
)
(defprotocol UpdateExtremes
(update-first [s afn])
(update-last [s afn]))
(defprotocol GetExtremes
(get-first [s])
(get-last [s]))
(defprotocol FastEmpty
(fast-empty? [s]))
(defnav PosNavigator [getter updater]
(select* [this structure next-fn]
(if-not (fast-empty? structure)
(next-fn (getter structure))
i/NONE))
(transform* [this structure next-fn]
(if (fast-empty? structure)
structure
(updater structure next-fn))))
(defn- update-first-list [l afn]
(cons (afn (first l)) (rest l)))
(defn- update-last-list [l afn]
(append (butlast l) (afn (last l))))
#+clj
(defn vec-count [^clojure.lang.IPersistentVector v]
(.length v))
#+cljs
(defn vec-count [v]
(count v))
#+clj
(defn transient-vec-count [^clojure.lang.ITransientVector v]
(.count v))
#+cljs
(defn transient-vec-count [v]
(count v))
(extend-protocol UpdateExtremes
#+clj clojure.lang.PersistentVector #+cljs cljs.core/PersistentVector
(update-first [v afn]
(let [val (nth v 0)]
(assoc v 0 (afn val))
))
(update-last [v afn]
;; type-hinting vec-count to ^int caused weird errors with case
(let [c (int (vec-count v))]
(case c
1 (let [[e] v] [(afn e)])
2 (let [[e1 e2] v] [e1 (afn e2)])
(let [i (dec c)]
(assoc v i (afn (nth v i)))
))))
#+clj Object #+cljs default
(update-first [l val]
(update-first-list l val))
(update-last [l val]
(update-last-list l val)
))
(extend-protocol GetExtremes
#+clj clojure.lang.IPersistentVector #+cljs cljs.core/PersistentVector
(get-first [v]
(nth v 0))
(get-last [v]
(peek v))
#+clj Object #+cljs default
(get-first [s]
(first s))
(get-last [s]
(last s)
))
(extend-protocol FastEmpty
nil
(fast-empty? [_] true)
#+clj clojure.lang.IPersistentVector #+cljs cljs.core/PersistentVector
(fast-empty? [v]
(= 0 (vec-count v)))
#+clj clojure.lang.ITransientVector #+cljs cljs.core/TransientVector
(fast-empty? [v]
(= 0 (transient-vec-count v)))
#+clj Object #+cljs default
(fast-empty? [s]
(empty? s))
)
(defn walk-until [pred on-match-fn structure]
(if (pred structure)
(on-match-fn structure)
(walk/walk (partial walk-until pred on-match-fn) identity structure)
))
(defn codewalk-until [pred on-match-fn structure]
(if (pred structure)
(on-match-fn structure)
(let [ret (walk/walk (partial codewalk-until pred on-match-fn) identity structure)]
(if (and (i/fn-invocation? structure) (i/fn-invocation? ret))
(with-meta ret (meta structure))
ret
))))
(def DISPENSE*
(i/no-params-rich-compiled-path
(reify i/RichNavigator
(rich-select* [this params params-idx vals structure next-fn]
(next-fn params params-idx [] structure))
(rich-transform* [this params params-idx vals structure next-fn]
(next-fn params params-idx [] structure)))))

View file

@ -1,6 +1,8 @@
(ns com.rpl.specter.protocols) (ns com.rpl.specter.protocols)
(defprotocol Navigator (defprotocol Navigator
"Do not use this protocol directly. All navigators must be created using
com.rpl.specter.macros namespace."
(select* [this structure next-fn] (select* [this structure next-fn]
"An implementation of `select*` must call `next-fn` on each "An implementation of `select*` must call `next-fn` on each
subvalue of `structure`. The result of `select*` is specified subvalue of `structure`. The result of `select*` is specified
@ -17,4 +19,9 @@
)) ))
(defprotocol Collector (defprotocol Collector
"Do not use this protocol directly. All navigators must be created using
com.rpl.specter.macros namespace."
(collect-val [this structure])) (collect-val [this structure]))
(defprotocol ImplicitNav
(implicit-nav [obj]))

View file

@ -8,7 +8,7 @@
[com.rpl.specter.macros :only [com.rpl.specter.macros :only
[defnav [defnav
defpathedfn]]) defpathedfn]])
(:require [com.rpl.specter.impl :as i] (:require [com.rpl.specter.navs :as n]
[com.rpl.specter :refer [subselect selected?]])) [com.rpl.specter :refer [subselect selected?]]))
(defnav (defnav
@ -21,9 +21,17 @@
(transform* [this structure next-fn] (transform* [this structure next-fn]
(assoc! structure key (next-fn (get structure key))))) (assoc! structure key (next-fn (get structure key)))))
(def END!
"Navigates to an empty (persistent) vector at the end of a transient vector."
(i/comp-paths* (i/->TransientEndNavigator))) (defnav
^{:doc "Navigates to an empty (persistent) vector at the end of a transient vector."}
END!
[]
(select* [this structure next-fn]
(next-fn []))
(transform* [this structure next-fn]
(let [res (next-fn [])]
(reduce conj! structure res))))
(defn- t-get-first (defn- t-get-first
[tv] [tv]
@ -31,7 +39,7 @@
(defn- t-get-last (defn- t-get-last
[tv] [tv]
(nth tv (dec (i/transient-vec-count tv)))) (nth tv (dec (n/transient-vec-count tv))))
(defn- t-update-first (defn- t-update-first
[tv next-fn] [tv next-fn]
@ -39,16 +47,17 @@
(defn- t-update-last (defn- t-update-last
[tv next-fn] [tv next-fn]
(let [i (dec (i/transient-vec-count tv))] (let [i (dec (n/transient-vec-count tv))]
(assoc! tv i (next-fn (nth tv i))))) (assoc! tv i (next-fn (nth tv i)))))
(def FIRST! (def FIRST!
"Navigates to the first element of a transient vector." "Navigates to the first element of a transient vector."
(i/->PosNavigator t-get-first t-update-first)) (n/PosNavigator t-get-first t-update-first))
(def LAST! (def LAST!
"Navigates to the last element of a transient vector." "Navigates to the last element of a transient vector."
(i/->PosNavigator t-get-last t-update-last)) (n/PosNavigator t-get-last t-update-last))
#+clj #+clj
(defn- select-keys-from-transient-map (defn- select-keys-from-transient-map

View file

@ -1291,7 +1291,7 @@
(deftest multi-path-vals-test (deftest multi-path-vals-test
(is (= {:a 1 :b 6 :c 3} (is (= {:a 1 :b 6 :c 3}
(transform [(s/multi-path (s/collect-one :a) (s/collect-one :c)) :b] (transform [(s/multi-path (s/collect-one :a) (s/collect-one :c)) :b]
+ +
{:a 1 :b 2 :c 3}))) {:a 1 :b 2 :c 3})))
(is (= [[1 2] [3 2]] (is (= [[1 2] [3 2]]
(select [(s/multi-path (s/collect-one :a) (s/collect-one :c)) :b] (select [(s/multi-path (s/collect-one :a) (s/collect-one :c)) :b]