[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: Re: lua & E4X ?
- From: Javier Guerra <javier@...>
- Date: Wed, 7 Mar 2007 18:12:19 -0500
On Wednesday 07 March 2007, Rici Lake wrote:
> On 7-Mar-07, at 4:14 PM, Javier Guerra wrote:
> > if anybody is interested, i might try to document it
>
> I'd be really interested.
attached is my XML module. it depends on lxp.
basically, you call xlp.new_page() with input and output functions. the
returned object is a table with this fields:
page.cb : initally empty, put here your callback functions
page.node : main node of XML
page.glob : table of all nodes with IDs, keyed by id, of course
page.outf : your output function.
on your XML template, just add a function="your_funcname" attribute whenever
you want to be called. your callback should be like this:
function page.cb.you_funcname (node, extraparams)
-- node is the node you tagged with the 'function' attribute
-- extraparams are usually nil, might be anything if you nest calls
end
in the callback you can modify the node, return a new node, or call
some 'output' functions.
page:out (node, ...) : renders the node and any content.
page:out_content (node, ...) : renders only the content of the node
page:out_tagstart (node, empty) : renders only the starting tag of the node.
if empty is true, then the tag is a non-content one (like <tag />)
page:out_tagend (node) : renders only the finish tag of the node
page:out() and page:out_content() render the main node if none is specified.
also, any extra parameters given here would be relayed to any nested callback
on inner nodes.
you can access (and modify) any attribute of a node like node.attr.attrname
(or attr.attr["attrname"] if the attribute name isn't very lua friendly).
to create a new node, call
xlp.new_node (tagname, attribute_table, content nodes ...)
or
xlp.new_node (oldnode, content nodes ...)
in the second case, the new node would copy the tagname and all attributes
from the oldnode.
if you need the XML as a string, call
page:render (node, ...)
or
page:render_content (node, ...)
these functions call out(node, ...) and out_content(node,...) respectively,
but replace the output function of the page with an accumulation and return
the result as a string.
hum.... i think that's all...
--
Javier
local assert, type, print, setmetatable = assert, type, print, setmetatable
local ipairs, pairs = ipairs, pairs
local table, string = table, string
local tinsert, tremove = table.insert, table.remove
local lxp = require "lxp"
module ("xlp")
local function top (t)
return t[#t]
end
local function attrstostr (a)
local out = {}
for i,attr in ipairs (a) do
if a[attr] then
tinsert (out, string.format ('%s="%s"', attr, a[attr]))
end
end
return table.concat (out, " ")
end
local function _tagstart (name, attr, empty)
local t = "<"..name
if attr and attr[1] then
t = t .. " "..attrstostr (attr)
end
if empty then
t = t .. " /"
end
t = t .. ">"
return t
end
function tagstart (node, empty)
assert (type (node) == "table")
if empty == nil then empty = not node[1] end
return _tagstart (node.tag, node.attr, empty)
end
function tagend (node)
assert (type (node) == "table")
return "</"..node.tag..">"
end
local function wholetag (name, attr, content)
local empty = not content or content==""
local t = _tagstart (name, attr, empty)
if not empty then
t = t .. content .. "</"..name..">"
end
return t
end
local function pack_node (node)
local empty, purestring = true, true
local acc, pack = {}, {}
for i,v in ipairs (node) do
empty = false
local t = type (v)
if t == "string" then
tinsert (acc, v)
else
purestring = false
tinsert (pack, table.concat (acc))
acc = {}
tinsert (pack, v)
end
end
tinsert (pack, table.concat (acc))
if purestring then
return wholetag (node.tag, node.attr, table.concat (pack))
else
pack.tag = node.tag
pack.attr = node.attr
return pack
end
end
local function simple_parser (cb, idattr)
cb = cb or {}
idattr = idattr or "id"
local stack = {{}}
local glob = {}
local function get_id (id)
return glob [id]
end
local function get_fun (node, ...)
-- print ("get_fun", node.tag, attrstostr (node.attr), cb [node.attr["function"]])
local fun = cb [node.attr["function"]] or pack_node
return fun (node, ...)
end
return lxp.new {
StartElement = function(parser, elementName, attributes)
tinsert (stack, {tag=elementName, attr=attributes})
end,
EndElement = function(parser, elementName)
local node = tremove (stack)
local parent = top (stack)
-- local hand = cb [elementName] or pack_node
-- local result = hand (node)
if node.attr["function"] then
tinsert (parent, {_f = get_fun, _p =node})
elseif node.attr[idattr] then
local id = node.attr[idattr]
glob [id] = pack_node (node)
tinsert (parent, {_f = get_id, _p=id})
else
tinsert (parent, pack_node (node))
end
end,
-- CharacterData = function (parser, string)
-- tinsert (stack [getn (stack)], string)
-- end,
Default = function (parser, string)
tinsert (top (stack), string)
end,
}, stack[1], glob
end
local function do_out (node, outf, contonly, ...)
local t = type (node)
if t == "string" then
outf (node)
else
if t == "table" then
if node._f then
do_out (node._f(node._p, ...), outf, contonly, ...)
else
if not contonly and node.tag then
outf (tagstart (node))
end
for _,v in ipairs (node) do
do_out (v, outf, nil, ...)
end
if not contonly and node.tag then
outf ("</"..node.tag..">")
end
end
end
end
end
function new_node (a, b, ...)
if type (a) == "table" then
return { tag = a.tag, attr=a.attr, b, ...}
else
return { tag = a, attr = b, ...}
end
end
local page_mt = {__index={}}
function page_mt.__index:out (node, ...)
return do_out (node or self.node, self.outf, nil, ...)
end
function page_mt.__index:out_content (node, ...)
return do_out (node or self.node, self.outf, true, ...)
end
function page_mt.__index:out_tagstart (node, empty)
return self.outf (tagstart (node, empty))
end
function page_mt.__index:out_tagend (node)
return self.outf (tagend (node))
end
function page_mt.__index:render (node, ...)
local outtab = {}
do_out (node or self.node, function (s) tinsert (outtab, s) end, nil, ...)
return table.concat (outtab)
end
function page_mt.__index:render_content (node, ...)
local outtab = {}
do_out (node or self.node, function (s) tinsert (outtab, s) end, true, ...)
return table.concat (outtab)
end
function new_page (input, output)
local cb = {}
local p,node,glob = simple_parser (cb)
local typ = type (input)
if typ == "string" then
p:parse (input)
elseif typ == "table" then
for _,v in ipairs (input) do
p:parse (v)
end
elseif typ == "function" then
local l = input()
while l ~= nil do
p:parse (l)
l = input ()
end
end
p:close()
return setmetatable ({
cb = cb,
node=node,
glob=glob,
outf = output
}, page_mt)
end
function make_handler (page)
return function (req, res)
page.outf = function (s) res:send_data (s) end
res.headers ["Content-Type"] = "text/xml"
page:out ()
end
end
Attachment:
pgpL4hf1If6yZ.pgp
Description: PGP signature