Mercurial > lasercutter
view src/clojure/main.clj @ 10:ef7dbbd6452c
added clojure source goodness
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Sat, 21 Aug 2010 06:25:44 -0400 |
parents | |
children |
line wrap: on
line source
1 ;; Copyright (c) Rich Hickey All rights reserved. The use and2 ;; distribution terms for this software are covered by the Eclipse Public3 ;; License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) which can be found4 ;; in the file epl-v10.html at the root of this distribution. By using this5 ;; software in any fashion, you are agreeing to be bound by the terms of6 ;; this license. You must not remove this notice, or any other, from this7 ;; software.9 ;; Originally contributed by Stephen C. Gilardi11 (ns ^{:doc "Top-level main function for Clojure REPL and scripts."12 :author "Stephen C. Gilardi and Rich Hickey"}13 clojure.main14 (:refer-clojure :exclude [with-bindings])15 (:import (clojure.lang Compiler Compiler$CompilerException16 LineNumberingPushbackReader RT)))18 (declare main)20 (defmacro with-bindings21 "Executes body in the context of thread-local bindings for several vars22 that often need to be set!: *ns* *warn-on-reflection* *math-context*23 *print-meta* *print-length* *print-level* *compile-path*24 *command-line-args* *1 *2 *3 *e"25 [& body]26 `(binding [*ns* *ns*27 *warn-on-reflection* *warn-on-reflection*28 *math-context* *math-context*29 *print-meta* *print-meta*30 *print-length* *print-length*31 *print-level* *print-level*32 *compile-path* (System/getProperty "clojure.compile.path" "classes")33 *command-line-args* *command-line-args*34 *assert* *assert*35 *1 nil36 *2 nil37 *3 nil38 *e nil]39 ~@body))41 (defn repl-prompt42 "Default :prompt hook for repl"43 []44 (printf "%s=> " (ns-name *ns*)))46 (defn skip-if-eol47 "If the next character on stream s is a newline, skips it, otherwise48 leaves the stream untouched. Returns :line-start, :stream-end, or :body49 to indicate the relative location of the next character on s. The stream50 must either be an instance of LineNumberingPushbackReader or duplicate51 its behavior of both supporting .unread and collapsing all of CR, LF, and52 CRLF to a single \\newline."53 [s]54 (let [c (.read s)]55 (cond56 (= c (int \newline)) :line-start57 (= c -1) :stream-end58 :else (do (.unread s c) :body))))60 (defn skip-whitespace61 "Skips whitespace characters on stream s. Returns :line-start, :stream-end,62 or :body to indicate the relative location of the next character on s.63 Interprets comma as whitespace and semicolon as comment to end of line.64 Does not interpret #! as comment to end of line because only one65 character of lookahead is available. The stream must either be an66 instance of LineNumberingPushbackReader or duplicate its behavior of both67 supporting .unread and collapsing all of CR, LF, and CRLF to a single68 \\newline."69 [s]70 (loop [c (.read s)]71 (cond72 (= c (int \newline)) :line-start73 (= c -1) :stream-end74 (= c (int \;)) (do (.readLine s) :line-start)75 (or (Character/isWhitespace c) (= c (int \,))) (recur (.read s))76 :else (do (.unread s c) :body))))78 (defn repl-read79 "Default :read hook for repl. Reads from *in* which must either be an80 instance of LineNumberingPushbackReader or duplicate its behavior of both81 supporting .unread and collapsing all of CR, LF, and CRLF into a single82 \\newline. repl-read:83 - skips whitespace, then84 - returns request-prompt on start of line, or85 - returns request-exit on end of stream, or86 - reads an object from the input stream, then87 - skips the next input character if it's end of line, then88 - returns the object."89 [request-prompt request-exit]90 (or ({:line-start request-prompt :stream-end request-exit}91 (skip-whitespace *in*))92 (let [input (read)]93 (skip-if-eol *in*)94 input)))96 (defn- root-cause97 "Returns the initial cause of an exception or error by peeling off all of98 its wrappers"99 [^Throwable throwable]100 (loop [cause throwable]101 (if-let [cause (.getCause cause)]102 (recur cause)103 cause)))105 (defn repl-exception106 "Returns CompilerExceptions in tact, but only the root cause of other107 throwables"108 [throwable]109 (if (instance? Compiler$CompilerException throwable)110 throwable111 (root-cause throwable)))113 (defn repl-caught114 "Default :caught hook for repl"115 [e]116 (.println *err* (repl-exception e)))118 (defn repl119 "Generic, reusable, read-eval-print loop. By default, reads from *in*,120 writes to *out*, and prints exception summaries to *err*. If you use the121 default :read hook, *in* must either be an instance of122 LineNumberingPushbackReader or duplicate its behavior of both supporting123 .unread and collapsing CR, LF, and CRLF into a single \\newline. Options124 are sequential keyword-value pairs. Available options and their defaults:126 - :init, function of no arguments, initialization hook called with127 bindings for set!-able vars in place.128 default: #()130 - :need-prompt, function of no arguments, called before each131 read-eval-print except the first, the user will be prompted if it132 returns true.133 default: (if (instance? LineNumberingPushbackReader *in*)134 #(.atLineStart *in*)135 #(identity true))137 - :prompt, function of no arguments, prompts for more input.138 default: repl-prompt140 - :flush, function of no arguments, flushes output141 default: flush143 - :read, function of two arguments, reads from *in*:144 - returns its first argument to request a fresh prompt145 - depending on need-prompt, this may cause the repl to prompt146 before reading again147 - returns its second argument to request an exit from the repl148 - else returns the next object read from the input stream149 default: repl-read151 - :eval, funtion of one argument, returns the evaluation of its152 argument153 default: eval155 - :print, function of one argument, prints its argument to the output156 default: prn158 - :caught, function of one argument, a throwable, called when159 read, eval, or print throws an exception or error160 default: repl-caught"161 [& options]162 (let [cl (.getContextClassLoader (Thread/currentThread))]163 (.setContextClassLoader (Thread/currentThread) (clojure.lang.DynamicClassLoader. cl)))164 (let [{:keys [init need-prompt prompt flush read eval print caught]165 :or {init #()166 need-prompt (if (instance? LineNumberingPushbackReader *in*)167 #(.atLineStart ^LineNumberingPushbackReader *in*)168 #(identity true))169 prompt repl-prompt170 flush flush171 read repl-read172 eval eval173 print prn174 caught repl-caught}}175 (apply hash-map options)176 request-prompt (Object.)177 request-exit (Object.)178 read-eval-print179 (fn []180 (try181 (let [input (read request-prompt request-exit)]182 (or (#{request-prompt request-exit} input)183 (let [value (eval input)]184 (print value)185 (set! *3 *2)186 (set! *2 *1)187 (set! *1 value))))188 (catch Throwable e189 (caught e)190 (set! *e e))))]191 (with-bindings192 (try193 (init)194 (catch Throwable e195 (caught e)196 (set! *e e)))197 (use '[clojure.repl :only (source apropos dir)])198 (use '[clojure.java.javadoc :only (javadoc)])199 (use '[clojure.pprint :only (pp pprint)])200 (prompt)201 (flush)202 (loop []203 (when-not204 (try (= (read-eval-print) request-exit)205 (catch Throwable e206 (caught e)207 (set! *e e)208 nil))209 (when (need-prompt)210 (prompt)211 (flush))212 (recur))))))214 (defn load-script215 "Loads Clojure source from a file or resource given its path. Paths216 beginning with @ or @/ are considered relative to classpath."217 [^String path]218 (if (.startsWith path "@")219 (RT/loadResourceScript220 (.substring path (if (.startsWith path "@/") 2 1)))221 (Compiler/loadFile path)))223 (defn- init-opt224 "Load a script"225 [path]226 (load-script path))228 (defn- eval-opt229 "Evals expressions in str, prints each non-nil result using prn"230 [str]231 (let [eof (Object.)232 reader (LineNumberingPushbackReader. (java.io.StringReader. str))]233 (loop [input (read reader false eof)]234 (when-not (= input eof)235 (let [value (eval input)]236 (when-not (nil? value)237 (prn value))238 (recur (read reader false eof)))))))240 (defn- init-dispatch241 "Returns the handler associated with an init opt"242 [opt]243 ({"-i" init-opt244 "--init" init-opt245 "-e" eval-opt246 "--eval" eval-opt} opt))248 (defn- initialize249 "Common initialize routine for repl, script, and null opts"250 [args inits]251 (in-ns 'user)252 (set! *command-line-args* args)253 (doseq [[opt arg] inits]254 ((init-dispatch opt) arg)))256 (defn- repl-opt257 "Start a repl with args and inits. Print greeting if no eval options were258 present"259 [[_ & args] inits]260 (when-not (some #(= eval-opt (init-dispatch (first %))) inits)261 (println "Clojure" (clojure-version)))262 (repl :init #(initialize args inits))263 (prn)264 (System/exit 0))266 (defn- script-opt267 "Run a script from a file, resource, or standard in with args and inits"268 [[path & args] inits]269 (with-bindings270 (initialize args inits)271 (if (= path "-")272 (load-reader *in*)273 (load-script path))))275 (defn- null-opt276 "No repl or script opt present, just bind args and run inits"277 [args inits]278 (with-bindings279 (initialize args inits)))281 (defn- help-opt282 "Print help text for main"283 [_ _]284 (println (:doc (meta (var main)))))286 (defn- main-dispatch287 "Returns the handler associated with a main option"288 [opt]289 (or290 ({"-r" repl-opt291 "--repl" repl-opt292 nil null-opt293 "-h" help-opt294 "--help" help-opt295 "-?" help-opt} opt)296 script-opt))298 (defn- legacy-repl299 "Called by the clojure.lang.Repl.main stub to run a repl with args300 specified the old way"301 [args]302 (println "WARNING: clojure.lang.Repl is deprecated.303 Instead, use clojure.main like this:304 java -cp clojure.jar clojure.main -i init.clj -r args...")305 (let [[inits [sep & args]] (split-with (complement #{"--"}) args)]306 (repl-opt (concat ["-r"] args) (map vector (repeat "-i") inits))))308 (defn- legacy-script309 "Called by the clojure.lang.Script.main stub to run a script with args310 specified the old way"311 [args]312 (println "WARNING: clojure.lang.Script is deprecated.313 Instead, use clojure.main like this:314 java -cp clojure.jar clojure.main -i init.clj script.clj args...")315 (let [[inits [sep & args]] (split-with (complement #{"--"}) args)]316 (null-opt args (map vector (repeat "-i") inits))))318 (defn main319 "Usage: java -cp clojure.jar clojure.main [init-opt*] [main-opt] [arg*]321 With no options or args, runs an interactive Read-Eval-Print Loop323 init options:324 -i, --init path Load a file or resource325 -e, --eval string Evaluate expressions in string; print non-nil values327 main options:328 -r, --repl Run a repl329 path Run a script from from a file or resource330 - Run a script from standard input331 -h, -?, --help Print this help message and exit333 operation:335 - Establishes thread-local bindings for commonly set!-able vars336 - Enters the user namespace337 - Binds *command-line-args* to a seq of strings containing command line338 args that appear after any main option339 - Runs all init options in order340 - Runs a repl or script if requested342 The init options may be repeated and mixed freely, but must appear before343 any main option. The appearance of any eval option before running a repl344 suppresses the usual repl greeting message: \"Clojure ~(clojure-version)\".346 Paths may be absolute or relative in the filesystem or relative to347 classpath. Classpath-relative paths have prefix of @ or @/"348 [& args]349 (try350 (if args351 (loop [[opt arg & more :as args] args inits []]352 (if (init-dispatch opt)353 (recur more (conj inits [opt arg]))354 ((main-dispatch opt) args inits)))355 (repl-opt nil nil))356 (finally357 (flush))))