123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149 |
- -- MIT License
- --
- -- Copyright (c) 2019 monolifed
- --
- -- Permission is hereby granted, free of charge, to any person obtaining a copy
- -- of this software and associated documentation files (the "Software"), to deal
- -- in the Software without restriction, including without limitation the rights
- -- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- -- copies of the Software, and to permit persons to whom the Software is
- -- furnished to do so, subject to the following conditions:
- --
- -- The above copyright notice and this permission notice shall be included in all
- -- copies or substantial portions of the Software.
- --
- -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- -- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- -- SOFTWARE.
- local _concat = table.concat
- local _sort = table.sort
- local _insert = table.insert
- local _remove = table.remove
- local _format = string.format
- local _sub = string.sub
- local _gsub = string.gsub
- local _find = string.find
- local _gmatch = string.gmatch
- local escaped = {
- ['''] = [[']],
- ['"'] = [["]],
- ['&' ] = [[&]],
- ['>' ] = [[>]],
- ['<' ] = [[<]],
- }
- -- (table) from attribute string ------------------
- local re_attr = [[(%w+)=(["'])(.-)%2]]
- local fromstring = function(s)
- local t = {}
- for k, _, v in _gmatch(s, re_attr) do
- for e, c in pairs(escaped) do v = _gsub(v, e, c) end
- t[k] = v
- end
- return t
- end
- -- (attribute string) from table --
- local fromtable = function(t)
- if not t then return "" end
- local s, sn = {}, 1
- for k, v in pairs(t) do
- v = tostring(v)
- for e, c in pairs(escaped) do v = _gsub(v, c, e) end
- s[sn] = _format(' %s="%s"', k, v); sn = sn + 1
- end
- _sort(s)
- return _concat(s)
- end
- -- XML string to DOM formatted table --
- local re_tag = "<(/?)([%w:]+)(.-)(/?)>" -- ls, tag, attr, rs
- local endtag_unexpected = "Unexpected end tag '%s'"
- local endtag_mismatched = "Cannot close '%s' with '%s'"
- local endtag_missing = "Missing end tag for '%s'"
- -- Based on classic Lua XML parser by Roberto Ierusalimschy
- local parse = function(s)
- local stack, size = {}, 0
- local top = {}
- _insert(stack, top); size = size + 1
-
- local si, ei, ls, tag, attr, rs
- local i = 1
- local text
- while true do
- si, ei, ls, tag, attr, rs = _find(s, re_tag, i)
- if not si then break end
- text = _sub(s, i, si - 1)
- if _find(text, "%S") then
- _insert(top, text)
- end
- if rs == "/" then -- self-closing tag
- _insert(top, {_name = tag, _attr = fromstring(attr), empty = 1})
- elseif ls == "" then -- start tag
- top = {_name = tag, _attr = fromstring(attr)}
- _insert(stack, top); size = size + 1
- else -- end tag
- local toclose = _remove(stack); size = size - 1
- top = stack[size]
- if size < 1 then
- return nil, endtag_unexpected:format(tag)
- end
- if toclose._name ~= tag then
- return nil, endtag_mismatched:format(toclose._name, tag)
- end
- _insert(top, toclose)
- end
- i = ei + 1
- end
- text = _sub(s, i)
- if _find(text, "%S") then
- _insert(top, text)
- end
- if size > 1 then
- return nil, endtag_missing:format(top._name)
- end
- return stack[1] --top
- end
- -- DOM formatted table to XML string --
- local _toxml
- _toxml = function(t, sub)
- if not sub then sub = {} end
- local tag = t._name
- local attr = fromtable(t._attr)
- if #t > 0 then
- _insert(sub, _format("<%s%s>\n", tag, attr))
- for _, v in ipairs(t) do
- if type(v) == "table" then
- _toxml(v, sub)
- else
- _insert(sub, _format("%s\n", v))
- end
- end
- _insert(sub, _format("</%s>\n", tag))
- else
- _insert(sub, _format("<%s%s/>\n", tag, attr))
- end
-
- return _concat(sub)
- end
- return {
- parse = parse,
- toxml = function(t) return _toxml(t, {}) end
- }
|