summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/tex/context/base/l-xml.lua
diff options
context:
space:
mode:
Diffstat (limited to 'Master/texmf-dist/tex/context/base/l-xml.lua')
-rw-r--r--Master/texmf-dist/tex/context/base/l-xml.lua484
1 files changed, 305 insertions, 179 deletions
diff --git a/Master/texmf-dist/tex/context/base/l-xml.lua b/Master/texmf-dist/tex/context/base/l-xml.lua
index b4e151ec32e..8b8a6ce4433 100644
--- a/Master/texmf-dist/tex/context/base/l-xml.lua
+++ b/Master/texmf-dist/tex/context/base/l-xml.lua
@@ -9,6 +9,8 @@ if not modules then modules = { } end modules ['l-xml'] = {
-- RJ: key=value ... lpeg.Ca(lpeg.Cc({}) * (pattern-producing-key-and-value / rawset)^0)
-- some code may move to l-xmlext
+-- some day we will really compile the lpaths (just construct functions)
+-- todo: some things per xml file, like namespace remapping
--[[ldx--
<p>The parser used here is inspired by the variant discussed in the lua book, but
@@ -37,15 +39,30 @@ optimize the code.</p>
xml = xml or { }
tex = tex or { }
-xml.trace_lpath = false
-xml.trace_print = false
-xml.trace_remap = false
+local concat, remove, insert = table.concat, table.remove, table.insert
+local type, next, tonumber, tostring, setmetatable, loadstring = type, next, tonumber, tostring, setmetatable, loadstring
+local format, lower, gmatch, gsub, find = string.format, string.lower, string.gmatch, string.gsub, string.find
-local format, concat = string.format, table.concat
+--[[ldx--
+<p>This module can be used stand alone but also inside <l n='mkiv'/> in
+which case it hooks into the tracker code. Therefore we provide a few
+functions that set the tracers.</p>
+--ldx]]--
---~ local pairs, next, type = pairs, next, type
+local trace_lpath, trace_remap = false, false
--- todo: some things per xml file, liek namespace remapping
+if trackers then
+ trackers.register("xml.lpath", function(v) trace_lpath = v end)
+ trackers.register("xml.remap", function(v) trace_remap = v end)
+end
+
+function xml.settrace(str,value)
+ if str == "lpath" then
+ trace_lpath = value or false
+ elseif str == "remap" then
+ trace_remap = value or false
+ end
+end
--[[ldx--
<p>First a hack to enable namespace resolving. A namespace is characterized by
@@ -72,7 +89,7 @@ do
--ldx]]--
function xml.registerns(namespace, pattern) -- pattern can be an lpeg
- check = check + lpeg.C(lpeg.P(pattern:lower())) / namespace
+ check = check + lpeg.C(lpeg.P(lower(pattern))) / namespace
parse = lpeg.P { lpeg.P(check) + 1 * lpeg.V(1) }
end
@@ -87,7 +104,7 @@ do
--ldx]]--
function xml.checkns(namespace,url)
- local ns = parse:match(url:lower())
+ local ns = parse:match(lower(url))
if ns and namespace ~= ns then
xml.xmlns[namespace] = ns
end
@@ -105,7 +122,7 @@ do
--ldx]]--
function xml.resolvens(url)
- return parse:match(url:lower()) or ""
+ return parse:match(lower(url)) or ""
end
--[[ldx--
@@ -154,7 +171,7 @@ do
-- not just one big nested table capture (lpeg overflow)
- local remove, nsremap, resolvens = table.remove, xml.xmlns, xml.resolvens
+ local nsremap, resolvens = xml.xmlns, xml.resolvens
local stack, top, dt, at, xmlns, errorstr, entities = {}, {}, {}, {}, {}, nil, {}
@@ -172,6 +189,9 @@ do
end
local function add_attribute(namespace,tag,value)
+ if cleanup and #value > 0 then
+ value = cleanup(value) -- new
+ end
if tag == "xmlns" then
xmlns[#xmlns+1] = resolvens(value)
at[tag] = value
@@ -206,7 +226,8 @@ do
end
dt = top.dt
dt[#dt+1] = toclose
- if at.xmlns then
+dt[0] = top
+ if toclose.at.xmlns then
remove(xmlns)
end
end
@@ -220,10 +241,10 @@ do
local t = { ns=namespace or "", rn=resolved, tg=tag, at=at, dt={}, __p__ = top }
dt[#dt+1] = t
setmetatable(t, mt)
- at = { }
if at.xmlns then
remove(xmlns)
end
+ at = { }
end
local function add_text(text)
if cleanup and #text > 0 then
@@ -243,7 +264,7 @@ do
end
end
local function set_message(txt)
- errorstr = "garbage at the end of the file: " .. txt:gsub("([ \n\r\t]*)","")
+ errorstr = "garbage at the end of the file: " .. gsub(txt,"([ \n\r\t]*)","")
end
local P, S, R, C, V = lpeg.P, lpeg.S, lpeg.R, lpeg.C, lpeg.V
@@ -291,7 +312,7 @@ do
local somecomment = C((1 - endcomment )^0)
local somecdata = C((1 - endcdata )^0)
- function entity(k,v) entities[k] = v end
+ local function entity(k,v) entities[k] = v end
local begindoctype = open * P("!DOCTYPE")
local enddoctype = close
@@ -387,7 +408,7 @@ do
return root and not root.error
end
- xml.error_handler = (logs and logs.report) or (input and input.report) or print
+ xml.error_handler = (logs and logs.report) or (input and logs.report) or print
end
@@ -436,7 +457,7 @@ can speed up things a bit. The second argument is not to be used!</p>
do
- function copy(old,tables)
+ local function copy(old,tables)
if old then
tables = tables or { }
local new = { }
@@ -480,22 +501,19 @@ do
elseif not nocommands then
local ec = e.command
if ec ~= nil then -- we can have all kind of types
-
-if e.special then -- todo test for true/false
- local etg, edt = e.tg, e.dt
- local spc = specialconverter and specialconverter[etg]
- if spc then
---~ print("SPECIAL",etg,table.serialize(specialconverter), spc)
- local result = spc(edt[1])
- if result then
- handle(result)
- return
- else
- -- no need to handle any further
- end
- end
-end
-
+ if e.special then
+ local etg, edt = e.tg, e.dt
+ local spc = specialconverter and specialconverter[etg]
+ if spc then
+ local result = spc(edt[1])
+ if result then
+ handle(result)
+ return
+ else
+ -- no need to handle any further
+ end
+ end
+ end
local xc = xml.command
if xc then
xc(e,ec)
@@ -536,27 +554,17 @@ end
local ats = eat and next(eat) and { } -- type test maybe faster
if ats then
if attributeconverter then
- for k,v in pairs(eat) do
+ for k,v in next, eat do
ats[#ats+1] = format('%s=%q',k,attributeconverter(v))
end
else
- for k,v in pairs(eat) do
+ for k,v in next, eat do
ats[#ats+1] = format('%s=%q',k,v)
end
end
end
- if ern and xml.trace_remap and ern ~= ens then
---~ if ats then
---~ ats[#ats+1] = format("xmlns:remapped='%s'",ern)
---~ else
---~ ats = { format("xmlns:remapped='%s'",ern) }
---~ end
---~ if ats then
---~ ats[#ats+1] = format("remappedns='%s'",ens or '-')
---~ else
---~ ats = { format("remappedns='%s'",ens or '-') }
---~ end
-ens = ern
+ if ern and trace_remap and ern ~= ens then
+ ens = ern
end
if ens ~= "" then
if edt and #edt > 0 then
@@ -600,7 +608,16 @@ ens = ern
handle("<" .. etg .. ">")
end
for i=1,#edt do
- serialize(edt[i],handle,textconverter,attributeconverter,specialconverter,nocommands)
+ local ei = edt[i]
+ if type(ei) == "string" then
+ if textconverter then
+ handle(textconverter(ei))
+ else
+ handle(ei)
+ end
+ else
+ serialize(ei,handle,textconverter,attributeconverter,specialconverter,nocommands)
+ end
end
-- handle(format("</%s>",etg))
handle("</" .. etg .. ">")
@@ -623,7 +640,16 @@ ens = ern
end
else
for i=1,#e do
- serialize(e[i],handle,textconverter,attributeconverter,specialconverter,nocommands)
+ local ei = e[i]
+ if type(ei) == "string" then
+ if textconverter then
+ handle(textconverter(ei))
+ else
+ handle(ei)
+ end
+ else
+ serialize(ei,handle,textconverter,attributeconverter,specialconverter,nocommands)
+ end
end
end
end
@@ -633,15 +659,16 @@ ens = ern
function xml.checkbom(root) -- can be made faster
if root.ri then
local dt, found = root.dt, false
- for k,v in ipairs(dt) do
+ for k=1,#dt do
+ local v = dt[k]
if type(v) == "table" and v.special and v.tg == "@pi" and v.dt:find("xml.*version=") then
found = true
break
end
end
if not found then
- table.insert(dt, 1, { special=true, ns="", tg="@pi@", dt = { "xml version='1.0' standalone='yes'"} } )
- table.insert(dt, 2, "\n" )
+ insert(dt, 1, { special=true, ns="", tg="@pi@", dt = { "xml version='1.0' standalone='yes'"} } )
+ insert(dt, 2, "\n" )
end
end
end
@@ -653,14 +680,14 @@ ens = ern
function xml.tostring(root) -- 25% overhead due to collecting
if root then
- if type(root) == 'string' then
- return root
- elseif next(root) then -- next is faster than type (and >0 test)
- local result = { }
- serialize(root,function(s) result[#result+1] = s end)
- return concat(result,"")
+ if type(root) == 'string' then
+ return root
+ elseif next(root) then -- next is faster than type (and >0 test)
+ local result = { }
+ serialize(root,function(s) result[#result+1] = s end)
+ return concat(result,"")
+ end
end
- end
return ""
end
@@ -725,6 +752,18 @@ function xml.content(root) -- bugged
return (root and root.dt and xml.tostring(root.dt)) or ""
end
+function xml.isempty(root, pattern)
+ if pattern == "" or pattern == "*" then
+ pattern = nil
+ end
+ if pattern then
+ -- todo
+ return false
+ else
+ return not root or not root.dt or #root.dt == 0 or root.dt == ""
+ end
+end
+
--[[ldx--
<p>The next helper erases an element but keeps the table as it is,
and since empty strings are not serialized (effectively) it does
@@ -767,11 +806,16 @@ of <l n='xpath'/> and since we're not compatible we call it <l n='lpath'/>. We
will explain more about its usage in other documents.</p>
--ldx]]--
+local lpathcalls = 0 -- statisctics
+local lpathcached = 0 -- statisctics
+
do
- xml.functions = xml.functions or { }
+ xml.functions = xml.functions or { }
+ xml.expressions = xml.expressions or { }
- local functions = xml.functions
+ local functions = xml.functions
+ local expressions = xml.expressions
local actions = {
[10] = "stay",
@@ -795,34 +839,32 @@ do
[40] = "processing instruction",
}
- --~ local function make_expression(str) --could also be an lpeg
- --~ str = str:gsub("@([a-zA-Z%-_]+)", "(a['%1'] or '')")
- --~ str = str:gsub("position%(%)", "i")
- --~ str = str:gsub("text%(%)", "t")
- --~ str = str:gsub("!=", "~=")
- --~ str = str:gsub("([^=!~<>])=([^=!~<>])", "%1==%2")
- --~ str = str:gsub("([a-zA-Z%-_]+)%(", "functions.%1(")
- --~ return str, loadstring(format("return function(functions,i,a,t) return %s end", str))()
- --~ end
-
-- a rather dumb lpeg
local P, S, R, C, V, Cc = lpeg.P, lpeg.S, lpeg.R, lpeg.C, lpeg.V, lpeg.Cc
- local lp_position = P("position()") / "id"
+ -- instead of using functions we just parse a few names which saves a call
+ -- later on
+
+ local lp_position = P("position()") / "ps"
+ local lp_index = P("index()") / "id"
local lp_text = P("text()") / "tx"
- local lp_name = P("name()") / "((rt.ns~='' and rt.ns..':'..rt.tg) or '')"
- local lp_tag = P("tag()") / "(rt.tg or '')"
- local lp_ns = P("ns()") / "(rt.ns or '')"
+ local lp_name = P("name()") / "(ns~='' and ns..':'..tg)" -- "((rt.ns~='' and rt.ns..':'..rt.tg) or '')"
+ local lp_tag = P("tag()") / "tg" -- (rt.tg or '')
+ local lp_ns = P("ns()") / "ns" -- (rt.ns or '')
local lp_noequal = P("!=") / "~=" + P("<=") + P(">=") + P("==")
local lp_doequal = P("=") / "=="
local lp_attribute = P("@") / "" * Cc("(at['") * R("az","AZ","--","__")^1 * Cc("'] or '')")
- local lp_function = C(R("az","AZ","--","__")^1) * P("(") / function(t)
- if functions[t] then
- return "functions." .. t .. "("
+ local lp_lua_function = C(R("az","AZ","--","__")^1 * (P(".") * R("az","AZ","--","__")^1)^1) * P("(") / function(t) -- todo: better . handling
+ return t .. "("
+ end
+
+ local lp_function = C(R("az","AZ","--","__")^1) * P("(") / function(t) -- todo: better . handling
+ if expressions[t] then
+ return "expressions." .. t .. "("
else
- return "functions.error("
+ return "expressions.error("
end
end
@@ -830,34 +872,45 @@ do
local rparent = lpeg.P(")")
local noparent = 1 - (lparent+rparent)
local nested = lpeg.P{lparent * (noparent + lpeg.V(1))^0 * rparent}
- local value = lpeg.P(lparent * lpeg.C((noparent + nested)^0) * rparent)
+ local value = lpeg.P(lparent * lpeg.C((noparent + nested)^0) * rparent) -- lpeg.P{"("*C(((1-S("()"))+V(1))^0)*")"}
---~ local value = P { "(" * C(((1 - S("()")) + V(1))^0) * ")" }
+ -- if we use a dedicated namespace then we don't need to pass rt and k
local lp_special = (C(P("name")+P("text")+P("tag"))) * value / function(t,s)
- if functions[t] then
+ if expressions[t] then
if s then
- return "functions." .. t .. "(rt,k," .. s ..")"
+ return "expressions." .. t .. "(r,k," .. s ..")"
else
- return "functions." .. t .. "(rt,k)"
+ return "expressions." .. t .. "(r,k)"
end
else
- return "functions.error(" .. t .. ")"
+ return "expressions.error(" .. t .. ")"
end
end
local converter = lpeg.Cs ( (
lp_position +
+ lp_index +
lp_text + lp_name + -- fast one
lp_special +
lp_noequal + lp_doequal +
lp_attribute +
+ lp_lua_function +
lp_function +
1 )^1 )
+ -- expressions,root,rootdt,k,e,edt,ns,tg,idx,hsh[tg] or 1
+
+ local template = [[
+ return function(expressions,r,d,k,e,dt,ns,tg,id,ps)
+ local at, tx = e.at or { }, dt[1] or ""
+ return %s
+ end
+ ]]
+
local function make_expression(str)
str = converter:match(str)
- return str, loadstring(format("return function(functions,id,at,tx,rt,k) return %s end", str))()
+ return str, loadstring(format(template,str))()
end
local map = { }
@@ -880,8 +933,6 @@ do
local bar = P('|')
local hat = P('^')
local valid = R('az', 'AZ', '09') + S('_-')
---~ local name_yes = C(valid^1 + star) * colon * C(valid^1 + star) -- permits ns:* *:tg *:*
---~ local name_nop = C(P(true)) * C(valid^1)
local name_yes = C(valid^1 + star) * colon * C(valid^1 + star) -- permits ns:* *:tg *:*
local name_nop = Cc("*") * C(valid^1)
local name = name_yes + name_nop
@@ -967,7 +1018,7 @@ do
local selector = (
instruction +
- many + any +
+--~ many + any + -- brrr, not here !
parent + stay +
dont_position + position +
dont_match_one_of_and_eq + dont_match_one_of_and_ne +
@@ -979,6 +1030,7 @@ do
has_attribute + has_value +
dont_match_one_of + match_one_of +
dont_match + match +
+ many + any +
crap + empty
)
@@ -1015,9 +1067,9 @@ do
return { map[2] }
end
if m ~= 11 and m ~= 12 and m ~= 13 and m ~= 14 and m ~= 15 and m ~= 16 then
- table.insert(map, 1, { 16 })
+ insert(map, 1, { 16 })
end
- -- print((table.serialize(map)):gsub("[ \n]+"," "))
+ -- print(gsub(table.serialize(map),"[ \n]+"," "))
return map
end
end
@@ -1026,13 +1078,15 @@ do
local cache = { }
function xml.lpath(pattern,trace)
+ lpathcalls = lpathcalls + 1
if type(pattern) == "string" then
local result = cache[pattern]
- if not result then
+ if result == nil then -- can be false which is valid -)
result = compose(pattern)
cache[pattern] = result
+ lpathcached = lpathcached + 1
end
- if trace or xml.trace_lpath then
+ if trace or trace_lpath then
xml.lshow(result)
end
return result
@@ -1041,10 +1095,17 @@ do
end
end
- local fallbackreport = (texio and texio.write) or io.write
+ function xml.cached_patterns()
+ return cache
+ end
+
+-- we run out of locals (limited to 200)
+--
+-- local fallbackreport = (texio and texio.write) or io.write
function xml.lshow(pattern,report)
- report = report or fallbackreport
+-- report = report or fallbackreport
+ report = report or (texio and texio.write) or io.write
local lp = xml.lpath(pattern)
if lp == false then
report(" -: root\n")
@@ -1054,7 +1115,8 @@ do
if type(pattern) == "string" then
report(format("pattern: %s\n",pattern))
end
- for k,v in ipairs(lp) do
+ for k=1,#lp do
+ local v = lp[k]
if #v > 1 then
local t = { }
for i=2,#v do
@@ -1075,7 +1137,8 @@ do
function xml.xshow(e,...) -- also handy when report is given, use () to isolate first e
local t = { ... }
- local report = (type(t[#t]) == "function" and t[#t]) or fallbackreport
+-- local report = (type(t[#t]) == "function" and t[#t]) or fallbackreport
+ local report = (type(t[#t]) == "function" and t[#t]) or (texio and texio.write) or io.write
if e == nil then
report("<!-- no element -->\n")
elseif type(e) ~= "table" then
@@ -1108,22 +1171,25 @@ functions.</p>
do
- local functions = xml.functions
+ local functions = xml.functions
+ local expressions = xml.expressions
- functions.contains = string.find
- functions.find = string.find
- functions.upper = string.upper
- functions.lower = string.lower
- functions.number = tonumber
- functions.boolean = toboolean
+ expressions.contains = string.find
+ expressions.find = string.find
+ expressions.upper = string.upper
+ expressions.lower = string.lower
+ expressions.number = tonumber
+ expressions.boolean = toboolean
- functions.oneof = function(s,...) -- slow
+ expressions.oneof = function(s,...) -- slow
local t = {...} for i=1,#t do if s == t[i] then return true end end return false
end
- functions.error = function(str)
- xml.error_handler("unknown function in lpath expression",str)
+
+ expressions.error = function(str)
+ xml.error_handler("unknown function in lpath expression",str or "?")
return false
end
+
functions.text = function(root,k,n) -- unchecked, maybe one deeper
local t = type(t)
if t == "string" then
@@ -1133,35 +1199,21 @@ do
return (rdt and rdt[k]) or root[k] or ""
end
end
- functions.name = function(root,k,n)
- -- way too fuzzy
- local found
- if not k or not n then
- local ns, tg = root.rn or root.ns or "", root.tg
- if not tg then
- for i=1,#root do
- local e = root[i]
- if type(e) == "table" then
- found = e
- break
- end
- end
- elseif ns ~= "" then
- return ns .. ":" .. tg
- else
- return tg
- end
+
+ functions.name = function(d,k,n) -- ns + tg
+ local found = false
+ n = n or 0
+ if not k then
+ -- not found
elseif n == 0 then
- local e = root[k]
- if type(e) ~= "table" then
- found = e
- end
+ local dk = d[k]
+ found = dk and (type(dk) == "table") and dk
elseif n < 0 then
for i=k-1,1,-1 do
- local e = root[i]
- if type(e) == "table" then
+ local di = d[i]
+ if type(di) == "table" then
if n == -1 then
- found = e
+ found = di
break
else
n = n + 1
@@ -1169,12 +1221,11 @@ do
end
end
else
---~ print(k,n,#root)
- for i=k+1,#root,1 do
- local e = root[i]
- if type(e) == "table" then
+ for i=k+1,#d,1 do
+ local di = d[i]
+ if type(di) == "table" then
if n == 1 then
- found = e
+ found = di
break
else
n = n - 1
@@ -1194,6 +1245,46 @@ do
end
end
+ functions.tag = function(d,k,n) -- only tg
+ local found = false
+ n = n or 0
+ if not k then
+ -- not found
+ elseif n == 0 then
+ local dk = d[k]
+ found = dk and (type(dk) == "table") and dk
+ elseif n < 0 then
+ for i=k-1,1,-1 do
+ local di = d[i]
+ if type(di) == "table" then
+ if n == -1 then
+ found = di
+ break
+ else
+ n = n + 1
+ end
+ end
+ end
+ else
+ for i=k+1,#d,1 do
+ local di = d[i]
+ if type(di) == "table" then
+ if n == 1 then
+ found = di
+ break
+ else
+ n = n - 1
+ end
+ end
+ end
+ end
+ return (found and found.tg) or ""
+ end
+
+ expressions.text = functions.text
+ expressions.name = functions.name
+ expressions.tag = functions.tag
+
local function traverse(root,pattern,handle,reverse,index,parent,wildcard) -- multiple only for tags, not for namespaces
if not root then -- error
return false
@@ -1266,10 +1357,13 @@ do
start, stop, step = stop, start, -1
end
local idx = 0
+ local hsh = { } -- this will slooow down the lot
for k=start,stop,step do -- we used to have functions for all but a case is faster
local e = rootdt[k]
local ns, tg = e.rn or e.ns, e.tg
if tg then
+ -- we can optimize this for simple searches, but it probably does not pay off
+ hsh[tg] = (hsh[tg] or 0) + 1
idx = idx + 1
if command == 30 then
local ns_a, tg_a = action[3], action[4]
@@ -1392,7 +1486,7 @@ do
end
if not action[2] then matched = not matched end
if matched then
- matched = action[6](functions,idx,e.at or { },edt[1],rootdt,k)
+ matched = action[6](expressions,root,rootdt,k,e,edt,ns,tg,idx,hsh[tg] or 1)
end
end
if matched then -- combine tg test and at test
@@ -1564,7 +1658,7 @@ do
local rt, dt, dk
traverse(root, lpath(pattern), function(r,d,k) rt, dt, dk = r, d, k return true end)
local ekat = (dt and dt[dk] and dt[dk].at) or (rt and rt.at)
- return (ekat and (ekat[arguments] or ekat[arguments:gsub("^([\"\'])(.*)%1$","%2")])) or ""
+ return (ekat and (ekat[arguments] or ekat[gsub(arguments,"^([\"\'])(.*)%1$","%2")])) or ""
end
function xml.filters.text(root,pattern,arguments) -- ?? why index, tostring slow
local dtk, rt, dt, dk = xml.filters.index(root,pattern,arguments)
@@ -1626,9 +1720,6 @@ do
function xml.filter(root,pattern)
local kind, a, b, c = parser:match(pattern)
---~ if xml.trace_lpath then
---~ print(pattern,kind,a,b,c)
---~ end
if kind == 1 or kind == 3 then
return (filters[b] or default_filter)(root,a,c)
elseif kind == 2 then
@@ -1867,11 +1958,20 @@ do
local r, d, k, element = m[1], m[2], m[3], m[4]
if not before then k = k + 1 end
if element.tg then
- table.insert(d,k,element) -- untested
- elseif element.dt then
- for _,v in ipairs(element.dt) do -- i added
- table.insert(d,k,v)
- k = k + 1
+ insert(d,k,element) -- untested
+--~ elseif element.dt then
+--~ for _,v in ipairs(element.dt) do -- i added
+--~ insert(d,k,v)
+--~ k = k + 1
+--~ end
+--~ end
+ else
+ local edt = element.dt
+ if edt then
+ for i=1,#edt do
+ insert(d,k,edt[i])
+ k = k + 1
+ end
end
end
end
@@ -1932,7 +2032,7 @@ do
end
if not name then
if ek.at then
- for a in (attribute or "href"):gmatch("([^|]+)") do
+ for a in gmatch(attribute or "href","([^|]+)") do
name = ek.at[a]
if name then break end
end
@@ -1958,15 +2058,27 @@ do
xml.each_element(xmldata, pattern, include)
end
- function xml.strip_whitespace(root, pattern)
+ function xml.strip_whitespace(root, pattern, nolines) -- strips all leading and trailing space !
traverse(root, lpath(pattern), function(r,d,k)
local dkdt = d[k].dt
if dkdt then -- can be optimized
local t = { }
for i=1,#dkdt do
local str = dkdt[i]
- if type(str) == "string" and str:find("^[ \n\r\t]*$") then
- -- stripped
+ if type(str) == "string" then
+
+ if str == "" then
+ -- stripped
+ else
+ if nolines then
+ str = gsub(str,"[ \n\r\t]+"," ")
+ end
+ if str == "" then
+ -- stripped
+ else
+ t[#t+1] = str
+ end
+ end
else
t[#t+1] = str
end
@@ -1976,9 +2088,8 @@ do
end)
end
- function xml.rename_space(root, oldspace, newspace) -- fast variant
+ local function rename_space(root, oldspace, newspace) -- fast variant
local ndt = #root.dt
- local rename = xml.rename_space
for i=1,ndt or 0 do
local e = root[i]
if type(e) == "table" then
@@ -1990,12 +2101,14 @@ do
end
local edt = e.dt
if edt then
- rename(edt, oldspace, newspace)
+ rename_space(edt, oldspace, newspace)
end
end
end
end
+ xml.rename_space = rename_space
+
function xml.remap_tag(root, pattern, newtg)
traverse(root, lpath(pattern), function(r,d,k)
d[k].tg = newtg
@@ -2074,10 +2187,12 @@ put them here instead of loading mode modules there then needed.</p>
--ldx]]--
function xml.gsub(t,old,new)
- if t.dt then
- for k,v in ipairs(t.dt) do
+ local dt = t.dt
+ if dt then
+ for k=1,#dt do
+ local v = dt[k]
if type(v) == "string" then
- t.dt[k] = v:gsub(old,new)
+ dt[k] = gsub(v,old,new)
else
xml.gsub(v,old,new)
end
@@ -2102,9 +2217,9 @@ end
--~ xml.escapes = { ['&'] = '&amp;', ['<'] = '&lt;', ['>'] = '&gt;', ['"'] = '&quot;' }
--~ xml.unescapes = { } for k,v in pairs(xml.escapes) do xml.unescapes[v] = k end
---~ function xml.escaped (str) return str:gsub("(.)" , xml.escapes ) end
---~ function xml.unescaped(str) return str:gsub("(&.-;)", xml.unescapes) end
---~ function xml.cleansed (str) return str:gsub("<.->" , '' ) end -- "%b<>"
+--~ function xml.escaped (str) return (gsub(str,"(.)" , xml.escapes )) end
+--~ function xml.unescaped(str) return (gsub(str,"(&.-;)", xml.unescapes)) end
+--~ function xml.cleansed (str) return (gsub(str,"<.->" , '' )) end -- "%b<>"
do
@@ -2136,6 +2251,10 @@ do
local cleansed = Cs(((P("<") * (1-P(">"))^0 * P(">"))/"" + 1)^0)
+ xml.escaped_pattern = escaped
+ xml.unescaped_pattern = unescaped
+ xml.cleansed_pattern = cleansed
+
function xml.escaped (str) return escaped :match(str) end
function xml.unescaped(str) return unescaped:match(str) end
function xml.cleansed (str) return cleansed :match(str) end
@@ -2168,9 +2287,9 @@ original entity is returned.</p>
do if unicode and unicode.utf8 then
- xml.entities = xml.entities or { } -- xml.entities.handler == function
+ xml.entities = xml.entities or { } -- xml.entity_handler == function
- function xml.entities.handler(e)
+ function xml.entity_handler(e)
return format("[%s]",e)
end
@@ -2180,16 +2299,14 @@ do if unicode and unicode.utf8 then
return char(tonumber(s,16))
end
- local entities = xml.entities -- global entities
-
- function utfize(root)
+ local function utfize(root)
local d = root.dt
for k=1,#d do
local dk = d[k]
if type(dk) == "string" then
-- test prevents copying if no match
- if dk:find("&#x.-;") then
- d[k] = dk:gsub("&#x(.-);",toutf)
+ if find(dk,"&#x.-;") then
+ d[k] = gsub(dk,"&#x(.-);",toutf)
end
else
utfize(dk)
@@ -2200,14 +2317,16 @@ do if unicode and unicode.utf8 then
xml.utfize = utfize
local function resolve(e) -- hex encoded always first, just to avoid mkii fallbacks
- if e:find("#x") then
+ if find(e,"^#x") then
return char(tonumber(e:sub(3),16))
+ elseif find(e,"^#") then
+ return char(tonumber(e:sub(2)))
else
- local ee = entities[e]
+ local ee = xml.entities[e] -- we cannot shortcut this one (is reloaded)
if ee then
return ee
else
- local h = xml.entities.handler
+ local h = xml.entity_handler
return (h and h(e)) or "&" .. e .. ";"
end
end
@@ -2219,8 +2338,8 @@ do if unicode and unicode.utf8 then
for k=1,#d do
local dk = d[k]
if type(dk) == "string" then
- if dk:find("&.-;") then
- d[k] = dk:gsub("&(.-);",resolve)
+ if find(dk,"&.-;") then
+ d[k] = gsub(dk,"&(.-);",resolve)
end
else
resolve_entities(dk)
@@ -2232,24 +2351,24 @@ do if unicode and unicode.utf8 then
xml.resolve_entities = resolve_entities
function xml.utfize_text(str)
- if str:find("&#") then
- return (str:gsub("&#x(.-);",toutf))
+ if find(str,"&#") then
+ return (gsub(str,"&#x(.-);",toutf))
else
return str
end
end
function xml.resolve_text_entities(str) -- maybe an lpeg. maybe resolve inline
- if str:find("&") then
- return (str:gsub("&(.-);",resolve))
+ if find(str,"&") then
+ return (gsub(str,"&(.-);",resolve))
else
return str
end
end
function xml.show_text_entities(str)
- if str:find("&") then
- return (str:gsub("&(.-);","[%1]"))
+ if find(str,"&") then
+ return (gsub(str,"&(.-);","[%1]"))
else
return str
end
@@ -2261,7 +2380,7 @@ do if unicode and unicode.utf8 then
local documententities = root.entities
local allentities = xml.entities
if documententities then
- for k, v in pairs(documententities) do
+ for k, v in next, documententities do
allentities[k] = v
end
end
@@ -2269,6 +2388,13 @@ do if unicode and unicode.utf8 then
end end
+function xml.statistics()
+ return {
+ lpathcalls = lpathcalls,
+ lpathcached = lpathcached,
+ }
+end
+
-- xml.set_text_cleanup(xml.show_text_entities)
-- xml.set_text_cleanup(xml.resolve_text_entities)
@@ -2288,7 +2414,7 @@ end end
--~ </a>
--~ ]])
---~ xml.trace_lpath = true
+--~ xml.settrace("lpath",true)
--~ xml.xshow(xml.first(x,"b[position() > 2 and position() < 5 and text() == 'ok']"))
--~ xml.xshow(xml.first(x,"b[position() > 2 and position() < 5 and text() == upper('ok')]"))