Software /
code /
prosody
Annotate
util/interpolation.lua @ 6720:936cf2f7531f
util.interpolation: A template engine for text
author | Kim Alvefur <zash@zash.se> |
---|---|
date | Tue, 19 May 2015 15:33:20 +0200 |
child | 6771:60957dd5b41b |
rev | line source |
---|---|
6720
936cf2f7531f
util.interpolation: A template engine for text
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
1 -- Simple template language |
936cf2f7531f
util.interpolation: A template engine for text
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
2 -- |
936cf2f7531f
util.interpolation: A template engine for text
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
3 -- The new() function takes a pattern and an escape function and returns |
936cf2f7531f
util.interpolation: A template engine for text
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
4 -- a render() function. Both are required. |
936cf2f7531f
util.interpolation: A template engine for text
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
5 -- |
936cf2f7531f
util.interpolation: A template engine for text
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
6 -- The function render() takes a string template and a table of values. |
936cf2f7531f
util.interpolation: A template engine for text
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
7 -- Sequences like {name} in the template string are substituted |
936cf2f7531f
util.interpolation: A template engine for text
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
8 -- with values from the table, optionally depending on a modifier |
936cf2f7531f
util.interpolation: A template engine for text
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
9 -- symbol. |
936cf2f7531f
util.interpolation: A template engine for text
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
10 -- |
936cf2f7531f
util.interpolation: A template engine for text
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
11 -- Variants are: |
936cf2f7531f
util.interpolation: A template engine for text
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
12 -- {name} is substituted for values["name"] and is escaped using the |
936cf2f7531f
util.interpolation: A template engine for text
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
13 -- second argument to new_render(). To disable the escaping, use {name!}. |
936cf2f7531f
util.interpolation: A template engine for text
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
14 -- {name.item} can be used to access table items. |
936cf2f7531f
util.interpolation: A template engine for text
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
15 -- To renter lists of items: {name# item number {idx} is {item} } |
936cf2f7531f
util.interpolation: A template engine for text
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
16 -- Or key-value pairs: {name% t[ {idx} ] = {item} } |
936cf2f7531f
util.interpolation: A template engine for text
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
17 -- To show a defaults for missing values {name? sub-template } can be used, |
936cf2f7531f
util.interpolation: A template engine for text
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
18 -- which renders a sub-template if values["name"] is false-ish. |
936cf2f7531f
util.interpolation: A template engine for text
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
19 -- {name& sub-template } does the opposite, the sub-template is rendered |
936cf2f7531f
util.interpolation: A template engine for text
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
20 -- if the selected value is anything but false or nil. |
936cf2f7531f
util.interpolation: A template engine for text
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
21 |
936cf2f7531f
util.interpolation: A template engine for text
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
22 local type, tostring = type, tostring; |
936cf2f7531f
util.interpolation: A template engine for text
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
23 local pairs, ipairs = pairs, ipairs; |
936cf2f7531f
util.interpolation: A template engine for text
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
24 local s_sub, s_gsub, s_match = string.sub, string.gsub, string.match; |
936cf2f7531f
util.interpolation: A template engine for text
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
25 local t_concat = table.concat; |
936cf2f7531f
util.interpolation: A template engine for text
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
26 |
936cf2f7531f
util.interpolation: A template engine for text
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
27 local function new_render(pat, escape) |
936cf2f7531f
util.interpolation: A template engine for text
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
28 -- assert(type(pat) == "string", "bad argument #1 to 'new_render' (string expected)"); |
936cf2f7531f
util.interpolation: A template engine for text
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
29 -- assert(type(escape) == "function", "bad argument #2 to 'new_render' (function expected)"); |
936cf2f7531f
util.interpolation: A template engine for text
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
30 local function render(template, values) |
936cf2f7531f
util.interpolation: A template engine for text
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
31 -- assert(type(template) == "string", "bad argument #1 to 'render' (string expected)"); |
936cf2f7531f
util.interpolation: A template engine for text
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
32 -- assert(type(values) == "table", "bad argument #2 to 'render' (table expected)"); |
936cf2f7531f
util.interpolation: A template engine for text
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
33 return (s_gsub(template, pat, function (block) |
936cf2f7531f
util.interpolation: A template engine for text
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
34 block = s_sub(block, 2, -2); |
936cf2f7531f
util.interpolation: A template engine for text
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
35 local name, opt, e = s_match(block, "^([%a_][%w_.]*)(%p?)()"); |
936cf2f7531f
util.interpolation: A template engine for text
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
36 if not name then return end |
936cf2f7531f
util.interpolation: A template engine for text
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
37 local value = values[name]; |
936cf2f7531f
util.interpolation: A template engine for text
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
38 if not value and name:find(".", 2, true) then |
936cf2f7531f
util.interpolation: A template engine for text
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
39 value = values; |
936cf2f7531f
util.interpolation: A template engine for text
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
40 for word in name:gmatch"[^.]+" do |
936cf2f7531f
util.interpolation: A template engine for text
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
41 value = value[word]; |
936cf2f7531f
util.interpolation: A template engine for text
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
42 if not value then break; end |
936cf2f7531f
util.interpolation: A template engine for text
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
43 end |
936cf2f7531f
util.interpolation: A template engine for text
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
44 end |
936cf2f7531f
util.interpolation: A template engine for text
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
45 if opt == '#' or opt == '%' then |
936cf2f7531f
util.interpolation: A template engine for text
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
46 if type(value) ~= "table" then return ""; end |
936cf2f7531f
util.interpolation: A template engine for text
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
47 local iter = opt == '#' and ipairs or pairs; |
936cf2f7531f
util.interpolation: A template engine for text
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
48 local out, i, subtpl = {}, 1, s_sub(block, e); |
936cf2f7531f
util.interpolation: A template engine for text
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
49 local subvalues = setmetatable({}, { __index = values }); |
936cf2f7531f
util.interpolation: A template engine for text
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
50 for idx, item in iter(value) do |
936cf2f7531f
util.interpolation: A template engine for text
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
51 subvalues.idx = idx; |
936cf2f7531f
util.interpolation: A template engine for text
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
52 subvalues.item = item; |
936cf2f7531f
util.interpolation: A template engine for text
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
53 out[i], i = render(subtpl, subvalues), i+1; |
936cf2f7531f
util.interpolation: A template engine for text
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
54 end |
936cf2f7531f
util.interpolation: A template engine for text
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
55 return t_concat(out); |
936cf2f7531f
util.interpolation: A template engine for text
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
56 elseif opt == '&' then |
936cf2f7531f
util.interpolation: A template engine for text
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
57 if not value then return ""; end |
936cf2f7531f
util.interpolation: A template engine for text
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
58 return render(s_sub(block, e), values); |
936cf2f7531f
util.interpolation: A template engine for text
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
59 elseif opt == '?' and not value then |
936cf2f7531f
util.interpolation: A template engine for text
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
60 return render(s_sub(block, e), values); |
936cf2f7531f
util.interpolation: A template engine for text
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
61 elseif value ~= nil then |
936cf2f7531f
util.interpolation: A template engine for text
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
62 if type(value) ~= "string" then |
936cf2f7531f
util.interpolation: A template engine for text
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
63 value = tostring(value); |
936cf2f7531f
util.interpolation: A template engine for text
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
64 end |
936cf2f7531f
util.interpolation: A template engine for text
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
65 if opt ~= '!' then |
936cf2f7531f
util.interpolation: A template engine for text
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
66 return escape(value); |
936cf2f7531f
util.interpolation: A template engine for text
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
67 end |
936cf2f7531f
util.interpolation: A template engine for text
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
68 return value; |
936cf2f7531f
util.interpolation: A template engine for text
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
69 end |
936cf2f7531f
util.interpolation: A template engine for text
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
70 end)); |
936cf2f7531f
util.interpolation: A template engine for text
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
71 end |
936cf2f7531f
util.interpolation: A template engine for text
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
72 return render; |
936cf2f7531f
util.interpolation: A template engine for text
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
73 end |
936cf2f7531f
util.interpolation: A template engine for text
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
74 |
936cf2f7531f
util.interpolation: A template engine for text
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
75 return { |
936cf2f7531f
util.interpolation: A template engine for text
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
76 new = new_render; |
936cf2f7531f
util.interpolation: A template engine for text
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
77 }; |