From cfb1f45e8b342857adab930b80831f567d0aa00d Mon Sep 17 00:00:00 2001 From: Michiel Borkent Date: Sat, 16 Nov 2019 23:11:42 +0100 Subject: [PATCH] [#112] Add binding, with-out-str and with-in-str --- reflection.json | 21 +++++++++++++++-- sci | 2 +- src/babashka/impl/clojure/core.clj | 38 ++++++++++++++++++++++++++++-- src/babashka/main.clj | 3 +++ test/babashka/main_test.clj | 12 ++++++++++ 5 files changed, 71 insertions(+), 5 deletions(-) diff --git a/reflection.json b/reflection.json index 1adb0a2a..c32b4653 100644 --- a/reflection.json +++ b/reflection.json @@ -64,6 +64,12 @@ "allPublicFields": true, "allPublicConstructors": true }, + { + "name":"clojure.lang.LineNumberingPushbackReader", + "allPublicMethods":true, + "allPublicFields": true, + "allPublicConstructors": true + }, { "name":"java.util.concurrent.LinkedBlockingQueue", "allPublicMethods":true @@ -87,8 +93,19 @@ "allPublicMethods":true, "allPublicFields": true, "allPublicConstructors": true - } - , + }, + { + "name":"java.io.StringReader", + "allPublicMethods":true, + "allPublicFields": true, + "allPublicConstructors": true + }, + { + "name":"java.io.StringWriter", + "allPublicMethods":true, + "allPublicFields": true, + "allPublicConstructors": true + }, { "name":"java.lang.System", "allPublicMethods":true, diff --git a/sci b/sci index ad83ca84..8e4b8ff5 160000 --- a/sci +++ b/sci @@ -1 +1 @@ -Subproject commit ad83ca84f8a9e6baf220b4757cde0f17b1558d6b +Subproject commit 8e4b8ff55b5afbd330a278d835fbb6fe0858df99 diff --git a/src/babashka/impl/clojure/core.clj b/src/babashka/impl/clojure/core.clj index 78e7973a..d9f79921 100644 --- a/src/babashka/impl/clojure/core.clj +++ b/src/babashka/impl/clojure/core.clj @@ -32,9 +32,39 @@ `(when-not ~x (throw (~'__assertion-error__ (str "Assert failed: " ~message "\n" (pr-str '~x))))))) +(defn binding* + "This macro only works with symbols that evaluate to vars themselves. See `*in*` and `*out*` below." + [_ _ bindings & body] + `(do + (let [] + (push-thread-bindings (hash-map ~@bindings)) + (try + ~@body + (finally + (pop-thread-bindings)))))) + +;; this works now! +"(def w (java.io.StringWriter.)) (push-thread-bindings {clojure.core/*out* w}) (try (println \"hello\") (finally (pop-thread-bindings))) (prn \">\" (str w))" + +;; this also works now! "(def w (java.io.StringWriter.)) (binding [clojure.core/*out* w] (println \"hello\")) (str w)" + +(defn with-out-str* + [_ _ & body] + `(let [s# (java.io.StringWriter.)] + (binding [*out* s#] + ~@body + (str s#)))) + +(defn with-in-str* + [_ _ s & body] + `(with-open [s# (-> (java.io.StringReader. ~s) clojure.lang.LineNumberingPushbackReader.)] + (binding [*in* s#] + ~@body))) + (def core-extras - {'*in* *in* - '*out* *out* + {'*in* #'*in* + '*out* #'*out* + 'binding (with-meta binding* {:sci/macro true}) 'file-seq file-seq 'future-call future-call 'future (with-meta future {:sci/macro true}) @@ -56,9 +86,13 @@ 'print print 'println println 'println-str println-str + 'pop-thread-bindings pop-thread-bindings + 'push-thread-bindings push-thread-bindings 'flush flush 'read-line read-line '__close!__ __close!__ 'with-open (with-meta with-open* {:sci/macro true}) + 'with-out-str (with-meta with-out-str* {:sci/macro true}) + 'with-in-str (with-meta with-in-str* {:sci/macro true}) '__assertion-error__ __assertion-error__ 'assert (with-meta assert* {:sci/macro true})}) diff --git a/src/babashka/main.clj b/src/babashka/main.clj index 5c160aae..a8550e71 100644 --- a/src/babashka/main.clj +++ b/src/babashka/main.clj @@ -223,8 +223,11 @@ Everything after that is bound to *command-line-args*.")) 'clojure.lang.ExceptionInfo clojure.lang.ExceptionInfo 'java.lang.Integer Integer 'java.io.File java.io.File + 'clojure.lang.LineNumberingPushbackReader clojure.lang.LineNumberingPushbackReader 'java.util.regex.Pattern java.util.regex.Pattern 'java.lang.String String + 'java.io.StringReader java.io.StringReader + 'java.io.StringWriter java.io.StringWriter 'java.lang.System System 'java.lang.Thread Thread} :imports '{ArithmeticException java.lang.ArithmeticException diff --git a/test/babashka/main_test.clj b/test/babashka/main_test.clj index a58674a8..0ec8a670 100644 --- a/test/babashka/main_test.clj +++ b/test/babashka/main_test.clj @@ -257,3 +257,15 @@ nil)" path)) (is (= "foobar\nbarfoo\n" (slurp path))))) + +(deftest binding-test + (is (= 6 (bb nil "(def w (java.io.StringWriter.)) + (binding [clojure.core/*out* w] + (println \"hello\")) + (count (str w))")))) + +(deftest with-out-str-test + (is (= 6 (bb nil "(count (with-out-str (println \"hello\")))")))) + +(deftest with-in-str-test + (is (= 5 (bb nil "(count (with-in-str \"hello\" (read-line)))"))))