summaryrefslogtreecommitdiff
path: root/xml.lua
diff options
context:
space:
mode:
Diffstat (limited to 'xml.lua')
-rw-r--r--xml.lua73
1 files changed, 73 insertions, 0 deletions
diff --git a/xml.lua b/xml.lua
new file mode 100644
index 0000000..7a0758a
--- /dev/null
+++ b/xml.lua
@@ -0,0 +1,73 @@
+local function parseargs(s)
+ local arg = {}
+ string.gsub(s, "([%-%w]+)=([\"'])(.-)%2", function (w, _, a)
+ arg[w] = a
+ end)
+ return arg
+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, {label=label, xarg=parseargs(xarg), empty=true}
+ elseif c == "" then -- start tag
+ return multi(s, nexti, {label=label, xarg=parseargs(xarg)})
+ else -- end tag
+ return nexti, {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
+ buf = buf..getmore()
+ end
+ end
+ end)
+end
+
+
+return { psingle = psingle, stanzae = stanzae, wrap=wrap }