diff --git a/src/clj/com/rpl/specter/impl.cljx b/src/clj/com/rpl/specter/impl.cljx index f4fcde3..8d12ac8 100644 --- a/src/clj/com/rpl/specter/impl.cljx +++ b/src/clj/com/rpl/specter/impl.cljx @@ -64,7 +64,7 @@ (defn throw-illegal [& args] (throw (js/Error. (apply str args)))) -;; need to get the expansion function like this so that +;; need to get the expansion function like this so that ;; this code compiles in a clojure environment where cljs.analyzer ;; namespace does not exist #+clj @@ -221,7 +221,7 @@ p11 p12 p13 p14 p15 p16 p17 p18 p19 p20 rest] (let [a (object-array - (concat + (concat [p01 p02 p03 p04 p05 p06 p07 p08 p09 p10 p11 p12 p13 p14 p15 p16 p17 p18 p19 p20] rest))] @@ -263,8 +263,12 @@ empty? not)) +(defn root-params-nav? [o] + (and (fn? o) (-> o meta :highernav))) + (defn- coerce-object [this] - (cond (satisfies? p/ImplicitNav this) (p/implicit-nav this) + (cond (root-params-nav? this) (-> this meta :highernav :params-needed-path) + (satisfies? p/ImplicitNav this) (p/implicit-nav this) :else (throw-illegal "Not a navigator: " this) )) @@ -300,7 +304,7 @@ #+cljs cljs.core/LazySeq #+cljs (coerce-path [this] (coerce-path (vec this))) - + #+clj Object #+cljs default (coerce-path [this] (coerce-object this))) @@ -356,7 +360,7 @@ (satisfies? Navigator (:nav path)) path - + :else (let [^ParameterizedRichNav prich-nav (:nav path) rich-nav (.-rich-nav prich-nav) @@ -482,7 +486,7 @@ (.-traverse-executor ex)) -;; amazingly doing this as a macro shows a big effect in the +;; amazingly doing this as a macro shows a big effect in the ;; benchmark for getting a value out of a nested map #+clj (defmacro compiled-traverse* [path result-fn structure] @@ -626,8 +630,9 @@ (defn verify-layerable! [anav] (if-not - (and (instance? ParamsNeededPath anav) - (> (:num-needed-params anav) 0)) + (or (root-params-nav? anav) + (and (instance? ParamsNeededPath anav) + (> (:num-needed-params anav) 0))) (throw-illegal "defnavconstructor must be used on a navigator defined with defnav with at least one parameter") )) @@ -771,6 +776,30 @@ 1 )) +(def lean-compiled-path-proxy + (->ParamsNeededPath + (reify RichNavigator + (rich-select* [this params params-idx vals structure next-fn] + (let [^CompiledPath apath (aget ^objects params params-idx) + ^Navigator nav (.-nav apath)] + (exec-select* + nav + structure + (fn [structure-next] + (next-fn params params-idx vals structure-next)) + ))) + (rich-transform* [this params params-idx vals structure next-fn] + (let [^CompiledPath apath (aget ^objects params params-idx) + ^Navigator nav (.-nav apath)] + (exec-transform* + nav + structure + (fn [structure-next] + (next-fn params params-idx vals structure-next)) + )))) + 1 + )) + (defn srange-transform* [structure start end next-fn] (let [structurev (vec structure) newpart (next-fn (-> structurev (subvec start end))) @@ -851,14 +880,14 @@ (if (-> v meta :dynamic) (magic-fail! "Var " (:sym op) " is dynamic") (cond - (instance? ParamsNeededPath vv) + (or (root-params-nav? vv) (instance? ParamsNeededPath vv)) ;;TODO: if all params are constants, then just bind the path right here ;;otherwise, add the params ;; - could extend this to see if it contains nested function calls which ;; are only on constants (do (swap! params-atom #(vec (concat % ps))) - vv + (coerce-path vv) ) (and (fn? vv) (-> v meta :pathedfn)) @@ -899,9 +928,12 @@ (and (fn? vv) (-> vv meta :layerednav)) (do + ;;TODO: if all args are constant then invoke it right here (swap! params-atom conj (:code p)) - rich-compiled-path-proxy - ) + (if (= (-> vv meta :layerednav) :lean) + lean-compiled-path-proxy + rich-compiled-path-proxy + )) :else (magic-fail! "Var " (:sym op) " must be either a parameterized " diff --git a/src/clj/com/rpl/specter/macros.clj b/src/clj/com/rpl/specter/macros.clj index 85242ed..4efe10e 100644 --- a/src/clj/com/rpl/specter/macros.clj +++ b/src/clj/com/rpl/specter/macros.clj @@ -45,10 +45,10 @@ (i/->ParamsNeededPath nav# num-params#) )))) -(defmacro lean-nav* [& impls] +(defmacro ^:no-doc lean-nav* [& impls] `(reify Navigator ~@impls)) -(defn operation-with-bindings [bindings params-sym params-idx-sym op-maker] +(defn ^:no-doc operation-with-bindings [bindings params-sym params-idx-sym op-maker] (let [bindings (partition 2 bindings) binding-fn-syms (gensyms (count bindings)) binding-syms (map first bindings) @@ -62,7 +62,7 @@ ~body ))) -(defmacro rich-nav-with-bindings [num-params-code bindings & impls] +(defmacro ^:no-doc rich-nav-with-bindings [num-params-code bindings & impls] (let [[[[_ s-structure-sym s-next-fn-sym] & s-body] [[_ t-structure-sym t-next-fn-sym] & t-body]] (apply determine-params-impls impls) @@ -97,7 +97,7 @@ )) ))))) -(defmacro collector-with-bindings [num-params-code bindings impl] +(defmacro ^:no-doc collector-with-bindings [num-params-code bindings impl] (let [[_ [_ structure-sym] & body] impl params-sym (gensym "params") params-idx-sym (gensym "params")] @@ -133,14 +133,19 @@ [params & impls] (if (empty? params) `(i/lean-compiled-path (lean-nav* ~@impls)) - `(i/->ParamsNeededPath - ;(fn ~params (lean-nav* ~@body)) - (rich-nav-with-bindings ~(count params) - ~(delta-param-bindings params) - ~@impls - ) - ~(count params) - ))) + `(vary-meta + (fn ~params (i/lean-compiled-path (lean-nav* ~@impls))) + assoc + :highernav + {:type :lean + :params-needed-path + (i/->ParamsNeededPath + (rich-nav-with-bindings ~(count params) + ~(delta-param-bindings params) + ~@impls + ) + ~(count params))} + ))) (defmacro collector "Defines a Collector with late bound parameters. This collector can be precompiled @@ -155,13 +160,20 @@ )] (if ~(empty? params) (i/no-params-rich-compiled-path rich-nav#) - (i/->ParamsNeededPath - ; (fn ~params - ; (collector-with-bindings 0 - ; ~impl-body)) - rich-nav# - ~(count params)) - ))) + (vary-meta + (fn ~params + (i/no-params-rich-compiled-path + (collector-with-bindings 0 [] + ~body))) + assoc + :highernav + {:type :rich + :params-needed-path + (i/->ParamsNeededPath + rich-nav# + ~(count params) + )} + )))) (defn ^:no-doc fixed-pathed-operation [bindings op-maker] (let [bindings (partition 2 bindings) @@ -413,7 +425,8 @@ (vary-meta (let [~csym (i/layered-wrapper ~anav)] (fn ~@checked-code)) - assoc :layerednav true)) + assoc :layerednav (or (-> ~anav meta :highernav :type) :rich) + )) )) diff --git a/test/com/rpl/specter/core_test.cljx b/test/com/rpl/specter/core_test.cljx index b81cd81..480baee 100644 --- a/test/com/rpl/specter/core_test.cljx +++ b/test/com/rpl/specter/core_test.cljx @@ -1291,7 +1291,7 @@ (deftest multi-path-vals-test (is (= {:a 1 :b 6 :c 3} (transform [(s/multi-path (s/collect-one :a) (s/collect-one :c)) :b] - + + + {:a 1 :b 2 :c 3}))) (is (= [[1 2] [3 2]] (select [(s/multi-path (s/collect-one :a) (s/collect-one :c)) :b]