diff options
Diffstat (limited to 'Master/texmf-dist/tex/context/base/strc-ref.lua')
-rw-r--r-- | Master/texmf-dist/tex/context/base/strc-ref.lua | 1874 |
1 files changed, 1185 insertions, 689 deletions
diff --git a/Master/texmf-dist/tex/context/base/strc-ref.lua b/Master/texmf-dist/tex/context/base/strc-ref.lua index ee32353f0c4..8a478a4e6e7 100644 --- a/Master/texmf-dist/tex/context/base/strc-ref.lua +++ b/Master/texmf-dist/tex/context/base/strc-ref.lua @@ -6,76 +6,96 @@ if not modules then modules = { } end modules ['strc-ref'] = { license = "see context related readme files" } +-- beware, this is a first step in the rewrite (just getting rid of +-- the tuo file); later all access and parsing will also move to lua + +-- the useddata and pagedata names might change +-- todo: pack exported data + +-- todo: autoload components when ::: + local format, find, gmatch, match, concat = string.format, string.find, string.gmatch, string.match, table.concat -local lpegmatch, lpegP, lpegCs = lpeg.match, lpeg.P, lpeg.Cs local texcount, texsetcount = tex.count, tex.setcount -local rawget = rawget +local rawget, tonumber = rawget, tonumber +local lpegmatch = lpeg.match +local copytable = table.copy -local allocate = utilities.storage.allocate -local mark = utilities.storage.mark -local setmetatableindex = table.setmetatableindex +local allocate = utilities.storage.allocate +local mark = utilities.storage.mark +local setmetatableindex = table.setmetatableindex -local trace_referencing = false trackers.register("structures.referencing", function(v) trace_referencing = v end) +local trace_referencing = false trackers.register("structures.referencing", function(v) trace_referencing = v end) +local trace_analyzing = false trackers.register("structures.referencing.analyzing", function(v) trace_analyzing = v end) +local trace_identifying = false trackers.register("structures.referencing.identifying", function(v) trace_identifying = v end) +local trace_importing = false trackers.register("structures.referencing.importing", function(v) trace_importing = v end) -local report_references = logs.reporter("structure","references") +local report_references = logs.reporter("references") +local report_unknown = logs.reporter("unknown") +local report_identifying = logs.reporter("references","identifying") +local report_importing = logs.reporter("references","importing") -local variables = interfaces.variables -local constants = interfaces.constants -local context = context +local variables = interfaces.variables +local constants = interfaces.constants +local context = context -local settings_to_array = utilities.parsers.settings_to_array -local unsetvalue = attributes.unsetvalue +local texcount = tex.count +local texconditionals = tex.conditionals --- beware, this is a first step in the rewrite (just getting rid of --- the tuo file); later all access and parsing will also move to lua +local productcomponent = resolvers.jobs.productcomponent +local justacomponent = resolvers.jobs.justacomponent --- the useddata and pagedata names might change --- todo: pack exported data +local logsnewline = logs.newline +local logspushtarget = logs.pushtarget +local logspoptarget = logs.poptarget -local structures = structures -local helpers = structures.helpers -local sections = structures.sections -local references = structures.references -local lists = structures.lists -local counters = structures.counters +local settings_to_array = utilities.parsers.settings_to_array +local unsetvalue = attributes.unsetvalue + +local structures = structures +local helpers = structures.helpers +local sections = structures.sections +local references = structures.references +local lists = structures.lists +local counters = structures.counters -- some might become local -references.defined = references.defined or allocate() - -local defined = references.defined -local derived = allocate() -local specials = allocate() -local runners = allocate() -local internals = allocate() -local exporters = allocate() -local imported = allocate() -local filters = allocate() -local executers = allocate() -local handlers = allocate() -local tobesaved = allocate() -local collected = allocate() -local tobereferred = allocate() -local referred = allocate() - -references.derived = derived -references.specials = specials -references.runners = runners -references.internals = internals -references.exporters = exporters -references.imported = imported -references.filters = filters -references.executers = executers -references.handlers = handlers -references.tobesaved = tobesaved -references.collected = collected -references.tobereferred = tobereferred -references.referred = referred +references.defined = references.defined or allocate() + +local defined = references.defined +local derived = allocate() +local specials = allocate() +local runners = allocate() +local internals = allocate() +local filters = allocate() +local executers = allocate() +local handlers = allocate() +local tobesaved = allocate() +local collected = allocate() +local tobereferred = allocate() +local referred = allocate() + +references.derived = derived +references.specials = specials +references.runners = runners +references.internals = internals +references.filters = filters +references.executers = executers +references.handlers = handlers +references.tobesaved = tobesaved +references.collected = collected +references.tobereferred = tobereferred +references.referred = referred + +local splitreference = references.splitreference +local splitprefix = references.splitcomponent -- replaces: references.splitprefix +local prefixsplitter = references.prefixsplitter +local componentsplitter = references.componentsplitter + +local currentreference = nil storage.register("structures/references/defined", references.defined, "structures.references.defined") -local currentreference = nil - local initializers = { } local finalizers = { } @@ -103,42 +123,90 @@ end job.register('structures.references.collected', tobesaved, initializer, finalizer) local maxreferred = 1 +local nofreferred = 0 + +-- local function initializer() -- can we use a tobesaved as metatable for collected? +-- tobereferred = references.tobereferred +-- referred = references.referred +-- nofreferred = #referred +-- end local function initializer() -- can we use a tobesaved as metatable for collected? tobereferred = references.tobereferred referred = references.referred - local function get(t,n) -- catch sparse, a bit slow but who cares - for i=n,1,-1 do -- we could make a tree ... too much work - local p = rawget(t,i) - if p then - return p - end - end - end - setmetatableindex(referred, get) + setmetatableindex(referred,get) -- hm, what is get ? end -local function finalizer() -- make sparse - local last +-- We make the array sparse (maybe a finalizer should optionally return a table) because +-- there can be quite some page links involved. We only store one action number per page +-- which is normally good enough for what we want (e.g. see above/below) and we do +-- a combination of a binary search and traverse backwards. A previous implementation +-- always did a traverse and was pretty slow on a large number of links (given that this +-- methods was used). It took me about a day to locate this as a bottleneck in processing +-- a 2500 page interactive document with 60 links per page. In that case, traversing +-- thousands of slots per link then brings processing to a grinding halt (especially when +-- there are no slots at all, which is the case in a first run). + +local sparsetobereferred = { } + +local function finalizer() + local lastr, lasti + local n = 0 for i=1,maxreferred do local r = tobereferred[i] - if not last then - last = r - elseif r == last then - tobereferred[i] = nil - else - last = r + if not lastr then + lastr = r + lasti = i + elseif r ~= lastr then + n = n + 1 + sparsetobereferred[n] = { lastr, lasti } + lastr = r + lasti = i end end + if lastr then + n = n + 1 + sparsetobereferred[n] = { lastr, lasti } + end end -job.register('structures.references.referred', tobereferred, initializer, finalizer) - -function references.referredpage(n) - return referred[n] or referred[n] or texcount.realpageno +job.register('structures.references.referred', sparsetobereferred, initializer, finalizer) + +local function referredpage(n) + local max = nofreferred + if max > 0 then + -- find match + local min = 1 + while true do + local mid = floor((min+max)/2) + local r = referred[mid] + local m = r[2] + if n == m then + return r[1] + elseif n > m then + min = mid + 1 + else + max = mid - 1 + end + if min > max then + break + end + end + -- find first previous + for i=min,1,-1 do + local r = referred[i] + if r and r[2] < n then + return r[1] + end + end + end + -- fallback + return texcount.realpageno end -function references.registerpage(n) +references.referredpage = referredpage + +function references.registerpage(n) -- called in the backend code if not tobereferred[n] then if n > maxreferred then maxreferred = n @@ -176,113 +244,89 @@ function references.currentorder(kind,name) context(orders[kind] and orders[kind][name] or lastorder) end +local function setcomponent(data) + -- we might consider doing this at the tex end, just like prefix + local component = productcomponent() + if component then + local references = data and data.references + if references then + references.component = component + end + return component + end + -- but for the moment we do it here (experiment) +end + +references.setcomponent = setcomponent + function references.set(kind,prefix,tag,data) +-- setcomponent(data) + local pd = tobesaved[prefix] + if not pd then + pd = { } + tobesaved[prefix] = pd + end for ref in gmatch(tag,"[^,]+") do - local p, r = match(ref,"^(%-):(.-)$") - if p and r then - prefix, ref = p, r - else - prefix = "" - end if ref ~= "" then - local pd = tobesaved[prefix] - if not pd then - pd = { } - tobesaved[prefix] = pd - end pd[ref] = data context.dofinishsomereference(kind,prefix,ref) end end end -function references.setandgetattribute(kind,prefix,tag,data,view) -- maybe do internal automatically here - references.set(kind,prefix,tag,data) - texcount.lastdestinationattribute = references.setinternalreference(prefix,tag,nil,view) or -0x7FFFFFFF -end - -function references.enhance(prefix,tag,spec) +function references.enhance(prefix,tag) local l = tobesaved[prefix][tag] if l then l.references.realpage = texcount.realpageno end end --- this reference parser is just an lpeg version of the tex based one - -local result = { } - -local lparent, rparent, lbrace, rbrace, dcolon, backslash = lpegP("("), lpegP(")"), lpegP("{"), lpegP("}"), lpegP("::"), lpegP("\\") - -local reset = lpegP("") / function() result = { } end -local b_token = backslash / function(s) result.has_tex = true return s end - -local o_token = 1 - rparent - rbrace - lparent - lbrace -local a_token = 1 - rbrace -local s_token = 1 - lparent - lbrace - lparent - lbrace -local i_token = 1 - lparent - lbrace -local f_token = 1 - lparent - lbrace - dcolon - -local outer = (f_token )^1 / function (s) result.outer = s end -local operation = lpegCs((b_token + o_token)^1) / function (s) result.operation = s end -local arguments = lpegCs((b_token + a_token)^0) / function (s) result.arguments = s end -local special = (s_token )^1 / function (s) result.special = s end -local inner = (i_token )^1 / function (s) result.inner = s end - -local outer_reference = (outer * dcolon)^0 - -operation = outer_reference * operation -- special case: page(file::1) and file::page(1) - -local optional_arguments = (lbrace * arguments * rbrace)^0 -local inner_reference = inner * optional_arguments -local special_reference = special * lparent * (operation * optional_arguments + operation^0) * rparent - -local scanner = (reset * outer_reference * (special_reference + inner_reference)^-1 * -1) / function() return result end - ---~ function references.analyze(str) -- overloaded ---~ return lpegmatch(scanner,str) ---~ end - -function references.split(str) - return lpegmatch(scanner,str or "") -end - ---~ print(table.serialize(references.analyze(""))) ---~ print(table.serialize(references.analyze("inner"))) ---~ print(table.serialize(references.analyze("special(operation{argument,argument})"))) ---~ print(table.serialize(references.analyze("special(operation)"))) ---~ print(table.serialize(references.analyze("special()"))) ---~ print(table.serialize(references.analyze("inner{argument}"))) ---~ print(table.serialize(references.analyze("outer::"))) ---~ print(table.serialize(references.analyze("outer::inner"))) ---~ print(table.serialize(references.analyze("outer::special(operation{argument,argument})"))) ---~ print(table.serialize(references.analyze("outer::special(operation)"))) ---~ print(table.serialize(references.analyze("outer::special()"))) ---~ print(table.serialize(references.analyze("outer::inner{argument}"))) ---~ print(table.serialize(references.analyze("special(outer::operation)"))) - -- -- -- related to strc-ini.lua -- -- -- references.resolvers = references.resolvers or { } +local resolvers = references.resolvers -function references.resolvers.section(var) - local vi = lists.collected[var.i[2]] +local function getfromlist(var) + local vi = var.i if vi then - var.i = vi - var.r = (vi.references and vi.references.realpage) or (vi.pagedata and vi.pagedata.realpage) or 1 + vi = vi[3] or lists.collected[vi[2]] + if vi then + local r = vi.references and vi.references + if r then + r = r.realpage + end + if not r then + r = vi.pagedata and vi.pagedata + if r then + r = r.realpage + end + end + var.i = vi + var.r = r or 1 + else + var.i = nil + var.r = 1 + end else var.i = nil var.r = 1 end end -references.resolvers.float = references.resolvers.section -references.resolvers.description = references.resolvers.section -references.resolvers.formula = references.resolvers.section -references.resolvers.note = references.resolvers.section +-- resolvers.section = getfromlist +-- resolvers.float = getfromlist +-- resolvers.description = getfromlist +-- resolvers.formula = getfromlist +-- resolvers.note = getfromlist + +setmetatableindex(resolvers,function(t,k) + local v = getfromlist + resolvers[k] = v + return v +end) -function references.resolvers.reference(var) - local vi = var.i[2] +function resolvers.reference(var) + local vi = var.i[2] -- check if vi then var.i = vi var.r = (vi.references and vi.references.realpage) or (vi.pagedata and vi.pagedata.realpage) or 1 @@ -292,22 +336,26 @@ function references.resolvers.reference(var) end end -local function register_from_lists(collected,derived) +local function register_from_lists(collected,derived,pages,sections) local g = derived[""] if not g then g = { } derived[""] = g end -- global for i=1,#collected do local entry = collected[i] local m, r = entry.metadata, entry.references if m and r then - local prefix, reference = r.referenceprefix or "", r.reference or "" + local reference = r.reference or "" + local prefix = r.referenceprefix or "" + local component = r.component and r.component or "" if reference ~= "" then local kind, realpage = m.kind, r.realpage if kind and realpage then local d = derived[prefix] if not d then d = { } derived[prefix] = d end - local t = { kind, i } +local c = derived[component] if not c then c = { } derived[component] = c end + local t = { kind, i, entry } for s in gmatch(reference,"%s*([^,]+)") do if trace_referencing then report_references("list entry %s provides %s reference '%s' on realpage %s",i,kind,s,realpage) end +c[s] = c[s] or t -- share them d[s] = d[s] or t -- share them g[s] = g[s] or t -- first wins end @@ -315,6 +363,7 @@ local function register_from_lists(collected,derived) end end end +-- inspect(derived) end references.registerinitializer(function() register_from_lists(lists.collected,derived) end) @@ -334,24 +383,40 @@ end local pushcatcodes, popcatcodes, txtcatcodes = context.pushcatcodes, context.popcatcodes, tex.txtcatcodes -function references.urls.get(name,method,space) -- method: none, before, after, both, space: yes/no +function references.urls.get(name) local u = urls[name] if u then local url, file = u[1], u[2] - pushcatcodes(txtcatcodes) if file and file ~= "" then - context("%s/%s",url,file) + return format("%s/%s",url,file) else - context(url) + return url end + end +end + +function commands.geturl(name) + local url = references.urls.get(name) + if url and url ~= "" then + pushcatcodes(txtcatcodes) + context(url) popcatcodes() end end +-- function commands.gethyphenatedurl(name,...) +-- local url = references.urls.get(name) +-- if url and url ~= "" then +-- hyphenatedurl(url,...) +-- end +-- end + function commands.doifurldefinedelse(name) commands.doifelse(urls[name]) end +commands.useurl= references.urls.define + -- files references.files = references.files or { } @@ -361,7 +426,7 @@ local files = references.files.data function references.files.define(name,file,description) if name and name ~= "" then - files[name] = { file or "", description or file or ""} + files[name] = { file or "", description or file or "" } end end @@ -376,6 +441,8 @@ function commands.doiffiledefinedelse(name) commands.doifelse(files[name]) end +commands.usefile= references.files.define + -- helpers function references.checkedfile(whatever) -- return whatever if not resolved @@ -462,31 +529,6 @@ function references.whatfrom(name) context((urls[name] and variables.url) or (files[name] and variables.file) or variables.unknown) end ---~ function references.from(name) ---~ local u = urls[name] ---~ if u then ---~ local url, file, description = u[1], u[2], u[3] ---~ if description ~= "" then ---~ context.dofromurldescription(description) ---~ -- ok ---~ elseif file and file ~= "" then ---~ context.dofromurlliteral(url .. "/" .. file) ---~ else ---~ context(dofromurlliteral,url) ---~ end ---~ else ---~ local f = files[name] ---~ if f then ---~ local description, file = f[1], f[2] ---~ if description ~= "" then ---~ context.dofromfiledescription(description) ---~ else ---~ context.dofromfileliteral(file) ---~ end ---~ end ---~ end ---~ end - function references.from(name) local u = urls[name] if u then @@ -502,7 +544,7 @@ function references.from(name) else local f = files[name] if f then - local description, file = f[1], f[2] + local file, description = f[1], f[2] if description ~= "" then context.dofromfiledescription(description) else @@ -512,160 +554,6 @@ function references.from(name) end end --- export - -exporters.references = exporters.references or { } -exporters.lists = exporters.lists or { } - -function exporters.references.generic(data) - local useddata = {} - local entries, userdata = data.entries, data.userdata - if entries then - for k, v in next, entries do - useddata[k] = v - end - end - if userdata then - for k, v in next, userdata do - useddata[k] = v - end - end - return useddata -end - -function exporters.lists.generic(data) - local useddata = { } - local titledata, numberdata = data.titledata, data.numberdata - if titledata then - useddata.title = titledata.title - end - if numberdata then - local numbers = numberdata.numbers - local t, tn = { }, 0 - for i=1,#numbers do - local n = numbers[i] - if n ~= 0 then - tn = tn + 1 - t[tn] = n - end - end - useddata.number = concat(t,".") - end - return useddata -end - -local function referencer(data) - local references = data.references - local realpage = references.realpage - local numberdata = jobpages.tobesaved[realpage] - local specification = numberdata.specification - return { - realpage = references.realpage, - number = numberdata.number, - conversion = specification.conversion, - -- prefix = only makes sense when bywhatever - } -end - --- Exported and imported references ... not yet used but don't forget it --- and redo it. - -function references.export(usedname) - local exported = { } - local e_references, e_lists = exporters.references, exporters.lists - local g_references, g_lists = e_references.generic, e_lists.generic - -- todo: pagenumbers - -- todo: some packing - for prefix, references in next, references.tobesaved do - local pe = exported[prefix] if not pe then pe = { } exported[prefix] = pe end - for key, data in next, references do - local metadata = data.metadata - local exporter = e_references[metadata.kind] or g_references - if exporter then - pe[key] = { - metadata = { - kind = metadata.kind, - catcodes = metadata.catcodes, - coding = metadata.coding, -- we can omit "tex" - }, - useddata = exporter(data), - pagedata = referencer(data), - } - end - end - end - local pe = exported[""] if not pe then pe = { } exported[""] = pe end - for n, data in next, lists.tobesaved do - local metadata = data.metadata - local exporter = e_lists[metadata.kind] or g_lists - if exporter then - local result = { - metadata = { - kind = metadata.kind, - catcodes = metadata.catcodes, - coding = metadata.coding, -- we can omit "tex" - }, - useddata = exporter(data), - pagedata = referencer(data), - } - for key in gmatch(data.references.reference,"[^,]+") do - pe[key] = result - end - end - end - local e = { - references = exported, - version = 1.00, - } - io.savedata(file.replacesuffix(usedname or tex.jobname,"tue"),table.serialize(e,true)) -end - -function references.import(usedname) - if usedname then - local jdn = imported[usedname] - if not jdn then - local filename = files[usedname] - if filename then -- only registered files - filename = filename[1] - else - filename = usedname - end - local data = io.loaddata(file.replacesuffix(filename,"tue")) or "" - if data == "" then - interfaces.showmessage("references",24,filename) - data = nil - else - data = loadstring(data) - if data then - data = data() - end - if data then - -- version check - end - if not data then - interfaces.showmessage("references",25,filename) - end - end - if data then - interfaces.showmessage("references",26,filename) - jdn = data - jdn.filename = filename - else - jdn = { filename = filename, references = { }, version = 1.00 } - end - imported[usedname] = jdn - imported[filename] = jdn - end - return jdn - else - return nil - end -end - -function references.load(usedname) - -- gone -end - function references.define(prefix,reference,list) local d = defined[prefix] if not d then d = { } defined[prefix] = d end d[reference] = { "defined", list } @@ -693,7 +581,6 @@ end local strict = false local function resolve(prefix,reference,args,set) -- we start with prefix,reference - texcount.referencehastexstate = 0 if reference and reference ~= "" then if not set then set = { prefix = prefix, reference = reference } @@ -719,7 +606,7 @@ local function resolve(prefix,reference,args,set) -- we start with prefix,refere if d then resolve(prefix,d[2],nil,set) else - local var = lpegmatch(scanner,ri) + local var = splitreference(ri) if var then var.reference = ri local vo, vi = var.outer, var.inner @@ -755,10 +642,6 @@ local function resolve(prefix,reference,args,set) -- we start with prefix,refere end end end - if set.has_tex then - texcount.referencehastexstate = 1 - end ---~ table.print(set) return set else return { } @@ -769,42 +652,6 @@ end references.currentset = nil ---~ local b, e = "\\ctxlua{local jc = structures.references.currentset;", "}" ---~ local o, a = 'jc[%s].operation=[[%s]];', 'jc[%s].arguments=[[%s]];' ---~ ---~ function references.expandcurrent() -- todo: two booleans: o_has_tex& a_has_tex ---~ local currentset = references.currentset ---~ if currentset and currentset.has_tex then ---~ local done = false ---~ for i=1,#currentset do ---~ local ci = currentset[i] ---~ local operation = ci.operation ---~ if operation then ---~ if find(operation,"\\") then -- if o_has_tex then ---~ if not done then ---~ context(b) ---~ done = true ---~ end ---~ context(o,i,operation) ---~ end ---~ end ---~ local arguments = ci.arguments ---~ if arguments then ---~ if find(arguments,"\\") then -- if a_has_tex then ---~ if not done then ---~ context(b) ---~ done = true ---~ end ---~ context(a,i,arguments) ---~ end ---~ end ---~ end ---~ if done then ---~ context(e) ---~ end ---~ end ---~ end - function commands.setreferenceoperation(k,v) references.currentset[k].operation = v end @@ -833,283 +680,806 @@ function references.expandcurrent() -- todo: two booleans: o_has_tex& a_has_tex end end ---~ local uo = urls[outer] ---~ if uo then ---~ special, operation, argument = "url", uo[1], inner or uo[2] -- maybe more is needed ---~ else ---~ local fo = files[outer] ---~ if fo then ---~ special, operation, argument = "file", fo[1], inner -- maybe more is needed ---~ end ---~ end +local externals = { } + +-- we have prefixes but also components: +-- +-- : prefix +-- :: always external +-- ::: internal (for products) or external (for components) + +local function loadexternalreferences(name,utilitydata) + local struc = utilitydata.structures + if struc then + local external = struc.references.collected -- direct references + local lists = struc.lists.collected -- indirect references (derived) + local pages = struc.pages.collected -- pagenumber data + -- a bit weird one, as we don't have the externals in the collected + for prefix, set in next, external do + for reference, data in next, set do + if trace_importing then + report_importing("registering external reference: regular | %s | %s | %s",name,prefix,reference) + end + local section = reference.section + local realpage = reference.realpage + if section then + reference.sectiondata = lists[section] + end + if realpage then + reference.pagedata = pages[realpage] + end + end + end + for i=1,#lists do + local entry = lists[i] + local metadata = entry.metadata + local references = entry.references + if metadata and references then + local reference = references.reference + if reference and reference ~= "" then + local kind = metadata.kind + local realpage = references.realpage + if kind and realpage then + references.pagedata = pages[realpage] + local prefix = references.referenceprefix or "" + local target = external[prefix] + if not target then + target = { } + external[prefix] = target + end + for s in gmatch(reference,"%s*([^,]+)") do + if trace_importing then + report_importing("registering external reference: %s | %s | %s | %s",kind,name,prefix,s) + end + target[s] = target[s] or entry + end + end + end + end + end + externals[name] = external + return external + end +end -local prefixsplitter = lpegCs(lpegP((1-lpegP(":"))^1 * lpegP(":"))) * lpegCs(lpegP(1)^1) +local externalfiles = { } --- todo: add lots of tracing here +table.setmetatableindex(externalfiles, function(t,k) + local v = files[k] + if not v then + v = { k, k } + end + externalfiles[k] = v + return v +end) -local n = 0 +table.setmetatableindex(externals,function(t,k) -- either or not automatically + local filename = externalfiles[k][1] -- filename + local fullname = file.replacesuffix(filename,"tuc") + if lfs.isfile(fullname) then -- todo: use other locator + local utilitydata = job.loadother(fullname) + if utilitydata then + local external = loadexternalreferences(k,utilitydata) + t[k] = external or false + return external + end + end + t[k] = false + return false +end) -local function identify(prefix,reference) - local set = resolve(prefix,reference) - local bug = false -n = n + 1 -set.n = n - for i=1,#set do - local var = set[i] - local special, inner, outer, arguments, operation = var.special, var.inner, var.outer, var.arguments, var.operation - if special then - local s = specials[special] - if s then - if outer then - if operation then - -- special(outer::operation) - var.kind = "special outer with operation" - else - -- special() - var.kind = "special outer" - end - var.f = outer - elseif operation then - if arguments then - -- special(operation{argument,argument}) - var.kind = "special operation with arguments" - else - -- special(operation) - var.kind = "special operation" - end - else - -- special() - var.kind = "special" +local productdata = { + productreferences = { }, + componentreferences = { }, + components = { }, +} + +local function loadproductreferences(productname,componentname,utilitydata) + local struc = utilitydata.structures + if struc then + local productreferences = struc.references.collected -- direct references + local lists = struc.lists.collected -- indirect references (derived) + local pages = struc.pages.collected -- pagenumber data + -- we use indirect tables to save room but as they are eventually + -- just references we resolve them to data here (the mechanisms + -- that use this data check for indirectness) + for prefix, set in next, productreferences do + for reference, data in next, set do + if trace_importing then + report_importing("registering product reference: regular | %s | %s | %s",productname,prefix,reference) + end + local section = reference.section + local realpage = reference.realpage + if section then + reference.sectiondata = lists[section] + end + if realpage then + reference.pagedata = pages[realpage] end - else - var.error = "unknown special" end - elseif outer then - local e = references.import(outer) - if e then - if inner then - local r = e.references - if r then - r = r[prefix] - if r then - r = r[inner] - if r then - if arguments then - -- outer::inner{argument} - var.kind = "outer with inner with arguments" - else - -- outer::inner - var.kind = "outer with inner" - end - var.i = { "reference", r } - references.resolvers.reference(var) - var.f = outer - var.e = true -- external + end + -- + local componentreferences = { } + for i=1,#lists do + local entry = lists[i] + local metadata = entry.metadata + local references = entry.references + if metadata and references then + local reference = references.reference + if reference and reference ~= "" then + local kind = metadata.kind + local realpage = references.realpage + if kind and realpage then + references.pagedata = pages[realpage] + local prefix = references.referenceprefix or "" + local component = references.component + local ctarget, ptarget + if component and component == componentname then + -- skip + else + -- one level up + local external = componentreferences[component] + if not external then + external = { } + componentreferences[component] = external + end + if component == prefix then + prefix = "" + end + ctarget = external[prefix] + if not ctarget then + ctarget = { } + external[prefix] = ctarget end end - end - if not r then - r = e.derived - if r then - r = r[prefix] - if r then - r = r[inner] - if r then - -- outer::inner - if arguments then - -- outer::inner{argument} - var.kind = "outer with inner with arguments" - else - -- outer::inner - var.kind = "outer with inner" - end - var.i = r - references.resolvers[r[1]](var) - var.f = outer + ptarget = productreferences[prefix] + if not ptarget then + ptarget = { } + productreferences[prefix] = ptarget + end + for s in gmatch(reference,"%s*([^,]+)") do + if ptarget then + if trace_importing then + report_importing("registering product reference: %s | %s | %s | %s",kind,productname,prefix,s) end + ptarget[s] = ptarget[s] or entry end - end - end - if not r then - var.error = "unknown outer" - end - elseif special then - local s = specials[special] - if s then - if operation then - if arguments then - -- outer::special(operation{argument,argument}) - var.kind = "outer with special and operation and arguments" - else - -- outer::special(operation) - var.kind = "outer with special and operation" + if ctarget then + if trace_importing then + report_importing("registering component reference: %s | %s | %s | %s",kind,productname,prefix,s) + end + ctarget[s] = ctarget[s] or entry end - else - -- outer::special() - var.kind = "outer with special" end - var.f = outer - else - var.error = "unknown outer with special" end - else - -- outer:: - var.kind = "outer" - var.f = outer end - else - if inner then - if arguments then - -- outer::inner{argument} - var.kind = "outer with inner with arguments" - else - -- outer::inner - var.kind = "outer with inner" + end + end + productdata.productreferences = productreferences -- not yet used + productdata.componentreferences = componentreferences + end +end + +local function loadproductvariables(product,component,utilitydata) + local struc = utilitydata.structures + if struc then + local lists = struc.lists and struc.lists.collected + if lists then + local pages = struc.pages and struc.pages.collected + for i=1,#lists do + local li = lists[i] + if li.metadata.kind == "section" and li.references.component == component then + local firstsection = li + if firstsection.numberdata then + local numbers = firstsection.numberdata.numbers + if numbers then + if trace_importing then + report_importing("initializing section number to %s",concat(numbers,":")) + end + productdata.firstsection = firstsection + structures.documents.preset(numbers) + end end - var.i = { "reference", inner } - references.resolvers.reference(var) - var.f = outer - elseif special then - local s = specials[special] - if s then - if operation then - if arguments then - -- outer::special(operation{argument,argument}) - var.kind = "outer with special and operation and arguments" - else - -- outer::special(operation) - var.kind = "outer with special and operation" + if pages and firstsection.references then + local firstpage = pages[firstsection.references.realpage] + local number = firstpage and firstpage.number + if number then + if trace_importing then + report_importing("initializing page number to %s",number) end - else - -- outer::special() - var.kind = "outer with special" + productdata.firstpage = firstpage + counters.set("userpage",1,number) end - var.f = outer - else - var.error = "unknown outer with special" end + break + end + end + end + end +end + +local function componentlist(tree,target) + local branches = tree and tree.branches + if branches then + for i=1,#branches do + local branch = branches[i] + local type = branch.type + if type == "component" then + if target then + target[#target+1] = branch.name else - -- outer:: - var.kind = "outer" - var.f = outer + target = { branch.name } end + elseif type == "product" or type == "component" then + target = componentlist(branch,target) end + end + end + return target +end + +local function loadproductcomponents(product,component,utilitydata) + local job = utilitydata.job + productdata.components = componentlist(job and job.structure and job.structure.collected) or { } +end + +function structures.references.loadpresets(product,component) -- we can consider a special components hash + if product and component and product~= "" and component ~= "" and not productdata.product then -- maybe: productdata.filename ~= filename + productdata.product = product + productdata.component = component + local fullname = file.replacesuffix(product,"tuc") + if lfs.isfile(fullname) then -- todo: use other locator + local utilitydata = job.loadother(fullname) + if utilitydata then + if trace_importing then + report_importing("loading references for component %s of product %s from %s",component,product,fullname) + end + loadproductvariables (product,component,utilitydata) + loadproductreferences(product,component,utilitydata) + loadproductcomponents(product,component,utilitydata) + -- inspect(productdata) + end + end + end +end + +structures.references.productdata = productdata + +local useproduct = commands.useproduct + +if useproduct then + + function commands.useproduct(product) + useproduct(product) + if texconditionals.autocrossfilereferences then + local component = justacomponent() + if component then + if trace_referencing or trace_importing then + report_references("loading presets for component '%s' of product '%s'",component,product) + end + structures.references.loadpresets(product,component) + end + end + end + +end + +-- productdata.firstsection.numberdata.numbers +-- productdata.firstpage.number + +local function report_identify_special(set,var,i,type) + local reference = set.reference + local prefix = set.prefix or "" + local special = var.special + local error = var.error + local kind = var.kind + if error then + report_identifying("type %s: %s, n: %s, prefix: %s, special: %s, error: %s",type,reference,i,prefix,special,error) + else + report_identifying("type %s: %s, n: %s, prefix: %s, special: %s, kind: %s",type,reference,i,prefix,special,kind) + end +end + +local function report_identify_arguments(set,var,i,type) + local reference = set.reference + local prefix = set.prefix or "" + local arguments = var.arguments + local error = var.error + local kind = var.kind + if error then + report_identifying("type %s: %s, n: %s, prefix: %s, arguments: %s, error: %s",type,reference,i,prefix,arguments,error) + else + report_identifying("type %s: %s, n: %s, prefix: %s, arguments: %s, kind: %s",type,reference,i,prefix,arguments,kind) + end +end + +local function report_identify_outer(set,var,i,type) + local reference = set.reference + local prefix = set.prefix or "" + local outer = var.outer + local error = var.error + local kind = var.kind + if outer then + if error then + report_identifying("type %s: %s, n: %s, prefix: %s, outer: %s, error: %s",type,reference,i,prefix,outer,error) + else + report_identifying("type %s: %s, n: %s, prefix: %s, outer: %s, kind: %s",type,reference,i,prefix,outer,kind) + end + else + if error then + report_identifying("type %s: %s, n: %s, prefix: %s, error: %s",type,reference,i,prefix,error) else + report_identifying("type %s: %s, n: %s, prefix: %s, kind: %s",type,reference,i,prefix,kind) + end + end +end + +local function identify_special(set,var,i) + local special = var.special + local s = specials[special] + if s then + local outer = var.outer + local operation = var.operation + local arguments = var.arguments + if outer then + if operation then + -- special(outer::operation) + var.kind = "special outer with operation" + else + -- special() + var.kind = "special outer" + end + var.f = outer + elseif operation then if arguments then - local s = specials[inner] - if s then - -- inner{argument} - var.kind = "special with arguments" - else - var.error = "unknown inner or special" - end + -- special(operation{argument,argument}) + var.kind = "special operation with arguments" else - -- inner ... we could move the prefix logic into the parser so that we have 'm for each entry - -- foo:bar -> foo == prefix (first we try the global one) - -- -:bar -> ignore prefix - local p, i = prefix, nil - local splitprefix, splitinner = lpegmatch(prefixsplitter,inner) - -- these are taken from other anonymous references - if splitprefix and splitinner then - if splitprefix == "-" then - i = collected[""] - i = i and i[splitinner] - if i then - p = "" - end - else - i = collected[splitprefix] - i = i and i[splitinner] - if i then - p = splitprefix - end - end - end - -- todo: strict here - if not i then - i = collected[prefix] - i = i and i[inner] - if i then - p = prefix - end + -- special(operation) + var.kind = "special operation" + end + else + -- special() + var.kind = "special" + end + if trace_identifying then + report_identify_special(set,var,i,"1a") + end + else + var.error = "unknown special" + end + return var +end + +local function identify_arguments(set,var,i) + local s = specials[var.inner] + if s then + -- inner{argument} + var.kind = "special with arguments" + else + var.error = "unknown inner or special" + end + if trace_identifying then + report_identify_arguments(set,var,i,"3a") + end + return var +end + +local function identify_inner(set,var,prefix,collected,derived,tobesaved) + local inner = var.inner + local outer = var.outer + -- inner ... we could move the prefix logic into the parser so that we have 'm for each entry + -- foo:bar -> foo == prefix (first we try the global one) + -- -:bar -> ignore prefix + local p, i = prefix, nil + local splitprefix, splitinner + -- the next test is a safeguard when references are auto loaded from outer + if inner then + splitprefix, splitinner = lpegmatch(prefixsplitter,inner) + end + -- these are taken from other anonymous references + if splitprefix and splitinner then + if splitprefix == "-" then + i = collected[""] + i = i and i[splitinner] + if i then + p = "" + end + else + i = collected[splitprefix] + i = i and i[splitinner] + if i then + p = splitprefix + end + end + end + -- todo: strict here + if not i then + i = collected[prefix] + i = i and i[inner] + if i then + p = prefix + end + end + if not i and prefix ~= "" then + i = collected[""] + i = i and i[inner] + if i then + p = "" + end + end + if i then + var.i = { "reference", i } + resolvers.reference(var) + var.kind = "inner" + var.p = p + elseif derived then + -- these are taken from other data structures (like lists) + if splitprefix and splitinner then + if splitprefix == "-" then + i = derived[""] + i = i and i[splitinner] + if i then + p = "" end - if not i and prefix ~= "" then - i = collected[""] - i = i and i[inner] - if i then - p = "" - end + else + i = derived[splitprefix] + i = i and i[splitinner] + if i then + p = splitprefix end + end + end + if not i then + i = derived[prefix] + i = i and i[inner] + if i then + p = prefix + end + end + if not i and prefix ~= "" then + i = derived[""] + i = i and i[inner] + if i then + p = "" + end + end + if i then + var.kind = "inner" + var.i = i + var.p = p + local ri = resolvers[i[1]] + if ri then + ri(var) + else + -- can't happen as we catch it with a metatable now + report_references("unknown inner resolver for '%s'",i[1]) + end + else + -- no prefixes here + local s = specials[inner] + if s then + var.kind = "special" + else + i = (collected and collected[""] and collected[""][inner]) or + (derived and derived [""] and derived [""][inner]) or + (tobesaved and tobesaved[""] and tobesaved[""][inner]) if i then - var.i = { "reference", i } - references.resolvers.reference(var) var.kind = "inner" - var.p = p + var.i = { "reference", i } + resolvers.reference(var) + var.p = "" else - -- these are taken from other data structures (like lists) ---~ print("!!!!!!!!!!!!!!",splitprefix,splitinner) ---~ table.print(derived) - if splitprefix and splitinner then - if splitprefix == "-" then - i = derived[""] - i = i and i[splitinner] - if i then - p = "" - end - else - i = derived[splitprefix] - i = i and i[splitinner] - if i then - p = splitprefix - end - end - end - if not i then - i = derived[prefix] - i = i and i[inner] - if i then - p = prefix + var.error = "unknown inner or special" + end + end + end + end + return var +end + +local function identify_outer(set,var,i) + local outer = var.outer + local inner = var.inner + local external = externals[outer] + if external then + local v = copytable(var) + v = identify_inner(set,v,nil,external) + if v.i and not v.error then + v.kind = "outer with inner" + set.external = true + if trace_identifying then + report_identify_outer(set,v,i,"2a") + end + return v + end + v = copytable(var) + local v = identify_inner(set,v,v.outer,external) + if v.i and not v.error then + v.kind = "outer with inner" + set.external = true + if trace_identifying then + report_identify_outer(set,v,i,"2b") + end + return v + end + end + local external = productdata.componentreferences[outer] + if external then + local v = identify_inner(set,copytable(var),nil,external) + if v.i and not v.error then + v.kind = "outer with inner" + set.external = true + if trace_identifying then + report_identify_outer(set,v,i,"2c") + end + return v + end + end + local external = productdata.productreferences[outer] + if external then + local vi = external[inner] + if vi then + var.kind = "outer with inner" + var.i = vi + set.external = true + if trace_identifying then + report_identify_outer(set,var,i,"2d") + end + return var + end + end + -- the rest + local special = var.special + local arguments = var.arguments + local operation = var.operation + if inner then + if arguments then + -- outer::inner{argument} + var.kind = "outer with inner with arguments" + else + -- outer::inner + var.kind = "outer with inner" + end + var.i = { "reference", inner } + resolvers.reference(var) + var.f = outer + if trace_identifying then + report_identify_outer(set,var,i,"2e") + end + elseif special then + local s = specials[special] + if s then + if operation then + if arguments then + -- outer::special(operation{argument,argument}) + var.kind = "outer with special and operation and arguments" + else + -- outer::special(operation) + var.kind = "outer with special and operation" + end + else + -- outer::special() + var.kind = "outer with special" + end + var.f = outer + else + var.error = "unknown outer with special" + end + if trace_identifying then + report_identify_outer(set,var,i,"2f") + end + else + -- outer:: + var.kind = "outer" + var.f = outer + if trace_identifying then + report_identify_outer(set,var,i,"2g") + end + end + return var +end + +local function identify_inner_or_outer(set,var,i) + -- here we fall back on product data + local inner = var.inner + if inner and inner ~= "" then + local v = identify_inner(set,copytable(var),set.prefix,collected,derived,tobesaved) + if v.i and not v.error then + v.kind = "inner" -- check this + if trace_identifying then + report_identify_outer(set,v,i,"4a") + end + return v + end + +local components = job.structure.components +if components then + for i=1,#components do + local component = components[i] + local data = collected[component] + local vi = data and data[inner] + if vi then + var.outer = component + var.i = vi + var.kind = "outer with inner" + set.external = true + if trace_identifying then + report_identify_outer(set,var,i,"4x") + end + return var + end + end +end + + local componentreferences = productdata.componentreferences + local productreferences = productdata.productreferences + local components = productdata.components + if components and componentreferences then + -- for component, data in next, productdata.componentreferences do -- better do this in order of processing: + for i=1,#components do + local component = components[i] + local data = componentreferences[component] + if data then + local d = data[""] + local vi = d and d[inner] + if vi then + var.outer = component + var.i = vi + var.kind = "outer with inner" + set.external = true + if trace_identifying then + report_identify_outer(set,var,i,"4b") end + return var end - if not i and prefix ~= "" then - i = derived[""] - i = i and i[inner] - if i then - p = "" - end + end + end + end + local component, inner = lpegmatch(componentsplitter,inner) + if component then + local data = componentreferences and componentreferences[component] + if data then + local d = data[""] + local vi = d and d[inner] + if vi then + var.inner = inner + var.outer = component + var.i = vi + var.kind = "outer with inner" + set.external = true + if trace_identifying then + report_identify_outer(set,var,i,"4c") end - if i then - var.kind = "inner" - var.i = i - references.resolvers[i[1]](var) - var.p = p - else - -- no prefixes here - local s = specials[inner] - if s then - var.kind = "special" - else - i = (collected[""] and collected[""][inner]) or - (derived [""] and derived [""][inner]) or - (tobesaved[""] and tobesaved[""][inner]) - if i then - var.kind = "inner" - var.i = { "reference", i } - references.resolvers.reference(var) - var.p = "" - else - var.error = "unknown inner or special" - end - end + return var + end + end + local data = productreferences and productreferences[component] + if data then + local vi = data[inner] + if vi then + var.inner = inner + var.outer = component + var.i = vi + var.kind = "outer with inner" + set.external = true + if trace_identifying then + report_identify_outer(set,var,i,"4d") end + return var end end end - bug = bug or var.error + var.error = "unknown inner" + else + var.error = "no inner" + end + if trace_identifying then + report_identify_outer(set,var,i,"4e") + end + return var +end + +-- local function identify_inner_or_outer(set,var,i) +-- -- we might consider first checking with a prefix prepended and then without +-- -- which is better for fig:oeps +-- local var = do_identify_inner_or_outer(set,var,i) +-- if var.error then +-- local prefix = set.prefix +-- if prefix and prefix ~= "" then +-- var.inner = prefix .. ':' .. var.inner +-- var.error = nil +-- return do_identify_inner_or_outer(set,var,i) +-- end +-- end +-- return var +-- end + +local function identify_inner_component(set,var,i) + -- we're in a product (maybe ignore when same as component) + local component = var.component + identify_inner(set,var,component,collected,derived,tobesaved) + if trace_identifying then + report_identify_outer(set,var,i,"5a") + end + return var +end + +local function identify_outer_component(set,var,i) + local component = var.component + local inner = var.inner + local data = productdata.componentreferences[component] + if data then + local d = data[""] + local vi = d and d[inner] + if vi then + var.inner = inner + var.outer = component + var.i = vi + var.kind = "outer with inner" + set.external = true + if trace_identifying then + report_identify_outer(set,var,i,"6a") + end + return var + end + end + local data = productdata.productreferences[component] + if data then + local vi = data[inner] + if vi then + var.inner = inner + var.outer = component + var.i = vi + var.kind = "outer with inner" + set.external = true + if trace_identifying then + report_identify_outer(set,var,i,"6b") + end + return var + end + end + var.error = "unknown component" + if trace_identifying then + report_identify_outer(set,var,i,"6c") + end + return var +end + +local nofidentified = 0 + +local function identify(prefix,reference) + if not reference then + prefix = "" + reference = prefix + end + local set = resolve(prefix,reference) + local bug = false + texcount.referencehastexstate = set.has_tex and 1 or 0 + nofidentified = nofidentified + 1 + set.n = nofidentified + for i=1,#set do + local var = set[i] + if var.special then + var = identify_special(set,var,i) + elseif var.outer then + var = identify_outer(set,var,i) + elseif var.arguments then + var = identify_arguments(set,var,i) + elseif not var.component then + var = identify_inner_or_outer(set,var,i) + elseif productcomponent() then + var = identify_inner_component(set,var,i) + else + var = identify_outer_component(set,var,i) + end set[i] = var + bug = bug or var.error end references.currentset = mark(set) -- mark, else in api doc ---~ table.print(set,tostring(bug)) + if trace_analyzing then + report_references(table.serialize(set,reference)) + end return set, bug end @@ -1136,19 +1506,25 @@ function references.doifelse(prefix,reference,highlight,newwindow,layer) currentreference = set[1] end -- we can do the expansion here which saves a call ---~ print("!!!!!!",not unknown) commands.doifelse(not unknown) end function references.reportproblems() -- might become local if nofunknowns > 0 then - interfaces.showmessage("references",5,nofunknowns) -- 5 = unknown, 6 = illegal - -- -- we need a proper logger specific for the log file - -- texio.write_nl("log",format("%s unknown references",nofunknowns)) - -- for k, v in table.sortedpairs(unknowns) do - -- texio.write_nl("log",format("%s (n=%s)",k,v)) - -- end - -- texio.write_nl("log","") + statistics.register("cross referencing", function() + return format("%s identified, %s unknown",nofidentified,nofunknowns) + end) + logspushtarget("logfile") + logsnewline() + report_references("start problematic references") + logsnewline() + for k, v in table.sortedpairs(unknowns) do + report_unknown("%4i: %s",v,k) + end + logsnewline() + report_references("stop problematic references") + logsnewline() + logspoptarget() end end @@ -1177,14 +1553,16 @@ directives.register("references.linkmethod", function(v) -- page mixed names references.setinnermethod(v) end) -function references.setinternalreference(prefix,tag,internal,view) +-- this is inconsistent + +function references.setinternalreference(prefix,tag,internal,view) -- needs checking if innermethod == "page" then return unsetvalue else local t, tn = { }, 0 -- maybe add to current if tag then if prefix and prefix ~= "" then - prefix = prefix .. ":" + prefix = prefix .. ":" -- watch out, : here for ref in gmatch(tag,"[^,]+") do tn = tn + 1 t[tn] = prefix .. ref @@ -1206,8 +1584,22 @@ function references.setinternalreference(prefix,tag,internal,view) end end +function references.setandgetattribute(kind,prefix,tag,data,view) -- maybe do internal automatically here + references.set(kind,prefix,tag,data) + texcount.lastdestinationattribute = references.setinternalreference(prefix,tag,nil,view) or -0x7FFFFFFF +end + function references.getinternalreference(n) -- n points into list (todo: registers) local l = lists.collected[n] + return l and l.references.internal or n +end + +function commands.setinternalreference(prefix,tag,internal,view) -- needs checking + context(references.setinternalreference(prefix,tag,internal,view)) +end + +function commands.getinternalreference(n) -- this will also be a texcount + local l = lists.collected[n] context(l and l.references.internal or n) end @@ -1233,22 +1625,27 @@ function references.getcurrentprefixspec(default) -- todo: message end function references.filter(name,...) -- number page title ... - local data = currentreference and currentreference.i + local data = currentreference and currentreference.i -- maybe we should take realpage from here if data then - local kind = data.metadata and data.metadata.kind - if kind then - local filter = filters[kind] or filters.generic - filter = filter and (filter[name] or filter.unknown or filters.generic[name] or filters.generic.unknown) - if filter then - if trace_referencing then - report_references("name '%s', kind '%s', using dedicated filter",name,kind) + if name == "realpage" then + local cs = references.analyze() -- normally already analyzed but also sets state + context(cs.realpage or 0) -- todo, return and in command namespace + else -- assumes data is table + local kind = type(data) == "table" and data.metadata and data.metadata.kind + if kind then + local filter = filters[kind] or filters.generic + filter = filter and (filter[name] or filter.unknown or filters.generic[name] or filters.generic.unknown) + if filter then + if trace_referencing then + report_references("name '%s', kind '%s', using dedicated filter",name,kind) + end + filter(data,name,...) + elseif trace_referencing then + report_references("name '%s', kind '%s', using generic filter",name,kind) end - filter(data,name,...) elseif trace_referencing then - report_references("name '%s', kind '%s', using generic filter",name,kind) + report_references("name '%s', unknown kind",name) end - elseif trace_referencing then - report_references("name '%s', unknown kind",name) end elseif trace_referencing then report_references("name '%s', no reference",name) @@ -1279,7 +1676,6 @@ function filters.generic.number(data,what,prefixspec) -- todo: spec and then no if data then local numberdata = data.numberdata if numberdata then ---~ print(table.serialize(prefixspec)) helpers.prefix(data,prefixspec) sections.typesetnumber(numberdata,"number",numberdata) else @@ -1295,7 +1691,7 @@ filters.generic.default = filters.generic.text function filters.generic.page(data,prefixspec,pagespec) local pagedata = data.pagedata - if pagedata then -- imported + if pagedata then local number, conversion = pagedata.number, pagedata.conversion if not number then -- error @@ -1336,14 +1732,21 @@ function filters.text.title(data) helpers.title(data.entries.text or "?",data.metadata) end -function filters.text.number(data) - helpers.title(data.entries.text or "?",data.metadata) -end +-- no longer considered useful: +-- +-- function filters.text.number(data) +-- helpers.title(data.entries.text or "?",data.metadata) +-- end function filters.text.page(data,prefixspec,pagespec) helpers.prefixpage(data,prefixspec,pagespec) end +filters.full = { } + +filters.full.title = filters.text.title +filters.full.page = filters.text.page + filters.section = { } function filters.section.number(data,what,prefixspec) @@ -1364,23 +1767,29 @@ filters.section.title = filters.generic.title filters.section.page = filters.generic.page filters.section.default = filters.section.number -filters.note = { default = filters.generic.number } -filters.formula = { default = filters.generic.number } -filters.float = { default = filters.generic.number } -filters.description = { default = filters.generic.number } -filters.item = { default = filters.generic.number } +-- filters.note = { default = filters.generic.number } +-- filters.formula = { default = filters.generic.number } +-- filters.float = { default = filters.generic.number } +-- filters.description = { default = filters.generic.number } +-- filters.item = { default = filters.generic.number } -function references.sectiontitle(n) - helpers.sectiontitle(lists.collected[tonumber(n) or 0]) -end +setmetatableindex(filters, function(t,k) -- beware, test with rawget + local v = { default = filters.generic.number } -- not copy as it might be extended differently + t[k] = v + return v +end) -function references.sectionnumber(n) - helpers.sectionnumber(lists.collected[tonumber(n) or 0]) -end +-- function references.sectiontitle(n) +-- helpers.sectiontitle(lists.collected[tonumber(n) or 0]) +-- end -function references.sectionpage(n,prefixspec,pagespec) - helpers.prefixedpage(lists.collected[tonumber(n) or 0],prefixspec,pagespec) -end +-- function references.sectionnumber(n) +-- helpers.sectionnumber(lists.collected[tonumber(n) or 0]) +-- end + +-- function references.sectionpage(n,prefixspec,pagespec) +-- helpers.prefixedpage(lists.collected[tonumber(n) or 0],prefixspec,pagespec) +-- end -- analyze @@ -1390,69 +1799,124 @@ references.testspecials = references.testspecials or { } local runners = references.testrunners local specials = references.testspecials +-- We need to prevent ending up in the 'relative location' analyzer as it is +-- pretty slow (progressively). In the pagebody one can best check the reference +-- real page to determine if we need contrastlocation as that is more lightweight. + local function checkedpagestate(n,page) - local r, p = referred[n] or texcount.realpageno, tonumber(page) + local r, p = referredpage(n), tonumber(page) if not p then return 0 elseif p > r then - return 3 + return 3 -- after elseif p < r then - return 2 + return 2 -- before else - return 1 + return 1 -- same end end -function references.analyze(actions) +local function setreferencerealpage(actions) actions = actions or references.currentset if not actions then - actions = { realpage = 0 } - texcount.referencepagestate = 0 - elseif actions.realpage then - -- already analyzed - texcount.referencepagestate = checkedpagestate(actions.n,actions.realpage) + return 0 else - -- we store some analysis data alongside the indexed array - -- at this moment only the real reference page is analyzed - -- normally such an analysis happens in the backend code + local realpage = actions.realpage + if realpage then + return realpage + end local nofactions = #actions if nofactions > 0 then for i=1,nofactions do local a = actions[i] local what = runners[a.kind] if what then - what = what(a,actions) + what = what(a,actions) -- needs documentation end end - texcount.referencepagestate = checkedpagestate(actions.n,actions.realpage) + realpage = actions.realpage + if realpage then + return realpage + end + end + actions.realpage = 0 + return 0 + end +end + +-- we store some analysis data alongside the indexed array +-- at this moment only the real reference page is analyzed +-- normally such an analysis happens in the backend code + +function references.analyze(actions) + actions = actions or references.currentset + if not actions then + actions = { realpage = 0, pagestate = 0 } + elseif actions.pagestate then + -- already done + else + local realpage = actions.realpage or setreferencerealpage(actions) + if realpage == 0 then + actions.pagestate = 0 + elseif actions.external then + actions.pagestate = 0 else - texcount.referencepagestate = 0 + actions.pagestate = checkedpagestate(actions.n,realpage) end end return actions end -function references.realpage() -- special case, we always want result - local cs = references.analyze() - context(cs.realpage or 0) +function commands.referencepagestate(actions) + actions = actions or references.currentset + if not actions then + context(0) + else + if not actions.pagestate then + references.analyze(actions) -- delayed unless explicitly asked for + end + context(actions.pagestate) + end +end + +function commands.referencerealpage(actions) + actions = actions or references.currentset + context(not actions and 0 or actions.realpage or setreferencerealpage(actions)) end -local plist +local plist, nofrealpages -local function realpageofpage(p) +local function realpageofpage(p) -- the last one counts ! if not plist then local pages = structures.pages.collected + nofrealpages = #pages plist = { } - for rp=1,#pages do + for rp=1,nofrealpages do plist[pages[rp].number] = rp end + references.nofrealpages = nofrealpages end return plist[p] end references.realpageofpage = realpageofpage --- +function references.checkedrealpage(r) + if not plist then + realpageofpage(r) -- just initialize + end + if not r then + return texcount.realpageno + elseif r < 1 then + return 1 + elseif r > nofrealpages then + return nofrealpages + else + return r + end +end + +-- use local ? local pages = allocate { [variables.firstpage] = function() return counters.record("realpage")["first"] end, @@ -1489,7 +1953,8 @@ end runners["special operation"] = runners["special"] runners["special operation with arguments"] = runners["special"] --- weird, why is this code here and in lpdf-ano +-- These are the testspecials not the real ones. They are used to +-- check the validity. function specials.internal(var,actions) local v = references.internals[tonumber(var.operation)] @@ -1530,3 +1995,34 @@ function specials.userpage(var,actions) actions.realpage = actions.realpage or p -- first wins end end + +function specials.deltapage(var,actions) + local p = tonumber(var.operation) + if p then + p = references.checkedrealpage(p + texcount.realpageno) + var.r = p + actions.realpage = actions.realpage or p -- first wins + end +end + +function specials.section(var,actions) + local sectionname = var.arguments + local destination = var.operation + local internal = structures.sections.internalreference(sectionname,destination) + if internal then + var.special = "internal" + var.operation = internal + var.arguments = nil + specials.internal(var,actions) + end +end + +-- needs a better split ^^^ + +commands.filterreference = references.filter + +-- done differently now: + +function references.export(usedname) end +function references.import(usedname) end +function references.load (usedname) end |