Mercurial > lasercutter
comparison 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 |
comparison
equal
deleted
inserted
replaced
9:35cf337adfcf | 10:ef7dbbd6452c |
---|---|
1 ;;; strint.clj -- String interpolation for Clojure | |
2 ;; originally proposed/published at http://muckandbrass.com/web/x/AgBP | |
3 | |
4 ;; by Chas Emerick <cemerick@snowtide.com> | |
5 ;; December 4, 2009 | |
6 | |
7 ;; Copyright (c) Chas Emerick, 2009. All rights reserved. The use | |
8 ;; and distribution terms for this software are covered by the Eclipse | |
9 ;; Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) | |
10 ;; which can be found in the file epl-v10.html at the root of this | |
11 ;; distribution. By using this software in any fashion, you are | |
12 ;; agreeing to be bound by the terms of this license. You must not | |
13 ;; remove this notice, or any other, from this software. | |
14 | |
15 (ns | |
16 ^{:author "Chas Emerick", | |
17 :doc "String interpolation for Clojure."} | |
18 clojure.contrib.strint) | |
19 | |
20 (defn- silent-read | |
21 "Attempts to clojure.core/read a single form from the provided String, returning | |
22 a vector containing the read form and a String containing the unread remainder | |
23 of the provided String. Returns nil if no valid form can be read from the | |
24 head of the String." | |
25 [s] | |
26 (try | |
27 (let [r (-> s java.io.StringReader. java.io.PushbackReader.)] | |
28 [(read r) (slurp r)]) | |
29 (catch Exception e))) ; this indicates an invalid form -- the head of s is just string data | |
30 | |
31 (defn- interpolate | |
32 "Yields a seq of Strings and read forms." | |
33 ([s atom?] | |
34 (lazy-seq | |
35 (if-let [[form rest] (silent-read (subs s (if atom? 2 1)))] | |
36 (cons form (interpolate (if atom? (subs rest 1) rest))) | |
37 (cons (subs s 0 2) (interpolate (subs s 2)))))) | |
38 ([^String s] | |
39 (if-let [start (->> ["~{" "~("] | |
40 (map #(.indexOf s %)) | |
41 (remove #(== -1 %)) | |
42 sort | |
43 first)] | |
44 (lazy-seq (cons | |
45 (subs s 0 start) | |
46 (interpolate (subs s start) (= \{ (.charAt s (inc start)))))) | |
47 [s]))) | |
48 | |
49 (defmacro << | |
50 "Takes a single string argument and emits a str invocation that concatenates | |
51 the string data and evaluated expressions contained within that argument. | |
52 Evaluation is controlled using ~{} and ~() forms. The former is used for | |
53 simple value replacement using clojure.core/str; the latter can be used to | |
54 embed the results of arbitrary function invocation into the produced string. | |
55 | |
56 Examples: | |
57 user=> (def v 30.5) | |
58 #'user/v | |
59 user=> (<< \"This trial required ~{v}ml of solution.\") | |
60 \"This trial required 30.5ml of solution.\" | |
61 user=> (<< \"There are ~(int v) days in November.\") | |
62 \"There are 30 days in November.\" | |
63 user=> (def m {:a [1 2 3]}) | |
64 #'user/m | |
65 user=> (<< \"The total for your order is $~(->> m :a (apply +)).\") | |
66 \"The total for your order is $6.\" | |
67 | |
68 Note that quotes surrounding string literals within ~() forms must be | |
69 escaped." | |
70 [string] | |
71 `(str ~@(interpolate string))) | |
72 |