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",
|
||||
"allPublicMethods":true
|
||||
"allPublicMethods":true,
|
||||
"allPublicFields": true,
|
||||
"allPublicConstructors": true
|
||||
},
|
||||
{
|
||||
"name": "java.lang.Class",
|
||||
|
|
|
|||
|
|
@ -2,65 +2,8 @@
|
|||
{:no-doc true}
|
||||
(: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
|
||||
{'*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})
|
||||
'future-cancel future-cancel
|
||||
'future-cancelled? future-cancelled?
|
||||
'future-done? future-done?
|
||||
'future? future?
|
||||
{'file-seq file-seq
|
||||
'agent agent
|
||||
'send send
|
||||
'send-off send-off
|
||||
|
|
@ -68,18 +11,4 @@
|
|||
'deliver deliver
|
||||
'shutdown-agents shutdown-agents
|
||||
'slurp slurp
|
||||
'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})})
|
||||
'spit spit})
|
||||
|
|
|
|||
|
|
@ -6,7 +6,9 @@
|
|||
[clojure.java.io :as io]
|
||||
[clojure.string :as str]
|
||||
[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]))
|
||||
|
||||
(set! *warn-on-reflection* true)
|
||||
|
|
@ -32,17 +34,20 @@
|
|||
v))
|
||||
request-exit))
|
||||
:eval (fn [expr]
|
||||
(let [ret (eval-form (update sci-ctx
|
||||
:env
|
||||
(fn [env]
|
||||
(swap! env update-in [:namespaces 'clojure.core]
|
||||
assoc
|
||||
'*1 *1
|
||||
'*2 *2
|
||||
'*3 *3
|
||||
'*e *e)
|
||||
env))
|
||||
expr)]
|
||||
(let [ret (sci/with-bindings {sci/in *in*
|
||||
sci/out *out*
|
||||
sci/err *err*}
|
||||
(eval-form (update sci-ctx
|
||||
:env
|
||||
(fn [env]
|
||||
(swap! env update-in [:namespaces 'clojure.core]
|
||||
assoc
|
||||
'*1 *1
|
||||
'*2 *2
|
||||
'*3 *3
|
||||
'*e *e)
|
||||
env))
|
||||
expr))]
|
||||
ret))
|
||||
:need-prompt (fn [] true)
|
||||
:prompt #(printf "%s=> " (-> sci-ctx :env deref :current-ns)))))
|
||||
|
|
@ -54,7 +59,7 @@
|
|||
[(first parts) (Integer. ^String (second parts))])
|
||||
host+port (if-not host (str "localhost:" port)
|
||||
host+port)
|
||||
sci-ctx (opts->ctx sci-opts)
|
||||
sci-ctx (init sci-opts)
|
||||
socket (server/start-server
|
||||
{:address host
|
||||
: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.socket-repl :as socket-repl]
|
||||
[babashka.impl.tools.cli :refer [tools-cli-namespace]]
|
||||
[babashka.impl.utils :refer [eval-string]]
|
||||
[babashka.wait :as wait]
|
||||
[clojure.edn :as edn]
|
||||
[clojure.java.io :as io]
|
||||
[clojure.java.shell :as shell]
|
||||
[clojure.string :as str]
|
||||
[sci.core :as sci])
|
||||
[sci.addons :as addons])
|
||||
(:gen-class))
|
||||
|
||||
(set! *warn-on-reflection* true)
|
||||
|
|
@ -146,10 +147,10 @@ Everything after that is bound to *command-line-args*."))
|
|||
|
||||
(defn load-file* [ctx file]
|
||||
(let [s (slurp file)]
|
||||
(sci/eval-string s ctx)))
|
||||
(eval-string s ctx)))
|
||||
|
||||
(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]
|
||||
(let [ctx (update ctx :bindings assoc
|
||||
|
|
@ -163,6 +164,10 @@ Everything after that is bound to *command-line-args*."))
|
|||
(defn exit [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
|
||||
[& args]
|
||||
(handle-pipe!)
|
||||
|
|
@ -215,6 +220,7 @@ Everything after that is bound to *command-line-args*."))
|
|||
'java.lang.AssertionError AssertionError
|
||||
'java.lang.Boolean Boolean
|
||||
'java.io.BufferedWriter java.io.BufferedWriter
|
||||
'java.io.BufferedReader java.io.BufferedReader
|
||||
'java.lang.Class Class
|
||||
'java.lang.Double Double
|
||||
'java.lang.Exception Exception
|
||||
|
|
@ -247,8 +253,9 @@ Everything after that is bound to *command-line-args*."))
|
|||
System java.lang.System
|
||||
Thread java.lang.Thread}}
|
||||
ctx (update ctx :bindings assoc 'eval #(eval* ctx %)
|
||||
'load-file #(load-file* ctx %))
|
||||
_preloads (some-> (System/getenv "BABASHKA_PRELOADS") (str/trim) (sci/eval-string ctx))
|
||||
'load-file #(load-file* ctx %))
|
||||
ctx (addons/future ctx)
|
||||
_preloads (some-> (System/getenv "BABASHKA_PRELOADS") (str/trim) (eval-string ctx))
|
||||
exit-code
|
||||
(or
|
||||
#_(binding [*out* *err*]
|
||||
|
|
@ -269,7 +276,7 @@ Everything after that is bound to *command-line-args*."))
|
|||
{:sci/deref! true})) in)]
|
||||
(if (identical? ::EOF in)
|
||||
[nil 0] ;; done streaming
|
||||
(let [res [(let [res (sci/eval-string expr ctx)]
|
||||
(let [res [(let [res (eval-string expr ctx)]
|
||||
(when (some? res)
|
||||
(if-let [pr-f (cond shell-out println
|
||||
edn-out prn)]
|
||||
|
|
|
|||
|
|
@ -6,10 +6,11 @@
|
|||
[clojure.java.shell :refer [sh]]
|
||||
[clojure.string :as str]
|
||||
[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]
|
||||
(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
|
||||
(is (= {:expression "(println 123)"}
|
||||
|
|
@ -82,6 +83,9 @@
|
|||
"(map-indexed #(-> [%1 %2]) *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
|
||||
(testing "bb doesn't wait for input if *in* isn't used"
|
||||
(is (= "2\n" (with-out-str (main/main "(inc 1)"))))))
|
||||
|
|
@ -93,8 +97,10 @@
|
|||
(is (not-empty s))))
|
||||
(let [out (java.io.StringWriter.)
|
||||
err (java.io.StringWriter.)
|
||||
exit-code (binding [*out* out *err* err]
|
||||
(main/main "--time" "(println \"Hello world!\") (System/exit 42)"))]
|
||||
exit-code (sci/with-bindings {sci/out out
|
||||
sci/err err}
|
||||
(binding [*out* out *err* err]
|
||||
(main/main "--time" "(println \"Hello world!\") (System/exit 42)")))]
|
||||
(is (= (str out) "Hello world!\n"))
|
||||
(is (re-find #"took" (str err)))
|
||||
(is (= 42 exit-code))))
|
||||
|
|
@ -110,12 +116,7 @@
|
|||
"Usage:"))))
|
||||
|
||||
(deftest ssl-test
|
||||
(let [graalvm-home (System/getenv "GRAALVM_HOME")
|
||||
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))]
|
||||
(let [resp (bb nil "(slurp \"https://www.google.com\")")]
|
||||
(is (re-find #"doctype html" resp))))
|
||||
|
||||
(deftest stream-test
|
||||
|
|
@ -288,3 +289,9 @@
|
|||
(java.nio.file.Files/copy p p' (into-array [java.nio.file.StandardCopyOption/REPLACE_EXISTING]))))))"
|
||||
temp-path))
|
||||
(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
|
||||
(:require
|
||||
[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)
|
||||
|
||||
(defn bb-jvm [input & args]
|
||||
(let [es (java.io.StringWriter.)
|
||||
os (java.io.StringWriter.)]
|
||||
(binding [*err* es
|
||||
*out* os]
|
||||
(let [res (if input
|
||||
(with-in-str input
|
||||
(apply main/main args))
|
||||
(apply main/main args))]
|
||||
(let [os (java.io.StringWriter.)
|
||||
es (java.io.StringWriter.)
|
||||
is (when input
|
||||
(java.io.StringReader. input))
|
||||
bindings-map (cond-> {sci/out os
|
||||
sci/err es}
|
||||
is (assoc sci/in is))]
|
||||
(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)
|
||||
(str os)
|
||||
(throw (ex-info (str es)
|
||||
|
|
|
|||
Loading…
Reference in a new issue