diff --git a/src/clj/com/rpl/specter.cljx b/src/clj/com/rpl/specter.cljx index 91b2599..83c3c87 100644 --- a/src/clj/com/rpl/specter.cljx +++ b/src/clj/com/rpl/specter.cljx @@ -8,6 +8,7 @@ defcollector defnav defpathedfn + richnav ]] [com.rpl.specter.util-macros :refer [doseqres]] @@ -19,7 +20,8 @@ fixed-pathed-nav defcollector defnav - defpathedfn]] + defpathedfn + richnav]] #+clj [com.rpl.specter.util-macros :only [doseqres]] ) (:require [com.rpl.specter.impl :as i] @@ -585,21 +587,67 @@ ([cond-p then-path] (if-path cond-p then-path STOP)) ([cond-p then-path else-path] - (if-let [afn (i/extract-basic-filter-fn cond-p)] - (fixed-pathed-nav [late-then then-path - late-else else-path] - (select* [this structure next-fn] - (i/if-select structure next-fn afn late-then late-else)) - (transform* [this structure next-fn] - (i/if-transform structure next-fn afn late-then late-else))) - (fixed-pathed-nav [late-cond cond-p - late-then then-path - late-else else-path] - (select* [this structure next-fn] - (i/if-select structure next-fn #(i/selected?* late-cond %) late-then late-else)) - (transform* [this structure next-fn] - (i/if-transform structure next-fn #(i/selected?* late-cond %) late-then late-else)) - )))) + (let [then-comp (i/comp-paths* then-path) + else-comp (i/comp-paths* 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)] + (richnav (+ then-needed else-needed) + (select* [params params-idx vals structure next-fn] + (i/if-select + params + params-idx + vals + structure + next-fn + afn + then-s + then-needed + else-s + )) + (transform* [params params-idx vals structure next-fn] + (i/if-transform + params + params-idx + vals + structure + next-fn + afn + then-t + then-needed + else-t + )))) + (let [cond-comp (i/comp-paths* 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] + (let [late-cond (i/parameterize-path cond-comp params params-idx)] + (i/if-select + params + (+ params-idx cond-needed) + vals + structure + next-fn + #(i/selected?* late-cond %) + then-s + then-needed + else-s + ))) + (transform* [params params-idx vals structure next-fn] + (let [late-cond (i/parameterize-path cond-comp params params-idx)] + (i/if-transform + params + (+ params-idx cond-needed) + vals + structure + next-fn + #(i/selected?* late-cond %) + then-t + then-needed + else-t + )))))))) (defpathedfn cond-path "Takes in alternating cond-path path cond-path path... diff --git a/src/clj/com/rpl/specter/impl.cljx b/src/clj/com/rpl/specter/impl.cljx index 0238167..9ac0f5d 100644 --- a/src/clj/com/rpl/specter/impl.cljx +++ b/src/clj/com/rpl/specter/impl.cljx @@ -417,7 +417,7 @@ (defn num-needed-params [path] (if (instance? CompiledPath path) 0 - (:num-needed-params path))) + (.-num-needed-params ^ParamsNeededPath path))) ;; cell implementation idea taken from prismatic schema library @@ -1075,18 +1075,33 @@ path ))) -(defn if-select [structure next-fn then-tester late-then late-else] - (let [apath (if (then-tester structure) - late-then - late-else)] - (compiled-traverse* apath next-fn structure))) -(defn if-transform [structure next-fn then-tester late-then late-else] - (let [apath (if (then-tester structure) - late-then - late-else)] - (compiled-transform* apath next-fn structure) - )) + +(defn if-select [params params-idx vals structure next-fn then-tester then-s then-params else-s] + (let [test? (then-tester structure) + sel (if test? + then-s + else-s) + idx (if test? params-idx (+ params-idx then-params))] + (sel params + idx + vals + structure + next-fn + ))) + +(defn if-transform [params params-idx vals structure next-fn then-tester then-t then-params else-t] + (let [test? (then-tester structure) + tran (if test? + then-t + else-t) + idx (if test? params-idx then-params)] + (tran params + idx + vals + structure + next-fn + ))) (defn filter-select [afn structure next-fn] (if (afn structure) @@ -1480,3 +1495,15 @@ expected-params " but got " needed-params)) (extend atype protpath-prot {m (fn [_] rp)}) )))) + +(defn parameterize-path [apath params params-idx] + (if (instance? CompiledPath apath) + apath + (bind-params* apath params params-idx) + )) + +(defn extract-rich-tfns [apath] + (let [tfns (coerce-tfns-rich (:transform-fns apath))] + [(:selector tfns) (:transformer tfns)] + )) + diff --git a/src/clj/com/rpl/specter/macros.clj b/src/clj/com/rpl/specter/macros.clj index 4d28071..1a48f87 100644 --- a/src/clj/com/rpl/specter/macros.clj +++ b/src/clj/com/rpl/specter/macros.clj @@ -124,6 +124,23 @@ (paramsnav* retrieve-params num-params [impl1 impl2]) )) +(defmacro richnav + "Defines a navigator with full access to collected vals, the parameters array, + and the parameters array index. `next-fn` expects to receive the params array, + the next params index, the collected vals, and finally the next structure. + This is the lowest level way of making navigators." + [num-params impl1 impl2] + (let [[select-impl transform-impl] (determine-params-impls impl1 impl2)] + `(let [tfns# (i/->TransformFunctions + i/RichPathExecutor + (fn ~@select-impl) + (fn ~@transform-impl) + )] + (if (zero? ~num-params) + (i/no-params-compiled-path tfns#) + (i/->ParamsNeededPath tfns# ~num-params) + )))) + (defmacro paramsfn [params [structure-sym] & impl] `(nav ~params (~'select* [this# structure# next-fn#]