diff --git a/CHANGELOG.md b/CHANGELOG.md index bcf1a087f..8037489aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added * Added the `CollReduce` and `KVReduce` protocols in `basilisp.core.protocols` and implemented `reduce` in terms of those protocols (#927) * Added `basilisp.pprint/print-table` function (#983) + * Added `basilisp.core/read-all` function (#986) ### Changed * Improved on the nREPL server exception messages by matching that of the REPL user friendly format (#968) diff --git a/src/basilisp/contrib/nrepl_server.lpy b/src/basilisp/contrib/nrepl_server.lpy index 2a75b9b61..f8d5f4b0f 100644 --- a/src/basilisp/contrib/nrepl_server.lpy +++ b/src/basilisp/contrib/nrepl_server.lpy @@ -122,7 +122,6 @@ [{:keys [client* code ns file _column _line] :as request} send-fn] (let [{:keys [*1 *2 *3 *e eval-ns]} @client* out-stream (StreamOutFn #(send-fn request {"out" %})) - reader (io/StringIO code) ctx (basilisp.lang.compiler.CompilerContext. (or file "")) eval-ns (if ns (create-ns (symbol ns)) @@ -134,13 +133,11 @@ *3 *3 *e *e] (try - (let [results (for [form (seq (basilisp.lang.reader/read reader - *resolver* - *data-readers*))] + (let [result (last + (for [form (read-all (io/StringIO code))] (basilisp.lang.compiler/compile-and-exec-form form ctx - *ns*)) - result (last results)] + *ns*)))] (send-value request send-fn [result {:ns (str *ns*)}])) (catch python/Exception e (debug :eval-exception e) @@ -183,9 +180,7 @@ [resolve-ns symbol-str] (let [reader (io/StringIO symbol-str) {:keys [form error]} (try {:form (binding [*ns* resolve-ns] - (first (seq (basilisp.lang.reader/read reader - *resolver* - *data-readers*))))} + (read reader))} (catch python/Exception e (debug :symbol-identify-reader-error :input symbol-str :exception e) {:error (str e)}))] diff --git a/src/basilisp/core.lpy b/src/basilisp/core.lpy index 9b697e4ef..7940a92ee 100644 --- a/src/basilisp/core.lpy +++ b/src/basilisp/core.lpy @@ -4428,6 +4428,17 @@ *resolver* basilisp.lang.runtime/resolve-alias) +(defn- read-iterator + [opts x] + (let [read (:read opts basilisp.lang.reader/read)] + (read x + *resolver* + *data-readers* + (:eof opts) + (= (:eof opts) :eofthrow) + (:features opts) + (not= :preserve (:read-cond opts))))) + (defn read-string "Read a string of Basilisp code. @@ -4440,17 +4451,7 @@ ([s] (read-string {:eof :eofthrow} s)) ([opts s] - (first (basilisp.lang.reader/read-str s - *resolver* - *data-readers* - (:eof opts) - (if (= (:eof opts) :eofthrow) - true - false) - (:features opts) - (if (= :preserve (:read-cond opts)) - false - true))))) + (first (read-iterator (assoc opts :read basilisp.lang.reader/read-str) s)))) (defn read "Read the next form from the ``stream``\\. If no stream is specified, uses the value @@ -4468,23 +4469,29 @@ ([stream] (read stream true nil)) ([opts stream] - (first (basilisp.lang.reader/read stream - *resolver* - *data-readers* - (:eof opts) - (if (= (:eof opts) :eofthrow) - true - false) - (:features opts) - (if (= :preserve (:read-cond opts)) - false - true)))) + (first (read-iterator opts stream))) ([stream eof-error? eof-value] - (first (basilisp.lang.reader/read stream - *resolver* - *data-readers* - eof-value - eof-error?)))) + (first (read-iterator {:eof (if eof-error? :eofthrow eof-value)} stream)))) + +(defn read-all + "Eagerly read all forms from the ``stream``\\. If no stream is specified, uses the + value currently bound to :lpy:var:`*in*`. + + Callers may bind a map of readers to :lpy:var:`*data-readers*` to customize + the data readers used reading this string + + The stream must satisfy the interface of :external:py:class:`io.TextIOBase`\\, but + does not require any pushback capabilities. The default + ``basilisp.lang.reader.StreamReader`` can wrap any object implementing ``TextIOBase`` + and provide pushback capabilities." + ([stream] + (read-all nil stream)) + ([opts stream] + (let [eof (python/object)] + (->> (read-iterator (assoc opts :eof eof) stream) + seq + (take-while #(not (identical? % eof))) + doall)))) (defn eval "Evaluate a form (not a string) and return its result. @@ -4517,9 +4524,7 @@ (python/str)) ctx (basilisp.lang.compiler.CompilerContext. (or src ""))] (last - (for [form (seq (basilisp.lang.reader/read reader - *resolver* - *data-readers*))] + (for [form (seq (read-all reader))] (basilisp.lang.compiler/compile-and-exec-form form ctx *ns*)))))