From 6e00b931a9df9c98f6441b3b11f946409c7c2cd8 Mon Sep 17 00:00:00 2001 From: Stuart Halloway Date: Tue, 12 Jul 2011 08:23:43 -0500 Subject: [PATCH] scopes spike --- src/clj/clojure/core.clj | 65 +++++++++++++++++++++++++++++++++++---------- src/clj/clojure/main.clj | 15 +++++----- 2 files changed, 58 insertions(+), 22 deletions(-) diff --git a/src/clj/clojure/core.clj b/src/clj/clojure/core.clj index 851df37..7941308 100644 --- a/src/clj/clojure/core.clj +++ b/src/clj/clojure/core.clj @@ -3351,26 +3351,61 @@ ([v start end] (. clojure.lang.RT (subvec v start end)))) +(def ^:dynamic *scope*) + +(defn ^:private cleanup-scope + "Explicitly close the resources bound to the current scope. + Not normally called from user code." + [] + (when-let [^Closeable c (peek @*scope*)] + (if (instance? java.io.Closeable c) + (.close ^java.io.Closeable c) + (c)) + (swap! *scope* pop) + (recur))) + +(defmacro with-scope + "Create a new scope and run the forms in body. In a finally + block, close any resources added to the scope." + {:added "1.3"} + [& body] + `(binding [*scope* (atom nil)] + (try + ~@body + (finally + (@#'cleanup-scope))))) + +(defn scope + "Fluent interface to add c to the current scope. + c can be a java.io.Closeable or an arbitrary fn + of one argument." + {:added "1.3"} + [c] + (swap! *scope* conj c) + c) + (defmacro with-open "bindings => [name init ...] - Evaluates body in a try expression with names bound to the values - of the inits, and a finally clause that calls (.close name) on each - name in reverse order." + Evaluates body with names bound to the values of the inits. + Values should be instances of java.io.Closeable or + zero-arg fns. Each value is added to the current scope. + Calls cleanup-scope from a finally block protecting body." {:added "1.0"} [bindings & body] - (assert-args with-open - (vector? bindings) "a vector for its binding" - (even? (count bindings)) "an even number of forms in binding vector") - (cond - (= (count bindings) 0) `(do ~@body) - (symbol? (bindings 0)) `(let ~(subvec bindings 0 2) - (try - (with-open ~(subvec bindings 2) ~@body) - (finally - (. ~(bindings 0) close)))) - :else (throw (IllegalArgumentException. - "with-open only allows Symbols in bindings")))) + (let [helper (fn helper [bindings body] + (cond + (= (count bindings) 0) `(do ~@body) + (symbol? (bindings 0)) `(let ~(subvec bindings 0 2) + (do + (swap! *scope* conj ~(bindings 0)) + ~(helper (subvec bindings 2) body)))))] + (cond + (= (count bindings) 0) `(do ~@body) + (symbol? (bindings 0)) `(with-scope + ~(helper bindings body)) + :else (throw (IllegalArgumentException. + "scoped only allows symbols in bindings"))))) (defmacro doto "Evaluates x then calls all of the methods and functions with the diff --git a/src/clj/clojure/main.clj b/src/clj/clojure/main.clj index 5598daf..1b34e81 100644 --- a/src/clj/clojure/main.clj +++ b/src/clj/clojure/main.clj @@ -239,13 +239,14 @@ read-eval-print (fn [] (try - (let [input (read request-prompt request-exit)] - (or (#{request-prompt request-exit} input) - (let [value (eval input)] - (print value) - (set! *3 *2) - (set! *2 *1) - (set! *1 value)))) + (with-scope + (let [input (read request-prompt request-exit)] + (or (#{request-prompt request-exit} input) + (let [value (eval input)] + (print value) + (set! *3 *2) + (set! *2 *1) + (set! *1 value))))) (catch Throwable e (caught e) (set! *e e))))] -- 1.7.3.5