Software / code / prosody
Comparison
util/template.lua @ 3637:bd491def3efb
util.template: Rewritten to be much faster than the util.stanza stanza building API.
| author | Waqas Hussain <waqas20@gmail.com> |
|---|---|
| date | Thu, 25 Nov 2010 08:38:26 +0500 |
| parent | 3546:cb1600dea3ad |
| child | 3640:4bc88bb748d1 |
comparison
equal
deleted
inserted
replaced
| 3636:88e4397e39a9 | 3637:bd491def3efb |
|---|---|
| 1 | 1 |
| 2 local t_insert = table.insert; | |
| 3 local st = require "util.stanza"; | 2 local st = require "util.stanza"; |
| 4 local lxp = require "lxp"; | 3 local lxp = require "lxp"; |
| 5 local setmetatable = setmetatable; | 4 local setmetatable = setmetatable; |
| 6 local pairs = pairs; | 5 local pairs = pairs; |
| 6 local ipairs = ipairs; | |
| 7 local error = error; | 7 local error = error; |
| 8 local s_gsub = string.gsub; | 8 local loadstring = loadstring; |
| 9 | 9 local debug = debug; |
| 10 local print = print; | |
| 11 | 10 |
| 12 module("template") | 11 module("template") |
| 13 | |
| 14 local function process_stanza(stanza, ops) | |
| 15 -- process attrs | |
| 16 for key, val in pairs(stanza.attr) do | |
| 17 if val:match("{[^}]*}") then | |
| 18 t_insert(ops, {stanza.attr, key, val}); | |
| 19 end | |
| 20 end | |
| 21 -- process children | |
| 22 local i = 1; | |
| 23 while i <= #stanza do | |
| 24 local child = stanza[i]; | |
| 25 if child.name then | |
| 26 process_stanza(child, ops); | |
| 27 elseif child:match("^{[^}]*}$") then -- text | |
| 28 t_insert(ops, {stanza, i, child:match("^{([^}]*)}$"), true}); | |
| 29 elseif child:match("{[^}]*}") then -- text | |
| 30 t_insert(ops, {stanza, i, child}); | |
| 31 end | |
| 32 i = i + 1; | |
| 33 end | |
| 34 end | |
| 35 | 12 |
| 36 local parse_xml = (function() | 13 local parse_xml = (function() |
| 37 local ns_prefixes = { | 14 local ns_prefixes = { |
| 38 ["http://www.w3.org/XML/1998/namespace"] = "xml"; | 15 ["http://www.w3.org/XML/1998/namespace"] = "xml"; |
| 39 }; | 16 }; |
| 81 return ok, err.." (line "..line..", col "..col..")"; | 58 return ok, err.." (line "..line..", col "..col..")"; |
| 82 end | 59 end |
| 83 end; | 60 end; |
| 84 end)(); | 61 end)(); |
| 85 | 62 |
| 63 local function create_string_string(str) | |
| 64 str = ("%q"):format(str); | |
| 65 str = str:gsub("{([^}]*)}", function(s) | |
| 66 return '"..(data["'..s..'"]or"").."'; | |
| 67 end); | |
| 68 return str; | |
| 69 end | |
| 70 local function create_attr_string(attr, xmlns) | |
| 71 local str = '{'; | |
| 72 for name,value in pairs(attr) do | |
| 73 if name ~= "xmlns" or value ~= xmlns then | |
| 74 str = str..("[%q]=%s;"):format(name, create_string_string(value)); | |
| 75 end | |
| 76 end | |
| 77 return str..'}'; | |
| 78 end | |
| 79 local function create_clone_string(stanza, lookup, xmlns) | |
| 80 if not lookup[stanza] then | |
| 81 local s = ('setmetatable({name=%q,attr=%s,last_add={},tags={'):format(stanza.name, create_attr_string(stanza.attr, xmlns)); | |
| 82 -- add tags | |
| 83 for i,tag in ipairs(stanza.tags) do | |
| 84 s = s..create_clone_string(tag, lookup, stanza.attr.xmlns)..";"; | |
| 85 end | |
| 86 s = s..'};'; | |
| 87 -- add children | |
| 88 for i,child in ipairs(stanza) do | |
| 89 if child.name then | |
| 90 s = s..create_clone_string(child, lookup, stanza.attr.xmlns)..";"; | |
| 91 else | |
| 92 s = s..create_string_string(child)..";" | |
| 93 end | |
| 94 end | |
| 95 s = s..'}, stanza_mt)'; | |
| 96 s = s:gsub('%.%.""', ""):gsub('([=;])""%.%.', "%1"):gsub(';"";', ";"); -- strip empty strings | |
| 97 local n = #lookup + 1; | |
| 98 lookup[n] = s; | |
| 99 lookup[stanza] = "_"..n; | |
| 100 end | |
| 101 return lookup[stanza]; | |
| 102 end | |
| 86 local stanza_mt = st.stanza_mt; | 103 local stanza_mt = st.stanza_mt; |
| 87 local function fast_st_clone(stanza, lookup) | 104 local function create_cloner(stanza, chunkname) |
| 88 local stanza_attr = stanza.attr; | 105 local lookup = {}; |
| 89 local stanza_tags = stanza.tags; | 106 local name = create_clone_string(stanza, lookup, ""); |
| 90 local tags, attr = {}, {}; | 107 local f = "local setmetatable,stanza_mt=...;return function(data)"; |
| 91 local clone = { name = stanza.name, attr = attr, tags = tags, last_add = {} }; | 108 for i=1,#lookup do |
| 92 for k,v in pairs(stanza_attr) do attr[k] = v; end | 109 f = f.."local _"..i.."="..lookup[i]..";"; |
| 93 lookup[stanza_attr] = attr; | |
| 94 for i=1,#stanza_tags do | |
| 95 local child = stanza_tags[i]; | |
| 96 local new = fast_st_clone(child, lookup); | |
| 97 tags[i] = new; | |
| 98 lookup[child] = new; | |
| 99 end | 110 end |
| 100 for i=1,#stanza do | 111 f = f.."return "..name..";end"; |
| 101 local child = stanza[i]; | 112 local f,err = loadstring(f, chunkname); |
| 102 clone[i] = lookup[child] or child; | 113 if not f then error(err); end |
| 103 end | 114 return f(setmetatable, stanza_mt); |
| 104 return setmetatable(clone, stanza_mt); | |
| 105 end | 115 end |
| 106 | 116 |
| 107 local function create_template(text) | 117 local template_mt = { __tostring = function(t) return t.name end }; |
| 118 local function create_template(templates, text) | |
| 108 local stanza, err = parse_xml(text); | 119 local stanza, err = parse_xml(text); |
| 109 if not stanza then error(err); end | 120 if not stanza then error(err); end |
| 110 local ops = {}; | 121 |
| 111 process_stanza(stanza, ops); | 122 local info = debug.getinfo(3, "Sl"); |
| 112 | 123 info = info and ("template(%s:%d)"):format(info.short_src:match("[^\\/]*$"), info.currentline) or "template(unknown)"; |
| 113 local template = {}; | 124 |
| 114 local lookup = {}; | 125 local template = setmetatable({ apply = create_cloner(stanza, info), name = info, text = text }, template_mt); |
| 115 function template.apply(data) | 126 templates[text] = template; |
| 116 local newstanza = fast_st_clone(stanza, lookup); | |
| 117 for i=1,#ops do | |
| 118 local op = ops[i]; | |
| 119 local t, k, v, g = op[1], op[2], op[3], op[4]; | |
| 120 if g then | |
| 121 lookup[t][k] = data[v]; | |
| 122 else | |
| 123 lookup[t][k] = s_gsub(v, "{([^}]*)}", data); | |
| 124 end | |
| 125 end | |
| 126 return newstanza; | |
| 127 end | |
| 128 return template; | 127 return template; |
| 129 end | 128 end |
| 130 | 129 |
| 131 local templates = setmetatable({}, { __mode = 'k' }); | 130 local templates = setmetatable({}, { __mode = 'k', __index = create_template }); |
| 132 return function(text) | 131 return function(text) |
| 133 local template = templates[text]; | 132 return templates[text]; |
| 134 if not template then | |
| 135 template = create_template(text); | |
| 136 templates[text] = template; | |
| 137 end | |
| 138 return template; | |
| 139 end; | 133 end; |