diff options
author | Taco Hoekwater <taco@elvenkind.com> | 2008-06-12 10:42:53 +0000 |
---|---|---|
committer | Taco Hoekwater <taco@elvenkind.com> | 2008-06-12 10:42:53 +0000 |
commit | 0d01365d53c456d246da0ca1f0b3cd9868f02b35 (patch) | |
tree | 01a655c8028e17cfb371456b299c1848fe08c05b /Master/texmf-dist/tex/context/base/lxml-ini.lua | |
parent | 44f3714442da07fdfc36a7f2a8dcd5d4294c5d26 (diff) |
ConTeXt release 2008.05.21
git-svn-id: svn://tug.org/texlive/trunk@8691 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/tex/context/base/lxml-ini.lua')
-rw-r--r-- | Master/texmf-dist/tex/context/base/lxml-ini.lua | 791 |
1 files changed, 791 insertions, 0 deletions
diff --git a/Master/texmf-dist/tex/context/base/lxml-ini.lua b/Master/texmf-dist/tex/context/base/lxml-ini.lua new file mode 100644 index 00000000000..f6dba95d4ca --- /dev/null +++ b/Master/texmf-dist/tex/context/base/lxml-ini.lua @@ -0,0 +1,791 @@ +if not modules then modules = { } end modules ['lxml-ini'] = { + version = 1.001, + comment = "companion to lxml-ini.tex", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +-- todo: speed up: remember last index/match combination + +local texsprint, texprint = tex.sprint or print, tex.print or print +local format, concat = string.format, table.concat +local type, next, tonumber = type, next, tonumber + +document = document or { } +document.xml = document.xml or { } + +-- todo: loaded and myself per document so that we can garbage collect buffers + +lxml = { } +lxml.loaded = { } +lxml.myself = { } + +local loaded = lxml.loaded +local myself = lxml.myself + +lxml.self = myself -- be backward compatible for a while + +local function get_id(id) + return (type(id) == "table" and id) or loaded[id] or myself[tonumber(id)] -- no need for tonumber if we pass without "" +end + +lxml.id = get_id + +function lxml.root(id) + return loaded[id] +end + +do + + xml.specialhandler = xml.specialhandler or { } + + local specialhandler = xml.specialhandler + local serialize = xml.serialize + + local crlf = lpeg.P("\r\n") + local cr = lpeg.P("\r") + 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 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 function sprint(root) + if not root then + -- quit + else + local tr = type(root) + if tr == "string" then -- can also be result of lpath + capture:match(root) + elseif tr == "table" then + serialize(root,sprint,nil,nil,specialhandler) + end + end + end + + xml.sprint = sprint + + function xml.tprint(root) -- we can move sprint inline + local tr = type(root) + if tr == "table" then + local n = #root + if n == 0 then + sprint("") -- empty element, else no setup triggered (check this! ) + else + for i=1,n do + sprint(root[i]) + end + end + elseif tr == "string" then + sprint(root) + end + end + + function xml.cprint(root) -- content + if not root then + -- quit + elseif type(root) == 'string' then + 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) + end + end + + -- lines (untested) + + local buffer = { } + + local capture = ( + newline^2 / function() buffer[#buffer+1] = "" end + + newline / function() buffer[#buffer] = buffer[#buffer] .. " " end + + content / function(s) buffer[#buffer] = buffer[#buffer] .. s end + )^0 + + function lines(root) + if not root then + -- quit + elseif type(root) == 'string' then + capture:match(root) + elseif next(root) then -- tr == 'table' + serialize(root, lines) + end + end + + function xml.lines(root) + buffer = { "" } + lines(root) + return result + end + + -- cdata + + local linecommand = "\\obeyedline" + local spacecommand = "\\obeyedspace" -- "\\strut\\obeyedspace" + local beforecommand = "" + 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 + )^0 + + function toverbatim(str) + if beforecommand then texsprint(tex.texcatcodes,beforecommand .. "{}") end + capture:match(str) + if aftercommand then texsprint(tex.texcatcodes,aftercommand .. "{}") end + end + + function lxml.set_verbatim(before,after,obeyedline,obeyedspace) + beforecommand, aftercommand, linecommand, spacecommand = before, after, obeyedline, obeyedspace + end + + function lxml.set_cdata() + specialhandler['@cd@'] = toverbatim + end + + function lxml.reset_cdata() + specialhandler['@cd@'] = nil + 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 + end + function lxml.inlineverbatim(id) + lxml.verbatim(id,"\\startxmlinlineverbatim","\\stopxmlinlineverbatim") + end + function lxml.displayverbatim(id) + lxml.verbatim(id,"\\startxmldisplayverbatim","\\stopxmldisplayverbatim") + end + + local pihandlers = { } + + specialhandler['@pi@'] = function(str) + for i=1,#pihandlers do + pihandlers[i](str) + end + end + + xml.pihandlers = pihandlers + + local kind = lpeg.P("context-") * lpeg.C((1-lpeg.P("-"))^1) * lpeg.P("-directive") + local space = lpeg.S(" \n\r") + local spaces = space^0 + local class = lpeg.C((1-space)^0) + local key = class + local value = lpeg.C(lpeg.P(1-(space * -1))^0) + + local parser = kind * spaces * class * spaces * key * spaces * value + + 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))) + end + + -- print(contextdirective("context-mathml-directive function reduction yes yes ")) + -- print(contextdirective("context-mathml-directive function ")) + +end + +local xmlsprint = xml.sprint +local xmltprint = xml.tprint + +-- redefine xml load + +xml.originalload = xml.load + +function xml.load(filename) + input.starttiming(xml) + local xmldata = xml.convert((filename and input.loadtexfile(texmf.instance,filename)) or "") + input.stoptiming(xml) + return xmldata +end + +function lxml.load(id,filename) + loaded[id] = xml.load(filename) + return loaded[id], filename +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) +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 +function lxml.first(id,pattern) + xmlsprint(xmlfirst(get_id(id),pattern)) +end +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) +end + +function lxml.nonspace(id,pattern) + 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) +end + +function lxml.text(id,pattern) + xmltprint(xmlcollect_texts(get_id(id),pattern) or {}) +end + +function lxml.tags(id,pattern) + local tags = xmlcollect_tags(get_id(id),pattern) + if tags then + texsprint(concat(tags,",")) + end +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,"")) + end +end + +function lxml.snippet(id,i) + local e = lxml.id(id) + if e then + local edt = e.dt + xmlsprint((edt and edt[i]) or "") + end +end + +function lxml.stripped(id,pattern) + local str = xmlcontent(get_id(id),pattern) or "" + xmlsprint((str:gsub("^%s*(.-)%s*$","%1"))) +end + +function lxml.flush(id) + xmlsprint(get_id(id).dt) +end +function lxml.direct(id) + xmlsprint(get_id(id)) +end + +function lxml.index(id,pattern,i) + xmlsprint((xmlindex(get_id(id),pattern,i))) +end + +function lxml.attribute(id,pattern,a,default) --todo: snelle xmlatt + local str = xmlattribute(get_id(id),pattern,a) or "" + texsprint((str == "" and default) or str) +end + +function lxml.count(id,pattern) + texsprint(xml.count(get_id(id),pattern) or 0) +end +function lxml.name(id) -- or remapped name? -> lxml.info, combine + local r = get_id(id) + local ns = t.rn or r.ns or "" + if ns ~= "" then + texsprint(ns .. ":" .. r.tg) + else + texsprint(r.tg) + end +end +function lxml.tag(id) -- tag vs name -> also in l-xml tag->name + texsprint(get_id(id).tg or "") +end +function lxml.namespace(id) -- or remapped name? + local root = get_id(id) + texsprint(root.rn or root.ns or "") +end + +--~ function lxml.concat(id,what,separator,lastseparator) +--~ texsprint(concat(xml.collect_texts(get_id(id),what,true),separator or "")) +--~ 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 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 + if stop < 0 then stop = #t + stop end -- -1 == last-1 + for i=start,stop do + xmlsprint(t[i]) + if i == #t then + -- nothing + elseif i == #t-1 and lastseparator ~= "" then + texsprint(tex.ctxcatcodes,lastseparator) + elseif separator ~= "" then + texsprint(tex.ctxcatcodes,separator) + end + end +end + +function lxml.concat(id,what,separator,lastseparator) + lxml.concatrange(id,what,false,false,separator,lastseparator) +end + +-- string : setup +-- true : text (no <self></self>) +-- false : ignore +-- function : call + +-- todo: free self after usage, i.e. after the setup, which +-- means a call to lua; we can also choose a proper maximum +-- and cycle or maybe free on demand + +-- problems with empty elements +-- we use a real tex.sprint, else spaces go wrong +-- maybe just a .. because this happens often + +function xml.command(root, command) + local tc = type(command) + if tc == "string" then + -- setup + local n = #myself + 1 + myself[n] = root + texsprint(tex.ctxcatcodes,format("\\xmlsetup{%i}{%s}",n,command)) + elseif tc == "function" then + -- function + command(root) + elseif command == true then + -- text (no <self></self>) / so, no mkii fallback then + xmltprint(root.dt) + elseif command == false then + -- ignore + else + -- fuzzy, so ignore too + end +end + +function lxml.setaction(id,pattern,action) + for rt, dt, dk in xmlelements(get_id(id),pattern) do + dt[dk].command = 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)) + end + end + end + else + local a, b = setup:match("^(.+:)([%*%-])$") + if a and b 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 + if b == "-" then + dtdk.command = false + if trace then + if ns == "" then + texio.write_nl(format("lpath matched -> %s -> skipped", tg)) + else + texio.write_nl(format("lpath matched -> %s:%s -> skipped", ns, tg)) + end + end + elseif b == "+" then + dtdk.command = true + if trace then + if ns == "" then + texio.write_nl(format("lpath matched -> %s -> text", tg)) + else + texio.write_nl(format("lpath matched -> %s:%s -> text", ns, tg)) + end + end + else + dtdk.command = a .. tg + if trace then + if ns == "" then + texio.write_nl(format("lpath matched -> %s -> %s", tg, dtdk.command)) + else + texio.write_nl(format("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)) + 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 + local ns, tg = dtdk.rn or dtdk.ns, dtdk.tg + if ns == "" then + texio.write_nl(format("lpath matched -> %s -> %s", tg, setup)) + else + texio.write_nl(format("lpath matched -> %s:%s -> %s", ns, tg, setup)) + end + end + end + end + end +end + +function lxml.idx(id,pattern,i) -- hm, hashed, needed? + local r = get_id(id) + if r then + local rp = r.patterns + if not rp then + rp = { } + r.patterns = rp + end + if not rp[pattern] then + rp[pattern] = xml.collect_elements(r,pattern) -- dd, rr + end + local rpi = rp[pattern] and rp[pattern][i] + if rpi then + xmlsprint(rpi) + end + end +end + +function lxml.info(id) + id = get_id(id) + local ns, tg = id.ns, id.tg + if ns and ns ~= "" then -- best make a function + tg = ns .. ":" .. tg + else + tg = tg or "?" + end + 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 n = #myself + 1 + myself[n] = m + texsprint(tex.ctxcatcodes,format("\\xmlsetup{%s}{%s}",n,cmd)) +end + end) + end + + xml.filters.command = command + + function lxml.command(id,pattern,cmd) + command(get_id(id),pattern,cmd) + end + +end + +do + + --~ <?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 + 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.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 + end + local setup = data[format("%s::%s::%s",element,attribute,value)] + 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 + end + end + end + end + +end + +function xml.getbuffer(name) -- we need to make sure that commands are processed + if not name or name == "" then + name = tex.jobname + end + xml.tostring(xml.convert(concat(buffers.data[name] or {},""))) +end + +function lxml.loadbuffer(id,name) + if not name or name == "" then + name = tex.jobname + end + input.starttiming(xml) + loaded[id] = xml.convert(buffers.collect(name or id,"\n")) + input.stoptiming(xml) + return loaded[id], name or id +end + +function lxml.loaddata(id,str) + input.starttiming(xml) + loaded[id] = xml.convert(str or "") + input.stoptiming(xml) + return loaded[id], id +end + +-- for the moment here: + +lxml.set_verbatim("\\xmlcdatabefore", "\\xmlcdataafter", "\\xmlcdataobeyedline", "\\xmlcdataobeyedspace") +lxml.set_cdata() + +do + + 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.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 + end + end + +end + +-- yes or no ... + +do + + 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 + end + end + end + end + + 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 + e.command = false -- i.e. skip + end + end + + -- 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.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(lxml.loaded) do + t[id] = lxml.get_command_status(id) + end + return t + end + end + +end |