Mercurial > lasercutter
view src/clojure/contrib/repl_ln.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) Stephen C. Gilardi. 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 can4 ;; be found in the file epl-v10.html at the root of this distribution. By5 ;; using this software in any fashion, you are agreeing to be bound by the6 ;; terms of this license. You must not remove this notice, or any other,7 ;; from this software.8 ;;9 ;; A repl with that provides support for lines and line numbers in the10 ;; input stream.11 ;;12 ;; scgilardi (gmail)13 ;; Created 28 November 200815 (ns16 ^{:author "Stephen C. Gilardi",17 :doc "A repl with that provides support for lines and line numbers in the18 input stream."}19 clojure.contrib.repl-ln20 (:gen-class)21 (:import (clojure.lang Compiler LineNumberingPushbackReader RT Var)22 (java.io InputStreamReader OutputStreamWriter PrintWriter)23 java.util.Date)24 (:require clojure.main)25 (:use [clojure.contrib.def26 :only (defmacro- defonce- defstruct- defvar-)]))28 ;; Private30 (declare repl)32 (defstruct- repl-info33 :name :started :name-fmt :prompt-fmt :serial :thread :depth)35 (defvar- +name-formats+36 {"%S" "%1$d" "%T" "%2$d" "%D" "%3$d"}37 "For set-name, maps our dynamic value codes to arg positions in38 the call to format in repl-name")40 (defvar- +prompt-formats+41 {"%S" "%1$d" "%T" "%2$d" "%D" "%3$d" "%L" "%4$d" "%N" "%5$s"}42 "For set-prompt, maps our dynamic value codes to arg positions in43 the call to format in repl-prompt")45 (defvar- +info-format+46 ["Name: %s"47 "Started: %s"48 "Name-fmt: \"%s\""49 "Prompt-fmt: \"%s\""50 "Serial: %d"51 "Thread: %d"52 "Depth: %d"53 "Line: %d"])55 (defvar- +info-defaults+56 (struct-map repl-info57 :name-fmt "repl-%S"58 :prompt-fmt "%S:%L %N=> "59 :depth 0)60 "Default/root values for repl info")62 (defonce- *serial-number* (atom 0)63 "Serial number counter")65 (defonce- *info* +info-defaults+66 "Public info for this repl")68 (defonce- *private* {}69 "Private info for this repl")71 (defmacro- update72 "Replaces the map thread-locally bound to map-var with a copy that73 includes updated and/or new values from keys and vals."74 [map-var & key-vals]75 `(set! ~map-var (assoc ~map-var ~@key-vals)))77 (defn- repl-name78 "Returns the repl name based on this repl's name-fmt"79 []80 (let [{:keys [name-fmt]} *private*81 {:keys [serial thread depth]} *info*]82 (format name-fmt serial thread depth)))84 (defn- prompt-hook85 []86 (let [prompt (*private* :prompt)]87 (var-set Compiler/LINE (.getLineNumber *in*))88 (prompt)))90 (defn- process-inits91 "Processes initial pairs of args of the form:93 -i filepath, or94 --init filepath96 by loading the referenced files, then accepts an optional terminating arg97 of the form:99 -r, or100 --repl102 Returns a seq of any remaining args."103 [args]104 (loop [[init filename & more :as args] args]105 (if (#{"-i" "--init"} init)106 (do107 (clojure.main/load-script filename)108 (recur more))109 (if (#{"-r" "--repl"} init)110 (rest args)111 args))))113 (defn- process-command-line114 "Args are strings passed in from the command line. Loads any requested115 init files and binds *command-line-args* to a seq of the remaining args"116 [args]117 (set! *command-line-args* (process-inits args)))119 (defn stream-repl120 "Repl entry point that provides convenient overriding of input, output,121 and err streams via sequential keyword-value pairs. Default values122 for :in, :out, and :err are streams associated with System/in,123 System/out, and System/err using UTF-8 encoding. Also supports all the124 options provided by clojure.contrib.repl-ln/repl."125 [& options]126 (let [enc RT/UTF8127 {:keys [in out err]128 :or {in (LineNumberingPushbackReader.129 (InputStreamReader. System/in enc))130 out (OutputStreamWriter. System/out enc)131 err (PrintWriter. (OutputStreamWriter. System/err enc))}}132 (apply hash-map options)]133 (binding [*in* in *out* out *err* err]134 (apply repl options))))136 (defn- -main137 "Main entry point, starts a repl enters the user namespace and processes138 command line args."139 [& args]140 (repl :init141 (fn []142 (println "Clojure" (clojure-version))143 (in-ns 'user)144 (process-command-line args))))146 ;; Public148 (defn repl-prompt149 "Returns the current repl prompt based on this repl's prompt-fmt"150 []151 (let [{:keys [prompt-fmt]} *private*152 {:keys [serial thread depth]} *info*153 line (.getLineNumber *in*)154 namespace (ns-name *ns*)]155 (format prompt-fmt serial thread depth line namespace)))157 (defn set-repl-name158 "Sets the repl name format to the string name-fmt. Include the following159 codes in the name to make the corresponding dynamic values part of it:161 %S - repl serial number162 %T - thread id163 %D - nesting depth in this thread165 With no arguments, resets the repl name to its default: \"repl-%S\""166 ([]167 (set-repl-name (+info-defaults+ :name-fmt)))168 ([name-fmt]169 (update *info* :name-fmt name-fmt)170 (loop [[[code fmt] & more] (seq +name-formats+)171 name-fmt name-fmt]172 (if code173 (recur more (.replace name-fmt code fmt))174 (update *private* :name-fmt name-fmt)))175 (let [name (repl-name)]176 (update *info* :name name)177 (var-set Compiler/SOURCE name))178 nil))180 (defn set-repl-prompt181 "Sets the repl prompt. Include the following codes in the prompt to make182 the corresponding dynamic values part of it:184 %S - repl serial number185 %T - thread id186 %D - nesting depth in this thread187 %L - input line number188 %N - namespace name190 With no arguments, resets the repl pompt to its default: \"%S:%L %N=> \""191 ([]192 (set-repl-prompt (+info-defaults+ :prompt-fmt)))193 ([prompt-fmt]194 (update *info* :prompt-fmt prompt-fmt)195 (loop [[[code fmt] & more] (seq +prompt-formats+)196 prompt-fmt prompt-fmt]197 (if code198 (recur more (.replace prompt-fmt code fmt))199 (update *private* :prompt-fmt prompt-fmt)))200 nil))202 (defn repl-info203 "Returns a map of info about the current repl"204 []205 (let [line (.getLineNumber *in*)]206 (assoc *info* :line line)))208 (defn print-repl-info209 "Prints info about the current repl"210 []211 (let [{:keys [name started name-fmt prompt-fmt serial thread depth line]}212 (repl-info)]213 (printf214 (apply str (interleave +info-format+ (repeat "\n")))215 name started name-fmt prompt-fmt serial thread depth line)))217 (defn repl218 "A repl that supports line numbers. For definitions and evaluations made219 at the repl, the repl-name and line number will be reported as the220 origin. Use set-repl-name and set-repl-prompt to customize the repl name221 and prompt. This repl supports all of the keyword arguments documented222 for clojure.main/repl with the following change and additions:224 - :prompt has a new default225 default: #(clojure.core/print (repl-prompt))227 - :name-fmt, Name format string228 default: the name-fmt of the parent repl, or \"repl-%S\"230 - :prompt-fmt, Prompt format string231 default: the prompt-fmt of the parent repl, or \"%S:%L %N=> \""232 [& options]233 (let [{:keys [init need-prompt prompt flush read eval print caught234 name-fmt prompt-fmt]235 :or {init #()236 need-prompt (if (instance? LineNumberingPushbackReader *in*)237 #(.atLineStart *in*)238 #(identity true))239 prompt #(clojure.core/print (repl-prompt))240 flush flush241 read clojure.main/repl-read242 eval eval243 print prn244 caught clojure.main/repl-caught245 name-fmt (*info* :name-fmt)246 prompt-fmt (*info* :prompt-fmt)}}247 (apply hash-map options)]248 (try249 (Var/pushThreadBindings250 {Compiler/SOURCE (var-get Compiler/SOURCE)251 Compiler/LINE (var-get Compiler/LINE)252 (var *info*) *info*253 (var *private*) {}})254 (update *info*255 :started (Date.)256 :serial (swap! *serial-number* inc)257 :thread (.getId (Thread/currentThread))258 :depth (inc (*info* :depth)))259 (update *private*260 :prompt prompt)261 (set-repl-name name-fmt)262 (set-repl-prompt prompt-fmt)263 (clojure.main/repl264 :init init265 :need-prompt need-prompt266 :prompt prompt-hook267 :flush flush268 :read read269 :eval eval270 :print print271 :caught caught)272 (finally273 (Var/popThreadBindings)274 (prn)))))