-
Notifications
You must be signed in to change notification settings - Fork 1
/
core.clj
123 lines (92 loc) · 3.41 KB
/
core.clj
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
(ns sats.stem.core
(:require [clojure.string :as str]
[sci.core :as sci]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; ----==| U T I L S |==---- ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn- deep-merge
"Like merge, but merges maps recursively."
[& maps]
(let [maps (filter (comp not nil?) maps)]
(if (every? map? maps)
(apply merge-with deep-merge maps)
(last maps))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; ----==| P R E P R O C E S S I N G |==---- ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(def ^:private var-pattern
#"(?<!\\)(?:\{\{)\s(.*?)\s(?:\}\})")
(def ^:private expr-pattern
#"(?<!\\)(?:\{%)\s((?s).*?)\s(?:%\})")
(def ^:private default-options
{:bindings {'join-with clojure.string/join}})
(def ^:private data-key '___data___)
(defn- tokenise-exprs
[s]
(re-seq expr-pattern s))
(defn- preprocess-vars
"Replaces vars which look like {{ var }} to {% (:var
<replacement_symbol>) %}"
[s]
(str/replace s var-pattern
(format "{%% (:$1 %s) %%}" data-key)))
(defn- preprocess-expr
[s]
(str/replace s var-pattern
(format "(:$1 %s)" data-key)))
(defn- preprocess-exprs
[s]
(let [tokens (re-seq expr-pattern s)]
(reduce
(fn [s [match expr]]
(->> expr
preprocess-expr
(format "{%% %s %%}")
(str/replace s match)))
s
tokens)))
(def ^:private preprocess
(comp preprocess-vars
preprocess-exprs))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; ----==| E V A L / R E N D E R |==---- ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn- evaluate-expressions
[{:keys [bindings] :as opts} s data]
(let [bindings' (assoc bindings data-key data)
tokens (tokenise-exprs s)]
(reduce
(fn [s [match expr]]
(let [replacement (sci/eval-string expr {:bindings bindings'})]
(str/replace s match replacement)))
s
tokens)))
(defn render-string
[s data & opts]
(let [opts (->> opts
(apply hash-map)
(deep-merge default-options))
preprocessed (preprocess s)]
(evaluate-expressions opts preprocessed data)))
(comment
(render-string
"Hello {{ name }}. Please select a product.\n{% (join-with \"\n\" (for [p {{ products }}] (name (:name p)))) %}"
{:name "John"
:products
[{:name :ipad}
{:name :phone}
{:name :oculus}]}
:bindings {})
(render-string
"Hello {% (get {:male \"Mr\" :other \"Mx\"} {{ gender }}) %}.{% (capitalize {{ name }}) %}"
{:name "sathya" :gender :male}
:bindings
{'capitalize clojure.string/capitalize})
;;
)