diff options
Diffstat (limited to 'Master/texmf-dist/tex/context/base/lxml-ini.lua')
-rw-r--r-- | Master/texmf-dist/tex/context/base/lxml-ini.lua | 931 |
1 files changed, 639 insertions, 292 deletions
diff --git a/Master/texmf-dist/tex/context/base/lxml-ini.lua b/Master/texmf-dist/tex/context/base/lxml-ini.lua index f6dba95d4ca..0c40a4bafb9 100644 --- a/Master/texmf-dist/tex/context/base/lxml-ini.lua +++ b/Master/texmf-dist/tex/context/base/lxml-ini.lua @@ -6,23 +6,73 @@ if not modules then modules = { } end modules ['lxml-ini'] = { license = "see context related readme files" } --- todo: speed up: remember last index/match combination +local utf = unicode.utf8 -local texsprint, texprint = tex.sprint or print, tex.print or print -local format, concat = string.format, table.concat +local texsprint, texprint, utfchar = tex.sprint or print, tex.print or print, utf.char +local format, concat, insert, remove = string.format, table.concat, table.insert, table.remove local type, next, tonumber = type, next, tonumber +local ctxcatcodes = tex.ctxcatcodes +local texcatcodes = tex.texcatcodes +local vrbcatcodes = tex.vrbcatcodes + +local trace_setups = false trackers.register("lxml.setups", function(v) trace_setups = v end) +local trace_loading = false trackers.register("lxml.loading", function(v) trace_loading = v end) + +-- for the moment here + +function table.insert_before_value(t,value,extra) + for i=1,#t do + if t[i] == extra then + remove(t,i) + end + end + for i=1,#t do + if t[i] == value then + insert(t,i,extra) + return + end + end + insert(t,1,extra) +end + +function table.insert_after_value(t,value,extra) + for i=1,#t do + if t[i] == extra then + remove(t,i) + end + end + for i=1,#t do + if t[i] == value then + insert(t,i+1,extra) + return + end + end + insert(t,#t+1,extra) +end + +-- todo: speed up: remember last index/match combination + +local traverse, lpath = xml.traverse, xml.lpath + +local xmlfilter, xmlfirst, xmllast, xmlall = xml.filter, xml.first, xml.last, xml.all +local xmlcollect, xmlcontent, xmlcollect_texts, xmlcollect_tags, xmlcollect_elements = xml.collect, xml.content, xml.collect_texts, xml.collect_tags, xml.collect_elements +local xmlattribute, xmlindex = xml.filters.attribute, xml.filters.index +local xmlelements = xml.elements + document = document or { } document.xml = document.xml or { } -- todo: loaded and myself per document so that we can garbage collect buffers -lxml = { } +lxml = lxml or { } lxml.loaded = { } lxml.myself = { } +lxml.n = 0 local loaded = lxml.loaded local myself = lxml.myself +local stack = lxml.stack lxml.self = myself -- be backward compatible for a while @@ -48,25 +98,64 @@ do local lf = lpeg.P("\n") local space = lpeg.S(" \t\f\v") local newline = crlf + cr + lf - local spacing = space^0 * newline * space^0 + local spacing = newline * space^0 local content = lpeg.C((1-spacing)^1) local verbose = lpeg.C((1-(space+newline))^1) + -- local capture = ( + -- newline^2 * lpeg.Cc("") / texprint + + -- newline * lpeg.Cc(" ") / texsprint + + -- content / texsprint + -- )^0 + local capture = ( - newline^2 * lpeg.Cc("") / texprint + - newline * lpeg.Cc(" ") / texsprint + - content / texsprint + space^0 * newline^2 * lpeg.Cc("") / texprint + + space^0 * newline * space^0 * lpeg.Cc(" ") / texsprint + + content / texsprint )^0 + local forceraw, rawroot = false, nil + + function lxml.startraw() + forceraw = true + end + function lxml.stopraw() + forceraw = false + end + function lxml.rawroot() + return rawroot + end + function lxml.rawpath(rootid) + if rawroot and type(rawroot) == "table" then + local text, path, rp + if not rawroot.dt then + text, path, rp = "text", "", rawroot[0] + else + path, rp = "tree", "", rawroot.__p__ + end + while rp do + local rptg = rp.tg + if rptg then + path = rptg .. "/" .. path + end + rp = rp.__p__ + end + return { rootid, "/" .. path, text } + end + end + local function sprint(root) if not root then + --~ rawroot = false -- quit else local tr = type(root) if tr == "string" then -- can also be result of lpath + --~ rawroot = false capture:match(root) elseif tr == "table" then - serialize(root,sprint,nil,nil,specialhandler) + rawroot = forceraw and root + serialize(root,sprint,nil,nil,specialhandler,forceraw) end end end @@ -91,13 +180,19 @@ do function xml.cprint(root) -- content if not root then +--~ rawroot = false -- quit elseif type(root) == 'string' then +--~ rawroot = false capture:match(root) - elseif root.dt then -- the main one - serialize(root.dt,sprint,nil,nil,specialhandler) - else -- probably dt - serialize(root,sprint,nil,nil,specialhandler) + else + local rootdt = root.dt + rawroot = forceraw and root + if rootdt then -- the main one + serialize(rootdt,sprint,nil,nil,specialhandler,forceraw) + else -- probably dt + serialize(root,sprint,nil,nil,specialhandler,forceraw) + end end end @@ -113,11 +208,14 @@ do function lines(root) if not root then +--~ rawroot = false -- quit elseif type(root) == 'string' then +--~ rawroot = false capture:match(root) elseif next(root) then -- tr == 'table' - serialize(root, lines) + rawroot = forceraw and root + serialize(root,lines,forceraw) end end @@ -135,15 +233,15 @@ do local aftercommand = "" local capture = ( - newline / function( ) texsprint(tex.texcatcodes,linecommand .. "{}") end + - verbose / function(s) texsprint(tex.vrbcatcodes,s) end + - space / function( ) texsprint(tex.texcatcodes,spacecommand .. "{}") end + newline / function( ) texsprint(texcatcodes,linecommand,"{}") end + + verbose / function(s) texsprint(vrbcatcodes,s) end + + space / function( ) texsprint(texcatcodes,spacecommand,"{}") end )^0 - function toverbatim(str) - if beforecommand then texsprint(tex.texcatcodes,beforecommand .. "{}") end + local function toverbatim(str) + if beforecommand then texsprint(texcatcodes,beforecommand,"{}") end capture:match(str) - if aftercommand then texsprint(tex.texcatcodes,aftercommand .. "{}") end + if aftercommand then texsprint(texcatcodes,aftercommand,"{}") end end function lxml.set_verbatim(before,after,obeyedline,obeyedspace) @@ -158,11 +256,27 @@ do specialhandler['@cd@'] = nil end + -- local capture = (space^0*newline)^0 * capture * (space+newline)^0 * -1 + + local function toverbatim(str) + if beforecommand then texsprint(texcatcodes,beforecommand,"{}") end + -- todo: add this to capture + str = str:gsub("^[ \t]+[\n\r]+","") + str = str:gsub("[ \t\n\r]+$","") + capture:match(str) + if aftercommand then texsprint(texcatcodes,aftercommand,"{}") end + end + function lxml.verbatim(id,before,after) local root = get_id(id) - if before then texsprint(tex.ctxcatcodes,format("%s[%s]",before,root.tg)) end - serialize(root.dt,toverbatim,nil,nil,nil,true) -- was root - if after then texsprint(tex.ctxcatcodes,after) end + if root then + if before then texsprint(ctxcatcodes,format("%s[%s]",before,root.tg)) end + -- serialize(root.dt,toverbatim,nil,nil,nil,true) -- was root + local t = { } + serialize(root.dt,function(s) t[#t+1] = s end,nil,nil,nil,true) -- was root + toverbatim(table.concat(t,"")) + if after then texsprint(ctxcatcodes,after) end + end end function lxml.inlineverbatim(id) lxml.verbatim(id,"\\startxmlinlineverbatim","\\stopxmlinlineverbatim") @@ -192,12 +306,25 @@ do pihandlers[#pihandlers+1] = function(str) -- local kind, class, key, value = parser:match(str) - texsprint(tex.ctxcatcodes,format("\\xmlcontextdirective{%s}{%s}{%s}{%s}",parser:match(str))) + if str then + local a, b, c, d = parser:match(str) + if d then + texsprint(ctxcatcodes,format("\\xmlcontextdirective{%s}{%s}{%s}{%s}",a,b,c,d)) + end + end end - -- print(contextdirective("context-mathml-directive function reduction yes yes ")) + -- print(contextdirective("context-mathml-directive function reduction yes ")) -- print(contextdirective("context-mathml-directive function ")) + function lxml.main(id) + serialize(get_id(id),sprint,nil,nil,specialhandler) -- the real root (@rt@) + end + + specialhandler['@dt@'] = function() + -- nothing + end + end local xmlsprint = xml.sprint @@ -205,35 +332,54 @@ local xmltprint = xml.tprint -- redefine xml load -xml.originalload = xml.load +xml.originalload = xml.originalload or xml.load + +local starttiming, stoptiming = statistics.starttiming, statistics.stoptiming function xml.load(filename) - input.starttiming(xml) - local xmldata = xml.convert((filename and input.loadtexfile(texmf.instance,filename)) or "") - input.stoptiming(xml) + lxml.n = lxml.n + 1 + starttiming(xml) + local xmldata = xml.convert((filename and resolvers.loadtexfile(filename)) or "") + stoptiming(xml) return xmldata end function lxml.load(id,filename) + lxml.n = lxml.n + 1 + filename = commands.preparedfile(filename) + if trace_loading then + commands.writestatus("lxml","loading file: %s",filename) + end loaded[id] = xml.load(filename) return loaded[id], filename end +function lxml.register(id,xmltable) + lxml.n = lxml.n + 1 + loaded[id] = xmltable + return xmltable +end + function lxml.include(id,pattern,attribute,recurse) - input.starttiming(xml) - xml.include(get_id(id),pattern,attribute,recurse,function(name) return (name and input.loadtexfile(texmf.instance,name)) or "" end) - input.stoptiming(xml) + starttiming(xml) + xml.include(get_id(id),pattern,attribute,recurse,function(filename) + if filename then + filename = commands.preparedfile(filename) + if trace_loading then + commands.writestatus("lxml","including file: %s",filename) + end + return resolvers.loadtexfile(filename) or "" + else + return "" + end + end) + stoptiming(xml) end function lxml.utfize(id) xml.utfize(get_id(id)) end -local xmlfilter, xmlfirst, xmllast, xmlall = xml.filter, xml.first, xml.last, xml.all -local xmlcollect, xmlcontent, xmlcollect_texts, xmlcollect_tags = xml.collect, xml.content, xml.collect_texts, xml.collect_tags -local xmlattribute, xmlindex = xml.filters.attribute, xml.filters.index -local xmlelements = xml.elements - function lxml.filter(id,pattern) xmlsprint(xmlfilter(get_id(id),pattern)) end @@ -244,20 +390,28 @@ function lxml.last(id,pattern) xmlsprint(xmllast(get_id(id),pattern)) end function lxml.all(id,pattern) -xmltprint(xmlcollect(get_id(id),pattern)) ---~ faster, no intermediate table, we need to clean up l-xml ---~ xml.traverse(get_id(id), xml.lpath(pattern), function(r,d,k) xmlsprint(d[k]) return false end) + -- xmltprint(xmlcollect(get_id(id),pattern)) + traverse(get_id(id), lpath(pattern), function(r,d,k) + -- to be checked for root:: + if d then + xmlsprint(d[k]) + else -- new, maybe wrong +--~ xmlsprint(r) + end + return false + end) end -function lxml.nonspace(id,pattern) +function lxml.nonspace(id,pattern) -- slow, todo loop xmltprint(xmlcollect(get_id(id),pattern,true)) end -function lxml.content(id,pattern) - xmlsprint(xmlcontent(get_id(id),pattern) or "") -end -function lxml.strip(id,pattern) - xml.strip(get_id(id),pattern) +--~ function lxml.content(id) +--~ xmlsprint(xmlcontent(get_id(id)) or "") +--~ end + +function lxml.strip(id,pattern,nolines) + xml.strip(get_id(id),pattern,nolines) end function lxml.text(id,pattern) @@ -274,26 +428,89 @@ end function lxml.raw(id,pattern) -- the content, untouched by commands local c = xmlfilter(get_id(id),pattern) if c then - texsprint(concat(c.dt,"")) + xml.serialize(c.dt,texsprint,nil,nil,nil,true) end end function lxml.snippet(id,i) - local e = lxml.id(id) + local e = get_id(id) if e then local edt = e.dt - xmlsprint((edt and edt[i]) or "") + if edt then + xmlsprint(edt[i]) + end + end +end + +function xml.element(e,n) + if e then + local edt = e.dt + if edt then + if n > 0 then + for i=1,#edt do + local ei = edt[i] + if type(ei) == "table" then + if n == 1 then + xmlsprint(ei) + return + else + n = n - 1 + end + end + end + elseif n < 0 then + for i=#edt,1,-1 do + local ei = edt[i] + if type(ei) == "table" then + if n == -1 then + xmlsprint(ei) + return + else + n = n + 1 + end + end + end + end + end end end -function lxml.stripped(id,pattern) +function lxml.element(id,n) + xml.element(get_id(id),n) +end + +function lxml.stripped(id,pattern,nolines) local str = xmlcontent(get_id(id),pattern) or "" - xmlsprint((str:gsub("^%s*(.-)%s*$","%1"))) + str = str:gsub("^%s*(.-)%s*$","%1") + if nolines then + str = str:gsub("%s+"," ") + end + xmlsprint(str) end function lxml.flush(id) - xmlsprint(get_id(id).dt) + id = get_id(id) + local dt = id and id.dt + if dt then + xmlsprint(dt) + end end + +--~ function lxml.strip(id,flush) +--~ local dt = get_id(id).dt +--~ local str = dt[1] +--~ if type(str) == "string" then +--~ dt[1] = str:gsub("^ *","") +--~ end +--~ str = dt[#dt] +--~ if type(str) == "string" then +--~ dt[#dt] = str:gsub(" *$","") +--~ end +--~ if flush then +--~ xmlsprint(dt) +--~ end +--~ end + function lxml.direct(id) xmlsprint(get_id(id)) end @@ -310,11 +527,26 @@ end function lxml.count(id,pattern) texsprint(xml.count(get_id(id),pattern) or 0) end +function lxml.nofelements(id) + local e = get_id(id) + local edt = e.dt + if edt and type(edt) == "table" then + local n = 0 + for i=1,#edt do + if type(edt[i]) == "table" then + n = n + 1 + end + end + texsprint(n) + else + texsprint(0) + end +end function lxml.name(id) -- or remapped name? -> lxml.info, combine local r = get_id(id) - local ns = t.rn or r.ns or "" + local ns = r.rn or r.ns or "" if ns ~= "" then - texsprint(ns .. ":" .. r.tg) + texsprint(ns,":",r.tg) else texsprint(r.tg) end @@ -332,7 +564,7 @@ end --~ end function lxml.concatrange(id,what,start,stop,separator,lastseparator) -- test this on mml - local t = xml.collect_elements(lxml.id(id),what,true) -- ignorespaces + local t = xmlcollect_elements(lxml.id(id),what,true) -- ignorespaces local separator = separator or "" local lastseparator = lastseparator or separator or "" start, stop = (start == "" and 1) or tonumber(start) or 1, (stop == "" and #t) or tonumber(stop) or #t @@ -342,9 +574,9 @@ function lxml.concatrange(id,what,start,stop,separator,lastseparator) -- test th if i == #t then -- nothing elseif i == #t-1 and lastseparator ~= "" then - texsprint(tex.ctxcatcodes,lastseparator) + texsprint(ctxcatcodes,lastseparator) elseif separator ~= "" then - texsprint(tex.ctxcatcodes,separator) + texsprint(ctxcatcodes,separator) end end end @@ -372,7 +604,7 @@ function xml.command(root, command) -- setup local n = #myself + 1 myself[n] = root - texsprint(tex.ctxcatcodes,format("\\xmlsetup{%i}{%s}",n,command)) + texsprint(ctxcatcodes,format("\\xmlsetup{%i}{%s}",n,command)) elseif tc == "function" then -- function command(root) @@ -392,29 +624,28 @@ function lxml.setaction(id,pattern,action) end end -lxml.trace_setups = false - function lxml.setsetup(id,pattern,setup) - local trace = lxml.trace_setups if not setup or setup == "" or setup == "*" or setup == "-" or setup == "+" then for rt, dt, dk in xmlelements(get_id(id),pattern) do local dtdk = dt and dt[dk] or rt local ns, tg = dtdk.rn or dtdk.ns, dtdk.tg - local command = (ns == "" and tg) or (ns .. ":" .. tg) - if setup == "-" then - dtdk.command = false - if trace then - texio.write_nl(format("lpath matched -> %s -> skipped", command)) - end - elseif setup == "+" then - dtdk.command = true - if trace then - texio.write_nl(format("lpath matched -> %s -> text", command)) - end - else - dtdk.command = command - if trace then - texio.write_nl(format("lpath matched -> %s -> %s", command, command)) + if tg then -- to be sure + local command = (ns == "" and tg) or (ns .. ":" .. tg) + if setup == "-" then + dtdk.command = false + if trace then + logs.report("lxml","lpath matched -> %s -> skipped", command) + end + elseif setup == "+" then + dtdk.command = true + if trace_setups then + logs.report("lxml","lpath matched -> %s -> text", command) + end + else + dtdk.command = command + if trace_setups then + logs.report("lxml","lpath matched -> %s -> %s", command, command) + end end end end @@ -426,46 +657,46 @@ function lxml.setsetup(id,pattern,setup) local ns, tg = dtdk.rn or dtdk.ns, dtdk.tg if b == "-" then dtdk.command = false - if trace then + if trace_setups then if ns == "" then - texio.write_nl(format("lpath matched -> %s -> skipped", tg)) + logs.report("lxml","lpath matched -> %s -> skipped", tg) else - texio.write_nl(format("lpath matched -> %s:%s -> skipped", ns, tg)) + logs.report("lxml","lpath matched -> %s:%s -> skipped", ns, tg) end end elseif b == "+" then dtdk.command = true - if trace then + if trace_setups then if ns == "" then - texio.write_nl(format("lpath matched -> %s -> text", tg)) + logs.report("lxml","lpath matched -> %s -> text", tg) else - texio.write_nl(format("lpath matched -> %s:%s -> text", ns, tg)) + logs.report("lxml","lpath matched -> %s:%s -> text", ns, tg) end end else dtdk.command = a .. tg - if trace then + if trace_setups then if ns == "" then - texio.write_nl(format("lpath matched -> %s -> %s", tg, dtdk.command)) + logs.report("lxml","lpath matched -> %s -> %s", tg, dtdk.command) else - texio.write_nl(format("lpath matched -> %s:%s -> %s", ns, tg, dtdk.command)) + logs.report("lxml","lpath matched -> %s:%s -> %s", ns, tg, dtdk.command) end end end end else - if trace then - texio.write_nl(format("lpath pattern -> %s -> %s", pattern, setup)) + if trace_setups then + logs.report("lxml","lpath pattern -> %s -> %s", pattern, setup) end for rt, dt, dk in xmlelements(get_id(id),pattern) do local dtdk = (dt and dt[dk]) or rt dtdk.command = setup - if trace then + if trace_setups then local ns, tg = dtdk.rn or dtdk.ns, dtdk.tg if ns == "" then - texio.write_nl(format("lpath matched -> %s -> %s", tg, setup)) + logs.report("lxml","lpath matched -> %s -> %s", tg, setup) else - texio.write_nl(format("lpath matched -> %s:%s -> %s", ns, tg, setup)) + logs.report("lxml","lpath matched -> %s:%s -> %s", ns, tg, setup) end end end @@ -482,7 +713,7 @@ function lxml.idx(id,pattern,i) -- hm, hashed, needed? r.patterns = rp end if not rp[pattern] then - rp[pattern] = xml.collect_elements(r,pattern) -- dd, rr + rp[pattern] = xmlcollect_elements(r,pattern) -- dd, rr end local rpi = rp[pattern] and rp[pattern][i] if rpi then @@ -502,124 +733,118 @@ function lxml.info(id) texsprint(tg) end -do - - local traverse = xml.traverse - local lpath = xml.lpath - local function command(root,pattern,cmd) -- met zonder '' - cmd = cmd:gsub("^([\'\"])(.-)%1$", "%2") - traverse(root, lpath(pattern), function(r,d,k) - -- this can become pretty large - local m = (d and d[k]) or r -- brrr this r, maybe away -if type(m) == "table" then -- probbaly a bug +local function command(root,pattern,cmd) -- met zonder '' + cmd = cmd:gsub("^([\'\"])(.-)%1$", "%2") + traverse(root, lpath(pattern), function(r,d,k) + -- this can become pretty large + local m = (d and d[k]) or r -- brrr this r, maybe away + if type(m) == "table" then -- probably a bug local n = #myself + 1 myself[n] = m - texsprint(tex.ctxcatcodes,format("\\xmlsetup{%s}{%s}",n,cmd)) + texsprint(ctxcatcodes,format("\\xmlsetup{%s}{%s}",n,cmd)) + end + end) end - end) - end - xml.filters.command = command +xml.filters.command = command - function lxml.command(id,pattern,cmd) - command(get_id(id),pattern,cmd) - end +function lxml.command(id,pattern,cmd) + command(get_id(id),pattern,cmd) +end +local function dofunction(root,pattern,fnc) + traverse(root, lpath(pattern), xml.functions[fnc]) -- r, d, t end -do +xml.filters["function"] = dofunction + +--~ <?xml version="1.0" standalone="yes"?> +--~ <!-- demo.cdx --> +--~ <directives> +--~ <!-- +--~ <directive attribute='id' value="100" setup="cdx:100"/> +--~ <directive attribute='id' value="101" setup="cdx:101"/> +--~ --> +--~ <!-- +--~ <directive attribute='cdx' value="colors" element="cals:table" setup="cdx:cals:table:colors"/> +--~ <directive attribute='cdx' value="vertical" element="cals:table" setup="cdx:cals:table:vertical"/> +--~ <directive attribute='cdx' value="noframe" element="cals:table" setup="cdx:cals:table:noframe"/> +--~ --> +--~ <directive attribute='cdx' value="*" element="cals:table" setup="cdx:cals:table:*"/> +--~ </directives> + +lxml.directives = { } + +local data = { + setup = { }, + before = { }, + after = { } +} - --~ <?xml version="1.0" standalone="yes"?> - --~ <!-- demo.cdx --> - --~ <directives> - --~ <!-- - --~ <directive attribute='id' value="100" setup="cdx:100"/> - --~ <directive attribute='id' value="101" setup="cdx:101"/> - --~ --> - --~ <!-- - --~ <directive attribute='cdx' value="colors" element="cals:table" setup="cdx:cals:table:colors"/> - --~ <directive attribute='cdx' value="vertical" element="cals:table" setup="cdx:cals:table:vertical"/> - --~ <directive attribute='cdx' value="noframe" element="cals:table" setup="cdx:cals:table:noframe"/> - --~ --> - --~ <directive attribute='cdx' value="*" element="cals:table" setup="cdx:cals:table:*"/> - --~ </directives> - - lxml.directives = { } - - local data = { - setup = { }, - before = { }, - after = { } - } - - function lxml.directives.load(filename) - if texmf then - local fullname = input.find_file(texmf.instance,filename) or "" - if fullname ~= "" then - filename = fullname - end - end - local root = xml.load(filename) - for r, d, k in xmlelements(root,"directive") do - local dk = d[k] - local at = dk.at - local attribute, value, element = at.attribute or "", at.value or "", at.element or '*' - local setup, before, after = at.setup or "", at.before or "", at.after or "" - if attribute ~= "" and value ~= "" then - local key = format("%s::%s::%s",element,attribute,value) - local t = data[key] or { } - if setup ~= "" then t.setup = setup end - if before ~= "" then t.before = before end - if after ~= "" then t.after = after end - data[key] = t - end +function lxml.directives.load(filename) + local fullname = resolvers.find_file(filename) or "" + if fullname ~= "" then + filename = fullname + end + local root = xml.load(filename) + for r, d, k in xmlelements(root,"directive") do + local dk = d[k] + local at = dk.at + local attribute, value, element = at.attribute or "", at.value or "", at.element or '*' + local setup, before, after = at.setup or "", at.before or "", at.after or "" + if attribute ~= "" and value ~= "" then + local key = format("%s::%s::%s",element,attribute,value) + local t = data[key] or { } + if setup ~= "" then t.setup = setup end + if before ~= "" then t.before = before end + if after ~= "" then t.after = after end + data[key] = t end end +end - function lxml.directives.setup(root,attribute,element) - lxml.directives.handle_setup('setup',root,attribute,element) - end - function lxml.directives.before(root,attribute,element) - lxml.directives.handle_setup('before',root,attribute,element) - end - function lxml.directives.after(root,attribute,element) - lxml.directives.handle_setup('after',root,attribute,element) - end +function lxml.directives.setup(root,attribute,element) + lxml.directives.handle_setup('setup',root,attribute,element) +end +function lxml.directives.before(root,attribute,element) + lxml.directives.handle_setup('before',root,attribute,element) +end +function lxml.directives.after(root,attribute,element) + lxml.directives.handle_setup('after',root,attribute,element) +end - function lxml.directives.handle_setup(category,root,attribute,element) - root = get_id(root) - attribute = attribute - if attribute then - local value = root.at[attribute] - if value then - if not element then - local ns, tg = root.rn or root.ns, root.tg - if ns == "" then - element = tg - else - element = ns .. ':' .. tg - end +function lxml.directives.handle_setup(category,root,attribute,element) + root = get_id(root) + attribute = attribute + if attribute then + local value = root.at[attribute] + if value then + if not element then + local ns, tg = root.rn or root.ns, root.tg + if ns == "" then + element = tg + else + element = ns .. ':' .. tg end - local setup = data[format("%s::%s::%s",element,attribute,value)] + end + local setup = data[format("%s::%s::%s",element,attribute,value)] + if setup then + setup = setup[category] + end + if setup then + texsprint(ctxcatcodes,format("\\directsetup{%s}",setup)) + else + setup = data[format("%s::%s::*",element,attribute)] if setup then setup = setup[category] end if setup then - texsprint(tex.ctxcatcodes,format("\\directsetup{%s}",setup)) - else - setup = data[format("%s::%s::*",element,attribute)] - if setup then - setup = setup[category] - end - if setup then - texsprint(tex.ctxcatcodes,format("\\directsetup{%s}",setup:gsub('%*',value))) - end + texsprint(ctxcatcodes,format("\\directsetup{%s}",setup:gsub('%*',value))) end end end end - end function xml.getbuffer(name) -- we need to make sure that commands are processed @@ -633,16 +858,20 @@ function lxml.loadbuffer(id,name) if not name or name == "" then name = tex.jobname end - input.starttiming(xml) + starttiming(xml) loaded[id] = xml.convert(buffers.collect(name or id,"\n")) - input.stoptiming(xml) + stoptiming(xml) return loaded[id], name or id end function lxml.loaddata(id,str) - input.starttiming(xml) + starttiming(xml) loaded[id] = xml.convert(str or "") - input.stoptiming(xml) + stoptiming(xml) + return loaded[id], id +end + +function lxml.loadregistered(id) return loaded[id], id end @@ -651,141 +880,259 @@ end lxml.set_verbatim("\\xmlcdatabefore", "\\xmlcdataafter", "\\xmlcdataobeyedline", "\\xmlcdataobeyedspace") lxml.set_cdata() -do - - local traced = { } +local traced = { } - function lxml.trace_text_entities(str) - return str:gsub("&(.-);",function(s) - traced[s] = (traced[s] or 0) + 1 - return "["..s.."]" - end) - end +function lxml.trace_text_entities(str) + return str:gsub("&(.-);",function(s) + traced[s] = (traced[s] or 0) + 1 + return "["..s.."]" + end) +end - function lxml.show_text_entities() - for k,v in ipairs(table.sortedkeys(traced)) do - local h = v:match("^#x(.-)$") - if h then - local d = tonumber(h,16) - local u = unicode.utf8.char(d) - texio.write_nl(format("entity: %s / %s / %s / n=%s",h,d,u,traced[v])) - else - texio.write_nl(format("entity: %s / n=%s",v,traced[v])) - end +function lxml.show_text_entities() + for k,v in ipairs(table.sortedkeys(traced)) do + local h = v:match("^#x(.-)$") + if h then + local d = tonumber(h,16) + local u = utfchar(d) + logs.report("lxml","entity: %s / %s / %s / n=%s",h,d,u,traced[v]) + else + logs.report("lxml","entity: %s / n=%s",v,traced[v]) end end - end --- yes or no ... +local error_entity_handler = function(s) return format("[%s]",s) end +local element_entity_handler = function(s) return format("<ctx:e n='%s'/>",s) end -do +function lxml.set_mkii_entityhandler() + xml.entity_handler = error_entity_handler + xml.set_text_cleanup() +end +function lxml.set_mkiv_entityhandler() + xml.entity_handler = element_entity_handler + xml.set_text_cleanup(xml.resolve_text_entities) +end +function lxml.reset_entityhandler() + xml.entity_handler = error_entity_handler + xml.set_text_cleanup() +end - local function with_elements_only(e,handle) - if e and handle then - local etg = e.tg - if etg then - if e.special and etg ~= "@rt@" then - if resthandle then - resthandle(e) - end - else - local edt = e.dt - if edt then - for i=1,#edt do - local e = edt[i] - if type(e) == "table" then - handle(e) - with_elements_only(e,handle) - end +local function with_elements_only(e,handle) + if e and handle then + local etg = e.tg + if etg then + if e.special and etg ~= "@rt@" then + if resthandle then + resthandle(e) + end + else + local edt = e.dt + if edt then + for i=1,#edt do + local e = edt[i] + if type(e) == "table" then + handle(e) + with_elements_only(e,handle) end end end end end end +end - local function with_elements_only(e,handle,depth) - if e and handle then - local edt = e.dt - if edt then - depth = depth or 0 - for i=1,#edt do - local e = edt[i] - if type(e) == "table" then - handle(e,depth) - with_elements_only(e,handle,depth+1) - end + local function with_elements_only(e,handle,depth) + if e and handle then + local edt = e.dt + if edt then + depth = depth or 0 + for i=1,#edt do + local e = edt[i] + if type(e) == "table" then + handle(e,depth) + with_elements_only(e,handle,depth+1) end end end end +end - xml.with_elements_only = with_elements_only +xml.with_elements_only = with_elements_only - local function to_text(e) - if e.command == nil then - local etg = e.tg - if etg and e.special and etg ~= "@rt@" then - e.command = false -- i.e. skip - else - e.command = true -- i.e. no <self></self> - end - end - end - local function to_none(e) - if e.command == nil then +local function to_text(e) + if e.command == nil then + local etg = e.tg + if etg and e.special and etg ~= "@rt@" then e.command = false -- i.e. skip + else + e.command = true -- i.e. no <self></self> end end +end +local function to_none(e) + if e.command == nil then + e.command = false -- i.e. skip + end +end + +-- can be made faster: just recurse over whole table, todo - -- can be made faster: just recurse over whole table, todo +function lxml.set_command_to_text(id) + xml.with_elements_only(get_id(id),to_text) +end + +function lxml.set_command_to_none(id) + xml.with_elements_only(get_id(id),to_none) +end - function lxml.set_command_to_text(id) - xml.with_elements_only(get_id(id),to_text) +function lxml.get_command_status(id) + local status, stack = {}, {} + local function get(e,d) + local ns, tg = e.ns, e.tg + local name = tg + if ns ~= "" then name = ns .. ":" .. tg end + stack[d] = name + local ec = e.command + if ec == true then + ec = "system: text" + elseif ec == false then + ec = "system: skip" + elseif ec == nil then + ec = "system: not set" + elseif type(ec) == "string" then + ec = "setup: " .. ec + else -- function + ec = tostring(ec) + end + local tag = table.concat(stack," => ",1,d) + local s = status[tag] + if not s then + s = { } + status[tag] = s + end + s[ec] = (s[ec] or 0) + 1 end + if id then + xml.with_elements_only(get_id(id),get) + return status + else + local t = { } + for id, _ in pairs(loaded) do + t[id] = lxml.get_command_status(id) + end + return t + end +end + +local setups = { } - function lxml.set_command_to_none(id) - xml.with_elements_only(get_id(id),to_none) +function lxml.installsetup(what,document,setup,where) + document = document or "*" + local sd = setups[document] + if not sd then sd = { } setups[document] = sd end + for k=1,#sd do + if sd[k] == setup then sd[k] = nil break end end + if what == 1 then + if trace_loading then + commands.writestatus("lxml","prepending setup %s for %s",setup,document) + end + insert(sd,1,setup) + elseif what == 2 then + if trace_loading then + commands.writestatus("lxml","appending setup %s for %s",setup,document) + end + insert(sd,setup) + elseif what == 3 then + if trace_loading then + commands.writestatus("lxml","inserting setup %s for %s before %s",setup,document,where) + end + table.insert_before_value(sd,setup,where) + elseif what == 4 then + if trace_loading then + commands.writestatus("lxml","inserting setup %s for %s after %s",setup,document,where) + end + table.insert_after_value(sd,setup,where) + end +end - function lxml.get_command_status(id) - local status, stack = {}, {} - local function get(e,d) - local ns, tg = e.ns, e.tg - local name = tg - if ns ~= "" then name = ns .. ":" .. tg end - stack[d] = name - local ec = e.command - if ec == true then - ec = "system: text" - elseif ec == false then - ec = "system: skip" - elseif ec == nil then - ec = "system: not set" - elseif type(ec) == "string" then - ec = "setup: " .. ec - else -- function - ec = tostring(ec) - end - local tag = table.concat(stack," => ",1,d) - local s = status[tag] - if not s then - s = { } - status[tag] = s +function lxml.flushsetups(...) + local done = { } + for _, document in ipairs({...}) do + local sd = setups[document] + if sd then + for k=1,#sd do + local v= sd[k] + if not done[v] then + if trace_loading then + commands.writestatus("lxml","applying setup %02i = %s to %s",k,v,document) + end + texsprint(ctxcatcodes,format("\\directsetup{%s}",v)) + done[v] = true + end end - s[ec] = (s[ec] or 0) + 1 + elseif trace_loading then + commands.writestatus("lxml","no setups for %s",document) end - if id then - xml.with_elements_only(get_id(id),get) - return status - else - local t = { } - for id, _ in pairs(lxml.loaded) do - t[id] = lxml.get_command_status(id) + end +end + +function lxml.resetsetups(document) + if trace_loading then + commands.writestatus("lxml","resetting all setups for %s",document) + end + setups[document] = { } +end + +function lxml.removesetup(document,setup) + local s = setups[document] + if s then + for i=1,#s do + if s[i] == setup then + if trace_loading then + commands.writestatus("lxml","removing setup %s for %s",setup,document) + end + remove(t,i) + break end - return t end end - end + +local found, isempty = xml.found, xml.isempty + +function lxml.doif (id,pattern) commands.doif (found(get_id(id),pattern,false)) end +function lxml.doifnot (id,pattern) commands.doifnot (found(get_id(id),pattern,false)) end +function lxml.doifelse (id,pattern) commands.doifelse(found(get_id(id),pattern,false)) end + +-- todo: if no second arg or second arg == "" then quick test + +function lxml.doiftext (id,pattern) commands.doif (found (get_id(id),pattern,true)) end +function lxml.doifnottext (id,pattern) commands.doifnot (found (get_id(id),pattern,true)) end +function lxml.doifelsetext (id,pattern) commands.doifelse(found (get_id(id),pattern,true)) end + +-- special case: "*" and "" -> self else lpath lookup + +function lxml.doifelseempty(id,pattern) commands.doifelse(isempty(get_id(id),pattern ~= "" and pattern ~= nil)) end -- not yet done, pattern + +-- status info + +statistics.register("xml load time", function() + local n = lxml.n + if n > 0 then + local stats = xml.statistics() + return format("%s seconds, lpath calls: %s, cached calls: %s", statistics.elapsedtime(xml), stats.lpathcalls, stats.lpathcached) + else + return nil + end +end) + +statistics.register("lxml load time", function() + local n = #lxml.self + if n > 0 then + return format("%s seconds preparation, backreferences: %i", statistics.elapsedtime(lxml),n) + else + return nil + end +end) |