| 1 | -- See Copyright Notice in license.html |
| 2 | |
| 3 | local lxp = require "lxp" |
| 4 | |
| 5 | local table = require"table" |
| 6 | local tinsert, tremove = table.insert, table.remove |
| 7 | local assert, pairs, type = assert, pairs, type |
| 8 | |
| 9 | |
| 10 | -- auxiliary functions ------------------------------------------------------- |
| 11 | local function starttag (p, tag, attr) |
| 12 | local stack = p:getcallbacks().stack |
| 13 | local newelement = {tag = tag, attr = attr} |
| 14 | tinsert(stack, newelement) |
| 15 | end |
| 16 | |
| 17 | local function endtag (p, tag) |
| 18 | local stack = p:getcallbacks().stack |
| 19 | local element = tremove(stack) |
| 20 | assert(element.tag == tag) |
| 21 | local level = #stack |
| 22 | tinsert(stack[level], element) |
| 23 | end |
| 24 | |
| 25 | local function text (p, txt) |
| 26 | local stack = p:getcallbacks().stack |
| 27 | local element = stack[#stack] |
| 28 | local n = #element |
| 29 | if type(element[n]) == "string" then |
| 30 | element[n] = element[n] .. txt |
| 31 | else |
| 32 | tinsert(element, txt) |
| 33 | end |
| 34 | end |
| 35 | |
| 36 | -- main function ------------------------------------------------------------- |
| 37 | local function parse (o) |
| 38 | local c = { StartElement = starttag, |
| 39 | EndElement = endtag, |
| 40 | CharacterData = text, |
| 41 | _nonstrict = true, |
| 42 | stack = {{}} |
| 43 | } |
| 44 | local p = lxp.new(c) |
| 45 | local to = type(o) |
| 46 | if to == "string" then |
| 47 | local status, err, line, col, pos = p:parse(o) |
| 48 | if not status then return nil, err, line, col, pos end |
| 49 | else |
| 50 | local iter, state, init |
| 51 | if to == "table" then |
| 52 | iter, state, init = pairs(o) |
| 53 | elseif to == "function" then |
| 54 | iter = o |
| 55 | elseif to == "userdata" and o.read then |
| 56 | iter, state = o.read, o |
| 57 | else |
| 58 | error ("Bad argument #1 to parse: expected a string, a table, a function or a file, but got "..to, 2) |
| 59 | end |
| 60 | for l in iter, state, init do |
| 61 | local status, err, line, col, pos = p:parse(l) |
| 62 | if not status then return nil, err, line, col, pos end |
| 63 | end |
| 64 | end |
| 65 | local status, err, line, col, pos = p:parse() -- close document |
| 66 | if not status then return nil, err, line, col, pos end |
| 67 | p:close() |
| 68 | return c.stack[1][1] |
| 69 | end |
| 70 | |
| 71 | -- utility functions --------------------------------------------------------- |
| 72 | local function find_elem (self, tag) |
| 73 | if self.tag == tag then |
| 74 | return self |
| 75 | end |
| 76 | for i = 1, #self do |
| 77 | local v = self[i] |
| 78 | if type(v) == "table" then |
| 79 | local found = find_elem (v, tag) |
| 80 | if found then |
| 81 | return found |
| 82 | end |
| 83 | end |
| 84 | end |
| 85 | return nil |
| 86 | end |
| 87 | |
| 88 | local function list_children (self, tag) |
| 89 | local i = 0 |
| 90 | return function () |
| 91 | i = i+1 |
| 92 | local v = self[i] |
| 93 | while v do |
| 94 | if type (v) == "table" and (tag == nil or tag == v.tag) then |
| 95 | return v |
| 96 | end |
| 97 | i = i+1 |
| 98 | v = self[i] |
| 99 | end |
| 100 | return nil |
| 101 | end |
| 102 | end |
| 103 | |
| 104 | return { |
| 105 | find_elem = find_elem, |
| 106 | list_children = list_children, |
| 107 | parse = parse, |
| 108 | } |