local xml_replacements = { { "&", "&" }, { "<", "<" }, { ">", ">" }, } local attribute_replacements = { { '"', '\\"' }, } local function generic_escape(str, replacements) local _, r for _, r in pairs(replacements) do str = string.gsub(str, r[1], r[2]) end return str end local function xml_escape(str) return generic_escape(str, xml_replacements) end local function attribute_escape(str) return generic_escape(str, attribute_replacements) end local function process_comment(xm, a) return { istop = true, nodes = { a }, type = "comment", } end local function process_lf(xm) return { istop = true, type = "lf", } end local function burrow(t) local k, v if type(t) ~= "table" then return end if type(t.istop) ~= "boolean" then return end t.istop = nil end local function clean_tops(t) local k, v for k, v in pairs(t) do if not v.istop then t[k] = nil end end end local function process_tag(xm, key, a) local ret, attrs, nodes, has_nodes, has_attrs, k, v, b = { istop = true, key = key }, {}, {}, false, false if type(a) == "table" then for k, v in ipairs(a) do if type(v) == "table" then burrow(v) has_nodes = true table.insert(nodes, v) elseif type(v) == "string" then has_nodes = true table.insert(nodes, xml_escape(v)) else error "Unsupported argument type." end end for k, v in pairs(a) do if (type(k) == "string") then has_attrs = true attrs[k] = attribute_escape(xml_escape(v)) end end elseif type(a) == "string" then has_nodes = true table.insert(nodes, xml_escape(a)) end if (has_nodes) then ret.nodes = nodes end if (has_attrs) then ret.attrs = attrs end table.insert(xm.tops, ret) clean_tops(xm.tops) return ret end local function render_attrs(t) local r, k, v if type(t) ~= "table" then return "" end for k, v in pairs(t) do r = r .. " " .. k .. '="' .. v .. '"' end return r end local function do_render_r(v) local middle, i, n = "" if v.type == "comment" then return "" elseif v.type == "lf" then return "\n" elseif type(v.key) == "string" then if type(v.nodes) == "table" then for i, n in ipairs(v.nodes) do if type(n) == "string" then middle = middle .. n elseif type(n) == "table" then middle = middle .. do_render_r(n) end end return "<" .. v.key .. render_attrs(v.attrs) .. ">" .. middle .. "" .. v.key .. ">" else return "<" .. v.key .. render_attrs(v.attrs) .. " />" end else error "Inconsistancy in the XML structure." end return "" end local function do_render(xm) local r, i, n = "" for i, n in pairs(xm.tops) do r = r .. do_render_r(n) end return xm.doctype .. "\n" .. r end local xmlmarkup_metatable = { __index = function(xm, key) local k = key return function(xm, a) return process_tag(xm, k, a) end end } function NewXmlMarkup() local r = { comment = process_comment, lf = process_lf, tops = {}, render = do_render, doctype = '', } setmetatable(r, xmlmarkup_metatable) return r end --[[ example of use: -- result is: --