1if not modules then modules = { } end modules ['util-tpl'] = {
2 version = 1.001,
3 comment = "companion to luat-lib.mkiv",
4 author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
5 copyright = "PRAGMA ADE / ConTeXt Development Team",
6 license = "see context related readme files"
7}
8
9
10
11
12
13utilities.templates = utilities.templates or { }
14local templates = utilities.templates
15
16local trace_template = false trackers.register("templates.trace",function(v) trace_template = v end)
17local report_template = logs.reporter("template")
18
19local tostring, next = tostring, next
20local format, sub, byte = string.format, string.sub, string.byte
21local P, C, R, Cs, Cc, Carg, lpegmatch, lpegpatterns = lpeg.P, lpeg.C, lpeg.R, lpeg.Cs, lpeg.Cc, lpeg.Carg, lpeg.match, lpeg.patterns
22
23local formatters = string.formatters
24
25
26
27local replacer
28
29local function replacekey(k,t,how,recursive)
30 local v = t[k]
31 if not v then
32 if trace_template then
33 report_template("unknown key %a",k)
34 end
35 return ""
36 else
37 v = tostring(v)
38 if trace_template then
39 report_template("setting key %a to value %a",k,v)
40 end
41 if recursive then
42 return lpegmatch(replacer,v,1,t,how,recursive)
43 else
44 return v
45 end
46 end
47end
48
49local sqlescape = lpeg.replacer {
50 { "'", "''" },
51 { "\\", "\\\\" },
52 { "\r\n", "\\n" },
53 { "\r", "\\n" },
54
55}
56
57local sqlquoted = Cs(Cc("'") * sqlescape * Cc("'"))
58
59lpegpatterns.sqlescape = sqlescape
60lpegpatterns.sqlquoted = sqlquoted
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
88local luaescape = lpegpatterns.luaescape
89
90
91
92local escapers = {
93 lua = function(s)
94
95 return lpegmatch(luaescape,s)
96 end,
97 sql = function(s)
98 return lpegmatch(sqlescape,s)
99 end,
100}
101
102local quotedescapers = {
103 lua = function(s)
104
105 return format("%q",s)
106 end,
107 sql = function(s)
108 return lpegmatch(sqlquoted,s)
109 end,
110}
111
112local luaescaper = escapers.lua
113local quotedluaescaper = quotedescapers.lua
114
115local function replacekeyunquoted(s,t,how,recurse)
116 if how == false then
117 return replacekey(s,t,how,recurse)
118 else
119 local escaper = how and escapers[how] or luaescaper
120 return escaper(replacekey(s,t,how,recurse))
121 end
122end
123
124local function replacekeyquoted(s,t,how,recurse)
125 if how == false then
126 return replacekey(s,t,how,recurse)
127 else
128 local escaper = how and quotedescapers[how] or quotedluaescaper
129 return escaper(replacekey(s,t,how,recurse))
130 end
131end
132
133local function replaceoptional(l,m,r,t,how,recurse)
134 local v = t[l]
135 return v and v ~= "" and lpegmatch(replacer,r,1,t,how or "lua",recurse or false) or ""
136end
137
138local function replaceformatted(l,m,r,t,how,recurse)
139 local v = t[r]
140 return v and formatters[l](v)
141end
142
143local single = P("%")
144local double = P("%%")
145local lquoted = P("%[")
146local rquoted = P("]%")
147local lquotedq = P("%(")
148local rquotedq = P(")%")
149
150local escape = double / '%%'
151local nosingle = single / ''
152local nodouble = double / ''
153local nolquoted = lquoted / ''
154local norquoted = rquoted / ''
155local nolquotedq = lquotedq / ''
156local norquotedq = rquotedq / ''
157
158local nolformatted = P(":") / "%%"
159local norformatted = P(":") / ""
160
161local noloptional = P("%?") / ''
162local noroptional = P("?%") / ''
163local nomoptional = P(":") / ''
164
165local args = Carg(1) * Carg(2) * Carg(3)
166local key = nosingle * ((C((1-nosingle)^1) * args) / replacekey) * nosingle
167local quoted = nolquotedq * ((C((1-norquotedq)^1) * args) / replacekeyquoted) * norquotedq
168local unquoted = nolquoted * ((C((1-norquoted)^1) * args) / replacekeyunquoted) * norquoted
169local optional = noloptional * ((C((1-nomoptional)^1) * nomoptional * C((1-noroptional)^1) * args) / replaceoptional) * noroptional
170local formatted = nosingle * ((Cs(nolformatted * (1-norformatted )^1) * norformatted * C((1-nosingle)^1) * args) / replaceformatted) * nosingle
171local any = P(1)
172
173 replacer = Cs((unquoted + quoted + formatted + escape + optional + key + any)^0)
174
175local function replace(str,mapping,how,recurse)
176 if mapping and str then
177 return lpegmatch(replacer,str,1,mapping,how or "lua",recurse or false) or str
178 else
179 return str
180 end
181end
182
183
184
185
186
187
188
189
190
191
192
193
194
195templates.replace = replace
196
197function templates.replacer(str,how,recurse)
198 return function(mapping)
199 return lpegmatch(replacer,str,1,mapping,how or "lua",recurse or false) or str
200 end
201end
202
203
204
205function templates.load(filename,mapping,how,recurse)
206 local data = io.loaddata(filename) or ""
207 if mapping and next(mapping) then
208 return replace(data,mapping,how,recurse)
209 else
210 return data
211 end
212end
213
214function templates.resolve(t,mapping,how,recurse)
215 if not mapping then
216 mapping = t
217 end
218 for k, v in next, t do
219 t[k] = replace(v,mapping,how,recurse)
220 end
221 return t
222end
223
224
225
226
227
228 |