Permalink
Browse files

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...
1 parent 59d3c72 commit d3b5665d21457ad27bda702f567ca2f55b14283b @stuartsierra stuartsierra committed Dec 9, 2011
@@ -6557,3 +6557,47 @@
"Returns true if a value has been produced for a promise, delay, future or lazy sequence."
{:added "1.3"}
[^clojure.lang.IPending x] (.isRealized x))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; data readers ;;;;;;;;;;;;;;;;;;
+
+(defn- data-reader-urls []
+ (enumeration-seq
+ (.. Thread currentThread getContextClassLoader
+ (getResources "data_readers.clj"))))
+
+(defn- assert-symbol [^clojure.lang.LineNumberingPushbackReader reader x]
+ (when-not (symbol? x)
+ (throw (ex-info "non-symbol in data-reader file"
+ {:file *file*
+ :line (.getLineNumber reader)
+ :value x}))))
+
+(defn- data-reader-var [sym]
+ (intern (create-ns (symbol (namespace sym)))
+ (symbol (name sym))))
+
+(defn- load-data-reader-file [mappings ^java.net.URL url]
+ (with-open [rdr (clojure.lang.LineNumberingPushbackReader.
+ (java.io.InputStreamReader.
+ (.openStream url) "UTF-8"))]
+ (binding [*file* (.getFile url)]
+ (loop [mappings mappings]
+ (if-let [tag (read rdr false nil)]
+ (do (assert-symbol rdr tag)
+ (when (contains? mappings tag)
+ (throw (ex-info "Conflicting data-reader mapping"
+ {:file *file*
+ :line (.getLineNumber rdr)
+ :symbol tag})))
+ (let [target (read rdr true nil)]
+ (assert-symbol rdr target)
+ (recur (assoc mappings tag (data-reader-var target)))))
+ mappings)))))
+
+(defn- load-data-readers []
+ (alter-var-root #'*data-readers*
+ (fn [mappings]
+ (reduce load-data-reader-file
+ mappings (data-reader-urls)))))
+
+(load-data-readers)
@@ -94,6 +94,7 @@
*print-meta* *print-meta*
*print-length* *print-length*
*print-level* *print-level*
+ *data-readers* *data-readers*
*compile-path* (System/getProperty "clojure.compile.path" "classes")
*command-line-args* *command-line-args*
*unchecked-math* *unchecked-math*
@@ -6898,7 +6898,8 @@ public static Object load(Reader rdr, String sourcePath, String sourceName) {
LINE_AFTER, pushbackReader.getLineNumber()
,RT.UNCHECKED_MATH, RT.UNCHECKED_MATH.deref()
,RT.WARN_ON_REFLECTION, RT.WARN_ON_REFLECTION.deref()
- ));
+ ,RT.DATA_READERS, RT.DATA_READERS.deref()
+ ));
try
{
@@ -7023,6 +7024,7 @@ public static Object compile(Reader rdr, String sourcePath, String sourceName) t
VARS, PersistentHashMap.EMPTY
,RT.UNCHECKED_MATH, RT.UNCHECKED_MATH.deref()
,RT.WARN_ON_REFLECTION, RT.WARN_ON_REFLECTION.deref()
+ ,RT.DATA_READERS, RT.DATA_READERS.deref()
// ,LOADER, RT.makeClassLoader()
));
@@ -1145,8 +1145,25 @@ public static List readDelimitedList(char delim, PushbackReader r, boolean isRec
public static class CtorReader extends AFn{
public Object invoke(Object reader, Object firstChar){
PushbackReader r = (PushbackReader) reader;
+ Object name = read(r, true, null, false);
+ if (!(name instanceof Symbol))
+ throw new RuntimeException("Reader tag must be a symbol");
+ Symbol sym = (Symbol)name;
+ return sym.getName().contains(".") ? readRecord(r, sym) : readTagged(r, sym);
+ }
+
+ private Object readTagged(PushbackReader reader, Symbol tag){
+ Object o = read(reader, true, null, true);
+
+ ILookup data_readers = (ILookup)RT.DATA_READERS.deref();
+ IFn data_reader = (IFn)RT.get(data_readers, tag);
+ if(data_reader == null)
+ throw new RuntimeException("No reader function for tag " + tag.toString());
+
+ return data_reader.invoke(o);
+ }
- Object recordName = read(r, true, null, false);
+ private Object readRecord(PushbackReader r, Symbol recordName){
Class recordClass = RT.classForName(recordName.toString());
char endch;
boolean shortForm = true;
@@ -182,6 +182,7 @@
final static Keyword CONST_KEY = Keyword.intern(null, "const");
final static public Var AGENT = Var.intern(CLOJURE_NS, Symbol.intern("*agent*"), null).setDynamic();
final static public Var READEVAL = Var.intern(CLOJURE_NS, Symbol.intern("*read-eval*"), T).setDynamic();
+final static public Var DATA_READERS = Var.intern(CLOJURE_NS, Symbol.intern("*data-readers*"), RT.map()).setDynamic();
final static public Var ASSERT = Var.intern(CLOJURE_NS, Symbol.intern("*assert*"), T).setDynamic();
final static public Var MATH_CONTEXT = Var.intern(CLOJURE_NS, Symbol.intern("*math-context*"), null).setDynamic();
static Keyword LINE_KEY = Keyword.intern(null, "line");

0 comments on commit d3b5665

Please sign in to comment.