Skip to content

Commit 5379f72

Browse files
CLJS-3294: data_readers.cljc doesn't provide a way to have target-specific behaviour (#120)
- The function that reads the data_readers.cljc files from the classpath has been moved to the analyzer ns, so that custom literals are available to the ana/forms-seq fns also. - in cljs.reader/add-data-readers macro, the set of custom data-readers returned is the fully qualified var names of those readers - as those will be interpreted as cljs functions. Co-authored-by: Henry Widd <henryw374@gmail.com>
1 parent b3670a6 commit 5379f72

8 files changed

Lines changed: 92 additions & 53 deletions

File tree

src/main/cljs/cljs/reader.clj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,6 @@
1313
(let [data-readers
1414
(->> (get @env/*compiler* :cljs.analyzer/data-readers)
1515
(map (fn [[k v]]
16-
`['~k (fn [x#] (~(vary-meta v assoc :cljs.analyzer/no-resolve true) x#))]))
16+
`['~k (fn [x#] (~(vary-meta (-> v meta :sym) assoc :cljs.analyzer/no-resolve true) x#))]))
1717
(into {}))]
1818
`(do (merge ~default-readers ~data-readers))))

src/main/clojure/cljs/analyzer.cljc

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -618,6 +618,65 @@
618618
#?(:clj
619619
(def load-mutex (Object.)))
620620

621+
#?(:clj
622+
(defn- load-data-reader-file [mappings ^java.net.URL url]
623+
(with-open [rdr (readers/input-stream-push-back-reader (.openStream url))]
624+
(binding [*file* (.getFile url)]
625+
(let [new-mappings (reader/read {:eof nil :read-cond :allow :features #{:cljs}} rdr)]
626+
(when (not (map? new-mappings))
627+
(throw (ex-info (str "Not a valid data-reader map")
628+
{:url url
629+
:clojure.error/phase :compilation})))
630+
(reduce
631+
(fn [m [k v]]
632+
(when (not (symbol? k))
633+
(throw (ex-info (str "Invalid form in data-reader file")
634+
{:url url
635+
:form k
636+
:clojure.error/phase :compilation})))
637+
(when (and (contains? mappings k)
638+
(not= (mappings k) v))
639+
(throw (ex-info "Conflicting data-reader mapping"
640+
{:url url
641+
:conflict k
642+
:mappings m
643+
:clojure.error/phase :compilation})))
644+
(assoc m k v))
645+
mappings
646+
new-mappings))))))
647+
648+
#?(:clj
649+
(defn get-data-readers*
650+
"returns a merged map containing all data readers defined by libraries
651+
on the classpath."
652+
([]
653+
(get-data-readers* (. (Thread/currentThread) (getContextClassLoader))))
654+
([^ClassLoader classloader]
655+
(let [data-reader-urls (enumeration-seq (. classloader (getResources "data_readers.cljc")))]
656+
(reduce load-data-reader-file {} data-reader-urls)))))
657+
658+
#?(:clj
659+
(def get-data-readers (memoize get-data-readers*)))
660+
661+
#?(:clj
662+
(defn load-data-readers* []
663+
(let [data-readers (get-data-readers)
664+
nses (map (comp symbol namespace) (vals data-readers))]
665+
(doseq [ns nses]
666+
(try
667+
(locking load-mutex
668+
(require ns))
669+
(catch Throwable _)))
670+
(->> data-readers
671+
(map (fn [[tag reader-fn]]
672+
[tag
673+
(-> reader-fn find-var var-get
674+
(with-meta {:sym reader-fn}))]))
675+
(into {})))))
676+
677+
#?(:clj
678+
(def load-data-readers (memoize load-data-readers*)))
679+
621680
#?(:clj
622681
(defn load-core []
623682
(when (not @-cljs-macros-loaded)
@@ -4310,6 +4369,7 @@
43104369
(resolve-var (assoc @env/*compiler* :ns (get-namespace *cljs-ns*))
43114370
sym)))))
43124371

4372+
43134373
#?(:clj
43144374
(defn forms-seq*
43154375
"Seq of Clojure/ClojureScript forms from rdr, a java.io.Reader. Optionally
@@ -4324,7 +4384,8 @@
43244384
{:read-cond :allow :features #{:cljs}}))
43254385
pbr (readers/indexing-push-back-reader
43264386
(PushbackReader. rdr) 1 filename)
4327-
data-readers tags/*cljs-data-readers*
4387+
data-readers (merge tags/*cljs-data-readers*
4388+
(load-data-readers))
43284389
forms-seq_
43294390
(fn forms-seq_ []
43304391
(lazy-seq
@@ -4352,7 +4413,8 @@
43524413
(let [rdr (io/reader f)
43534414
pbr (readers/indexing-push-back-reader
43544415
(PushbackReader. rdr) 1 filename)
4355-
data-readers tags/*cljs-data-readers*
4416+
data-readers (merge tags/*cljs-data-readers*
4417+
(load-data-readers))
43564418
forms-seq*
43574419
(fn forms-seq* []
43584420
(lazy-seq

src/main/clojure/cljs/closure.clj

Lines changed: 2 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -2976,52 +2976,9 @@
29762976
opts js-modules)))
29772977
opts)))
29782978

2979-
(defn- load-data-reader-file [mappings ^java.net.URL url]
2980-
(with-open [rdr (readers/input-stream-push-back-reader (.openStream url))]
2981-
(binding [*file* (.getFile url)]
2982-
(let [new-mappings (reader/read {:eof nil :read-cond :allow} rdr)]
2983-
(when (not (map? new-mappings))
2984-
(throw (ex-info (str "Not a valid data-reader map")
2985-
{:url url
2986-
:clojure.error/phase :compilation})))
2987-
(reduce
2988-
(fn [m [k v]]
2989-
(when (not (symbol? k))
2990-
(throw (ex-info (str "Invalid form in data-reader file")
2991-
{:url url
2992-
:form k
2993-
:clojure.error/phase :compilation})))
2994-
(when (and (contains? mappings k)
2995-
(not= (mappings k) v))
2996-
(throw (ex-info "Conflicting data-reader mapping"
2997-
{:url url
2998-
:conflict k
2999-
:mappings m
3000-
:clojure.error/phase :compilation})))
3001-
(assoc m k v))
3002-
mappings
3003-
new-mappings)))))
3004-
3005-
(defn get-data-readers*
3006-
"returns a merged map containing all data readers defined by libraries
3007-
on the classpath."
3008-
([]
3009-
(get-data-readers* (. (Thread/currentThread) (getContextClassLoader))))
3010-
([classloader]
3011-
(let [data-reader-urls (enumeration-seq (. classloader (getResources "data_readers.cljc")))]
3012-
(reduce load-data-reader-file {} data-reader-urls))))
3013-
3014-
(def get-data-readers (memoize get-data-readers*))
3015-
30162979
(defn load-data-readers! [compiler]
3017-
(let [data-readers (get-data-readers)
3018-
nses (map (comp symbol namespace) (vals data-readers))]
3019-
(swap! compiler update-in [:cljs.analyzer/data-readers] merge (get-data-readers))
3020-
(doseq [ns nses]
3021-
(try
3022-
(locking ana/load-mutex
3023-
(require ns))
3024-
(catch Throwable _)))))
2980+
(swap! compiler update-in [:cljs.analyzer/data-readers] merge
2981+
(ana/load-data-readers)))
30252982

30262983
(defn add-externs-sources [opts]
30272984
(cond-> opts

src/main/clojure/cljs/core/server.clj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,8 @@
9191
(when (try
9292
(let [[form s] (binding [*ns* (create-ns ana/*cljs-ns*)
9393
reader/resolve-symbol ana/resolve-symbol
94-
reader/*data-readers* tags/*cljs-data-readers*
94+
reader/*data-readers* (merge tags/*cljs-data-readers*
95+
(ana/load-data-readers))
9596
reader/*alias-map*
9697
(apply merge
9798
((juxt :requires :require-macros)

src/main/clojure/cljs/repl.cljc

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1143,7 +1143,8 @@
11431143
(fn []
11441144
(let [input (binding [*ns* (create-ns ana/*cljs-ns*)
11451145
reader/resolve-symbol ana/resolve-symbol
1146-
reader/*data-readers* tags/*cljs-data-readers*
1146+
reader/*data-readers* (merge tags/*cljs-data-readers*
1147+
(ana/load-data-readers))
11471148
reader/*alias-map*
11481149
(apply merge
11491150
((juxt :requires :require-macros :as-aliases)
@@ -1510,7 +1511,8 @@ itself (not its value) is returned. The reader macro #'x expands to (var x)."}})
15101511
(let [rdr (readers/source-logging-push-back-reader pbr)]
15111512
(dotimes [_ (dec (:line v))] (readers/read-line rdr))
15121513
(binding [reader/*alias-map* identity
1513-
reader/*data-readers* tags/*cljs-data-readers*]
1514+
reader/*data-readers* (merge tags/*cljs-data-readers*
1515+
(ana/load-data-readers))]
15141516
(-> (reader/read {:read-cond :allow :features #{:cljs}} rdr)
15151517
meta :source)))))))))
15161518

src/test/cljs/data_readers.cljc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,5 @@
99
{cljs/tag clojure.core/identity
1010
cljs/inc clojure.core/inc
1111
cljs/union clojure.set/union
12-
test/custom-identity data-readers-test.core/custom-identity}
12+
test/custom-identity data-readers-test.core/custom-identity
13+
test/custom-form #?(:cljs data-readers-test.core/custom-form-cljs :clj clojure.core/identity)}

src/test/cljs/data_readers_test/core.cljc

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,15 @@
1111
(def custom-identity identity)
1212

1313
(assert (= 1 #test/custom-identity 1))
14+
15+
(defn custom-form-cljs
16+
"a clojure and clojurescript function - in both cases targeting only cljs.
17+
18+
returns a clojurescript form (from :clj branch, when compiling)
19+
and executes js from :cljs branch when using cljs.reader/read"
20+
[x]
21+
#?(:clj `(js/Array.of ~x)
22+
:cljs (js/Array.of x)))
23+
24+
#?(:cljs
25+
(def result #test/custom-form"foo"))

src/test/clojure/cljs/build_api_tests.clj

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -456,7 +456,11 @@
456456
cenv (env/default-compiler-env)]
457457
(test/delete-out-files out)
458458
(build/build (build/inputs (io/file inputs "data_readers_test")) opts cenv)
459-
(is (contains? (-> @cenv ::ana/data-readers) 'test/custom-identity))))
459+
(is (contains? (-> @cenv ::ana/data-readers) 'test/custom-identity))
460+
(is (true? (boolean (re-find #"Array\.of\(\"foo\"\)"
461+
(slurp (io/file
462+
out ;"data-readers-test-out"
463+
"data_readers_test" "core.js"))))))))
460464

461465
(deftest test-data-readers-records
462466
(let [out (.getPath (io/file (test/tmp-dir) "data-readers-test-records-out"))

0 commit comments

Comments
 (0)