src/lxp/lom.lua

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
}