A complete Clojure reader and an EDN-only reader, works with Clojure versions >= 1.3.0
- Rationale
- Releases and Dependency Information
- Changelog
- API Index
- Developer Information
- Example Usage
- Differences from LispReader.java
- License
clojure.tools.reader offers all functionality of the reader from clojure-1.5.0, and more.
This means safer read/read-string, an edn-only reader, tagged-literals support, default-data-reader-fn support for every Clojure version >=1.3.0
For a list of additional features of the reader, read Differences from LispReader.java
Moreover, by using reader types from clojure.tools.reader.reader-types
, if using an IndexingReader, column info is available and both line and column metadata is attached not only to lists, but to symbols, vectors and maps too, when additional debugging info is needed (note that the edn reader doesn't add any line/column metadata at all).
Note that it uses ex-info
which is available on clojure.core
only from clojure-1.4.0.
If using clojure-1.3.0 and needing access to ex-data, use clojure.tools.reader.impl.utils/ex-data
YourKit has given an open source license for their profiler, greatly simplifying the profiling of tools.reader performance.
YourKit is kindly supporting open source projects with its full-featured Java Profiler. YourKit, LLC is the creator of innovative and intelligent tools for profiling Java and .NET applications. Take a look at YourKit's leading software products:
Latest stable release: 0.8.13
Leiningen dependency information:
[org.clojure/tools.reader "0.8.13"]
Maven dependency information:
<dependency>
<groupId>org.clojure</groupId>
<artifactId>tools.reader</artifactId>
<version>0.8.13</version>
</dependency>
To read data structures, functions from clojure.tools.reader.edn
should be used, since those are safe and don't allow any code execution at all.
Remember that when using read
you need to use a reader that implements IPushbackReader
such as string-push-back-reader
.
Note that since no code-execution is permitted, reader literals are also disabled.
(require '[clojure.tools.reader.edn :as edn])
;=> nil
(edn/read-string "1")
;=> 1
(edn/read-string "#inst \"2010-11-12T13:14:15.666\"")
;=> #inst "2010-11-12T13:14:15.666-00:00"
(let [my-unknown (fn [tag val] {:unknown-tag tag :value val})]
(edn/read-string {:default my-unknown} "#foo bar"))
;=> {:unknown-tag foo, :value bar}
(edn/read-string {:readers {'foo (constantly 1)}} "#foo bar")
;=> 1
To switch from using clojure.core/read-string
to clojure.tools.reader.edn/read-string
in your projects, put this in your namespace declaration:
(:refer-clojure :exclude [read read-string])
(:use [clojure.tools.reader.edn :only [read read-string]])
If (and only if) reading from a trusted source, and advanced features that need some level of code-execution during read are needed, functions from clojure.tools.reader
should be used.
(require '[clojure.tools.reader :as r])
;=> nil
(r/read-string "1")
;=> 1
;; WARNING!
(r/read-string "#=(+ 1 2)")
;=> 3
(binding [r/*read-eval* false]
(r/read-string "#=(+ 1 2)"))
=> ExceptionInfo #= not allowed when *read-eval* is false
To switch from using clojure.core/read-string
to clojure.tools.reader/read-string
in your projects, put this in your namespace declaration:
(:refer-clojure :exclude [read read-string *default-data-reader-fn* *read-eval* *data-readers*])
(:use [clojure.tools.reader :only [read read-string *default-data-reader-fn* *read-eval* *data-readers*]])
Reader types example usage:
(require '[clojure.tools.reader.reader-types :as t])
;=> nil
(def reader (t/string-push-back-reader "1"))
;=> #'user/reader
(t/read-char reader)
;=> \1
(t/unread reader \a)
;=> \a
(t/peek-char reader)
;=> \a
(t/read-char reader)
;=> \a
(t/read-char reader)
;=> nil
Note that the pushback buffer is of dimension 1 by default, and an exception will be thrown if trying to unread more chars than the pushback buffer dimension.
Every predefined reader type has an additional arity that allows to specify the pushback buffer dimension.
(def reader (t/string-push-back-reader "" 2))
;=> nil
(t/unread reader \a)
;=> \a
(t/unread reader \b)
;=> \b
(t/read-char reader)
;=> \b
(t/read-char reader)
;=> \a
(t/read-char reader)
;=> nil
There are small differences from clojure.lang.LispReader:
read
throws anex-info
for almost every exception, whereasclojure.lang.LispReader/read
throws aReaderException
wrapping the causing exception.read
is capable of reading\x
escaped charsread
is capable of readingInfinity
+Infinity
-Infinity
andNaN
as per #CLJ-1074read
is capable of reading literal tags containing periods, fixing #CLJ-1100clojure.tools.reader/read
checks ifclojure.tools.reader/*alias-map*
is bound, if that's the case, aliases will be resolved by querying it (must be a map), otherwhise (ns-aliases *ns*) will be usedclojure.tools.reader/read
adds additional line/column info to symbols, vectors and maps when possibleclojure.tools.reader.reader-types/read-line
has an additional arity with which is possible to specify the reader to read from
Copyright © 2013-2014 Nicola Mometto, Rich Hickey & contributors.
Licensed under the EPL. (See the file epl.html.)