Mercurial > lasercutter
diff src/clojure/contrib/strint.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 diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/clojure/contrib/strint.clj Sat Aug 21 06:25:44 2010 -0400 1.3 @@ -0,0 +1,72 @@ 1.4 +;;; strint.clj -- String interpolation for Clojure 1.5 +;; originally proposed/published at http://muckandbrass.com/web/x/AgBP 1.6 + 1.7 +;; by Chas Emerick <cemerick@snowtide.com> 1.8 +;; December 4, 2009 1.9 + 1.10 +;; Copyright (c) Chas Emerick, 2009. All rights reserved. The use 1.11 +;; and distribution terms for this software are covered by the Eclipse 1.12 +;; Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 1.13 +;; which can be found in the file epl-v10.html at the root of this 1.14 +;; distribution. By using this software in any fashion, you are 1.15 +;; agreeing to be bound by the terms of this license. You must not 1.16 +;; remove this notice, or any other, from this software. 1.17 + 1.18 +(ns 1.19 + ^{:author "Chas Emerick", 1.20 + :doc "String interpolation for Clojure."} 1.21 + clojure.contrib.strint) 1.22 + 1.23 +(defn- silent-read 1.24 + "Attempts to clojure.core/read a single form from the provided String, returning 1.25 + a vector containing the read form and a String containing the unread remainder 1.26 + of the provided String. Returns nil if no valid form can be read from the 1.27 + head of the String." 1.28 + [s] 1.29 + (try 1.30 + (let [r (-> s java.io.StringReader. java.io.PushbackReader.)] 1.31 + [(read r) (slurp r)]) 1.32 + (catch Exception e))) ; this indicates an invalid form -- the head of s is just string data 1.33 + 1.34 +(defn- interpolate 1.35 + "Yields a seq of Strings and read forms." 1.36 + ([s atom?] 1.37 + (lazy-seq 1.38 + (if-let [[form rest] (silent-read (subs s (if atom? 2 1)))] 1.39 + (cons form (interpolate (if atom? (subs rest 1) rest))) 1.40 + (cons (subs s 0 2) (interpolate (subs s 2)))))) 1.41 + ([^String s] 1.42 + (if-let [start (->> ["~{" "~("] 1.43 + (map #(.indexOf s %)) 1.44 + (remove #(== -1 %)) 1.45 + sort 1.46 + first)] 1.47 + (lazy-seq (cons 1.48 + (subs s 0 start) 1.49 + (interpolate (subs s start) (= \{ (.charAt s (inc start)))))) 1.50 + [s]))) 1.51 + 1.52 +(defmacro << 1.53 + "Takes a single string argument and emits a str invocation that concatenates 1.54 + the string data and evaluated expressions contained within that argument. 1.55 + Evaluation is controlled using ~{} and ~() forms. The former is used for 1.56 + simple value replacement using clojure.core/str; the latter can be used to 1.57 + embed the results of arbitrary function invocation into the produced string. 1.58 + 1.59 + Examples: 1.60 + user=> (def v 30.5) 1.61 + #'user/v 1.62 + user=> (<< \"This trial required ~{v}ml of solution.\") 1.63 + \"This trial required 30.5ml of solution.\" 1.64 + user=> (<< \"There are ~(int v) days in November.\") 1.65 + \"There are 30 days in November.\" 1.66 + user=> (def m {:a [1 2 3]}) 1.67 + #'user/m 1.68 + user=> (<< \"The total for your order is $~(->> m :a (apply +)).\") 1.69 + \"The total for your order is $6.\" 1.70 + 1.71 + Note that quotes surrounding string literals within ~() forms must be 1.72 + escaped." 1.73 + [string] 1.74 + `(str ~@(interpolate string))) 1.75 +