Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

CLJ-890: tagged reader literals

* tag definitions loaded from /data_readers.clj files on classpath
* read as a series of symbol-symbol pairs
* "target" symbol interpreted as a Var
* creates namespaces and interns Vars as necessary
* may be more than one file
* throws exception on conflict
* sets root binding of *data-readers*
  • Loading branch information...
commit d3b5665d21457ad27bda702f567ca2f55b14283b 1 parent 59d3c72
Stuart Sierra stuartsierra authored
44 src/clj/clojure/core.clj
@@ -6557,3 +6557,47 @@
6557 6557 "Returns true if a value has been produced for a promise, delay, future or lazy sequence."
6558 6558 {:added "1.3"}
6559 6559 [^clojure.lang.IPending x] (.isRealized x))
  6560 +
  6561 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; data readers ;;;;;;;;;;;;;;;;;;
  6562 +
  6563 +(defn- data-reader-urls []
  6564 + (enumeration-seq
  6565 + (.. Thread currentThread getContextClassLoader
  6566 + (getResources "data_readers.clj"))))
  6567 +
  6568 +(defn- assert-symbol [^clojure.lang.LineNumberingPushbackReader reader x]
  6569 + (when-not (symbol? x)
  6570 + (throw (ex-info "non-symbol in data-reader file"
  6571 + {:file *file*
  6572 + :line (.getLineNumber reader)
  6573 + :value x}))))
  6574 +
  6575 +(defn- data-reader-var [sym]
  6576 + (intern (create-ns (symbol (namespace sym)))
  6577 + (symbol (name sym))))
  6578 +
  6579 +(defn- load-data-reader-file [mappings ^java.net.URL url]
  6580 + (with-open [rdr (clojure.lang.LineNumberingPushbackReader.
  6581 + (java.io.InputStreamReader.
  6582 + (.openStream url) "UTF-8"))]
  6583 + (binding [*file* (.getFile url)]
  6584 + (loop [mappings mappings]
  6585 + (if-let [tag (read rdr false nil)]
  6586 + (do (assert-symbol rdr tag)
  6587 + (when (contains? mappings tag)
  6588 + (throw (ex-info "Conflicting data-reader mapping"
  6589 + {:file *file*
  6590 + :line (.getLineNumber rdr)
  6591 + :symbol tag})))
  6592 + (let [target (read rdr true nil)]
  6593 + (assert-symbol rdr target)
  6594 + (recur (assoc mappings tag (data-reader-var target)))))
  6595 + mappings)))))
  6596 +
  6597 +(defn- load-data-readers []
  6598 + (alter-var-root #'*data-readers*
  6599 + (fn [mappings]
  6600 + (reduce load-data-reader-file
  6601 + mappings (data-reader-urls)))))
  6602 +
  6603 +(load-data-readers)
1  src/clj/clojure/main.clj
@@ -94,6 +94,7 @@
94 94 *print-meta* *print-meta*
95 95 *print-length* *print-length*
96 96 *print-level* *print-level*
  97 + *data-readers* *data-readers*
97 98 *compile-path* (System/getProperty "clojure.compile.path" "classes")
98 99 *command-line-args* *command-line-args*
99 100 *unchecked-math* *unchecked-math*
4 src/jvm/clojure/lang/Compiler.java
@@ -6898,7 +6898,8 @@ public static Object load(Reader rdr, String sourcePath, String sourceName) {
6898 6898 LINE_AFTER, pushbackReader.getLineNumber()
6899 6899 ,RT.UNCHECKED_MATH, RT.UNCHECKED_MATH.deref()
6900 6900 ,RT.WARN_ON_REFLECTION, RT.WARN_ON_REFLECTION.deref()
6901   - ));
  6901 + ,RT.DATA_READERS, RT.DATA_READERS.deref()
  6902 + ));
6902 6903
6903 6904 try
6904 6905 {
@@ -7023,6 +7024,7 @@ public static Object compile(Reader rdr, String sourcePath, String sourceName) t
7023 7024 VARS, PersistentHashMap.EMPTY
7024 7025 ,RT.UNCHECKED_MATH, RT.UNCHECKED_MATH.deref()
7025 7026 ,RT.WARN_ON_REFLECTION, RT.WARN_ON_REFLECTION.deref()
  7027 + ,RT.DATA_READERS, RT.DATA_READERS.deref()
7026 7028 // ,LOADER, RT.makeClassLoader()
7027 7029 ));
7028 7030
19 src/jvm/clojure/lang/LispReader.java
@@ -1145,8 +1145,25 @@ public static List readDelimitedList(char delim, PushbackReader r, boolean isRec
1145 1145 public static class CtorReader extends AFn{
1146 1146 public Object invoke(Object reader, Object firstChar){
1147 1147 PushbackReader r = (PushbackReader) reader;
  1148 + Object name = read(r, true, null, false);
  1149 + if (!(name instanceof Symbol))
  1150 + throw new RuntimeException("Reader tag must be a symbol");
  1151 + Symbol sym = (Symbol)name;
  1152 + return sym.getName().contains(".") ? readRecord(r, sym) : readTagged(r, sym);
  1153 + }
  1154 +
  1155 + private Object readTagged(PushbackReader reader, Symbol tag){
  1156 + Object o = read(reader, true, null, true);
  1157 +
  1158 + ILookup data_readers = (ILookup)RT.DATA_READERS.deref();
  1159 + IFn data_reader = (IFn)RT.get(data_readers, tag);
  1160 + if(data_reader == null)
  1161 + throw new RuntimeException("No reader function for tag " + tag.toString());
  1162 +
  1163 + return data_reader.invoke(o);
  1164 + }
1148 1165
1149   - Object recordName = read(r, true, null, false);
  1166 + private Object readRecord(PushbackReader r, Symbol recordName){
1150 1167 Class recordClass = RT.classForName(recordName.toString());
1151 1168 char endch;
1152 1169 boolean shortForm = true;
1  src/jvm/clojure/lang/RT.java
@@ -182,6 +182,7 @@
182 182 final static Keyword CONST_KEY = Keyword.intern(null, "const");
183 183 final static public Var AGENT = Var.intern(CLOJURE_NS, Symbol.intern("*agent*"), null).setDynamic();
184 184 final static public Var READEVAL = Var.intern(CLOJURE_NS, Symbol.intern("*read-eval*"), T).setDynamic();
  185 +final static public Var DATA_READERS = Var.intern(CLOJURE_NS, Symbol.intern("*data-readers*"), RT.map()).setDynamic();
185 186 final static public Var ASSERT = Var.intern(CLOJURE_NS, Symbol.intern("*assert*"), T).setDynamic();
186 187 final static public Var MATH_CONTEXT = Var.intern(CLOJURE_NS, Symbol.intern("*math-context*"), null).setDynamic();
187 188 static Keyword LINE_KEY = Keyword.intern(null, "line");

0 comments on commit d3b5665

Please sign in to comment.
Something went wrong with that request. Please try again.