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 | 849 |
1 files changed, 569 insertions, 280 deletions
diff --git a/Master/texmf-dist/tex/context/base/strc-ref.lua b/Master/texmf-dist/tex/context/base/strc-ref.lua index 475ab318a8a..ee32353f0c4 100644 --- a/Master/texmf-dist/tex/context/base/strc-ref.lua +++ b/Master/texmf-dist/tex/context/base/strc-ref.lua @@ -7,14 +7,24 @@ if not modules then modules = { } end modules ['strc-ref'] = { } local format, find, gmatch, match, concat = string.format, string.find, string.gmatch, string.match, table.concat -local lpegmatch = lpeg.match -local texsprint, texwrite, texcount, texsetcount = tex.sprint, tex.write, tex.count, tex.setcount +local lpegmatch, lpegP, lpegCs = lpeg.match, lpeg.P, lpeg.Cs +local texcount, texsetcount = tex.count, tex.setcount +local rawget = rawget -local trace_referencing = false trackers.register("structure.referencing", function(v) trace_referencing = v end) +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 report_references = logs.reporter("structure","references") -local ctxcatcodes = tex.ctxcatcodes local variables = interfaces.variables local constants = interfaces.constants +local context = context + +local settings_to_array = utilities.parsers.settings_to_array +local unsetvalue = attributes.unsetvalue -- 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 @@ -22,50 +32,119 @@ local constants = interfaces.constants -- the useddata and pagedata names might change -- todo: pack exported data -jobreferences = jobreferences or { } -jobreferences.tobesaved = jobreferences.tobesaved or { } -jobreferences.collected = jobreferences.collected or { } -jobreferences.defined = jobreferences.defined or { } -- indirect ones -jobreferences.derived = jobreferences.derived or { } -- taken from lists -jobreferences.specials = jobreferences.specials or { } -- system references -jobreferences.runners = jobreferences.runners or { } -jobreferences.internals = jobreferences.internals or { } -jobreferences.exporters = jobreferences.exporters or { } -jobreferences.imported = jobreferences.imported or { } - -storage.register("jobreferences/defined", jobreferences.defined, "jobreferences.defined") - -local tobesaved, collected = jobreferences.tobesaved, jobreferences.collected -local defined, derived, specials = jobreferences.defined, jobreferences.derived, jobreferences.specials -local exporters, runners = jobreferences.exporters, jobreferences.runners +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 + +storage.register("structures/references/defined", references.defined, "structures.references.defined") local currentreference = nil local initializers = { } local finalizers = { } -function jobreferences.registerinitializer(func) -- we could use a token register instead +function references.registerinitializer(func) -- we could use a token register instead initializers[#initializers+1] = func end -function jobreferences.registerfinalizer(func) -- we could use a token register instead +function references.registerfinalizer(func) -- we could use a token register instead finalizers[#finalizers+1] = func end -local function initializer() - tobesaved, collected = jobreferences.tobesaved, jobreferences.collected +local function initializer() -- can we use a tobesaved as metatable for collected? + tobesaved = references.tobesaved + collected = references.collected for i=1,#initializers do initializers[i](tobesaved,collected) end end + local function finalizer() - tobesaved = jobreferences.tobesaved for i=1,#finalizers do finalizers[i](tobesaved) end end -if job then - job.register('jobreferences.collected', jobreferences.tobesaved, initializer, finalizer) +job.register('structures.references.collected', tobesaved, initializer, finalizer) + +local maxreferred = 1 + +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) +end + +local function finalizer() -- make sparse + local last + 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 + end + end +end + +job.register('structures.references.referred', tobereferred, initializer, finalizer) + +function references.referredpage(n) + return referred[n] or referred[n] or texcount.realpageno +end + +function references.registerpage(n) + if not tobereferred[n] then + if n > maxreferred then + maxreferred = n + end + tobereferred[n] = texcount.realpageno + end end -- todo: delay split till later as in destinations we split anyway @@ -86,18 +165,18 @@ local function setnextorder(kind,name) texsetcount("global","locationorder",lastorder) end -jobreferences.setnextorder = setnextorder +references.setnextorder = setnextorder -function jobreferences.setnextinternal(kind,name) +function references.setnextinternal(kind,name) setnextorder(kind,name) -- always incremented with internal texsetcount("global","locationcount",texcount.locationcount + 1) end -function jobreferences.currentorder(kind,name) - texwrite((orders[kind] and orders[kind][name]) or lastorder) +function references.currentorder(kind,name) + context(orders[kind] and orders[kind][name] or lastorder) end -function jobreferences.set(kind,prefix,tag,data) +function references.set(kind,prefix,tag,data) for ref in gmatch(tag,"[^,]+") do local p, r = match(ref,"^(%-):(.-)$") if p and r then @@ -112,17 +191,17 @@ function jobreferences.set(kind,prefix,tag,data) tobesaved[prefix] = pd end pd[ref] = data - texsprint(ctxcatcodes,format("\\dofinish%sreference{%s}{%s}",kind,prefix,ref)) + context.dofinishsomereference(kind,prefix,ref) end end end -function jobreferences.setandgetattribute(kind,prefix,tag,data,view) -- maybe do internal automatically here - jobreferences.set(kind,prefix,tag,data) - texcount.lastdestinationattribute = jobreferences.setinternalreference(prefix,tag,nil,view) or -0x7FFFFFFF +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 jobreferences.enhance(prefix,tag,spec) +function references.enhance(prefix,tag,spec) local l = tobesaved[prefix][tag] if l then l.references.realpage = texcount.realpageno @@ -133,9 +212,9 @@ end local result = { } -local lparent, rparent, lbrace, rbrace, dcolon, backslash = lpeg.P("("), lpeg.P(")"), lpeg.P("{"), lpeg.P("}"), lpeg.P("::"), lpeg.P("\\") +local lparent, rparent, lbrace, rbrace, dcolon, backslash = lpegP("("), lpegP(")"), lpegP("{"), lpegP("}"), lpegP("::"), lpegP("\\") -local reset = lpeg.P("") / function() result = { } end +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 @@ -144,11 +223,11 @@ 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 = lpeg.Cs((b_token + o_token)^1) / function (s) result.operation = s end -local arguments = lpeg.Cs((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 = (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 @@ -160,34 +239,34 @@ local special_reference = special * lparent * (operation * optional_arguments + local scanner = (reset * outer_reference * (special_reference + inner_reference)^-1 * -1) / function() return result end ---~ function jobreferences.analyse(str) -- overloaded +--~ function references.analyze(str) -- overloaded --~ return lpegmatch(scanner,str) --~ end -function jobreferences.split(str) +function references.split(str) return lpegmatch(scanner,str or "") end ---~ print(table.serialize(jobreferences.analyse(""))) ---~ print(table.serialize(jobreferences.analyse("inner"))) ---~ print(table.serialize(jobreferences.analyse("special(operation{argument,argument})"))) ---~ print(table.serialize(jobreferences.analyse("special(operation)"))) ---~ print(table.serialize(jobreferences.analyse("special()"))) ---~ print(table.serialize(jobreferences.analyse("inner{argument}"))) ---~ print(table.serialize(jobreferences.analyse("outer::"))) ---~ print(table.serialize(jobreferences.analyse("outer::inner"))) ---~ print(table.serialize(jobreferences.analyse("outer::special(operation{argument,argument})"))) ---~ print(table.serialize(jobreferences.analyse("outer::special(operation)"))) ---~ print(table.serialize(jobreferences.analyse("outer::special()"))) ---~ print(table.serialize(jobreferences.analyse("outer::inner{argument}"))) ---~ print(table.serialize(jobreferences.analyse("special(outer::operation)"))) +--~ 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 -- -- -- -jobreferences.resolvers = jobreferences.resolvers or { } +references.resolvers = references.resolvers or { } -function jobreferences.resolvers.section(var) - local vi = structure.lists.collected[var.i[2]] +function references.resolvers.section(var) + local vi = lists.collected[var.i[2]] if vi then var.i = vi var.r = (vi.references and vi.references.realpage) or (vi.pagedata and vi.pagedata.realpage) or 1 @@ -197,12 +276,12 @@ function jobreferences.resolvers.section(var) end end -jobreferences.resolvers.float = jobreferences.resolvers.section -jobreferences.resolvers.description = jobreferences.resolvers.section -jobreferences.resolvers.formula = jobreferences.resolvers.section -jobreferences.resolvers.note = jobreferences.resolvers.section +references.resolvers.float = references.resolvers.section +references.resolvers.description = references.resolvers.section +references.resolvers.formula = references.resolvers.section +references.resolvers.note = references.resolvers.section -function jobreferences.resolvers.reference(var) +function references.resolvers.reference(var) local vi = var.i[2] if vi then var.i = vi @@ -214,6 +293,7 @@ function jobreferences.resolvers.reference(var) end local function register_from_lists(collected,derived) + 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 @@ -226,9 +306,10 @@ local function register_from_lists(collected,derived) local t = { kind, i } for s in gmatch(reference,"%s*([^,]+)") do if trace_referencing then - logs.report("referencing","list entry %s provides %s reference '%s' on realpage %s",i,kind,s,realpage) + report_references("list entry %s provides %s reference '%s' on realpage %s",i,kind,s,realpage) end - d[s] = t -- share them + d[s] = d[s] or t -- share them + g[s] = g[s] or t -- first wins end end end @@ -236,30 +317,34 @@ local function register_from_lists(collected,derived) end end -jobreferences.registerinitializer(function() register_from_lists(structure.lists.collected,derived) end) +references.registerinitializer(function() register_from_lists(lists.collected,derived) end) -- urls -jobreferences.urls = jobreferences.urls or { } -jobreferences.urls.data = jobreferences.urls.data or { } +references.urls = references.urls or { } +references.urls.data = references.urls.data or { } -local urls = jobreferences.urls.data +local urls = references.urls.data -function jobreferences.urls.define(name,url,file,description) +function references.urls.define(name,url,file,description) if name and name ~= "" then urls[name] = { url or "", file or "", description or url or file or ""} end end -function jobreferences.urls.get(name,method,space) -- method: none, before, after, both, space: yes/no +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 local u = urls[name] if u then local url, file = u[1], u[2] + pushcatcodes(txtcatcodes) if file and file ~= "" then - texsprint(ctxcatcodes,url,"/",file) + context("%s/%s",url,file) else - texsprint(ctxcatcodes,url) + context(url) end + popcatcodes() end end @@ -269,21 +354,21 @@ end -- files -jobreferences.files = jobreferences.files or { } -jobreferences.files.data = jobreferences.files.data or { } +references.files = references.files or { } +references.files.data = references.files.data or { } -local files = jobreferences.files.data +local files = references.files.data -function jobreferences.files.define(name,file,description) +function references.files.define(name,file,description) if name and name ~= "" then files[name] = { file or "", description or file or ""} end end -function jobreferences.files.get(name,method,space) -- method: none, before, after, both, space: yes/no +function references.files.get(name,method,space) -- method: none, before, after, both, space: yes/no local f = files[name] if f then - texsprint(ctxcatcodes,f[1]) + context(f[1]) end end @@ -293,7 +378,7 @@ end -- helpers -function jobreferences.checkedfile(whatever) -- return whatever if not resolved +function references.checkedfile(whatever) -- return whatever if not resolved if whatever then local w = files[whatever] if w then @@ -304,7 +389,7 @@ function jobreferences.checkedfile(whatever) -- return whatever if not resolved end end -function jobreferences.checkedurl(whatever) -- return whatever if not resolved +function references.checkedurl(whatever) -- return whatever if not resolved if whatever then local w = urls[whatever] if w then @@ -320,7 +405,7 @@ function jobreferences.checkedurl(whatever) -- return whatever if not resolved end end -function jobreferences.checkedfileorurl(whatever,default) -- return nil, nil if not resolved +function references.checkedfileorurl(whatever,default) -- return nil, nil if not resolved if whatever then local w = files[whatever] if w then @@ -342,25 +427,25 @@ end -- programs -jobreferences.programs = jobreferences.programs or { } -jobreferences.programs.data = jobreferences.programs.data or { } +references.programs = references.programs or { } +references.programs.data = references.programs.data or { } -local programs = jobreferences.programs.data +local programs = references.programs.data -function jobreferences.programs.define(name,file,description) +function references.programs.define(name,file,description) if name and name ~= "" then programs[name] = { file or "", description or file or ""} end end -function jobreferences.programs.get(name) +function references.programs.get(name) local f = programs[name] if f then - texsprint(ctxcatcodes,f[1]) + context(f[1]) end end -function jobreferences.checkedprogram(whatever) -- return whatever if not resolved +function references.checkedprogram(whatever) -- return whatever if not resolved if whatever then local w = programs[whatever] if w then @@ -373,36 +458,36 @@ end -- shared by urls and files -function jobreferences.whatfrom(name) - texsprint(ctxcatcodes,(urls[name] and variables.url) or (files[name] and variables.file) or variables.unknown) +function references.whatfrom(name) + context((urls[name] and variables.url) or (files[name] and variables.file) or variables.unknown) end ---~ function jobreferences.from(name) +--~ function references.from(name) --~ local u = urls[name] --~ if u then --~ local url, file, description = u[1], u[2], u[3] --~ if description ~= "" then ---~ texsprint(ctxcatcodes,format("\\dofromurldescription{%s}",description)) +--~ context.dofromurldescription(description) --~ -- ok --~ elseif file and file ~= "" then ---~ texsprint(ctxcatcodes,format("\\dofromurlliteral{%s}",url .. "/" .. file)) +--~ context.dofromurlliteral(url .. "/" .. file) --~ else ---~ texsprint(ctxcatcodes,format("\\dofromurlliteral{%s}",url)) +--~ context(dofromurlliteral,url) --~ end --~ else --~ local f = files[name] --~ if f then --~ local description, file = f[1], f[2] --~ if description ~= "" then ---~ texsprint(ctxcatcodes,format("\\dofromfiledescription{%s}",description)) +--~ context.dofromfiledescription(description) --~ else ---~ texsprint(ctxcatcodes,format("\\dofromfileliteral{%s}",file)) +--~ context.dofromfileliteral(file) --~ end --~ end --~ end --~ end -function jobreferences.from(name) +function references.from(name) local u = urls[name] if u then local url, file, description = u[1], u[2], u[3] @@ -456,11 +541,12 @@ function exporters.lists.generic(data) end if numberdata then local numbers = numberdata.numbers - local t = { } + local t, tn = { }, 0 for i=1,#numbers do local n = numbers[i] if n ~= 0 then - t[#t+1] = n + tn = tn + 1 + t[tn] = n end end useddata.number = concat(t,".") @@ -481,13 +567,16 @@ local function referencer(data) } end -function jobreferences.export(usedname) +-- 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, jobreferences.tobesaved do + 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 @@ -506,7 +595,7 @@ function jobreferences.export(usedname) end end local pe = exported[""] if not pe then pe = { } exported[""] = pe end - for n, data in next, structure.lists.tobesaved do + for n, data in next, lists.tobesaved do local metadata = data.metadata local exporter = e_lists[metadata.kind] or g_lists if exporter then @@ -531,9 +620,8 @@ function jobreferences.export(usedname) io.savedata(file.replacesuffix(usedname or tex.jobname,"tue"),table.serialize(e,true)) end -function jobreferences.import(usedname) +function references.import(usedname) if usedname then - local imported = jobreferences.imported local jdn = imported[usedname] if not jdn then local filename = files[usedname] @@ -574,20 +662,20 @@ function jobreferences.import(usedname) end end -function jobreferences.load(usedname) +function references.load(usedname) -- gone end -function jobreferences.define(prefix,reference,list) +function references.define(prefix,reference,list) local d = defined[prefix] if not d then d = { } defined[prefix] = d end d[reference] = { "defined", list } end ---~ function jobreferences.registerspecial(name,action,...) +--~ function references.registerspecial(name,action,...) --~ specials[name] = { action, ... } --~ end -function jobreferences.reset(prefix,reference) +function references.reset(prefix,reference) local d = defined[prefix] if d then d[reference] = nil @@ -600,25 +688,55 @@ end -- t.special t.operation t.arguments t.outer t.inner -local settings_to_array = aux.settings_to_array +-- to what extend do we check the non prefixed variant + +local strict = false local function resolve(prefix,reference,args,set) -- we start with prefix,reference texcount.referencehastexstate = 0 if reference and reference ~= "" then - set = set or { } + if not set then + set = { prefix = prefix, reference = reference } + else + set.reference = set.reference or reference + set.prefix = set.prefix or prefix + end local r = settings_to_array(reference) for i=1,#r do local ri = r[i] - local dp = defined[prefix] or defined[""] - local d = dp[ri] + local d + if strict then + d = defined[prefix] or defined[""] + d = d and d[ri] + else + d = defined[prefix] + d = d and d[ri] + if not d then + d = defined[""] + d = d and d[ri] + end + end if d then resolve(prefix,d[2],nil,set) else local var = lpegmatch(scanner,ri) if var then var.reference = ri - if not var.outer and var.inner then - local d = defined[prefix][var.inner] or defined[""][var.inner] + local vo, vi = var.outer, var.inner + if not vo and vi then + -- to be checked + if strict then + d = defined[prefix] or defined[""] + d = d and d[vi] + else + d = defined[prefix] + d = d and d[vi] + if not d then + d = defined[""] + d = d and d[vi] + end + end + -- if d then resolve(prefix,d[2],var.arguments,set) -- args can be nil else @@ -633,13 +751,14 @@ local function resolve(prefix,reference,args,set) -- we start with prefix,refere set.has_tex = true end else - -- logs.report("references","funny pattern: %s",ri or "?") + -- report_references("funny pattern: %s",ri or "?") end end end if set.has_tex then texcount.referencehastexstate = 1 end +--~ table.print(set) return set else return { } @@ -648,41 +767,69 @@ end -- prefix == "" is valid prefix which saves multistep lookup -jobreferences.currentset = nil +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 + +function commands.setreferencearguments(k,v) + references.currentset[k].arguments = v +end -local b, e = "\\ctxlua{local jc = jobreferences.currentset;", "}" -local o, a = 'jc[%s].operation=[[%s]];', 'jc[%s].arguments=[[%s]];' +local expandreferenceoperation = context.expandreferenceoperation +local expandreferencearguments = context.expandreferencearguments -function jobreferences.expandcurrent() -- todo: two booleans: o_has_tex& a_has_tex - local currentset = jobreferences.currentset +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 - texsprint(ctxcatcodes,b) - done = true - end - texsprint(ctxcatcodes,format(o,i,operation)) - end + if operation and find(operation,"\\") then -- if o_has_tex then + expandreferenceoperation(i,operation) end local arguments = ci.arguments - if arguments then - if find(arguments,"\\") then -- if a_has_tex then - if not done then - texsprint(ctxcatcodes,b) - done = true - end - texsprint(ctxcatcodes,format(a,i,arguments)) - end + if arguments and find(arguments,"\\") then -- if a_has_tex then + expandreferencearguments(i,arguments) end end - if done then - texsprint(ctxcatcodes,e) - end end end @@ -696,9 +843,17 @@ end --~ end --~ end +local prefixsplitter = lpegCs(lpegP((1-lpegP(":"))^1 * lpegP(":"))) * lpegCs(lpegP(1)^1) + +-- todo: add lots of tracing here + +local n = 0 + 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 @@ -730,7 +885,7 @@ local function identify(prefix,reference) var.error = "unknown special" end elseif outer then - local e = jobreferences.import(outer) + local e = references.import(outer) if e then if inner then local r = e.references @@ -747,7 +902,7 @@ local function identify(prefix,reference) var.kind = "outer with inner" end var.i = { "reference", r } - jobreferences.resolvers.reference(var) + references.resolvers.reference(var) var.f = outer var.e = true -- external end @@ -769,7 +924,7 @@ local function identify(prefix,reference) var.kind = "outer with inner" end var.i = r - jobreferences.resolvers[r[1]](var) + references.resolvers[r[1]](var) var.f = outer end end @@ -812,7 +967,7 @@ local function identify(prefix,reference) var.kind = "outer with inner" end var.i = { "reference", inner } - jobreferences.resolvers.reference(var) + references.resolvers.reference(var) var.f = outer elseif special then local s = specials[special] @@ -849,47 +1004,101 @@ local function identify(prefix,reference) var.error = "unknown inner or special" end else - -- inner ---~ local i = tobesaved[prefix] - local i = collected[prefix] - i = i and i[inner] + -- 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 + 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 } - jobreferences.resolvers.reference(var) + references.resolvers.reference(var) var.kind = "inner" - var.p = prefix + var.p = p else - i = derived[prefix] - i = i and i[inner] + -- 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 + 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 - jobreferences.resolvers[i[1]](var) - var.p = prefix + references.resolvers[i[1]](var) + var.p = p else - i = collected[prefix] - i = i and i[inner] - if i then - var.kind = "inner" - var.i = { "reference", i } - jobreferences.resolvers.reference(var) - var.p = prefix + -- no prefixes here + local s = specials[inner] + if s then + var.kind = "special" else - local s = specials[inner] - if s then - var.kind = "special" + 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 - 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 } - jobreferences.resolvers.reference(var) - var.p = "" - else - var.error = "unknown inner or special" - end + var.error = "unknown inner or special" end end end @@ -899,87 +1108,131 @@ local function identify(prefix,reference) bug = bug or var.error set[i] = var end - jobreferences.currentset = set --- print(bug,table.serialize(set)) + references.currentset = mark(set) -- mark, else in api doc +--~ table.print(set,tostring(bug)) return set, bug end -jobreferences.identify = identify +references.identify = identify -function jobreferences.doifelse(prefix,reference,highlight,newwindow,layer) +local unknowns, nofunknowns = { }, 0 + +function references.doifelse(prefix,reference,highlight,newwindow,layer) local set, bug = identify(prefix,reference) local unknown = bug or #set == 0 if unknown then currentreference = nil -- will go away + local str = format("[%s][%s]",prefix,reference) + local u = unknowns[str] + if not u then + interfaces.showmessage("references",1,str) -- 1 = unknown, 4 = illegal + unknowns[str] = 1 + nofunknowns = nofunknowns + 1 + else + unknowns[str] = u + 1 + end else set.highlight, set.newwindow,set.layer = 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 jobreferences.setinternalreference(prefix,tag,internal,view) - local t = { } - if tag then - if prefix and prefix ~= "" then - prefix = prefix .. ":" - for ref in gmatch(tag,"[^,]+") do - t[#t+1] = prefix .. ref - end - else - for ref in gmatch(tag,"[^,]+") do - t[#t+1] = ref - 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","") + end +end + +luatex.registerstopactions(references.reportproblems) + +local innermethod = "names" + +function references.setinnermethod(m) + if m then + if m == "page" or m == "mixed" or m == "names" then + innermethod = m + elseif m == true or m == variables.yes then + innermethod = "page" end end - if internal then - t[#t+1] = "aut:" .. internal + function references.setinnermethod() + report_references("inner method is already set and frozen to '%s'",innermethod) end - local destination = jobreferences.mark(t,nil,nil,view) -- returns an attribute - texcount.lastdestinationattribute = destination - return destination end -function jobreferences.getinternalreference(n) -- n points into list - local l = structure.lists.collected[n] - texsprint(ctxcatcodes,(l and l.references.internal) or n) +function references.getinnermethod() + return innermethod or "names" +end + +directives.register("references.linkmethod", function(v) -- page mixed names + references.setinnermethod(v) +end) + +function references.setinternalreference(prefix,tag,internal,view) + 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 .. ":" + for ref in gmatch(tag,"[^,]+") do + tn = tn + 1 + t[tn] = prefix .. ref + end + else + for ref in gmatch(tag,"[^,]+") do + tn = tn + 1 + t[tn] = ref + end + end + end + if internal and innermethod == "names" then -- mixed or page + tn = tn + 1 + t[tn] = "aut:" .. internal + end + local destination = references.mark(t,nil,nil,view) -- returns an attribute + texcount.lastdestinationattribute = destination + return destination + end end +function references.getinternalreference(n) -- n points into list (todo: registers) + local l = lists.collected[n] + context(l and l.references.internal or n) +end -- -function jobreferences.get_current_metadata(tag) +function references.getcurrentmetadata(tag) local data = currentreference and currentreference.i data = data and data.metadata and data.metadata[tag] if data then - texsprint(ctxcatcodes,data) + context(data) end end -local function current_metadata(tag) + +local function currentmetadata(tag) local data = currentreference and currentreference.i return data and data.metadata and data.metadata[tag] end -jobreferences.current_metadata = current_metadata -function jobreferences.get_current_prefixspec(default) -- todo: message - texsprint(ctxcatcodes,"\\getreferencestructureprefix{", - current_metadata("kind") or "?", "}{", current_metadata("name") or "?", "}{", default or "?", "}") -end - ---~ function jobreferences.get_current_prefixspec(default) -- we can consider storing the data at the lua end ---~ context.getreferencestructureprefix(current_metadata("kind"),current_metadata("name"),default) ---~ end +references.currentmetadata = currentmetadata --- - -jobreferences.filters = jobreferences.filters or { } - -local filters = jobreferences.filters -local helpers = structure.helpers -local sections = structure.sections +function references.getcurrentprefixspec(default) -- todo: message + context.getreferencestructureprefix(currentmetadata("kind") or "?",currentmetadata("name") or "?",default or "?") +end -function jobreferences.filter(name,...) -- number page title ... +function references.filter(name,...) -- number page title ... local data = currentreference and currentreference.i if data then local kind = data.metadata and data.metadata.kind @@ -988,17 +1241,17 @@ function jobreferences.filter(name,...) -- number page title ... filter = filter and (filter[name] or filter.unknown or filters.generic[name] or filters.generic.unknown) if filter then if trace_referencing then - logs.report("referencing","name '%s', kind '%s', using dedicated filter",name,kind) + report_references("name '%s', kind '%s', using dedicated filter",name,kind) end filter(data,name,...) elseif trace_referencing then - logs.report("referencing","name '%s', kind '%s', using generic filter",name,kind) + report_references("name '%s', kind '%s', using generic filter",name,kind) end elseif trace_referencing then - logs.report("referencing","name '%s', unknown kind",name) + report_references("name '%s', unknown kind",name) end elseif trace_referencing then - logs.report("referencing","name '%s', no reference",name) + report_references("name '%s', no reference",name) end end @@ -1032,7 +1285,7 @@ function filters.generic.number(data,what,prefixspec) -- todo: spec and then no else local useddata = data.useddata if useddata and useddsta.number then - tex.sprint(tex.ctxcatcodes,useddata.number) + context(useddata.number) end end end @@ -1047,9 +1300,9 @@ function filters.generic.page(data,prefixspec,pagespec) if not number then -- error elseif conversion then - tex.sprint(tex.ctxcatcodes,format("\\convertnumber{%s}{%s}",conversion,number)) + context.convertnumber(conversion,number) else - tex.sprint(tex.ctxcatcodes,number) + context(number) end else helpers.prefixpage(data,prefixspec,pagespec) @@ -1072,7 +1325,7 @@ function filters.user.unknown(data,name) end local namedata = userdata and userdata[name] if namedata then - texsprint(ctxcatcodes,namedata) + context(namedata) end end end @@ -1080,12 +1333,10 @@ end filters.text = { } function filters.text.title(data) --- texsprint(ctxcatcodes,"[text title]") helpers.title(data.entries.text or "?",data.metadata) end function filters.text.number(data) --- texsprint(ctxcatcodes,"[text number]") helpers.title(data.entries.text or "?",data.metadata) end @@ -1103,7 +1354,7 @@ function filters.section.number(data,what,prefixspec) else local useddata = data.useddata if useddata and useddata.number then - tex.sprint(tex.ctxcatcodes,useddata.number) + context(useddata.number) end end end @@ -1119,12 +1370,6 @@ filters.float = { default = filters.generic.number } filters.description = { default = filters.generic.number } filters.item = { default = filters.generic.number } -structure.references = structure.references or { } -structure.helpers = structure.helpers or { } - -local references = structure.references -local helpers = structure.helpers - function references.sectiontitle(n) helpers.sectiontitle(lists.collected[tonumber(n) or 0]) end @@ -1137,25 +1382,39 @@ function references.sectionpage(n,prefixspec,pagespec) helpers.prefixedpage(lists.collected[tonumber(n) or 0],prefixspec,pagespec) end --- analyse +-- analyze -jobreferences.testrunners = jobreferences.testrunners or { } -jobreferences.testspecials = jobreferences.testspecials or { } +references.testrunners = references.testrunners or { } +references.testspecials = references.testspecials or { } -local runners = jobreferences.testrunners -local specials = jobreferences.testspecials +local runners = references.testrunners +local specials = references.testspecials -function jobreferences.analyse(actions) - actions = actions or jobreferences.currentset +local function checkedpagestate(n,page) + local r, p = referred[n] or texcount.realpageno, tonumber(page) + if not p then + return 0 + elseif p > r then + return 3 + elseif p < r then + return 2 + else + return 1 + end +end + +function references.analyze(actions) + actions = actions or references.currentset if not actions then actions = { realpage = 0 } + texcount.referencepagestate = 0 elseif actions.realpage then - -- already analysed + -- already analyzed + texcount.referencepagestate = checkedpagestate(actions.n,actions.realpage) else -- we store some analysis data alongside the indexed array - -- at this moment only the real reference page is analysed + -- at this moment only the real reference page is analyzed -- normally such an analysis happens in the backend code - texcount.referencepagestate = 0 local nofactions = #actions if nofactions > 0 then for i=1,nofactions do @@ -1165,43 +1424,53 @@ function jobreferences.analyse(actions) what = what(a,actions) end end - local realpage, p = texcount.realpageno, tonumber(actions.realpage) - if not p then - -- sorry - elseif p > realpage then - texcount.referencepagestate = 3 - elseif p < realpage then - texcount.referencepagestate = 2 - else - texcount.referencepagestate = 1 - end + texcount.referencepagestate = checkedpagestate(actions.n,actions.realpage) + else + texcount.referencepagestate = 0 end end return actions end -function jobreferences.realpage() -- special case, we always want result - local cs = jobreferences.analyse() - texwrite(cs.realpage or 0) +function references.realpage() -- special case, we always want result + local cs = references.analyze() + context(cs.realpage or 0) end +local plist + +local function realpageofpage(p) + if not plist then + local pages = structures.pages.collected + plist = { } + for rp=1,#pages do + plist[pages[rp].number] = rp + end + end + return plist[p] +end + +references.realpageofpage = realpageofpage + -- -jobreferences.pages = { - [variables.firstpage] = function() return structure.counters.record("realpage")["first"] end, - [variables.previouspage] = function() return structure.counters.record("realpage")["previous"] end, - [variables.nextpage] = function() return structure.counters.record("realpage")["next"] end, - [variables.lastpage] = function() return structure.counters.record("realpage")["last"] end, +local pages = allocate { + [variables.firstpage] = function() return counters.record("realpage")["first"] end, + [variables.previouspage] = function() return counters.record("realpage")["previous"] end, + [variables.nextpage] = function() return counters.record("realpage")["next"] end, + [variables.lastpage] = function() return counters.record("realpage")["last"] end, - [variables.firstsubpage] = function() return structure.counters.record("subpage" )["first"] end, - [variables.previoussubpage] = function() return structure.counters.record("subpage" )["previous"] end, - [variables.nextsubpage] = function() return structure.counters.record("subpage" )["next"] end, - [variables.lastsubpage] = function() return structure.counters.record("subpage" )["last"] end, + [variables.firstsubpage] = function() return counters.record("subpage" )["first"] end, + [variables.previoussubpage] = function() return counters.record("subpage" )["previous"] end, + [variables.nextsubpage] = function() return counters.record("subpage" )["next"] end, + [variables.lastsubpage] = function() return counters.record("subpage" )["last"] end, - [variables.forward] = function() return structure.counters.record("realpage")["forward"] end, - [variables.backward] = function() return structure.counters.record("realpage")["backward"] end, + [variables.forward] = function() return counters.record("realpage")["forward"] end, + [variables.backward] = function() return counters.record("realpage")["backward"] end, } +references.pages = pages + -- maybe some day i will merge this in the backend code with a testmode (so each -- runner then implements a branch) @@ -1220,10 +1489,10 @@ end runners["special operation"] = runners["special"] runners["special operation with arguments"] = runners["special"] -local pages = jobreferences.pages +-- weird, why is this code here and in lpdf-ano function specials.internal(var,actions) - local v = jobreferences.internals[tonumber(var.operation)] + local v = references.internals[tonumber(var.operation)] local r = v and v.references.realpage if r then actions.realpage = r @@ -1232,12 +1501,32 @@ end specials.i = specials.internal -function specials.page(var,actions) -- is this ok? - local p = pages[var.operation] +function specials.page(var,actions) + local o = var.operation + local p = pages[o] if type(p) == "function" then p = p() + else + p = tonumber(realpageofpage(tonumber(o))) + end + if p then + var.r = p + actions.realpage = actions.realpage or p -- first wins end +end + +function specials.realpage(var,actions) + local p = tonumber(var.operation) + if p then + var.r = p + actions.realpage = actions.realpage or p -- first wins + end +end + +function specials.userpage(var,actions) + local p = tonumber(realpageofpage(var.operation)) if p then - actions.realpage = p + var.r = p + actions.realpage = actions.realpage or p -- first wins end end |