Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 218 lines (190 sloc) 6.863 kB
547ff85 @Bronsa added namespace docstring
Bronsa authored
1 (ns ^{:doc "Protocols and default Reader types implementation"
2 :author "Bronsa"}
3 clojure.tools.reader.reader-types
e33881a @Bronsa clojure.tools.reader.edn: add an edn-only reader implementation
Bronsa authored
4 (:refer-clojure :exclude [char read-line])
a6aae86 @Bronsa require :refer -> use :only for clojure 1.3 compatibility
Bronsa authored
5 (:use clojure.tools.reader.impl.utils)
81451a8 @Bronsa move out reader types to their own namespace
Bronsa authored
6 (:import clojure.lang.LineNumberingPushbackReader
e33881a @Bronsa clojure.tools.reader.edn: add an edn-only reader implementation
Bronsa authored
7 (java.io InputStream BufferedReader)))
81451a8 @Bronsa move out reader types to their own namespace
Bronsa authored
8
9 (defmacro ^:private update! [what f]
10 (list 'set! what (list f what)))
11
12 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
13 ;; reader protocols
14 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
15
16 (defprotocol Reader
1541a4b @Bronsa Added comprehensive docstrings
Bronsa authored
17 (read-char [reader]
18 "Returns the next char from the Reader, nil if the end of stream has been reached")
19 (peek-char [reader]
20 "Returns the next char from the Reader without removing it from the reader stream"))
81451a8 @Bronsa move out reader types to their own namespace
Bronsa authored
21
22 (defprotocol IPushbackReader
1541a4b @Bronsa Added comprehensive docstrings
Bronsa authored
23 (unread [reader ch]
24 "Pushes back a single character on to the stream"))
81451a8 @Bronsa move out reader types to their own namespace
Bronsa authored
25
26 (defprotocol IndexingReader
1541a4b @Bronsa Added comprehensive docstrings
Bronsa authored
27 (get-line-number [reader]
28 "Returns the line number of the next character to be read from the stream")
29 (get-column-number [reader]
30 "Returns the line number of the next character to be read from the stream"))
81451a8 @Bronsa move out reader types to their own namespace
Bronsa authored
31
32 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
33 ;; reader deftypes
34 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
35
36 (deftype StringReader
37 [^String s s-len ^:unsynchronized-mutable s-pos]
38 Reader
39 (read-char [reader]
40 (when (> s-len s-pos)
41 (let [r (nth s s-pos)]
42 (update! s-pos inc)
43 r)))
44 (peek-char [reader]
45 (when (> s-len s-pos)
46 (nth s s-pos))))
47
67c2978 @Bronsa removed primitive hinting in favour of casting functions for clojure …
Bronsa authored
48 (deftype InputStreamReader [^InputStream is ^:unsynchronized-mutable ^"[B" buf]
81451a8 @Bronsa move out reader types to their own namespace
Bronsa authored
49 Reader
50 (read-char [reader]
51 (if buf
52 (let [c (aget buf 0)]
53 (set! buf nil)
54 (char c))
55 (let [c (.read is)]
0cb2eb5 @jafingerhut TRDR-1: Correct bad read-char behavior where it returns nil (EOF) on …
jafingerhut authored
56 (when (>= c 0)
81451a8 @Bronsa move out reader types to their own namespace
Bronsa authored
57 (char c)))))
58 (peek-char [reader]
59 (when-not buf
60 (set! buf (byte-array 1))
61 (when (== -1 (.read is buf))
62 (set! buf nil)))
63 (when buf
64 (char (aget buf 0)))))
65
66 (deftype PushbackReader
67c2978 @Bronsa removed primitive hinting in favour of casting functions for clojure …
Bronsa authored
67 [rdr ^"[Ljava.lang.Object;" buf buf-len ^:unsynchronized-mutable buf-pos]
81451a8 @Bronsa move out reader types to their own namespace
Bronsa authored
68 Reader
69 (read-char [reader]
70 (char
71 (if (< buf-pos buf-len)
72 (let [r (aget buf buf-pos)]
73 (update! buf-pos inc)
74 r)
75 (read-char rdr))))
76 (peek-char [reader]
77 (char
78 (if (< buf-pos buf-len)
79 (aget buf buf-pos)
80 (peek-char rdr))))
81 IPushbackReader
82 (unread [reader ch]
83 (when ch
84 (if (zero? buf-pos) (throw (RuntimeException. "Pushback buffer is full")))
85 (update! buf-pos dec)
86 (aset buf buf-pos ch))))
87
88 (defn- normalize-newline [rdr ch]
89 (if (identical? \return ch)
90 (let [c (peek-char rdr)]
91 (when (identical? \formfeed c)
92 (read-char rdr))
93 \newline)
94 ch))
95
96 (deftype IndexingPushbackReader
97 [rdr ^:unsynchronized-mutable line ^:unsynchronized-mutable column
98 ^:unsynchronized-mutable line-start? ^:unsynchronized-mutable prev]
99 Reader
100 (read-char [reader]
101 (when-let [ch (read-char rdr)]
102 (let [ch (normalize-newline rdr ch)]
103 (set! prev line-start?)
104 (set! line-start? (newline? ch))
105 (when line-start?
106 (set! column 0)
107 (update! line inc))
108 (update! column inc)
109 ch)))
110
111 (peek-char [reader]
112 (peek-char rdr))
113
114 IPushbackReader
115 (unread [reader ch]
116 (when line-start? (update! line dec))
117 (set! line-start? prev)
118 (update! column dec)
119 (unread rdr ch))
120
121 IndexingReader
122 (get-line-number [reader] (int (inc line)))
123 (get-column-number [reader] (int column)))
124
125 (extend-type java.io.PushbackReader
126 Reader
127 (read-char [rdr]
128 (let [c (.read ^java.io.PushbackReader rdr)]
0cb2eb5 @jafingerhut TRDR-1: Correct bad read-char behavior where it returns nil (EOF) on …
jafingerhut authored
129 (when (>= c 0)
81451a8 @Bronsa move out reader types to their own namespace
Bronsa authored
130 (normalize-newline rdr (char c)))))
131
132 (peek-char [rdr]
133 (when-let [c (read-char rdr)]
134 (unread rdr c)
135 c))
136
137 IPushbackReader
138 (unread [rdr c]
139 (when c
140 (.unread ^java.io.PushbackReader rdr (int c)))))
141
142 (extend LineNumberingPushbackReader
143 IndexingReader
144 {:get-line-number (fn [rdr] (.getLineNumber ^LineNumberingPushbackReader rdr))
e27e1ec @Bronsa Remove reflection warning for calls to char, see #TRDR-6
Bronsa authored
145 :get-column-number (compile-if >=clojure-1-5-alpha*?
81451a8 @Bronsa move out reader types to their own namespace
Bronsa authored
146 (fn [rdr]
147 (.getColumnNumber ^LineNumberingPushbackReader rdr))
148 (fn [rdr] 0))})
149
150 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
151 ;; Public API
152 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
153
154 ;; fast check for provided implementations
5d63214 @Bronsa Moved docstring before params-vector
Bronsa authored
155 (defn indexing-reader?
81451a8 @Bronsa move out reader types to their own namespace
Bronsa authored
156 "Returns true if the reader satisfies IndexingReader"
5d63214 @Bronsa Moved docstring before params-vector
Bronsa authored
157 [rdr]
81451a8 @Bronsa move out reader types to their own namespace
Bronsa authored
158 (or (instance? clojure.tools.reader.reader_types.IndexingReader rdr)
e33881a @Bronsa clojure.tools.reader.edn: add an edn-only reader implementation
Bronsa authored
159 (instance? LineNumberingPushbackReader rdr)
9ea7aa3 @Bronsa Small performance gains, some style fixes
Bronsa authored
160 (and (not (instance? clojure.tools.reader.reader_types.PushbackReader rdr))
161 (not (instance? clojure.tools.reader.reader_types.StringReader rdr))
81451a8 @Bronsa move out reader types to their own namespace
Bronsa authored
162 (not (instance? clojure.tools.reader.reader_types.InputStreamReader rdr))
163 (get (:impls IndexingReader) (class rdr)))))
164
165 (defn string-reader
166 "Creates a StringReader from a given string"
167 ([^String s]
168 (StringReader. s (count s) 0)))
169
170 (defn string-push-back-reader
171 "Creates a PushbackReader from a given string"
172 ([s]
173 (string-push-back-reader s 1))
174 ([^String s buf-len]
175 (PushbackReader. (string-reader s) (object-array buf-len) buf-len buf-len)))
176
177 (defn input-stream-reader
1beaf84 @jafingerhut TRDR-6: Eliminate reflection in tools.reader via type hints
jafingerhut authored
178 "Creates an InputStreamReader from an InputStream"
81451a8 @Bronsa move out reader types to their own namespace
Bronsa authored
179 [is]
180 (InputStreamReader. is nil))
181
182 (defn input-stream-push-back-reader
1beaf84 @jafingerhut TRDR-6: Eliminate reflection in tools.reader via type hints
jafingerhut authored
183 "Creates a PushbackReader from a given InputStream"
81451a8 @Bronsa move out reader types to their own namespace
Bronsa authored
184 ([is]
185 (input-stream-push-back-reader is 1))
186 ([^InputStream is buf-len]
187 (PushbackReader. (input-stream-reader is) (object-array buf-len) buf-len buf-len)))
188
189 (defn indexing-push-back-reader
190 "Creates an IndexingPushbackReader from a given string or Reader"
191 ([s-or-rdr]
192 (indexing-push-back-reader s-or-rdr 1))
193 ([s-or-rdr buf-len]
194 (IndexingPushbackReader.
195 (if (string? s-or-rdr) (string-push-back-reader s-or-rdr buf-len) s-or-rdr) 0 1 true nil)))
196
e33881a @Bronsa clojure.tools.reader.edn: add an edn-only reader implementation
Bronsa authored
197 (defn read-line
198 "Reads a line from the reader or from *in* if no reader is specified"
199 ([] (read-line *in*))
200 ([rdr]
201 (if (or (instance? LineNumberingPushbackReader rdr)
202 (instance? BufferedReader rdr))
203 (clojure.core/read-line rdr)
204 (loop [c (read-char rdr) s (StringBuilder.)]
205 (if (newline? c)
206 (str s)
207 (recur (read-char rdr) (.append s c)))))))
208
81451a8 @Bronsa move out reader types to their own namespace
Bronsa authored
209 (defn reader-error
1541a4b @Bronsa Added comprehensive docstrings
Bronsa authored
210 "Throws an ExceptionInfo with the given message.
211 If rdr is an IndexingReader, additional information about column and line number is provided"
81451a8 @Bronsa move out reader types to their own namespace
Bronsa authored
212 [rdr & msg]
213 (throw (ex-info (apply str msg)
214 (merge {:type :reader-exception}
215 (when (indexing-reader? rdr)
216 {:line (get-line-number rdr)
217 :column (get-column-number rdr)})))))
Something went wrong with that request. Please try again.