From dd6dafc3905c777966e8b7cb55baaff6487c5db5 Mon Sep 17 00:00:00 2001 From: Nathan Marz Date: Thu, 14 Jan 2016 17:36:49 -0500 Subject: [PATCH] implemented declarepath/providepath --- CHANGES.md | 1 + src/clj/com/rpl/specter.cljx | 6 +---- src/clj/com/rpl/specter/macros.clj | 40 ++++++++++++++++++++++++++--- test/com/rpl/specter/core_test.cljx | 30 ++++++++++++++++++++-- 4 files changed, 67 insertions(+), 10 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index f3d43e8..14ce071 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -6,6 +6,7 @@ * Fix replace-in to work with value collection * Added STAY selector * Added stay-then-continue and continue-then-stay selectors which enable pre-order/post-order traversals +* Added declarepath and providepath, which enable arbitrary recursive or mutually recursive paths ## 0.9.1 * Fixed reflection in protocol path code diff --git a/src/clj/com/rpl/specter.cljx b/src/clj/com/rpl/specter.cljx index 4e9ec2c..74235a2 100644 --- a/src/clj/com/rpl/specter.cljx +++ b/src/clj/com/rpl/specter.cljx @@ -7,8 +7,6 @@ fixed-pathed-path defcollector defpath - paramscollector - paramspath ]] ) (:use [com.rpl.specter.protocols :only [StructurePath]] @@ -17,9 +15,7 @@ variable-pathed-path fixed-pathed-path defcollector - defpath - paramscollector - paramspath]] + defpath]] ) (:require [com.rpl.specter.impl :as i] [clojure.set :as set]) diff --git a/src/clj/com/rpl/specter/macros.clj b/src/clj/com/rpl/specter/macros.clj index c442267..756e6a9 100644 --- a/src/clj/com/rpl/specter/macros.clj +++ b/src/clj/com/rpl/specter/macros.clj @@ -108,7 +108,7 @@ (apply concat))) -(defmacro paramspath +(defmacro path "Defines a StructurePath with late bound parameters. This path can be precompiled with other selectors without knowing the parameters. When precompiled with other selectors, the resulting selector takes in parameters for all selectors in the path @@ -120,7 +120,7 @@ )) (defmacro paramsfn [params [structure-sym] & impl] - `(paramspath ~params + `(path ~params (~'select* [this# structure# next-fn#] (let [afn# (fn [~structure-sym] ~@impl)] (filter-select afn# structure# next-fn#) @@ -143,7 +143,7 @@ )) (defmacro defpath [name & body] - `(def ~name (paramspath ~@body))) + `(def ~name (path ~@body))) (defmacro defcollector [name & body] `(def ~name (paramscollector ~@body))) @@ -247,5 +247,39 @@ ) ))))) + +(defn declared-name [name] + (symbol (str name "-declared"))) + +(defmacro declarepath [name] + (let [declared (declared-name name) + rargs [(gensym "params") (gensym "pidx") (gensym "vals") + (gensym "structure") (gensym "next-fn")]] + `(do + (declare ~declared) + (def ~name + (no-params-compiled-path + (->TransformFunctions + RichPathExecutor + (fn ~rargs + (let [selector# (compiled-selector ~declared)] + (selector# ~@rargs) + )) + (fn ~rargs + (let [transformer# (compiled-transformer ~declared)] + (transformer# ~@rargs) + )))) + )))) + +(defmacro providepath [name apath] + `(def ~(declared-name name) + (update-in (comp-paths* ~apath) + [:transform-fns] + coerce-tfns-rich) + )) + +;;TODO: hmm... not sure how to proxy it as don't know if its paramsneeded/compiler or rich/regular +;;could require later definition to be done with "defproxiedpath" or wrapped in proxied-path + (defmacro extend-protocolpath [protpath & extensions] `(extend-protocolpath* ~protpath ~(protpath-sym protpath) ~(vec extensions))) diff --git a/test/com/rpl/specter/core_test.cljx b/test/com/rpl/specter/core_test.cljx index 8fd08ee..c0d7d74 100644 --- a/test/com/rpl/specter/core_test.cljx +++ b/test/com/rpl/specter/core_test.cljx @@ -3,12 +3,16 @@ [cljs.test :refer [is deftest]] [cljs.test.check.cljs-test :refer [defspec]] [com.rpl.specter.cljs-test-helpers :refer [for-all+]] - [com.rpl.specter.macros :refer [paramsfn defprotocolpath defpath extend-protocolpath]]) + [com.rpl.specter.macros + :refer [paramsfn defprotocolpath defpath extend-protocolpath + declarepath providepath]]) (:use #+clj [clojure.test :only [deftest is]] #+clj [clojure.test.check.clojure-test :only [defspec]] #+clj [com.rpl.specter.test-helpers :only [for-all+]] - #+clj [com.rpl.specter.macros :only [paramsfn defprotocolpath defpath extend-protocolpath]] + #+clj [com.rpl.specter.macros + :only [paramsfn defprotocolpath defpath extend-protocolpath + declarepath providepath]] ) @@ -589,6 +593,28 @@ (s/select (s/continue-then-stay s/ALL odd?) [1 2 3]))) ) + +(declarepath MyWalker) + +(providepath MyWalker + (s/if-path vector? + (s/if-path [s/FIRST #(= :abc %)] + (s/continue-then-stay s/ALL MyWalker) + [s/ALL MyWalker] + ))) + +(deftest recursive-path-test + (is (= [9 1 10 3 1] + (s/select [MyWalker s/ALL number?] + [:bb [:aa 34 [:abc 10 [:ccc 9 8 [:abc 9 1]]]] [:abc 1 [:abc 3]]]) + )) + (is (= [:bb [:aa 34 [:abc 11 [:ccc 9 8 [:abc 10 2]]]] [:abc 2 [:abc 4]]] + (s/transform [MyWalker s/ALL number?] inc + [:bb [:aa 34 [:abc 10 [:ccc 9 8 [:abc 9 1]]]] [:abc 1 [:abc 3]]]) + )) + ) + + #+clj (deftest large-params-test (let [path (apply s/comp-paths (repeat 25 s/keypath))