annotate src/clojure/contrib/command_line.clj @ 10:ef7dbbd6452c

added clojure source goodness
author Robert McIntyre <rlm@mit.edu>
date Sat, 21 Aug 2010 06:25:44 -0400
parents
children
rev   line source
rlm@10 1 ; Copyright (c) Chris Houser, Nov-Dec 2008. All rights reserved.
rlm@10 2 ; The use and distribution terms for this software are covered by the
rlm@10 3 ; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
rlm@10 4 ; which can be found in the file epl-v10.html at the root of this distribution.
rlm@10 5 ; By using this software in any fashion, you are agreeing to be bound by
rlm@10 6 ; the terms of this license.
rlm@10 7 ; You must not remove this notice, or any other, from this software.
rlm@10 8
rlm@10 9 ; Process command-line arguments according to a given cmdspec
rlm@10 10
rlm@10 11 (ns
rlm@10 12 ^{:author "Chris Houser",
rlm@10 13 :doc "Process command-line arguments according to a given cmdspec"}
rlm@10 14 clojure.contrib.command-line
rlm@10 15 (:use (clojure.contrib [string :only (join)])))
rlm@10 16
rlm@10 17 (defn make-map [args cmdspec]
rlm@10 18 (let [{spec true [rest-sym] false} (group-by vector? cmdspec)
rlm@10 19 rest-str (str rest-sym)
rlm@10 20 key-data (into {} (for [[syms [_ default]] (map #(split-with symbol? %)
rlm@10 21 (conj spec '[help? h?]))
rlm@10 22 sym syms]
rlm@10 23 [(re-find #"^.*[^?]" (str sym))
rlm@10 24 {:sym (str (first syms)) :default default}]))
rlm@10 25 defaults (into {} (for [[_ {:keys [default sym]}] key-data
rlm@10 26 :when default]
rlm@10 27 [sym default]))]
rlm@10 28 (loop [[argkey & [argval :as r]] args
rlm@10 29 cmdmap (assoc defaults :cmdspec cmdspec rest-str [])]
rlm@10 30 (if argkey
rlm@10 31 (let [[_ & [keybase]] (re-find #"^--?(.*)" argkey)]
rlm@10 32 (cond
rlm@10 33 (= keybase nil) (recur r (update-in cmdmap [rest-str] conj argkey))
rlm@10 34 (= keybase "") (update-in cmdmap [rest-str] #(apply conj % r))
rlm@10 35 :else (if-let [found (key-data keybase)]
rlm@10 36 (if (= \? (last (:sym found)))
rlm@10 37 (recur r (assoc cmdmap (:sym found) true))
rlm@10 38 (recur (next r) (assoc cmdmap (:sym found)
rlm@10 39 (if (or (nil? r) (= \- (ffirst r)))
rlm@10 40 (:default found)
rlm@10 41 (first r)))))
rlm@10 42 (throw (Exception. (str "Unknown option " argkey))))))
rlm@10 43 cmdmap))))
rlm@10 44
rlm@10 45 (defn- align
rlm@10 46 "Align strings given as vectors of columns, with first vector
rlm@10 47 specifying right or left alignment (:r or :l) for each column."
rlm@10 48 [spec & rows]
rlm@10 49 (let [maxes (vec (for [n (range (count (first rows)))]
rlm@10 50 (apply max (map (comp count #(nth % n)) rows))))
rlm@10 51 fmt (join " "
rlm@10 52 (for [n (range (count maxes))]
rlm@10 53 (str "%"
rlm@10 54 (when-not (zero? (maxes n))
rlm@10 55 (str (when (= (spec n) :l) "-") (maxes n)))
rlm@10 56 "s")))]
rlm@10 57 (join "\n"
rlm@10 58 (for [row rows]
rlm@10 59 (apply format fmt row)))))
rlm@10 60
rlm@10 61 (defn- rmv-q
rlm@10 62 "Remove ?"
rlm@10 63 [^String s]
rlm@10 64 (if (.endsWith s "?")
rlm@10 65 (.substring s 0 (dec (count s)))
rlm@10 66 s))
rlm@10 67
rlm@10 68 (defn print-help [desc cmdmap]
rlm@10 69 (println desc)
rlm@10 70 (println "Options")
rlm@10 71 (println
rlm@10 72 (apply align [:l :l :l]
rlm@10 73 (for [spec (:cmdspec cmdmap) :when (vector? spec)]
rlm@10 74 (let [[argnames [text default]] (split-with symbol? spec)
rlm@10 75 [_ opt q] (re-find #"^(.*[^?])(\??)$"
rlm@10 76 (str (first argnames)))
rlm@10 77 argnames (map (comp rmv-q str) argnames)
rlm@10 78 argnames
rlm@10 79 (join ", "
rlm@10 80 (for [arg argnames]
rlm@10 81 (if (= 1 (count arg))
rlm@10 82 (str "-" arg)
rlm@10 83 (str "--" arg))))]
rlm@10 84 [(str " " argnames (when (= "" q) " <arg>") " ")
rlm@10 85 text
rlm@10 86 (if-not default
rlm@10 87 ""
rlm@10 88 (str " [default " default "]"))])))))
rlm@10 89
rlm@10 90 (defmacro with-command-line
rlm@10 91 "Bind locals to command-line args."
rlm@10 92 [args desc cmdspec & body]
rlm@10 93 (let [locals (vec (for [spec cmdspec]
rlm@10 94 (if (vector? spec)
rlm@10 95 (first spec)
rlm@10 96 spec)))]
rlm@10 97 `(let [{:strs ~locals :as cmdmap#} (make-map ~args '~cmdspec)]
rlm@10 98 (if (cmdmap# "help?")
rlm@10 99 (print-help ~desc cmdmap#)
rlm@10 100 (do ~@body)))))
rlm@10 101
rlm@10 102 (comment
rlm@10 103
rlm@10 104 ; example of usage:
rlm@10 105
rlm@10 106 (with-command-line *command-line-args*
rlm@10 107 "tojs -- Compile ClojureScript to JavaScript"
rlm@10 108 [[simple? s? "Runs some simple built-in tests"]
rlm@10 109 [serve "Starts a repl server on the given port" 8081]
rlm@10 110 [mkboot? "Generates a boot.js file"]
rlm@10 111 [verbose? v? "Includes extra fn names and comments in js"]
rlm@10 112 filenames]
rlm@10 113 (binding [*debug-fn-names* verbose? *debug-comments* verbose?]
rlm@10 114 (cond
rlm@10 115 simple? (simple-tests)
rlm@10 116 serve (start-server (Integer/parseInt serve))
rlm@10 117 mkboot? (mkboot)
rlm@10 118 :else (doseq [filename filenames]
rlm@10 119 (filetojs filename)))))
rlm@10 120
rlm@10 121 )