Skip to content

Commit 03eae28

Browse files
committed
Fix read with nil/false eof-value
When calling (read stream false nil), the eof-value nil was treated as falsy by if-let, causing an EOF exception instead of returning nil. Same issue for false as eof-value. Fix: use contains? to check for :eof key in opts, and use the eof-error? parameter to decide whether to include :eof in opts.
1 parent 7243477 commit 03eae28

File tree

3 files changed

+42
-10
lines changed

3 files changed

+42
-10
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ SCI is used in [babashka](https://github.com/babashka/babashka),
1212

1313
## Unreleased
1414

15+
- Fix `read` with `nil` or `false` as eof-value throwing instead of returning the eof-value
1516
- `deftype` now macroexpands to `deftype*`, matching JVM Clojure, enabling code walkers like riddley
1617
- `case` now macroexpands to JVM-compatible `case*` format, enabling tools like riddley and cloverage to work with SCI
1718
- Fix `.method` on class objects (e.g. `(.getDeclaredField String "value")`) routing to static instead of instance method path

src/sci/impl/read.cljc

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,13 @@
99

1010
(defn- eof-or-throw [opts v]
1111
(if (utils/kw-identical? parser/eof v)
12-
(if-let [eof (:eof opts)]
13-
(if (not (utils/kw-identical? :eofthrow eof))
14-
eof
15-
(throw (ex-info "EOF while reading"
16-
{:type :sci.error/parse
17-
:opts opts})))
12+
(if (contains? opts :eof)
13+
(let [eof (:eof opts)]
14+
(if (not (utils/kw-identical? :eofthrow eof))
15+
eof
16+
(throw (ex-info "EOF while reading"
17+
{:type :sci.error/parse
18+
:opts opts}))))
1819
(throw (ex-info "EOF while reading"
1920
{:type :sci.error/parse
2021
:opts opts})))
@@ -44,12 +45,14 @@
4445
(read stream true nil))
4546
([stream eof-error? eof-value]
4647
(read stream eof-error? eof-value false))
47-
([stream _eof-error? eof-value _recursive?]
48-
(let [v (parser/parse-next (store/get-ctx) stream
49-
(-> {:eof eof-value}
48+
([stream eof-error? eof-value _recursive?]
49+
(let [opts (cond-> {}
50+
(not eof-error?) (assoc :eof eof-value))
51+
v (parser/parse-next (store/get-ctx) stream
52+
(-> opts
5053
(with-resolver)
5154
(with-suppressed)))]
52-
(eof-or-throw {:eof eof-value} v)))
55+
(eof-or-throw opts v)))
5356
([opts stream]
5457
(let [opts (-> opts with-resolver with-suppressed)
5558
opts (if (:read-cond opts)

test/sci/read_test.cljc

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,34 @@ form
102102
(testing "symbol normally gets line number attached, but we can't do this with pushbackreader"
103103
(is (= 'a (sci/eval-string (pr-str '(read (java.io.PushbackReader. (java.io.StringReader. "a")))) {:classes {'java.io.PushbackReader java.io.PushbackReader}}))))))
104104

105+
#?(:clj
106+
(deftest read-eof-value-test
107+
(let [opts {:classes {'java.io.PushbackReader java.io.PushbackReader
108+
'java.io.StringReader java.io.StringReader}}]
109+
(testing "read with nil eof-value returns nil at EOF"
110+
(is (= [42 nil]
111+
(sci/eval-string
112+
"(with-open [r (java.io.PushbackReader. (java.io.StringReader. \"42\"))]
113+
[(read r false nil) (read r false nil)])"
114+
opts))))
115+
(testing "read with false eof-value returns false at EOF"
116+
(is (= [42 false]
117+
(sci/eval-string
118+
"(with-open [r (java.io.PushbackReader. (java.io.StringReader. \"42\"))]
119+
[(read r false false) (read r false false)])"
120+
opts))))
121+
(testing "read with custom eof-value returns it at EOF"
122+
(is (= [42 :my-eof]
123+
(sci/eval-string
124+
"(with-open [r (java.io.PushbackReader. (java.io.StringReader. \"42\"))]
125+
[(read r false :my-eof) (read r false :my-eof)])"
126+
opts))))
127+
(testing "read with eof-error? true throws at EOF"
128+
(is (thrown-with-msg? Exception #"EOF while reading"
129+
(sci/eval-string
130+
"(read (java.io.PushbackReader. (java.io.StringReader. \"\")))"
131+
opts)))))))
132+
105133
#?(:clj (deftest suppress-read-test
106134
(sci/eval-string
107135
"(tagged-literal? (binding [*suppress-read* true] (read-string \"#foo 1\")))")))

0 commit comments

Comments
 (0)