Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 77 lines (68 sloc) 3.256 kb
e7627ef0 »
2011-09-12 migrate clojure.contrib.strint => clojure.core.strint, per http://dev…
1 ;;; strint.clj -- String interpolation for Clojure
2 ;; originally proposed/published at http://cemerick.com/2009/12/04/string-interpolation-in-clojure/
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 "Compile-time string interpolation for Clojure."}
18 clojure.core.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 <<
9f2590f2 »
2012-09-07 clojure.core.strint/<< now variadic
50 "Accepts one or more strings; 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.
e7627ef0 »
2011-09-12 migrate clojure.contrib.strint => clojure.core.strint, per http://dev…
55
9f2590f2 »
2012-09-07 clojure.core.strint/<< now variadic
56 Examples:
e7627ef0 »
2011-09-12 migrate clojure.contrib.strint => clojure.core.strint, per http://dev…
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.\"
9f2590f2 »
2012-09-07 clojure.core.strint/<< now variadic
67 user=> (<< \"Just split a long interpolated string up into ~(-> m :a (get 0)), \"
68 \"~(-> m :a (get 1)), or even ~(-> m :a (get 2)) separate strings \"
69 \"if you don't want a << expression to end up being e.g. ~(* 4 (int v)) \"
70 \"columns wide.\")
71 \"Just split a long interpolated string up into 1, 2, or even 3 separate strings if you don't want a << expression to end up being e.g. 120 columns wide.\"
72
73 Note that quotes surrounding string literals within ~() forms must be
74 escaped."
75 [& strings]
76 `(str ~@(interpolate (apply str strings))))
Something went wrong with that request. Please try again.