sci: support for dynamic vars
This commit is contained in:
parent
a227b71d2a
commit
02d9a315cb
7 changed files with 77 additions and 112 deletions
|
|
@ -25,7 +25,9 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name":"java.io.BufferedReader",
|
"name":"java.io.BufferedReader",
|
||||||
"allPublicMethods":true
|
"allPublicMethods":true,
|
||||||
|
"allPublicFields": true,
|
||||||
|
"allPublicConstructors": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "java.lang.Class",
|
"name": "java.lang.Class",
|
||||||
|
|
|
||||||
|
|
@ -2,65 +2,8 @@
|
||||||
{:no-doc true}
|
{:no-doc true}
|
||||||
(:refer-clojure :exclude [future]))
|
(:refer-clojure :exclude [future]))
|
||||||
|
|
||||||
(defn future
|
|
||||||
[_ _ & body]
|
|
||||||
`(~'future-call (fn [] ~@body)))
|
|
||||||
|
|
||||||
(defn __close!__ [^java.io.Closeable x]
|
|
||||||
(.close x))
|
|
||||||
|
|
||||||
(defn with-open*
|
|
||||||
[_ _ bindings & body]
|
|
||||||
(cond
|
|
||||||
(= (count bindings) 0) `(do ~@body)
|
|
||||||
(symbol? (bindings 0)) `(let ~(subvec bindings 0 2)
|
|
||||||
(try
|
|
||||||
(with-open ~(subvec bindings 2) ~@body)
|
|
||||||
(finally
|
|
||||||
(~'__close!__ ~(bindings 0)))))
|
|
||||||
:else (throw (IllegalArgumentException.
|
|
||||||
"with-open only allows Symbols in bindings"))))
|
|
||||||
|
|
||||||
(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
|
(def core-extras
|
||||||
{'*in* #'*in*
|
{'file-seq file-seq
|
||||||
'*out* #'*out*
|
|
||||||
'binding (with-meta binding* {:sci/macro true})
|
|
||||||
'file-seq file-seq
|
|
||||||
'future-call future-call
|
|
||||||
'future (with-meta future {:sci/macro true})
|
|
||||||
'future-cancel future-cancel
|
|
||||||
'future-cancelled? future-cancelled?
|
|
||||||
'future-done? future-done?
|
|
||||||
'future? future?
|
|
||||||
'agent agent
|
'agent agent
|
||||||
'send send
|
'send send
|
||||||
'send-off send-off
|
'send-off send-off
|
||||||
|
|
@ -68,18 +11,4 @@
|
||||||
'deliver deliver
|
'deliver deliver
|
||||||
'shutdown-agents shutdown-agents
|
'shutdown-agents shutdown-agents
|
||||||
'slurp slurp
|
'slurp slurp
|
||||||
'spit spit
|
'spit spit})
|
||||||
'pmap pmap
|
|
||||||
'pr pr
|
|
||||||
'prn prn
|
|
||||||
'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})})
|
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,9 @@
|
||||||
[clojure.java.io :as io]
|
[clojure.java.io :as io]
|
||||||
[clojure.string :as str]
|
[clojure.string :as str]
|
||||||
[clojure.tools.reader.reader-types :as r]
|
[clojure.tools.reader.reader-types :as r]
|
||||||
[sci.impl.interpreter :refer [opts->ctx eval-form]]
|
[sci.core :as sci]
|
||||||
|
[sci.impl.interpreter :refer [eval-form]]
|
||||||
|
[sci.impl.opts :refer [init]]
|
||||||
[sci.impl.parser :as parser]))
|
[sci.impl.parser :as parser]))
|
||||||
|
|
||||||
(set! *warn-on-reflection* true)
|
(set! *warn-on-reflection* true)
|
||||||
|
|
@ -32,17 +34,20 @@
|
||||||
v))
|
v))
|
||||||
request-exit))
|
request-exit))
|
||||||
:eval (fn [expr]
|
:eval (fn [expr]
|
||||||
(let [ret (eval-form (update sci-ctx
|
(let [ret (sci/with-bindings {sci/in *in*
|
||||||
:env
|
sci/out *out*
|
||||||
(fn [env]
|
sci/err *err*}
|
||||||
(swap! env update-in [:namespaces 'clojure.core]
|
(eval-form (update sci-ctx
|
||||||
assoc
|
:env
|
||||||
'*1 *1
|
(fn [env]
|
||||||
'*2 *2
|
(swap! env update-in [:namespaces 'clojure.core]
|
||||||
'*3 *3
|
assoc
|
||||||
'*e *e)
|
'*1 *1
|
||||||
env))
|
'*2 *2
|
||||||
expr)]
|
'*3 *3
|
||||||
|
'*e *e)
|
||||||
|
env))
|
||||||
|
expr))]
|
||||||
ret))
|
ret))
|
||||||
:need-prompt (fn [] true)
|
:need-prompt (fn [] true)
|
||||||
:prompt #(printf "%s=> " (-> sci-ctx :env deref :current-ns)))))
|
:prompt #(printf "%s=> " (-> sci-ctx :env deref :current-ns)))))
|
||||||
|
|
@ -54,7 +59,7 @@
|
||||||
[(first parts) (Integer. ^String (second parts))])
|
[(first parts) (Integer. ^String (second parts))])
|
||||||
host+port (if-not host (str "localhost:" port)
|
host+port (if-not host (str "localhost:" port)
|
||||||
host+port)
|
host+port)
|
||||||
sci-ctx (opts->ctx sci-opts)
|
sci-ctx (init sci-opts)
|
||||||
socket (server/start-server
|
socket (server/start-server
|
||||||
{:address host
|
{:address host
|
||||||
:port port
|
:port port
|
||||||
|
|
|
||||||
9
src/babashka/impl/utils.clj
Normal file
9
src/babashka/impl/utils.clj
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
(ns babashka.impl.utils
|
||||||
|
{:no-doc true}
|
||||||
|
(:require [sci.core :as sci]))
|
||||||
|
|
||||||
|
(defn eval-string [expr ctx]
|
||||||
|
(sci/with-bindings {sci/out *out*
|
||||||
|
sci/in *in*
|
||||||
|
sci/err *err*}
|
||||||
|
(sci/eval-string expr ctx)))
|
||||||
|
|
@ -10,12 +10,13 @@
|
||||||
[babashka.impl.pipe-signal-handler :refer [handle-pipe! pipe-signal-received?]]
|
[babashka.impl.pipe-signal-handler :refer [handle-pipe! pipe-signal-received?]]
|
||||||
[babashka.impl.socket-repl :as socket-repl]
|
[babashka.impl.socket-repl :as socket-repl]
|
||||||
[babashka.impl.tools.cli :refer [tools-cli-namespace]]
|
[babashka.impl.tools.cli :refer [tools-cli-namespace]]
|
||||||
|
[babashka.impl.utils :refer [eval-string]]
|
||||||
[babashka.wait :as wait]
|
[babashka.wait :as wait]
|
||||||
[clojure.edn :as edn]
|
[clojure.edn :as edn]
|
||||||
[clojure.java.io :as io]
|
[clojure.java.io :as io]
|
||||||
[clojure.java.shell :as shell]
|
[clojure.java.shell :as shell]
|
||||||
[clojure.string :as str]
|
[clojure.string :as str]
|
||||||
[sci.core :as sci])
|
[sci.addons :as addons])
|
||||||
(:gen-class))
|
(:gen-class))
|
||||||
|
|
||||||
(set! *warn-on-reflection* true)
|
(set! *warn-on-reflection* true)
|
||||||
|
|
@ -146,10 +147,10 @@ Everything after that is bound to *command-line-args*."))
|
||||||
|
|
||||||
(defn load-file* [ctx file]
|
(defn load-file* [ctx file]
|
||||||
(let [s (slurp file)]
|
(let [s (slurp file)]
|
||||||
(sci/eval-string s ctx)))
|
(eval-string s ctx)))
|
||||||
|
|
||||||
(defn eval* [ctx form]
|
(defn eval* [ctx form]
|
||||||
(sci/eval-string (pr-str form) ctx))
|
(eval-string (pr-str form) ctx))
|
||||||
|
|
||||||
(defn start-socket-repl! [address ctx read-next]
|
(defn start-socket-repl! [address ctx read-next]
|
||||||
(let [ctx (update ctx :bindings assoc
|
(let [ctx (update ctx :bindings assoc
|
||||||
|
|
@ -163,6 +164,10 @@ Everything after that is bound to *command-line-args*."))
|
||||||
(defn exit [n]
|
(defn exit [n]
|
||||||
(throw (ex-info "" {:bb/exit-code n})))
|
(throw (ex-info "" {:bb/exit-code n})))
|
||||||
|
|
||||||
|
;; (sci/set-var-root! sci/*in* *in*)
|
||||||
|
;; (sci/set-var-root! sci/*out* *out*)
|
||||||
|
;; (sci/set-var-root! sci/*err* *err*)
|
||||||
|
|
||||||
(defn main
|
(defn main
|
||||||
[& args]
|
[& args]
|
||||||
(handle-pipe!)
|
(handle-pipe!)
|
||||||
|
|
@ -215,6 +220,7 @@ Everything after that is bound to *command-line-args*."))
|
||||||
'java.lang.AssertionError AssertionError
|
'java.lang.AssertionError AssertionError
|
||||||
'java.lang.Boolean Boolean
|
'java.lang.Boolean Boolean
|
||||||
'java.io.BufferedWriter java.io.BufferedWriter
|
'java.io.BufferedWriter java.io.BufferedWriter
|
||||||
|
'java.io.BufferedReader java.io.BufferedReader
|
||||||
'java.lang.Class Class
|
'java.lang.Class Class
|
||||||
'java.lang.Double Double
|
'java.lang.Double Double
|
||||||
'java.lang.Exception Exception
|
'java.lang.Exception Exception
|
||||||
|
|
@ -247,8 +253,9 @@ Everything after that is bound to *command-line-args*."))
|
||||||
System java.lang.System
|
System java.lang.System
|
||||||
Thread java.lang.Thread}}
|
Thread java.lang.Thread}}
|
||||||
ctx (update ctx :bindings assoc 'eval #(eval* ctx %)
|
ctx (update ctx :bindings assoc 'eval #(eval* ctx %)
|
||||||
'load-file #(load-file* ctx %))
|
'load-file #(load-file* ctx %))
|
||||||
_preloads (some-> (System/getenv "BABASHKA_PRELOADS") (str/trim) (sci/eval-string ctx))
|
ctx (addons/future ctx)
|
||||||
|
_preloads (some-> (System/getenv "BABASHKA_PRELOADS") (str/trim) (eval-string ctx))
|
||||||
exit-code
|
exit-code
|
||||||
(or
|
(or
|
||||||
#_(binding [*out* *err*]
|
#_(binding [*out* *err*]
|
||||||
|
|
@ -269,7 +276,7 @@ Everything after that is bound to *command-line-args*."))
|
||||||
{:sci/deref! true})) in)]
|
{:sci/deref! true})) in)]
|
||||||
(if (identical? ::EOF in)
|
(if (identical? ::EOF in)
|
||||||
[nil 0] ;; done streaming
|
[nil 0] ;; done streaming
|
||||||
(let [res [(let [res (sci/eval-string expr ctx)]
|
(let [res [(let [res (eval-string expr ctx)]
|
||||||
(when (some? res)
|
(when (some? res)
|
||||||
(if-let [pr-f (cond shell-out println
|
(if-let [pr-f (cond shell-out println
|
||||||
edn-out prn)]
|
edn-out prn)]
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,11 @@
|
||||||
[clojure.java.shell :refer [sh]]
|
[clojure.java.shell :refer [sh]]
|
||||||
[clojure.string :as str]
|
[clojure.string :as str]
|
||||||
[clojure.test :as test :refer [deftest is testing]]
|
[clojure.test :as test :refer [deftest is testing]]
|
||||||
[clojure.java.io :as io]))
|
[clojure.java.io :as io]
|
||||||
|
[sci.core :as sci]))
|
||||||
|
|
||||||
(defn bb [input & args]
|
(defn bb [input & args]
|
||||||
(edn/read-string (apply test-utils/bb (str input) (map str args))))
|
(edn/read-string (apply test-utils/bb (when (some? input) (str input)) (map str args))))
|
||||||
|
|
||||||
(deftest parse-opts-test
|
(deftest parse-opts-test
|
||||||
(is (= {:expression "(println 123)"}
|
(is (= {:expression "(println 123)"}
|
||||||
|
|
@ -82,6 +83,9 @@
|
||||||
"(map-indexed #(-> [%1 %2]) *in*)")
|
"(map-indexed #(-> [%1 %2]) *in*)")
|
||||||
(bb "(keep #(when (re-find #\"(?i)clojure\" (second %)) (first %)) *in*)"))))))
|
(bb "(keep #(when (re-find #\"(?i)clojure\" (second %)) (first %)) *in*)"))))))
|
||||||
|
|
||||||
|
(deftest println-test
|
||||||
|
(is (= "hello\n" (test-utils/bb nil "(println \"hello\")"))))
|
||||||
|
|
||||||
(deftest input-test
|
(deftest input-test
|
||||||
(testing "bb doesn't wait for input if *in* isn't used"
|
(testing "bb doesn't wait for input if *in* isn't used"
|
||||||
(is (= "2\n" (with-out-str (main/main "(inc 1)"))))))
|
(is (= "2\n" (with-out-str (main/main "(inc 1)"))))))
|
||||||
|
|
@ -93,8 +97,10 @@
|
||||||
(is (not-empty s))))
|
(is (not-empty s))))
|
||||||
(let [out (java.io.StringWriter.)
|
(let [out (java.io.StringWriter.)
|
||||||
err (java.io.StringWriter.)
|
err (java.io.StringWriter.)
|
||||||
exit-code (binding [*out* out *err* err]
|
exit-code (sci/with-bindings {sci/out out
|
||||||
(main/main "--time" "(println \"Hello world!\") (System/exit 42)"))]
|
sci/err err}
|
||||||
|
(binding [*out* out *err* err]
|
||||||
|
(main/main "--time" "(println \"Hello world!\") (System/exit 42)")))]
|
||||||
(is (= (str out) "Hello world!\n"))
|
(is (= (str out) "Hello world!\n"))
|
||||||
(is (re-find #"took" (str err)))
|
(is (re-find #"took" (str err)))
|
||||||
(is (= 42 exit-code))))
|
(is (= 42 exit-code))))
|
||||||
|
|
@ -110,12 +116,7 @@
|
||||||
"Usage:"))))
|
"Usage:"))))
|
||||||
|
|
||||||
(deftest ssl-test
|
(deftest ssl-test
|
||||||
(let [graalvm-home (System/getenv "GRAALVM_HOME")
|
(let [resp (bb nil "(slurp \"https://www.google.com\")")]
|
||||||
lib-path (format "%1$s/jre/lib:%1$s/jre/lib/amd64" graalvm-home)
|
|
||||||
;; _ (prn "lib-path" lib-path)
|
|
||||||
resp (bb nil (format "(System/setProperty \"java.library.path\" \"%s\")
|
|
||||||
(slurp \"https://www.google.com\")"
|
|
||||||
lib-path))]
|
|
||||||
(is (re-find #"doctype html" resp))))
|
(is (re-find #"doctype html" resp))))
|
||||||
|
|
||||||
(deftest stream-test
|
(deftest stream-test
|
||||||
|
|
@ -288,3 +289,9 @@
|
||||||
(java.nio.file.Files/copy p p' (into-array [java.nio.file.StandardCopyOption/REPLACE_EXISTING]))))))"
|
(java.nio.file.Files/copy p p' (into-array [java.nio.file.StandardCopyOption/REPLACE_EXISTING]))))))"
|
||||||
temp-path))
|
temp-path))
|
||||||
(is (.exists f2))))
|
(is (.exists f2))))
|
||||||
|
|
||||||
|
(deftest future-print-test
|
||||||
|
(testing "the root binding of sci/*out*"
|
||||||
|
(is (= "hello" (bb nil "@(future (prn \"hello\"))"))))
|
||||||
|
|
||||||
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,25 @@
|
||||||
(ns babashka.test-utils
|
(ns babashka.test-utils
|
||||||
(:require
|
(:require
|
||||||
[babashka.main :as main]
|
[babashka.main :as main]
|
||||||
[me.raynes.conch :refer [let-programs] :as sh]))
|
[me.raynes.conch :refer [let-programs] :as sh]
|
||||||
|
[sci.core :as sci]))
|
||||||
|
|
||||||
(set! *warn-on-reflection* true)
|
(set! *warn-on-reflection* true)
|
||||||
|
|
||||||
(defn bb-jvm [input & args]
|
(defn bb-jvm [input & args]
|
||||||
(let [es (java.io.StringWriter.)
|
(let [os (java.io.StringWriter.)
|
||||||
os (java.io.StringWriter.)]
|
es (java.io.StringWriter.)
|
||||||
(binding [*err* es
|
is (when input
|
||||||
*out* os]
|
(java.io.StringReader. input))
|
||||||
(let [res (if input
|
bindings-map (cond-> {sci/out os
|
||||||
(with-in-str input
|
sci/err es}
|
||||||
(apply main/main args))
|
is (assoc sci/in is))]
|
||||||
(apply main/main args))]
|
(sci/with-bindings bindings-map
|
||||||
|
(let [res (binding [*out* os
|
||||||
|
*err* es]
|
||||||
|
(if input
|
||||||
|
(with-in-str input (apply main/main args))
|
||||||
|
(apply main/main args)))]
|
||||||
(if (zero? res)
|
(if (zero? res)
|
||||||
(str os)
|
(str os)
|
||||||
(throw (ex-info (str es)
|
(throw (ex-info (str es)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue