util/xmllex.lua

Tue, 04 Jan 2011 00:23:51 +0000

author
Daurnimator <quae@daurnimator.com>
date
Tue, 04 Jan 2011 00:23:51 +0000
changeset 3995
7214dc7a5642
parent 3992
de77ec2b49bc
child 4002
2b53b4b5d46e
permissions
-rw-r--r--

util.xmppstream, util.xmllex: Basic test passes

local assert, ipairs , pairs , setmetatable , rawget , rawset , tostring =
	assert, ipairs , pairs , setmetatable , rawget , rawset , tostring
local strsub = string.sub
local tblconcat = table.concat
local tblinsert = table.insert

local function getstring ( msgs , startpos , finishpos )
	if #msgs == 1 then --All originated in same string
		return strsub ( msgs[1] , startpos , finishpos )
	else -- Over multiple source strings
		return strsub ( msgs[1] , startpos , -1 )
			.. tblconcat ( msgs , "" , 2 , #msgs - 1 )
			.. strsub ( msgs[#msgs] , 1 , finishpos )
	end
end

local m_mt = {
	__tostring = function ( v )
		local str = v.stringform
		if str then
			return str
		else
			str = getstring ( v.msgs , v.start , v.finish )
			v.stringform = str
			return str
		end
	end
}

local function handleoutside ( str , r , initial )
	local a , b , close = str:find ( "<(/?)" , initial )

	if not a then
		r.state = "outside"
		return false
	end
	
	--Finalise text object
	local m = r[#r]
	m.finish = a - 1
	m.type = "text"
	
	local m = setmetatable ( {
		msgs = { str } ;
		start = a ;
		starte = b + 1 ;
	} , m_mt )
	
	if close ~= "/" then
		r.depth = r.depth + 1
		m.type = "open"
	else
		r.depth = r.depth - 1
		m.type = "close"
	end
	
	tblinsert ( r , m )

	r.state = "inside"
	return true
end

local function handleinside ( str, r , initial )
	local c , d , selfclosing = str:find ( "(/?)>" , initial )
	if not c then
		r.state = "inside"
		return false
	end
	
	local m = r[#r]
	m.finish = d
	m.finishs = c - 1
	if selfclosing == "/" then
		m.type = "selfclosing"
		r.depth = r.depth - 1
	end

	local m = setmetatable ( {
		msgs = { str } ;
		start = d + 1 ;
		type = "text" ;
	} , m_mt )
	tblinsert ( r , m )
	
	r.state = "outside"
	return true
end

local function index ( str , r )
	r = r or { depth = 0, state = "outside" }
	
	local initial = 1
	
	if not r[#r] then
		r[1] = setmetatable ( {
			msgs = { str } ;
			type = "text" ;
			start = 1 ;
		} , m_mt )
	else
		tblinsert ( r[#r].msgs , str )
	end
	
	while true do
		if r.state == "outside" then
			if not handleoutside ( str , r , initial ) then
				break
			end
		else
			if not handleinside ( str , r , initial ) then
				break
			end
		end
		initial = r[#r].start
	end

	return r
end

local function process_starttag ( starttag )
	local str = tostring ( starttag )
	local attr = { }
	
	local elem = str:match ( "[^%s=></]+" )
	for name , quote, attvalue in str:gmatch ( [=[([^%s=<]+)%s*=%s*(["'])([^"]*)%2]=] ) do
		attr [ name ] = attvalue
	end
	return elem , attr
end

local stanza_mt = {
	__index = function ( t , k )
		if k == "name" or k == "attr" then
			local elem , attr = process_starttag ( t.opentag )
			rawset ( t , "name" , elem )
			rawset ( t , "attr" , attr )
			return rawget ( t , k )
		else
			print("METHOD",k)
			return stanza_methods [ k ]
		end
	end ;
	__tostring = function ( t )
		local opentag = t.opentag
		local endtag = assert ( rawget ( t , "endtag" ) or rawget ( t , "selfclosing" ) and t.opentag )
		return getstring ( opentag.msgs , opentag.start , endtag.finish )
	end ;
}

local function new_stanza ( )
	return setmetatable ( { tags = { } } , stanza_mt )
end

local function tagindex_to_tree(indices)
	if not start then
		start = 1
		finish = #indices
	end
	
	local root = { tags = { } }
	local leaf = root
	
	for i = start , finish do
		local v = indices [ i ]
		
		if v.type == "selfclosing" then
			local newleaf = new_stanza ( )
			newleaf.opentag = v
			newleaf.selfclosing = true
			newleaf.parent = leaf
			
			tblinsert ( leaf , newleaf )
			tblinsert ( leaf.tags , newleaf )
		elseif v.type == "close" then -- Close tag
			leaf.endtag = v
			leaf = leaf.parent
		elseif v.type == "text" then
			tblinsert ( leaf, v )
		else -- Open tag
			local newleaf = new_stanza ( )
			newleaf.opentag = v
			newleaf.parent = leaf
			
			tblinsert ( leaf , newleaf )
			tblinsert ( leaf.tags , newleaf )
			
			leaf = newleaf
		end
	end
	
	assert ( leaf == root , "Mismatched opening/closing tags" )
	return root;
end

return {
	index = index ;
	tagindex_to_tree = tagindex_to_tree ;
	process_starttag = process_starttag ;
};

mercurial