sci: support for dynamic vars

This commit is contained in:
Michiel Borkent 2019-12-07 11:48:57 +01:00 committed by GitHub
parent a227b71d2a
commit 02d9a315cb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 77 additions and 112 deletions

View file

@ -25,7 +25,9 @@
},
{
"name":"java.io.BufferedReader",
"allPublicMethods":true
"allPublicMethods":true,
"allPublicFields": true,
"allPublicConstructors": true
},
{
"name": "java.lang.Class",

View file

@ -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})

View file

@ -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

View 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)))

View file

@ -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)]

View file

@ -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\"))"))))
)

View file

@ -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)