From f8d74d5884aecc7f00ffd22cec39ccd5d7939494 Mon Sep 17 00:00:00 2001 From: Nathan Marz Date: Thu, 1 Sep 2016 16:31:00 -0400 Subject: [PATCH] add direct nav hints --- src/clj/com/rpl/specter/impl.cljc | 36 +++++++++++++++++++----------- src/clj/com/rpl/specter/macros.clj | 12 ++++++---- 2 files changed, 31 insertions(+), 17 deletions(-) diff --git a/src/clj/com/rpl/specter/impl.cljc b/src/clj/com/rpl/specter/impl.cljc index 2920816..6ca49e5 100644 --- a/src/clj/com/rpl/specter/impl.cljc +++ b/src/clj/com/rpl/specter/impl.cljc @@ -603,6 +603,14 @@ (defn dynamic-var? [v] (-> v meta :dynamic)) +(defn maybe-direct-nav [obj direct-nav?] + (if direct-nav? + (vary-meta obj assoc :direct-nav true) + obj)) + +(defn direct-nav? [o] + (-> o meta :direct-nav)) + ;; don't do coerce-nav here... save that for resolve-magic-code (defn- magic-precompilation* [o] (cond (sequential? o) @@ -610,11 +618,18 @@ (instance? VarUse o) (if (dynamic-var? (:var o)) - (->DynamicVal (:sym o)) - (:val o)) + (->DynamicVal (maybe-direct-nav + (:sym o) + (or (-> o :var direct-nav?) + (-> o :sym direct-nav?)))) + + (maybe-direct-nav + (:val o) + (or (-> o :var direct-nav?) + (-> o :sym direct-nav?) + (-> o :val direct-nav?)))) (instance? LocalSym o) - ;; TODO: check metadata on locals to determine if it's definitely a direct-nav or not (->DynamicVal (:sym o)) (instance? SpecialFormUse o) @@ -692,22 +707,17 @@ (resolve-magic-code path))) (instance? DynamicVal o) - ;;TODO: check ^:nav hint to see whether this is necessary - ;;this is relevant for localsyms and dynamicvars - ;;for localsyms can check metadata in the env as well as metadata on the symbol itself - ;;for dynamic vars check the var metadata - `(coerce-nav ~(:code o)) + (if (-> o :code direct-nav?) + (:code o) + `(coerce-nav ~(:code o))) (instance? DynamicFunction o) - ;;TODO: check ^:nav hint on op to see whether coerce-nav is necessary - ;; checked when resolving varuse on the function to know if it returns a direct-nav or not - ;; ":direct-nav-fn" metadata as opposed to :direct-nav metadata which is used for symbols/values (let [op (resolve-dynamic-fn-arg (:op o)) params (map resolve-dynamic-fn-arg (:params o))] (if (all-static? (conj params op)) (coerce-nav (apply op params)) - `(coerce-nav (~(resolve-dynamic-fn-arg-code op) - ~@(map resolve-dynamic-fn-arg-code params))))) + (let [code `(~(resolve-dynamic-fn-arg-code op) ~@(map resolve-dynamic-fn-arg-code params))] + (if (direct-nav? op) code `(coerce-nav ~code))))) :else (coerce-nav o))) diff --git a/src/clj/com/rpl/specter/macros.clj b/src/clj/com/rpl/specter/macros.clj index fa23a0e..10ebc10 100644 --- a/src/clj/com/rpl/specter/macros.clj +++ b/src/clj/com/rpl/specter/macros.clj @@ -14,9 +14,11 @@ (defmacro richnav [params & impls] (if (empty? params) `(reify RichNavigator ~@impls) - `(fn ~params - (reify RichNavigator - ~@impls)))) + `(vary-meta + (fn ~params + (reify RichNavigator + ~@impls)) + assoc :direct-nav true))) (defmacro nav [params & impls] @@ -196,7 +198,9 @@ (symbol? path) (if (contains? locals-set path) - `(com.rpl.specter.impl/->LocalSym ~path (quote ~path)) + (let [s (get locals-set path) + embed (i/maybe-direct-nav path (-> s meta :direct-nav))] + `(com.rpl.specter.impl/->LocalSym ~path (quote ~embed))) ;; var-get doesn't work in cljs, so capture the val in the macro instead `(com.rpl.specter.impl/->VarUse ~path (var ~path) (quote ~path)))