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
[com.rpl.specter.macros
:refer
[pathed-collector
variable-pathed-nav
[fixed-pathed-collector
fixed-pathed-nav
defcollector
defnav
@ -14,10 +13,9 @@
[com.rpl.specter.util-macros :refer
[doseqres]]
)
(:use [com.rpl.specter.protocols :only [Navigator]]
(:use [com.rpl.specter.protocols :only [ImplicitNav]]
#+clj [com.rpl.specter.macros :only
[pathed-collector
variable-pathed-nav
[fixed-pathed-collector
fixed-pathed-nav
defcollector
defnav
@ -27,6 +25,7 @@
#+clj [com.rpl.specter.util-macros :only [doseqres]]
)
(:require [com.rpl.specter.impl :as i]
[com.rpl.specter.navs :as n]
[clojure.set :as set])
)
@ -172,19 +171,14 @@
(defn params-reset [params-path]
;; TODO: error if not paramsneededpath
(let [s (i/params-needed-selector params-path)
t (i/params-needed-transformer params-path)
(let [nav (i/params-needed-nav params-path)
needed (i/num-needed-params params-path)]
(i/->ParamsNeededPath
(i/->TransformFunctions
i/RichPathExecutor
(fn [params params-idx vals structure next-fn]
(s 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)))
(richnav 0
(select* [this params params-idx vals structure next-fn]
(i/exec-rich-select* nav params (- params-idx needed) vals structure next-fn))
(transform* [this params params-idx vals structure next-fn]
(i/exec-rich-transform* nav params (- params-idx needed) vals structure next-fn)
))))
;; Built-in pathing and context operations
@ -199,6 +193,7 @@
structure
))
(defnav
^{:doc "Stays navigated at the current point. Essentially a no-op navigator."}
STAY
@ -208,7 +203,6 @@
(transform* [this structure next-fn]
(next-fn structure)))
(def
^{:doc "For usage with `multi-transform`, defines an endpoint in the navigation
that will have the parameterized transform function run. The transform
@ -216,10 +210,10 @@
given as the first arguments"}
terminal
(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"))
(transform* [params params-idx vals structure next-fn]
(i/terminal* params params-idx vals structure)
(transform* [this params params-idx vals structure next-fn]
(n/terminal* params params-idx vals structure)
)))
(defnavconstructor terminal-val
@ -229,11 +223,15 @@
[v]
(p (i/fast-constantly v)))
(def
(defnav
^{:doc "Navigate to every element of the collection. For maps navigates to
a vector of `[key value]`."}
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
^{:doc "Navigate to each value of the map. This is more efficient than
@ -245,23 +243,25 @@
(next-fn v)
))
(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
^{:doc "Navigate to the last element of the collection. If the collection is
empty navigation is stopped at this point."}
LAST
(comp-paths (i/->PosNavigator i/get-last i/update-last)))
(n/PosNavigator n/get-last n/update-last))
(def
^{:doc "Navigate to the first element of the collection. If the collection is
empty navigation is stopped at this point."}
FIRST
(comp-paths (i/->PosNavigator i/get-first i/update-first)))
(n/PosNavigator n/get-first n/update-first))
(defnav
^{:doc "Uses start-fn and end-fn to determine the bounds of the subsequence
@ -269,9 +269,9 @@
srange-dynamic
[start-fn end-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]
(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
@ -280,9 +280,9 @@
srange
[start end]
(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]
(i/srange-transform structure start end next-fn)
(n/srange-transform structure start end next-fn)
))
(defnav
@ -290,15 +290,15 @@
continuous-subseqs
[pred]
(select* [this structure next-fn]
(doseqres NONE [[s e] (i/matching-ranges structure pred)]
(i/srange-select structure s e next-fn)
(doseqres NONE [[s e] (n/matching-ranges structure pred)]
(n/srange-select structure s e next-fn)
))
(transform* [this structure next-fn]
(reduce
(fn [structure [s e]]
(i/srange-transform structure s e next-fn))
(n/srange-transform structure s e next-fn))
structure
(reverse (i/matching-ranges structure pred))
(reverse (n/matching-ranges structure pred))
)))
(defnav
@ -309,7 +309,7 @@
(next-fn []))
(transform* [this structure next-fn]
(let [to-prepend (next-fn [])]
(i/prepend-all structure to-prepend)
(n/prepend-all structure to-prepend)
)))
(defnav
@ -320,7 +320,7 @@
(next-fn []))
(transform* [this structure next-fn]
(let [to-append (next-fn [])]
(i/append-all structure to-append)
(n/append-all structure to-append)
)))
(defnav
@ -360,18 +360,18 @@
walker
[afn]
(select* [this structure next-fn]
(i/walk-select afn next-fn structure))
(n/walk-select afn next-fn structure))
(transform* [this structure next-fn]
(i/walk-until afn next-fn structure)))
(n/walk-until afn next-fn structure)))
(defnav
^{:doc "Like `walker` but maintains metadata of any forms traversed."}
codewalker
[afn]
(select* [this structure next-fn]
(i/walk-select afn next-fn structure))
(n/walk-select afn next-fn structure))
(transform* [this structure next-fn]
(i/codewalk-until afn next-fn structure)))
(n/codewalk-until afn next-fn structure)))
(defpathedfn subselect
"Navigates to a sequence that contains the results of (select ...),
@ -460,32 +460,32 @@
will be parameterized in the order of which the parameterized navigators
were declared."
[& path]
(if-let [afn (i/extract-basic-filter-fn path)]
(if-let [afn (n/extract-basic-filter-fn path)]
afn
(fixed-pathed-nav [late path]
(select* [this structure next-fn]
(i/filter-select
#(i/selected?* late %)
#(n/selected?* late %)
structure
next-fn))
(transform* [this structure next-fn]
(i/filter-transform
#(i/selected?* late %)
#(n/selected?* late %)
structure
next-fn)))))
(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)))
(fixed-pathed-nav [late path]
(select* [this structure next-fn]
(i/filter-select
#(i/not-selected?* late %)
#(n/not-selected?* late %)
structure
next-fn))
(transform* [this structure next-fn]
(i/filter-transform
#(i/not-selected?* late %)
#(n/not-selected?* late %)
structure
next-fn)))))
@ -514,28 +514,6 @@
(transform* [this structure next-fn]
(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
^{: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."}
@ -543,6 +521,23 @@
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
^{:doc "Navigates to the provided val if the structure is nil. Otherwise it stays
navigated at the structure."}
@ -585,7 +580,7 @@
current value to the collected vals."}
collect
[& path]
(pathed-collector [late path]
(fixed-pathed-collector [late path]
(collect-val [this structure]
(compiled-select late structure)
)))
@ -595,7 +590,7 @@
current value to the collected vals."}
collect-one
[& path]
(pathed-collector [late path]
(fixed-pathed-collector [late path]
(collect-val [this structure]
(compiled-select-one late structure)
)))
@ -615,7 +610,7 @@
(def
^{:doc "Drops all collected values for subsequent navigation."}
DISPENSE i/DISPENSE*)
DISPENSE n/DISPENSE*)
(defpathedfn if-path
@ -623,66 +618,66 @@
([cond-p then-path]
(if-path cond-p then-path STOP))
([cond-p then-path else-path]
(let [then-comp (i/comp-paths* then-path)
else-comp (i/comp-paths* else-path)
(let [then-comp (i/comp-paths-internalized then-path)
else-comp (i/comp-paths-internalized else-path)
then-needed (i/num-needed-params then-comp)
else-needed (i/num-needed-params else-comp)
[then-s then-t] (i/extract-rich-tfns then-comp)
[else-s else-t] (i/extract-rich-tfns else-comp)]
(if-let [afn (i/extract-basic-filter-fn cond-p)]
then-nav (i/extract-rich-nav then-comp)
else-nav (i/extract-rich-nav else-comp)]
(if-let [afn (n/extract-basic-filter-fn cond-p)]
(richnav (+ then-needed else-needed)
(select* [params params-idx vals structure next-fn]
(i/if-select
(select* [this params params-idx vals structure next-fn]
(n/if-select
params
params-idx
vals
structure
next-fn
afn
then-s
then-nav
then-needed
else-s
else-nav
))
(transform* [params params-idx vals structure next-fn]
(i/if-transform
(transform* [this params params-idx vals structure next-fn]
(n/if-transform
params
params-idx
vals
structure
next-fn
afn
then-t
then-nav
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)]
(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)]
(i/if-select
(n/if-select
params
(+ params-idx cond-needed)
vals
structure
next-fn
#(i/selected?* late-cond %)
then-s
#(n/selected?* late-cond %)
then-nav
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)]
(i/if-transform
(n/if-transform
params
(+ params-idx cond-needed)
vals
structure
next-fn
#(i/selected?* late-cond %)
then-t
#(n/selected?* late-cond %)
then-nav
then-needed
else-t
else-nav
))))))))
(defpathedfn cond-path
@ -710,23 +705,23 @@
([] STAY)
([path] (i/comp-paths* path))
([path1 path2]
(let [comp1 (i/comp-paths* path1)
comp2 (i/comp-paths* path2)
(let [comp1 (i/comp-paths-internalized path1)
comp2 (i/comp-paths-internalized path2)
comp1-needed (i/num-needed-params comp1)
[s1 t1] (i/extract-rich-tfns comp1)
[s2 t2] (i/extract-rich-tfns comp2)
nav1 (i/extract-rich-nav comp1)
nav2 (i/extract-rich-nav comp2)
]
(richnav (+ comp1-needed (i/num-needed-params comp2))
(select* [params params-idx vals structure next-fn]
(let [res1 (s1 params params-idx vals structure next-fn)
res2 (s2 params (+ params-idx comp1-needed) vals structure next-fn)]
(select* [this params params-idx vals structure next-fn]
(let [res1 (i/exec-rich-select* nav1 params params-idx vals structure next-fn)
res2 (i/exec-rich-select* nav2 params (+ params-idx comp1-needed) vals structure next-fn)]
(if (identical? NONE res2)
res1
res2
)))
(transform* [params params-idx vals structure next-fn]
(let [s1 (t1 params params-idx vals structure next-fn)]
(t2 params (+ params-idx comp1-needed) vals s1 next-fn)
(transform* [this params params-idx vals structure next-fn]
(let [s1 (i/exec-rich-transform* nav1 params params-idx vals structure next-fn)]
(i/exec-rich-transform* nav2 params (+ params-idx comp1-needed) vals s1 next-fn)
)))))
([path1 path2 & paths]
(reduce multi-path (multi-path path1 path2) paths)

View file

@ -13,7 +13,7 @@
~@setters
(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
~@impls
~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)
(defprotocol Navigator
"Do not use this protocol directly. All navigators must be created using
com.rpl.specter.macros namespace."
(select* [this structure next-fn]
"An implementation of `select*` must call `next-fn` on each
subvalue of `structure`. The result of `select*` is specified
@ -17,4 +19,9 @@
))
(defprotocol Collector
"Do not use this protocol directly. All navigators must be created using
com.rpl.specter.macros namespace."
(collect-val [this structure]))
(defprotocol ImplicitNav
(implicit-nav [obj]))

View file

@ -8,7 +8,7 @@
[com.rpl.specter.macros :only
[defnav
defpathedfn]])
(:require [com.rpl.specter.impl :as i]
(:require [com.rpl.specter.navs :as n]
[com.rpl.specter :refer [subselect selected?]]))
(defnav
@ -21,9 +21,17 @@
(transform* [this structure next-fn]
(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
[tv]
@ -31,7 +39,7 @@
(defn- t-get-last
[tv]
(nth tv (dec (i/transient-vec-count tv))))
(nth tv (dec (n/transient-vec-count tv))))
(defn- t-update-first
[tv next-fn]
@ -39,16 +47,17 @@
(defn- t-update-last
[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)))))
(def FIRST!
"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!
"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
(defn- select-keys-from-transient-map