summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/tex/context/base/strc-ref.lua
diff options
context:
space:
mode:
Diffstat (limited to 'Master/texmf-dist/tex/context/base/strc-ref.lua')
-rw-r--r--Master/texmf-dist/tex/context/base/strc-ref.lua1874
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