I'm personally not fond of string interpolation either.
But for fun, here's an (i ...) macro, that will give you ${}
interpolation in strings (if it works at all, I test it very
thorougly!).
(defn my-interleave [a b]
"Like interleave, but uses all elements from both lists."
(loop [acc [] a a b b]
(if (and (nil? a) (nil? b))
acc
(let [acc2 (if (nil? a)
acc
(conj acc (first a)))
acc3 (if (nil? b)
acc2
(conj acc2 (first b)))]
(recur acc3 (rest a) (rest b))))))
(defn read-from-string [s]
(read (java.io.PushbackReader. (java.io.StringReader. s))))
(defn tokenize [s] ;; not pretty but it works.
(let [positions (let [mm (re-matcher #"\\$\\{.*?\\}" s)]
(loop [acc []]
(if (.find mm)
(recur (conj acc [(.start mm) (.end mm)]))
acc)))
intermed (conj (apply vector 0 (apply concat positions))
(.length s))
textposns (partition 2 intermed)]
(my-interleave (map (fn [[a b]] [:text (.substring s a b)]) textposns)
(map (fn [[a b]] [:pat (.substring s (+ a 2) (- b 1))])
positions))))
(defmacro i [s]
(apply list 'str
(map (fn [[type value]]
(if (= type :text)
value
(read-from-string value)))
(tokenize s))))
;; test
(let [greeting "Hello" name "Fred" age 33]
(prn (i "${greeting}, my name is ${name} and my age is ${age}.")))
-- Graham
Haha, nor did I spell- or grammar-check very thoroughly!
I meant: I didn't test the code very thoroughly, so I hope it works at all.
Graham
Yes, thank you for the macro. I anticipate using this approach (I'm
accustomed to it from Ruby, Perl and JScheme), but wanted to support a
way of stopping the parser (by backslashing the opening brace:
"$\\{x}" ). I think adding the zero-width assertion in the re-matcher
and the replaceAll to knock the backslash out gets me there:
(defn tokenize [s]
(let [positions (let [mm (re-matcher #"\\$(?!\\\\)\\{.*?\\}" s)]
(loop [acc []]
(if (.find mm)
(recur (conj acc [(.start mm) (.end mm)]))
acc)))
intermed (conj (apply vector 0 (apply concat positions))
(.length s))
textposns (partition 2 intermed)]
(my-interleave (map (fn [[a b]] [:text (. (.substring s a b)
(replaceAll "\\$\\\\\\{" "\\${"))])
textposns)
(map (fn [[a b]] [:pat (.substring s (+ a 2) (- b 1))])
positions))))
Regards,
Kyle Burton
You're very welcome! The backslash-fix makes good sense.
Graham