-- originally from http://lua-users.org/wiki/LuaXml -- modified by me a bit local function parseargs(s) local arg = {} string.gsub(s, "([%-%w]+)=([\"'])(.-)%2", function (w, _, a) arg[w] = a end) return arg end local function tag(x) return setmetatable(x, {__call=function(x, label) -- search for child with given label for _,c in ipairs(x) do if c.label == label then return c end end return nil end}) end local psingle local function pmulti(s, i, parent) ::again:: local nexti, child = psingle(s, i) if child.close and child.label == parent.label then return nexti, parent else table.insert(parent, child) i = nexti goto again end end psingle = function(s, i) i = i or 1 local ts,j,c,label,xarg,empty = s:find("<(%/?)([%w:]+)(.-)(%/?)>", i) if not ts then local rest = s:sub(i) if rest:find("<",i) then error('ill formed (eof?)') elseif #rest == 0 then error('empty string') else return i+#rest, rest end end local nexti = j+1 local pretext = s:sub(i, ts-1) if not pretext:find("^%s*$") then -- not entirely whitespace return ts, pretext end if empty == "/" then return nexti, tag{label=label, xarg=parseargs(xarg), empty=true} elseif c == "" then -- start tag return pmulti(s, nexti, tag{label=label, xarg=parseargs(xarg)}) else -- end tag return nexti, tag{label=label, close=true} end end local wrap do local _, cqa = pcall(require, 'cqueues.auxlib') wrap = cqa and cqa.wrap or coroutine.wrap end local function stanzae(getmore) return wrap(function() local buf = '' while true do local ok, ni, el = pcall(psingle, buf) if ok then coroutine.yield(el) buf = buf:sub(ni) else local more = getmore() buf = buf .. more end end end) end return { psingle = psingle, stanzae = stanzae, wrap=wrap }