util-tpl.lua /size: 7722 b    last modification: 2020-07-01 14:35
1
if
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
-- This is experimental code. Coming from dos and windows, I've always used %whatever%
10
-- as template variables so let's stick to it. After all, it's easy to parse and stands
11
-- out well. A double %% is turned into a regular %.
12 13
utilities
.
templates
=
utilities
.
templates
or
{
}
14
local
templates
=
utilities
.
templates
15 16
local
trace_template
=
false
trackers
.
register
(
"
templates.trace
"
,
function
(
v
)
trace_template
=
v
end
)
17
local
report_template
=
logs
.
reporter
(
"
template
"
)
18 19
local
tostring
,
next
=
tostring
,
next
20
local
format
,
sub
,
byte
=
string
.
format
,
string
.
sub
,
string
.
byte
21
local
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 23
local
formatters
=
string
.
formatters
24 25
-- todo: make installable template.new
26 27
local
replacer
28 29
local
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
47
end
48 49
local
sqlescape
=
lpeg
.
replacer
{
50
{
"
'
"
,
"
''
"
}
,
51
{
"
\\
"
,
"
\\\\
"
}
,
52
{
"
\r\n
"
,
"
\\n
"
}
,
53
{
"
\r
"
,
"
\\n
"
}
,
54
-- { "\t", "\\t" },
55
}
56 57
local
sqlquoted
=
Cs
(
Cc
(
"
'
"
)
*
sqlescape
*
Cc
(
"
'
"
)
)
58 59
lpegpatterns
.
sqlescape
=
sqlescape
60
lpegpatterns
.
sqlquoted
=
sqlquoted
61 62
-- escapeset : \0\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\"\\\127
63
-- test string: [[1\0\31test23"\\]] .. string.char(19) .. "23"
64
--
65
-- slow:
66
--
67
-- local luaescape = lpeg.replacer {
68
-- { '"', [[\"]] },
69
-- { '\\', [[\\]] },
70
-- { R("\0\9") * #R("09"), function(s) return "\\00" .. byte(s) end },
71
-- { R("\10\31") * #R("09"), function(s) return "\\0" .. byte(s) end },
72
-- { R("\0\31") , function(s) return "\\" .. byte(s) end },
73
-- }
74
--
75
-- slightly faster:
76 77
-- local luaescape = Cs ((
78
-- P('"' ) / [[\"]] +
79
-- P('\\') / [[\\]] +
80
-- Cc("\\00") * (R("\0\9") / byte) * #R("09") +
81
-- Cc("\\0") * (R("\10\31") / byte) * #R("09") +
82
-- Cc("\\") * (R("\0\31") / byte) +
83
-- P(1)
84
-- )^0)
85 86
----- xmlescape = lpegpatterns.xmlescape
87
----- texescape = lpegpatterns.texescape
88
local
luaescape
=
lpegpatterns
.
luaescape
89
----- sqlquoted = lpegpatterns.sqlquoted
90
----- luaquoted = lpegpatterns.luaquoted
91 92
local
escapers
=
{
93
lua
=
function
(
s
)
94
-- return sub(format("%q",s),2,-2)
95
return
lpegmatch
(
luaescape
,
s
)
96
end
,
97
sql
=
function
(
s
)
98
return
lpegmatch
(
sqlescape
,
s
)
99
end
,
100
}
101 102
local
quotedescapers
=
{
103
lua
=
function
(
s
)
104
-- return lpegmatch(luaquoted,s)
105
return
format
(
"
%q
"
,
s
)
106
end
,
107
sql
=
function
(
s
)
108
return
lpegmatch
(
sqlquoted
,
s
)
109
end
,
110
}
111 112
local
luaescaper
=
escapers
.
lua
113
local
quotedluaescaper
=
quotedescapers
.
lua
114 115
local
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
122
end
123 124
local
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
131
end
132 133
local
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
"
"
136
end
137 138
local
function
replaceformatted
(
l
,
m
,
r
,
t
,
how
,
recurse
)
139
local
v
=
t
[
r
]
140
return
v
and
formatters
[
l
]
(
v
)
141
end
142 143
local
single
=
P
(
"
%
"
)
-- test %test% test : resolves test
144
local
double
=
P
(
"
%%
"
)
-- test 10%% test : %% becomes %
145
local
lquoted
=
P
(
"
%[
"
)
-- test '%[test]%' test : resolves to test with escaped "'s
146
local
rquoted
=
P
(
"
]%
"
)
--
147
local
lquotedq
=
P
(
"
%(
"
)
-- test %(test)% test : resolves to 'test' with escaped "'s
148
local
rquotedq
=
P
(
"
)%
"
)
--
149 150
local
escape
=
double
/
'
%%
'
151
local
nosingle
=
single
/
'
'
152
local
nodouble
=
double
/
'
'
153
local
nolquoted
=
lquoted
/
'
'
154
local
norquoted
=
rquoted
/
'
'
155
local
nolquotedq
=
lquotedq
/
'
'
156
local
norquotedq
=
rquotedq
/
'
'
157 158
local
nolformatted
=
P
(
"
:
"
)
/
"
%%
"
159
local
norformatted
=
P
(
"
:
"
)
/
"
"
160 161
local
noloptional
=
P
(
"
%?
"
)
/
'
'
162
local
noroptional
=
P
(
"
?%
"
)
/
'
'
163
local
nomoptional
=
P
(
"
:
"
)
/
'
'
164 165
local
args
=
Carg
(
1
)
*
Carg
(
2
)
*
Carg
(
3
)
166
local
key
=
nosingle
*
(
(
C
(
(
1
-
nosingle
)
^
1
)
*
args
)
/
replacekey
)
*
nosingle
167
local
quoted
=
nolquotedq
*
(
(
C
(
(
1
-
norquotedq
)
^
1
)
*
args
)
/
replacekeyquoted
)
*
norquotedq
168
local
unquoted
=
nolquoted
*
(
(
C
(
(
1
-
norquoted
)
^
1
)
*
args
)
/
replacekeyunquoted
)
*
norquoted
169
local
optional
=
noloptional
*
(
(
C
(
(
1
-
nomoptional
)
^
1
)
*
nomoptional
*
C
(
(
1
-
noroptional
)
^
1
)
*
args
)
/
replaceoptional
)
*
noroptional
170
local
formatted
=
nosingle
*
(
(
Cs
(
nolformatted
*
(
1
-
norformatted
)
^
1
)
*
norformatted
*
C
(
(
1
-
nosingle
)
^
1
)
*
args
)
/
replaceformatted
)
*
nosingle
171
local
any
=
P
(
1
)
172 173
replacer
=
Cs
(
(
unquoted
+
quoted
+
formatted
+
escape
+
optional
+
key
+
any
)
^
0
)
174 175
local
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
181
end
182 183
-- print(replace("test '%[x]%' test",{ x = [[a 'x'  a]] }))
184
-- print(replace("test '%x%' test",{ x = [[a "x"  a]] }))
185
-- print(replace([[test "%[x]%" test]],{ x = [[a "x"  a]] }))
186
-- print(replace("test '%[x]%' test",{ x = true }))
187
-- print(replace("test '%[x]%' test",{ x = [[a 'x'  a]], y = "oeps" },'sql'))
188
-- print(replace("test '%[x]%' test",{ x = [[a '%y%'  a]], y = "oeps" },'sql',true))
189
-- print(replace([[test %[x]% test]],{ x = [[a "x"  a]]}))
190
-- print(replace([[test %(x)% test]],{ x = [[a "x"  a]]}))
191
-- print(replace([[convert %?x: -x "%x%" ?% %?y: -y "%y%" ?%]],{ x = "yes" }))
192
-- print(replace("test %:0.3N:x% test",{ x = 123.45 }))
193
-- print(replace("test %:0.3N:x% test",{ x = 12345 }))
194 195
templates
.
replace
=
replace
196 197
function
templates
.
replacer
(
str
,
how
,
recurse
)
-- reads nicer
198
return
function
(
mapping
)
199
return
lpegmatch
(
replacer
,
str
,
1
,
mapping
,
how
or
"
lua
"
,
recurse
or
false
)
or
str
200
end
201
end
202 203
-- local cmd = templates.replacer([[foo %bar%]]) print(cmd { bar = "foo" })
204 205
function
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
212
end
213 214
function
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
222
end
223 224
-- inspect(utilities.templates.replace("test %one% test", { one = "%two%", two = "two" }))
225
-- inspect(utilities.templates.resolve({ one = "%two%", two = "two", three = "%three%" }))
226
-- inspect(utilities.templates.replace("test %one% test", { one = "%two%", two = "two" },false,true))
227
-- inspect(utilities.templates.replace("test %one% test", { one = "%two%", two = "two" },false))
228