diff options
Diffstat (limited to 'Master/texmf-dist/tex/context/base/lxml-tex.lua')
-rw-r--r-- | Master/texmf-dist/tex/context/base/lxml-tex.lua | 407 |
1 files changed, 328 insertions, 79 deletions
diff --git a/Master/texmf-dist/tex/context/base/lxml-tex.lua b/Master/texmf-dist/tex/context/base/lxml-tex.lua index 0c58a47cc9f..adb849347e1 100644 --- a/Master/texmf-dist/tex/context/base/lxml-tex.lua +++ b/Master/texmf-dist/tex/context/base/lxml-tex.lua @@ -11,7 +11,7 @@ if not modules then modules = { } end modules ['lxml-tex'] = { -- be an cldf-xml helper library. local utfchar = utf.char -local concat, insert, remove = table.concat, table.insert, table.remove +local concat, insert, remove, sortedkeys = table.concat, table.insert, table.remove, table.sortedkeys local format, sub, gsub, find, gmatch, match = string.format, string.sub, string.gsub, string.find, string.gmatch, string.match local type, next, tonumber, tostring, select = type, next, tonumber, tostring, select local lpegmatch = lpeg.match @@ -19,43 +19,65 @@ local P, S, C, Cc = lpeg.P, lpeg.S, lpeg.C, lpeg.Cc local tex, xml = tex, xml local lowerchars, upperchars, lettered = characters.lower, characters.upper, characters.lettered +local basename, dirname, joinfile = file.basename, file.dirname, file.join lxml = lxml or { } local lxml = lxml -local catcodenumbers = catcodes.numbers -local ctxcatcodes = catcodenumbers.ctxcatcodes -- todo: use different method -local notcatcodes = catcodenumbers.notcatcodes -- todo: use different method - -local commands = commands -local context = context -local contextsprint = context.sprint -- with catcodes (here we use fast variants, but with option for tracing) - -local xmlelements, xmlcollected, xmlsetproperty = xml.elements, xml.collected, xml.setproperty -local xmlwithelements = xml.withelements -local xmlserialize, xmlcollect, xmltext, xmltostring = xml.serialize, xml.collect, xml.text, xml.tostring -local xmlapplylpath = xml.applylpath -local xmlunprivatized, xmlprivatetoken, xmlprivatecodes = xml.unprivatized, xml.privatetoken, xml.privatecodes -local xmlstripelement = xml.stripelement - -local variables = interfaces and interfaces.variables or { } +local catcodenumbers = catcodes.numbers +local ctxcatcodes = catcodenumbers.ctxcatcodes -- todo: use different method +local notcatcodes = catcodenumbers.notcatcodes -- todo: use different method + +local commands = commands +local context = context +local contextsprint = context.sprint -- with catcodes (here we use fast variants, but with option for tracing) + +local implement = interfaces.implement + +local xmlelements = xml.elements +local xmlcollected = xml.collected +local xmlsetproperty = xml.setproperty +local xmlwithelements = xml.withelements +local xmlserialize = xml.serialize +local xmlcollect = xml.collect +local xmltext = xml.text +local xmltostring = xml.tostring +local xmlapplylpath = xml.applylpath +local xmlunprivatized = xml.unprivatized +local xmlprivatetoken = xml.privatetoken +local xmlprivatecodes = xml.privatecodes +local xmlstripelement = xml.stripelement +local xmlinclusion = xml.inclusion +local xmlinclusions = xml.inclusions +local xmlcontent = xml.content + +local variables = interfaces and interfaces.variables or { } local settings_to_hash = utilities.parsers.settings_to_hash +local settings_to_set = utilities.parsers.settings_to_set +local options_to_hash = utilities.parsers.options_to_hash +local options_to_array = utilities.parsers.options_to_array + local insertbeforevalue = utilities.tables.insertbeforevalue local insertaftervalue = utilities.tables.insertaftervalue -local starttiming, stoptiming = statistics.starttiming, statistics.stoptiming +local resolveprefix = resolvers.resolve + +local starttiming = statistics.starttiming +local stoptiming = statistics.stoptiming -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) -local trace_access = false trackers.register("lxml.access", function(v) trace_access = v end) -local trace_comments = false trackers.register("lxml.comments", function(v) trace_comments = v end) -local trace_entities = false trackers.register("xml.entities", function(v) trace_entities = v end) +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) +local trace_access = false trackers.register("lxml.access", function(v) trace_access = v end) +local trace_comments = false trackers.register("lxml.comments", function(v) trace_comments = v end) +local trace_entities = false trackers.register("xml.entities", function(v) trace_entities = v end) +local trace_selectors = false trackers.register("lxml.selectors",function(v) trace_selectors = v end) -local report_lxml = logs.reporter("xml","tex") -local report_xml = logs.reporter("xml","tex") +local report_lxml = logs.reporter("lxml","tex") +local report_xml = logs.reporter("xml","tex") -local forceraw, rawroot = false, nil +local forceraw = false +local forceraw = nil -- tex entities -- @@ -65,7 +87,7 @@ lxml.entities = lxml.entities or { } storage.register("lxml/entities",lxml.entities,"lxml.entities") ---~ xml.placeholders.unknown_any_entity = nil -- has to be per xml +-- xml.placeholders.unknown_any_entity = nil -- has to be per xml local xmlentities = xml.entities local texentities = lxml.entities @@ -417,7 +439,7 @@ function lxml.convert(id,data,entities,compress,currentresource) end function lxml.load(id,filename,compress,entities) - filename = commands.preparedfile(filename) -- not commands! + filename = ctxrunner.preparedfile(filename) if trace_loading then report_lxml("loading file %a as %a",filename,id) end @@ -436,37 +458,6 @@ function lxml.register(id,xmltable,filename) return xmltable end --- function lxml.include(id,pattern,attribute,recurse,resolve) --- starttiming(xml) --- local root = getid(id) --- xml.include(root,pattern,attribute,recurse,function(filename) --- if filename then --- -- preprocessing --- filename = commands.preparedfile(filename) --- -- some protection --- if file.dirname(filename) == "" and root.filename then --- local dn = file.dirname(root.filename) --- if dn ~= "" then --- filename = file.join(dn,filename) --- end --- end --- if trace_loading then --- report_lxml("including file %a",filename) --- end --- -- handy if we have a flattened structure --- if resolve then --- filename = resolvers.resolve(filename) or filename --- end --- -- todo: check variants and provide --- noffiles, nofconverted = noffiles + 1, nofconverted + 1 --- return resolvers.loadtexfile(filename) or "" --- else --- return "" --- end --- end) --- stoptiming(xml) --- end - -- recurse prepare rootpath resolve basename local options_true = { "recurse", "prepare", "rootpath" } @@ -492,17 +483,17 @@ function lxml.include(id,pattern,attribute,options) end -- handy if we have a flattened structure if options.basename then - filename = file.basename(filename) + filename = basename(filename) end if options.resolve then - filename = resolvers.resolve(filename) or filename + filename = resolveprefix(filename) or filename end -- some protection if options.rootpath then - if file.dirname(filename) == "" and root.filename then - local dn = file.dirname(root.filename) + if dirname(filename) == "" and root.filename then + local dn = dirname(root.filename) if dn ~= "" then - filename = file.join(dn,filename) + filename = joinfile(dn,filename) end end end @@ -518,6 +509,20 @@ function lxml.include(id,pattern,attribute,options) stoptiming(xml) end +function lxml.inclusion(id,default) + local inclusion = xmlinclusion(getid(id),default) + if inclusion then + context(inclusion) + end +end + +function lxml.inclusions(id,sorted) + local inclusions = xmlinclusions(getid(id),sorted) + if inclusions then + context(table.concat(inclusions,",")) + end +end + function lxml.save(id,name) xml.save(getid(id),name) end @@ -603,30 +608,49 @@ local function tex_element(e,handlers) end end +-- <?context-directive foo ... ?> +-- <?context-foo-directive ... ?> + local pihandlers = { } xml.pihandlers = pihandlers -local category = P("context-") * C((1-P("-"))^1) * P("-directive") local space = S(" \n\r") local spaces = space^0 local class = C((1-space)^0) local key = class +local rest = C(P(1)^0) local value = C(P(1-(space * -1))^0) - -local parser = category * spaces * class * spaces * key * spaces * value - -pihandlers[#pihandlers+1] = function(str) - if str then - local a, b, c, d = lpegmatch(parser,str) - if d then - contextsprint(ctxcatcodes,"\\xmlcontextdirective{",a,"}{",b,"}{",c,"}{",d,"}") +local category = P("context-") * ( + C((1-P("-"))^1) * P("-directive") + + P("directive") * spaces * key + ) + +local c_parser = category * spaces * value -- rest +local k_parser = class * spaces * key * spaces * rest --value + +implement { + name = "xmlinstalldirective", + arguments = { "string", "string" }, + actions = function(name,csname) + if csname then + local keyvalueparser = k_parser / context[csname] + local keyvaluechecker = function(category,rest,e) + lpegmatch(keyvalueparser,rest) + end + pihandlers[name] = keyvaluechecker end end -end +} local function tex_pi(e,handlers) local str = e.dt[1] - for i=1,#pihandlers do - pihandlers[i](str) + if str and str ~= "" then + local category, rest = lpegmatch(c_parser,str) + if category and rest and #rest > 0 then + local handler = pihandlers[category] + if handler then + handler(category,rest,e) + end + end end end @@ -1500,7 +1524,7 @@ end lxml.content = text function lxml.position(id,pattern,n) - position(xmlapplylpath(getid(id),pattern),n) + position(xmlapplylpath(getid(id),pattern),tonumber(n)) end function lxml.chainattribute(id,pattern,a,default) @@ -1516,7 +1540,7 @@ function lxml.concat(id,pattern,separator,lastseparator,textonly) end function lxml.element(id,n) - position(xmlapplylpath(getid(id),"/*"),n) + position(xmlapplylpath(getid(id),"/*"),tonumber(n)) -- tonumber handy end lxml.index = lxml.position @@ -1554,6 +1578,8 @@ function lxml.att(id,a,default) end elseif str ~= "" then contextsprint(notcatcodes,str) + else + -- explicit empty is valid end elseif default and default ~= "" then contextsprint(notcatcodes,default) @@ -1679,7 +1705,20 @@ function lxml.doifelsetext (id,pattern) doifelse(not empty(getid(id),pattern)) e -- special case: "*" and "" -> self else lpath lookup ---~ function lxml.doifelseempty(id,pattern) doifelse(isempty(getid(id),pattern ~= "" and pattern ~= nil)) end -- not yet done, pattern +local function checkedempty(id,pattern) + local e = getid(id) + if not pattern or pattern == "" then + local dt = e.dt + local nt = #dt + return (nt == 0) or (nt == 1 and dt[1] == "") + else + return isempty(getid(id),pattern) + end +end + +function lxml.doifempty (id,pattern) doif (checkedempty(id,pattern)) end +function lxml.doifnotempty (id,pattern) doifnot (checkedempty(id,pattern)) end +function lxml.doifelseempty(id,pattern) doifelse(checkedempty(id,pattern)) end -- status info @@ -1807,3 +1846,213 @@ end texfinalizers.upperall = xmlfinalizers.upperall texfinalizers.lowerall = xmlfinalizers.lowerall + +function lxml.tobuffer(id,pattern,name,unescaped) + local collected = xmlapplylpath(getid(id),pattern) + if collected then + if unescaped then + collected = xmlcontent(collected[1]) -- expanded entities ! + else + collected = tostring(collected[1]) + end + buffers.assign(name,collected) + else + buffers.erase(name) + end +end + +-- relatively new: + +local permitted = nil +local ctx_xmlinjector = context.xmlinjector + +xml.pihandlers["injector"] = function(category,rest,e) + local options = options_to_array(rest) + local action = options[1] + if not action then + return + end + local n = #options + if n > 1 then + local category = options[2] + if category == "*" then + ctx_xmlinjector(action) + elseif permitted then + if n == 2 then + if permitted[category] then + ctx_xmlinjector(action) + end + else + for i=2,n do + local category = options[i] + if category == "*" or permitted[category] then + ctx_xmlinjector(action) + return + end + end + end + end + else + ctx_xmlinjector(action) + end +end + +local pattern = P("context-") * C((1-lpeg.patterns.whitespace)^1) * C(P(1)^1) + +function lxml.applyselectors(id) + local root = getid(id) + local function filter(e) + local dt = e.dt + local ndt = #dt + local done = false + local i = 1 + while i <= ndt do + local dti = dt[i] + if type(dti) == "table" then + if dti.tg == "@pi@" then + local text = dti.dt[1] + local what, rest = lpegmatch(pattern,text) + if what == "select" then + local categories = options_to_hash(rest) + if categories["begin"] then + local okay = false + for k, v in next, permitted do + if categories[k] then + okay = k + break + end + end + if not trace_selectors then + -- skip + elseif okay then + report_lxml("accepting selector: %s",okay) + else + categories.begin = false + report_lxml("rejecting selector: % t",sortedkeys(categories)) + end + for j=i,ndt do + local dtj = dt[j] + if type(dtj) == "table" then + local tg = dtj.tg + if tg == "@pi@" then + local text = dtj.dt[1] + local what, rest = lpegmatch(pattern,text) + if what == "select" then + local categories = options_to_hash(rest) + if categories["end"] then + i = j + break + else + -- error + end + end + elseif not okay then + dtj.tg = "@cm@" + end + else +-- dt[j] = "" -- okay ? + end + end + end + elseif what == "include" then + local categories = options_to_hash(rest) + if categories["begin"] then + local okay = false + for k, v in next, permitted do + if categories[k] then + okay = k + break + end + end + if not trace_selectors then + -- skip + elseif okay then + report_lxml("accepting include: %s",okay) + else + categories.begin = false + report_lxml("rejecting include: % t",sortedkeys(categories)) + end + if okay then + for j=i,ndt do + local dtj = dt[j] + if type(dtj) == "table" then + local tg = dtj.tg + if tg == "@cm@" then + local content = dtj.dt[1] + local element = root and xml.toelement(content,root) + dt[j] = element + element.__p__ = dt -- needs checking + done = true + elseif tg == "@pi@" then + local text = dtj.dt[1] + local what, rest = lpegmatch(pattern,text) + if what == "include" then + local categories = options_to_hash(rest) + if categories["end"] then + i = j + break + else + -- error + end + end + end + end + end + end + end + else + filter(dti) + end + end + if done then + -- probably not needed + xml.reindex(dt) + end + end + i = i + 1 + end + end + xmlwithelements(root,filter) +end + +function xml.setinjectors(set) + local s = settings_to_set(set) + if permitted then + for k, v in next, s do + permitted[k] = true + end + else + permitted = s + end +end + +function xml.resetinjectors(set) + if permitted and set and set ~= "" then + local s = settings_to_set(set) + for k, v in next, s do + if v then + permitted[k] = nil + end + end + else + permitted = nil + end +end + +implement { + name = "xmlsetinjectors", + actions = xml.setinjectors, + arguments = "string" +} + +implement { + name = "xmlresetinjectors", + actions = xml.resetinjectors, + arguments = "string" +} + +implement { + name = "xmlapplyselectors", + actions = lxml.applyselectors, + arguments = "string" +} |