diff options
Diffstat (limited to 'tex/context/base/strc-num.lua')
-rw-r--r-- | tex/context/base/strc-num.lua | 1298 |
1 files changed, 649 insertions, 649 deletions
diff --git a/tex/context/base/strc-num.lua b/tex/context/base/strc-num.lua index 6245a537e..b0eae6b78 100644 --- a/tex/context/base/strc-num.lua +++ b/tex/context/base/strc-num.lua @@ -1,649 +1,649 @@ -if not modules then modules = { } end modules ['strc-num'] = {
- version = 1.001,
- comment = "companion to strc-num.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-local format = string.format
-local next, type = next, type
-local min, max = math.min, math.max
-local texcount, texsetcount = tex.count, tex.setcount
-
--- Counters are managed here. They can have multiple levels which makes it easier to synchronize
--- them. Synchronization is sort of special anyway, as it relates to document structuring.
-
-local allocate = utilities.storage.allocate
-local setmetatableindex = table.setmetatableindex
-
-local trace_counters = false trackers.register("structures.counters", function(v) trace_counters = v end)
-local report_counters = logs.reporter("structure","counters")
-
-local structures = structures
-local helpers = structures.helpers
-local sections = structures.sections
-local counters = structures.counters
-local documents = structures.documents
-
-local variables = interfaces.variables
-local v_start = variables.start
-local v_page = variables.page
-local v_reverse = variables.reverse
-local v_first = variables.first
-local v_next = variables.next
-local v_previous = variables.previous
-local v_prev = variables.prev
-local v_last = variables.last
------ v_no = variables.no
-local v_backward = variables.backward
-local v_forward = variables.forward
------ v_subs = variables.subs or "subs"
-
--- states: start stop none reset
-
--- specials are used for counters that are set and incremented in special ways, like
--- pagecounters that get this treatment in the page builder
-
-counters.specials = counters.specials or { }
-local counterspecials = counters.specials
-
-local counterranges, tbs = { }, 0
-
-counters.collected = allocate()
-counters.tobesaved = counters.tobesaved or { }
-counters.data = counters.data or { }
-
-storage.register("structures/counters/data", counters.data, "structures.counters.data")
-storage.register("structures/counters/tobesaved", counters.tobesaved, "structures.counters.tobesaved")
-
-local collected = counters.collected
-local tobesaved = counters.tobesaved
-local counterdata = counters.data
-
-local function initializer() -- not really needed
- collected = counters.collected
- tobesaved = counters.tobesaved
- counterdata = counters.data
-end
-
-local function finalizer()
- for name, cd in next, counterdata do
- local cs = tobesaved[name]
- local data = cd.data
- for i=1,#data do
- local d = data[i]
- local r = d.range
- cs[i][r] = d.number
- d.range = r + 1
- end
- end
-end
-
-job.register('structures.counters.collected', tobesaved, initializer, finalizer)
-
-local constructor = { -- maybe some day we will provide an installer for more variants
-
- last = function(t,name,i)
- local cc = collected[name]
- local stop = (cc and cc[i] and cc[i][t.range]) or 0 -- stop is available for diagnostics purposes only
- t.stop = stop
- if t.offset then
- return stop - t.step
- else
- return stop
- end
- end,
-
- first = function(t,name,i)
- local start = t.start
- if start > 0 then
- return start -- brrr
- elseif t.offset then
- return start + t.step + 1
- else
- return start + 1
- end
- end,
-
- prev = function(t,name,i)
- return max(t.first,t.number-1) -- todo: step
- end,
-
- previous = function(t,name,i)
- return max(t.first,t.number-1) -- todo: step
- end,
-
- next = function(t,name,i)
- return min(t.last,t.number+1) -- todo: step
- end,
-
- backward =function(t,name,i)
- if t.number - 1 < t.first then
- return t.last
- else
- return t.previous
- end
- end,
-
- forward = function(t,name,i)
- if t.number + 1 > t.last then
- return t.first
- else
- return t.next
- end
- end,
-
- subs = function(t,name,i)
- local cc = collected[name]
- t.subs = (cc and cc[i+1] and cc[i+1][t.range]) or 0
- return t.subs
- end,
-
-}
-
-local function dummyconstructor(t,name,i)
- return nil -- was 0, but that is fuzzy in testing for e.g. own
-end
-
-setmetatableindex(constructor,function(t,k)
- if trace_counters then
- report_counters("unknown constructor %a",k)
- end
- return dummyconstructor
-end)
-
-local function enhance()
- for name, cd in next, counterdata do
- local data = cd.data
- for i=1,#data do
- local ci = data[i]
- setmetatableindex(ci, function(t,s) return constructor[s](t,name,i) end)
- end
- end
- enhance = nil
-end
-
-local function allocate(name,i) -- can be metatable
- local cd = counterdata[name]
- if not cd then
- cd = {
- level = 1,
- -- block = "", -- todo
- numbers = nil,
- state = v_start, -- true
- data = { },
- saved = { },
- }
- tobesaved[name] = { }
- counterdata[name] = cd
- end
- cd = cd.data
- local ci = cd[i]
- if not ci then
- ci = {
- number = 0,
- start = 0,
- saved = 0,
- step = 1,
- range = 1,
- offset = false,
- stop = 0, -- via metatable: last, first, stop only for tracing
- }
- setmetatableindex(ci, function(t,s) return constructor[s](t,name,i) end)
- cd[i] = ci
- tobesaved[name][i] = { }
- else
- if enhance then enhance() end -- not stored in bytecode
- end
- return ci
-end
-
-function counters.record(name,i)
- return allocate(name,i or 1)
-end
-
-local function savevalue(name,i)
- if name then
- local cd = counterdata[name].data[i]
- local cs = tobesaved[name][i]
- local cc = collected[name]
- if trace_counters then
- report_counters("action %a, counter %s, value %s","save",name,cd.number)
- end
- local cr = cd.range
- local old = (cc and cc[i] and cc[i][cr]) or 0
- local number = cd.number
- if cd.method == v_page then
- -- we can be one page ahead
- number = number - 1
- end
- cs[cr] = (number >= 0) and number or 0
- cd.range = cr + 1
- return old
- else
- return 0
- end
-end
-
-function counters.define(specification)
- local name = specification.name
- if name and name ~= "" then
- -- todo: step
- local d = allocate(name,1)
- d.start = tonumber(specification.start) or 0
- d.state = v_state or ""
- local counter = specification.counter
- if counter and counter ~= "" then
- d.counter = counter -- only for special purposes, cannot be false
- d.method = specification.method -- frozen at define time
- end
- end
-end
-
-function counters.raw(name)
- return counterdata[name]
-end
-
-function counters.compact(name,level,onlynumbers)
- local cd = counterdata[name]
- if cd then
- local data = cd.data
- local compact = { }
- for i=1,level or #data do
- local d = data[i]
- if d.number ~= 0 then
- compact[i] = (onlynumbers and d.number) or d
- end
- end
- return compact
- end
-end
-
--- depends on when incremented, before or after (driven by d.offset)
-
-function counters.previous(name,n)
- return allocate(name,n).previous
-end
-
-function counters.next(name,n)
- return allocate(name,n).next
-end
-
-counters.prev = counters.previous
-
-function counters.currentvalue(name,n)
- return allocate(name,n).number
-end
-
-function counters.first(name,n)
- return allocate(name,n).first
-end
-
-function counters.last(name,n)
- return allocate(name,n).last
-end
-
-function counters.subs(name,n)
- return counterdata[name].data[n].subs or 0
-end
-
-local function setvalue(name,tag,value)
- local cd = counterdata[name]
- if cd then
- cd[tag] = value
- end
-end
-
-counters.setvalue = setvalue
-
-function counters.setstate(name,value) -- true/false
- value = variables[value]
- if value then
- setvalue(name,"state",value)
- end
-end
-
-function counters.setlevel(name,value)
- setvalue(name,"level",value)
-end
-
-function counters.setoffset(name,value)
- setvalue(name,"offset",value)
-end
-
-local function synchronize(name,d)
- local dc = d.counter
- if dc then
- if trace_counters then
- report_counters("action %a, name %a, counter %a, value %a","synchronize",name,dc,d.number)
- end
- texsetcount("global",dc,d.number)
- end
- local cs = counterspecials[name]
- if cs then
- if trace_counters then
- report_counters("action %a, name %a, counter %a","synccommand",name,dc)
- end
- cs(name)
- end
-end
-
-local function reset(name,n)
- local cd = counterdata[name]
- if cd then
- for i=n or 1,#cd.data do
- local d = cd.data[i]
- savevalue(name,i)
- local number = d.start or 0
- d.number = number
- d.own = nil
- if trace_counters then
- report_counters("action %a, name %a, sub %a, value %a","reset",name,i,number)
- end
- synchronize(name,d)
- end
- cd.numbers = nil
- else
- end
-end
-
-local function set(name,n,value)
- local cd = counterdata[name]
- if cd then
- local d = allocate(name,n)
- local number = value or 0
- d.number = number
- d.own = nil
- if trace_counters then
- report_counters("action %a, name %a, sub %a, value %a","set",name,"no",number)
- end
- synchronize(name,d)
- end
-end
-
-local function check(name,data,start,stop)
- for i=start or 1,stop or #data do
- local d = data[i]
- savevalue(name,i)
- local number = d.start or 0
- d.number = number
- d.own = nil
- if trace_counters then
- report_counters("action %a, name %a, sub %a, value %a","check",name,i,number)
- end
- synchronize(name,d)
- end
-end
-
-counters.reset = reset
-counters.set = set
-
-function counters.setown(name,n,value)
- local cd = counterdata[name]
- if cd then
- local d = allocate(name,n)
- d.own = value
- d.number = (d.number or d.start or 0) + (d.step or 0)
- local level = cd.level
- if not level or level == -1 then
- -- -1 is signal that we reset manually
- elseif level > 0 or level == -3 then
- check(name,d,n+1)
- elseif level == 0 then
- -- happens elsewhere, check this for block
- end
- synchronize(name,d)
- end
-end
-
-function counters.restart(name,n,newstart,noreset)
- local cd = counterdata[name]
- if cd then
- newstart = tonumber(newstart)
- if newstart then
- local d = allocate(name,n)
- d.start = newstart
- if not noreset then
- reset(name,n) -- hm
- end
- end
- end
-end
-
-function counters.save(name) -- or just number
- local cd = counterdata[name]
- if cd then
- table.insert(cd.saved,table.copy(cd.data))
- end
-end
-
-function counters.restore(name)
- local cd = counterdata[name]
- if cd and cd.saved then
- cd.data = table.remove(cd.saved)
- end
-end
-
-function counters.add(name,n,delta)
- local cd = counterdata[name]
- if cd and (cd.state == v_start or cd.state == "") then
- local data = cd.data
- local d = allocate(name,n)
- d.number = (d.number or d.start or 0) + delta*(d.step or 0)
- -- d.own = nil
- local level = cd.level
- if not level or level == -1 then
- -- -1 is signal that we reset manually
- if trace_counters then
- report_counters("action %a, name %a, sub %a, how %a","add",name,"no","no checking")
- end
- elseif level == -2 then
- -- -2 is signal that we work per text
- if trace_counters then
- report_counters("action %a, name %a, sub %a, how %a","add",name,"text","checking")
- end
- check(name,data,n+1)
- elseif level > 0 or level == -3 then
- -- within countergroup
- if trace_counters then
- report_counters("action %a, name %a, sub %a, how %a","add",name,level,"checking within group")
- end
- check(name,data,n+1)
- elseif level == 0 then
- -- happens elsewhere
- if trace_counters then
- report_counters("action %a, name %a, sub %a, how %a","add",name,level,"no checking")
- end
- else
- if trace_counters then
- report_counters("action %a, name %a, sub %a, how %a","add",name,"unknown","no checking")
- end
- end
- synchronize(name,d)
- return d.number -- not needed
- end
- return 0
-end
-
-function counters.check(level)
- for name, cd in next, counterdata do
- if level > 0 and cd.level == -3 then -- could become an option
- if trace_counters then
- report_counters("action %a, name %a, sub %a, detail %a","reset",name,level,"head")
- end
- reset(name)
- elseif cd.level == level then
- if trace_counters then
- report_counters("action %a, name %a, sub %a, detail %a","reset",name,level,"normal")
- end
- reset(name)
- end
- end
-end
-
-local function get(name,n,key)
- local d = allocate(name,n)
- d = d and d[key]
- if not d then
- return 0
- elseif type(d) == "function" then
- return d()
- else
- return d
- end
-end
-
-counters.get = get
-
-function counters.value(name,n) -- what to do with own
- return get(name,n or 1,'number') or 0
-end
-
-function counters.converted(name,spec) -- name can be number and reference to storage
- local cd
- if type(name) == "number" then
- cd = specials.retrieve("counter",name)
- cd = cd and cd.counter
- else
- cd = counterdata[name]
- end
- if cd then
- local spec = spec or { }
- local numbers, ownnumbers = { }, { }
- local reverse = spec.order == v_reverse
- local kind = spec.type or "number"
- local data = cd.data
- for k=1,#data do
- local v = data[k]
- -- somewhat messy, what if subnr? only last must honour kind?
- local vn
- if v.own then
- numbers[k], ownnumbers[k] = v.number, v.own
- else
- if kind == v_first then
- vn = v.first
- elseif kind == v_next then
- vn = v.next
- elseif kind == v_prev or kind == v_previous then
- vn = v.prev
- elseif kind == v_last then
- vn = v.last
- else
- vn = v.number
- if reverse then
- local vf = v.first
- local vl = v.last
- if vl > 0 then
- -- vn = vl - vn + 1 + vf
- vn = vl - vn + vf -- see testbed for test
- end
- end
- end
- numbers[k], ownnumbers[k] = vn or v.number, nil
- end
- end
- cd.numbers = numbers
- cd.ownnumbers = ownnumbers
- sections.typesetnumber(cd,'number',spec)
- cd.numbers = nil
- cd.ownnumbers = nil
- end
-end
-
--- interfacing
-
-commands.definecounter = counters.define
-commands.setcounter = counters.set
-commands.setowncounter = counters.setown
-commands.resetcounter = counters.reset
-commands.restartcounter = counters.restart
-commands.savecounter = counters.save
-commands.restorecounter = counters.restore
-commands.addcounter = counters.add
-
-commands.rawcountervalue = function(...) context(counters.raw (...)) end
-commands.countervalue = function(...) context(counters.value (...)) end
-commands.lastcountervalue = function(...) context(counters.last (...)) end
-commands.firstcountervalue = function(...) context(counters.first (...)) end
-commands.nextcountervalue = function(...) context(counters.next (...)) end
-commands.prevcountervalue = function(...) context(counters.previous(...)) end
-commands.subcountervalues = function(...) context(counters.subs (...)) end
-
-function commands.showcounter(name)
- local cd = counterdata[name]
- if cd then
- context("[%s:",name)
- local data = cd.data
- for i=1,#data do
- local d = data[i]
- context(" (%s: %s,%s,%s s:%s r:%s)",i,d.start or 0,d.number or 0,d.last,d.step or 0,d.range or 0)
- end
- context("]")
- end
-end
-
-function commands.doifelsecounter(name) commands.doifelse(counterdata[name]) end
-function commands.doifcounter (name) commands.doif (counterdata[name]) end
-function commands.doifnotcounter (name) commands.doifnot (counterdata[name]) end
-
-function commands.incrementedcounter(...) context(counters.add(...)) end
-
-function commands.checkcountersetup(name,level,start,state)
- counters.restart(name,1,start,true) -- no reset
- counters.setstate(name,state)
- counters.setlevel(name,level)
- sections.setchecker(name,level,counters.reset)
-end
-
--- -- move to strc-pag.lua
---
--- function counters.analyze(name,counterspecification)
--- local cd = counterdata[name]
--- -- safeguard
--- if not cd then
--- return false, false, "no counter data"
--- end
--- -- section data
--- local sectiondata = sections.current()
--- if not sectiondata then
--- return cd, false, "not in section"
--- end
--- local references = sectiondata.references
--- if not references then
--- return cd, false, "no references"
--- end
--- local section = references.section
--- if not section then
--- return cd, false, "no section"
--- end
--- sectiondata = sections.collected[references.section]
--- if not sectiondata then
--- return cd, false, "no section data"
--- end
--- -- local preferences
--- local no = v_no
--- if counterspecification and counterspecification.prefix == no then
--- return cd, false, "current spec blocks prefix"
--- end
--- -- stored preferences (not used)
--- if cd.prefix == no then
--- return cd, false, "entry blocks prefix"
--- end
--- -- sectioning
--- -- if sectiondata.prefix == no then
--- -- return false, false, "sectiondata blocks prefix"
--- -- end
--- -- final verdict
--- return cd, sectiondata, "okay"
--- end
---
--- function counters.prefixedconverted(name,prefixspec,numberspec)
--- local cd, prefixdata, result = counters.analyze(name,prefixspec)
--- if cd then
--- if prefixdata then
--- sections.typesetnumber(prefixdata,"prefix",prefixspec or false,cd or false)
--- end
--- counters.converted(name,numberspec)
--- end
--- end
+if not modules then modules = { } end modules ['strc-num'] = { + version = 1.001, + comment = "companion to strc-num.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +local format = string.format +local next, type = next, type +local min, max = math.min, math.max +local texcount, texsetcount = tex.count, tex.setcount + +-- Counters are managed here. They can have multiple levels which makes it easier to synchronize +-- them. Synchronization is sort of special anyway, as it relates to document structuring. + +local allocate = utilities.storage.allocate +local setmetatableindex = table.setmetatableindex + +local trace_counters = false trackers.register("structures.counters", function(v) trace_counters = v end) +local report_counters = logs.reporter("structure","counters") + +local structures = structures +local helpers = structures.helpers +local sections = structures.sections +local counters = structures.counters +local documents = structures.documents + +local variables = interfaces.variables +local v_start = variables.start +local v_page = variables.page +local v_reverse = variables.reverse +local v_first = variables.first +local v_next = variables.next +local v_previous = variables.previous +local v_prev = variables.prev +local v_last = variables.last +----- v_no = variables.no +local v_backward = variables.backward +local v_forward = variables.forward +----- v_subs = variables.subs or "subs" + +-- states: start stop none reset + +-- specials are used for counters that are set and incremented in special ways, like +-- pagecounters that get this treatment in the page builder + +counters.specials = counters.specials or { } +local counterspecials = counters.specials + +local counterranges, tbs = { }, 0 + +counters.collected = allocate() +counters.tobesaved = counters.tobesaved or { } +counters.data = counters.data or { } + +storage.register("structures/counters/data", counters.data, "structures.counters.data") +storage.register("structures/counters/tobesaved", counters.tobesaved, "structures.counters.tobesaved") + +local collected = counters.collected +local tobesaved = counters.tobesaved +local counterdata = counters.data + +local function initializer() -- not really needed + collected = counters.collected + tobesaved = counters.tobesaved + counterdata = counters.data +end + +local function finalizer() + for name, cd in next, counterdata do + local cs = tobesaved[name] + local data = cd.data + for i=1,#data do + local d = data[i] + local r = d.range + cs[i][r] = d.number + d.range = r + 1 + end + end +end + +job.register('structures.counters.collected', tobesaved, initializer, finalizer) + +local constructor = { -- maybe some day we will provide an installer for more variants + + last = function(t,name,i) + local cc = collected[name] + local stop = (cc and cc[i] and cc[i][t.range]) or 0 -- stop is available for diagnostics purposes only + t.stop = stop + if t.offset then + return stop - t.step + else + return stop + end + end, + + first = function(t,name,i) + local start = t.start + if start > 0 then + return start -- brrr + elseif t.offset then + return start + t.step + 1 + else + return start + 1 + end + end, + + prev = function(t,name,i) + return max(t.first,t.number-1) -- todo: step + end, + + previous = function(t,name,i) + return max(t.first,t.number-1) -- todo: step + end, + + next = function(t,name,i) + return min(t.last,t.number+1) -- todo: step + end, + + backward =function(t,name,i) + if t.number - 1 < t.first then + return t.last + else + return t.previous + end + end, + + forward = function(t,name,i) + if t.number + 1 > t.last then + return t.first + else + return t.next + end + end, + + subs = function(t,name,i) + local cc = collected[name] + t.subs = (cc and cc[i+1] and cc[i+1][t.range]) or 0 + return t.subs + end, + +} + +local function dummyconstructor(t,name,i) + return nil -- was 0, but that is fuzzy in testing for e.g. own +end + +setmetatableindex(constructor,function(t,k) + if trace_counters then + report_counters("unknown constructor %a",k) + end + return dummyconstructor +end) + +local function enhance() + for name, cd in next, counterdata do + local data = cd.data + for i=1,#data do + local ci = data[i] + setmetatableindex(ci, function(t,s) return constructor[s](t,name,i) end) + end + end + enhance = nil +end + +local function allocate(name,i) -- can be metatable + local cd = counterdata[name] + if not cd then + cd = { + level = 1, + -- block = "", -- todo + numbers = nil, + state = v_start, -- true + data = { }, + saved = { }, + } + tobesaved[name] = { } + counterdata[name] = cd + end + cd = cd.data + local ci = cd[i] + if not ci then + ci = { + number = 0, + start = 0, + saved = 0, + step = 1, + range = 1, + offset = false, + stop = 0, -- via metatable: last, first, stop only for tracing + } + setmetatableindex(ci, function(t,s) return constructor[s](t,name,i) end) + cd[i] = ci + tobesaved[name][i] = { } + else + if enhance then enhance() end -- not stored in bytecode + end + return ci +end + +function counters.record(name,i) + return allocate(name,i or 1) +end + +local function savevalue(name,i) + if name then + local cd = counterdata[name].data[i] + local cs = tobesaved[name][i] + local cc = collected[name] + if trace_counters then + report_counters("action %a, counter %s, value %s","save",name,cd.number) + end + local cr = cd.range + local old = (cc and cc[i] and cc[i][cr]) or 0 + local number = cd.number + if cd.method == v_page then + -- we can be one page ahead + number = number - 1 + end + cs[cr] = (number >= 0) and number or 0 + cd.range = cr + 1 + return old + else + return 0 + end +end + +function counters.define(specification) + local name = specification.name + if name and name ~= "" then + -- todo: step + local d = allocate(name,1) + d.start = tonumber(specification.start) or 0 + d.state = v_state or "" + local counter = specification.counter + if counter and counter ~= "" then + d.counter = counter -- only for special purposes, cannot be false + d.method = specification.method -- frozen at define time + end + end +end + +function counters.raw(name) + return counterdata[name] +end + +function counters.compact(name,level,onlynumbers) + local cd = counterdata[name] + if cd then + local data = cd.data + local compact = { } + for i=1,level or #data do + local d = data[i] + if d.number ~= 0 then + compact[i] = (onlynumbers and d.number) or d + end + end + return compact + end +end + +-- depends on when incremented, before or after (driven by d.offset) + +function counters.previous(name,n) + return allocate(name,n).previous +end + +function counters.next(name,n) + return allocate(name,n).next +end + +counters.prev = counters.previous + +function counters.currentvalue(name,n) + return allocate(name,n).number +end + +function counters.first(name,n) + return allocate(name,n).first +end + +function counters.last(name,n) + return allocate(name,n).last +end + +function counters.subs(name,n) + return counterdata[name].data[n].subs or 0 +end + +local function setvalue(name,tag,value) + local cd = counterdata[name] + if cd then + cd[tag] = value + end +end + +counters.setvalue = setvalue + +function counters.setstate(name,value) -- true/false + value = variables[value] + if value then + setvalue(name,"state",value) + end +end + +function counters.setlevel(name,value) + setvalue(name,"level",value) +end + +function counters.setoffset(name,value) + setvalue(name,"offset",value) +end + +local function synchronize(name,d) + local dc = d.counter + if dc then + if trace_counters then + report_counters("action %a, name %a, counter %a, value %a","synchronize",name,dc,d.number) + end + texsetcount("global",dc,d.number) + end + local cs = counterspecials[name] + if cs then + if trace_counters then + report_counters("action %a, name %a, counter %a","synccommand",name,dc) + end + cs(name) + end +end + +local function reset(name,n) + local cd = counterdata[name] + if cd then + for i=n or 1,#cd.data do + local d = cd.data[i] + savevalue(name,i) + local number = d.start or 0 + d.number = number + d.own = nil + if trace_counters then + report_counters("action %a, name %a, sub %a, value %a","reset",name,i,number) + end + synchronize(name,d) + end + cd.numbers = nil + else + end +end + +local function set(name,n,value) + local cd = counterdata[name] + if cd then + local d = allocate(name,n) + local number = value or 0 + d.number = number + d.own = nil + if trace_counters then + report_counters("action %a, name %a, sub %a, value %a","set",name,"no",number) + end + synchronize(name,d) + end +end + +local function check(name,data,start,stop) + for i=start or 1,stop or #data do + local d = data[i] + savevalue(name,i) + local number = d.start or 0 + d.number = number + d.own = nil + if trace_counters then + report_counters("action %a, name %a, sub %a, value %a","check",name,i,number) + end + synchronize(name,d) + end +end + +counters.reset = reset +counters.set = set + +function counters.setown(name,n,value) + local cd = counterdata[name] + if cd then + local d = allocate(name,n) + d.own = value + d.number = (d.number or d.start or 0) + (d.step or 0) + local level = cd.level + if not level or level == -1 then + -- -1 is signal that we reset manually + elseif level > 0 or level == -3 then + check(name,d,n+1) + elseif level == 0 then + -- happens elsewhere, check this for block + end + synchronize(name,d) + end +end + +function counters.restart(name,n,newstart,noreset) + local cd = counterdata[name] + if cd then + newstart = tonumber(newstart) + if newstart then + local d = allocate(name,n) + d.start = newstart + if not noreset then + reset(name,n) -- hm + end + end + end +end + +function counters.save(name) -- or just number + local cd = counterdata[name] + if cd then + table.insert(cd.saved,table.copy(cd.data)) + end +end + +function counters.restore(name) + local cd = counterdata[name] + if cd and cd.saved then + cd.data = table.remove(cd.saved) + end +end + +function counters.add(name,n,delta) + local cd = counterdata[name] + if cd and (cd.state == v_start or cd.state == "") then + local data = cd.data + local d = allocate(name,n) + d.number = (d.number or d.start or 0) + delta*(d.step or 0) + -- d.own = nil + local level = cd.level + if not level or level == -1 then + -- -1 is signal that we reset manually + if trace_counters then + report_counters("action %a, name %a, sub %a, how %a","add",name,"no","no checking") + end + elseif level == -2 then + -- -2 is signal that we work per text + if trace_counters then + report_counters("action %a, name %a, sub %a, how %a","add",name,"text","checking") + end + check(name,data,n+1) + elseif level > 0 or level == -3 then + -- within countergroup + if trace_counters then + report_counters("action %a, name %a, sub %a, how %a","add",name,level,"checking within group") + end + check(name,data,n+1) + elseif level == 0 then + -- happens elsewhere + if trace_counters then + report_counters("action %a, name %a, sub %a, how %a","add",name,level,"no checking") + end + else + if trace_counters then + report_counters("action %a, name %a, sub %a, how %a","add",name,"unknown","no checking") + end + end + synchronize(name,d) + return d.number -- not needed + end + return 0 +end + +function counters.check(level) + for name, cd in next, counterdata do + if level > 0 and cd.level == -3 then -- could become an option + if trace_counters then + report_counters("action %a, name %a, sub %a, detail %a","reset",name,level,"head") + end + reset(name) + elseif cd.level == level then + if trace_counters then + report_counters("action %a, name %a, sub %a, detail %a","reset",name,level,"normal") + end + reset(name) + end + end +end + +local function get(name,n,key) + local d = allocate(name,n) + d = d and d[key] + if not d then + return 0 + elseif type(d) == "function" then + return d() + else + return d + end +end + +counters.get = get + +function counters.value(name,n) -- what to do with own + return get(name,n or 1,'number') or 0 +end + +function counters.converted(name,spec) -- name can be number and reference to storage + local cd + if type(name) == "number" then + cd = specials.retrieve("counter",name) + cd = cd and cd.counter + else + cd = counterdata[name] + end + if cd then + local spec = spec or { } + local numbers, ownnumbers = { }, { } + local reverse = spec.order == v_reverse + local kind = spec.type or "number" + local data = cd.data + for k=1,#data do + local v = data[k] + -- somewhat messy, what if subnr? only last must honour kind? + local vn + if v.own then + numbers[k], ownnumbers[k] = v.number, v.own + else + if kind == v_first then + vn = v.first + elseif kind == v_next then + vn = v.next + elseif kind == v_prev or kind == v_previous then + vn = v.prev + elseif kind == v_last then + vn = v.last + else + vn = v.number + if reverse then + local vf = v.first + local vl = v.last + if vl > 0 then + -- vn = vl - vn + 1 + vf + vn = vl - vn + vf -- see testbed for test + end + end + end + numbers[k], ownnumbers[k] = vn or v.number, nil + end + end + cd.numbers = numbers + cd.ownnumbers = ownnumbers + sections.typesetnumber(cd,'number',spec) + cd.numbers = nil + cd.ownnumbers = nil + end +end + +-- interfacing + +commands.definecounter = counters.define +commands.setcounter = counters.set +commands.setowncounter = counters.setown +commands.resetcounter = counters.reset +commands.restartcounter = counters.restart +commands.savecounter = counters.save +commands.restorecounter = counters.restore +commands.addcounter = counters.add + +commands.rawcountervalue = function(...) context(counters.raw (...)) end +commands.countervalue = function(...) context(counters.value (...)) end +commands.lastcountervalue = function(...) context(counters.last (...)) end +commands.firstcountervalue = function(...) context(counters.first (...)) end +commands.nextcountervalue = function(...) context(counters.next (...)) end +commands.prevcountervalue = function(...) context(counters.previous(...)) end +commands.subcountervalues = function(...) context(counters.subs (...)) end + +function commands.showcounter(name) + local cd = counterdata[name] + if cd then + context("[%s:",name) + local data = cd.data + for i=1,#data do + local d = data[i] + context(" (%s: %s,%s,%s s:%s r:%s)",i,d.start or 0,d.number or 0,d.last,d.step or 0,d.range or 0) + end + context("]") + end +end + +function commands.doifelsecounter(name) commands.doifelse(counterdata[name]) end +function commands.doifcounter (name) commands.doif (counterdata[name]) end +function commands.doifnotcounter (name) commands.doifnot (counterdata[name]) end + +function commands.incrementedcounter(...) context(counters.add(...)) end + +function commands.checkcountersetup(name,level,start,state) + counters.restart(name,1,start,true) -- no reset + counters.setstate(name,state) + counters.setlevel(name,level) + sections.setchecker(name,level,counters.reset) +end + +-- -- move to strc-pag.lua +-- +-- function counters.analyze(name,counterspecification) +-- local cd = counterdata[name] +-- -- safeguard +-- if not cd then +-- return false, false, "no counter data" +-- end +-- -- section data +-- local sectiondata = sections.current() +-- if not sectiondata then +-- return cd, false, "not in section" +-- end +-- local references = sectiondata.references +-- if not references then +-- return cd, false, "no references" +-- end +-- local section = references.section +-- if not section then +-- return cd, false, "no section" +-- end +-- sectiondata = sections.collected[references.section] +-- if not sectiondata then +-- return cd, false, "no section data" +-- end +-- -- local preferences +-- local no = v_no +-- if counterspecification and counterspecification.prefix == no then +-- return cd, false, "current spec blocks prefix" +-- end +-- -- stored preferences (not used) +-- if cd.prefix == no then +-- return cd, false, "entry blocks prefix" +-- end +-- -- sectioning +-- -- if sectiondata.prefix == no then +-- -- return false, false, "sectiondata blocks prefix" +-- -- end +-- -- final verdict +-- return cd, sectiondata, "okay" +-- end +-- +-- function counters.prefixedconverted(name,prefixspec,numberspec) +-- local cd, prefixdata, result = counters.analyze(name,prefixspec) +-- if cd then +-- if prefixdata then +-- sections.typesetnumber(prefixdata,"prefix",prefixspec or false,cd or false) +-- end +-- counters.converted(name,numberspec) +-- end +-- end |