summaryrefslogtreecommitdiff
path: root/tex/context/base/strc-num.lua
diff options
context:
space:
mode:
authorMarius <mariausol@gmail.com>2010-07-04 15:32:09 +0300
committerMarius <mariausol@gmail.com>2010-07-04 15:32:09 +0300
commit85b7bc695629926641c7cb752fd478adfdf374f3 (patch)
tree80293f5aaa7b95a500a78392c39688d8ee7a32fc /tex/context/base/strc-num.lua
downloadcontext-85b7bc695629926641c7cb752fd478adfdf374f3.tar.gz
stable 2010-05-24 13:10
Diffstat (limited to 'tex/context/base/strc-num.lua')
-rw-r--r--tex/context/base/strc-num.lua521
1 files changed, 521 insertions, 0 deletions
diff --git a/tex/context/base/strc-num.lua b/tex/context/base/strc-num.lua
new file mode 100644
index 000000000..8165d0786
--- /dev/null
+++ b/tex/context/base/strc-num.lua
@@ -0,0 +1,521 @@
+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 texsprint, texcount = tex.sprint, tex.count
+
+local trace_counters = false trackers.register("structure.counters", function(v) trace_counters = v end)
+
+structure = structure or { }
+structure.helpers = structure.helpers or { }
+structure.sections = structure.sections or { }
+structure.counters = structure.counters or { }
+structure.documents = structure.documents or { }
+
+structure.counters = structure.counters or { }
+structure.counters.data = structure.counters.data or { }
+structure.counters.specials = structure.counters.specials or { }
+
+local helpers = structure.helpers
+local sections = structure.sections
+local counters = structure.counters
+local documents = structure.documents
+
+local variables = interfaces.variables
+
+-- state: start stop none reset
+
+local counterdata = counters.data
+local counterranges, tbs = { }, 0
+local counterspecials = counters.specials
+
+counters.collected = counters.collected or { }
+counters.tobesaved = counters.tobesaved or { }
+
+storage.register("structure/counters/data", structure.counters.data, "structure.counters.data")
+storage.register("structure/counters/tobesaved", structure.counters.tobesaved, "structure.counters.tobesaved")
+
+local collected, tobesaved = counters.collected, counters.tobesaved
+
+local function finalizer()
+ local ct = counters.tobesaved
+ 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
+
+local function initializer()
+ collected, tobesaved = counters.collected, counters.tobesaved
+end
+
+if job then
+ job.register('structure.counters.collected', structure.counters.tobesaved, initializer, finalizer)
+end
+
+local function constructor(t,s,name,i)
+ if s == "last" then
+ local cc = collected[name]
+ t.stop = (cc and cc[i] and cc[i][t.range]) or 0 -- stop is available for diagnostics purposes only
+ if t.offset then
+ return t.stop - t.step
+ else
+ return t.stop
+ end
+ elseif s == "first" then
+ if t.start > 0 then
+ return t.start -- brrr
+ elseif t.offset then
+ return t.start + t.step + 1
+ else
+ return t.start + 1
+ end
+ elseif s == "prev" or s == "previous" then
+ return max(t.first,t.number-1) -- todo: step
+ elseif s == "next" then
+ return min(t.last,t.number+1) -- todo: step
+ elseif s == "backward" then
+ if t.number - 1 < t.first then
+ return t.last
+ else
+ return t.previous
+ end
+ elseif s == "forward" then
+ if t.number + 1 > t.last then
+ return t.first
+ else
+ return t.next
+ end
+ elseif s == "subs" then
+ local cc = collected[name]
+ t.subs = (cc and cc[i+1] and cc[i+1][t.range]) or 0
+ return t.subs
+ else
+ return nil -- was 0, but that is fuzzy in testing for e.g. own
+ end
+end
+
+local enhance = function()
+ for name, cd in next, counterdata do
+ local data = cd.data
+ for i=1,#data do
+ local ci = data[i]
+ setmetatable(ci, { __index = function(t,s) return constructor(t,s,name,i) end })
+ end
+ end
+ enhance = nil
+end
+
+local function allocate(name,i)
+ local cd = counterdata[name]
+ if not cd then
+ cd = {
+ level = 1,
+--~ block = "", -- todo
+ numbers = nil,
+ state = variables.start, -- true
+ data = { }
+ }
+ 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,
+ -- via metatable: last, first, and for tracing:
+ stop = 0,
+ }
+ setmetatable(ci, { __index = function(t,s) return constructor(t,s,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]
+ local cr = cd.range
+ local old = (cc and cc[i] and cc[i][cr]) or 0
+ cs[cr] = cd.number
+ cd.range = cr + 1
+ return old
+ else
+ return 0
+ end
+end
+
+function counters.define(name, start, counter) -- todo: step
+ local d = allocate(name,1)
+ d.start = start
+ if counter ~= "" then
+ d.counter = counter -- only for special purposes, cannot be false
+ end
+end
+
+function counters.trace(name)
+ local cd = counterdata[name]
+ if cd then
+ texsprint(format("[%s:",name))
+ local data = cd.data
+ for i=1,#data do
+ local d = data[i]
+ texsprint(format(" (%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
+ texsprint("]")
+ end
+end
+
+function counters.raw(name)
+ return counterdata[name]
+end
+
+function counters.compact(name,level,onlynumbers)
+ local cd = counterdata[name]
+--~ print(name,cd)
+ if cd then
+ local data = cd.data
+ local compact = { }
+ for i=1,level or #data do
+ local d = data[i]
+--~ print(name,i,d.number)
+ if d.number ~= 0 then
+ compact[i] = (onlynumbers and d.number) or d
+ end
+ end
+--~ print(table.serialize(compact))
+ return compact
+ end
+end
+
+-- depends on when incremented, before or after (driven by d.offset)
+
+function counters.doifelse(name)
+ commands.doifelse(counterdata[name])
+end
+
+function counters.previous(name,n)
+ texsprint(allocate(name,n).previous)
+end
+
+function counters.next(name,n)
+ texsprint(allocate(name,n).next)
+end
+
+counters.prev = counters.previous
+
+function counters.current(name,n)
+ texsprint(allocate(name,n).number)
+end
+
+function counters.first(name,n)
+ texsprint(allocate(name,n).first)
+end
+
+function counters.last(name,n)
+ texsprint(allocate(name,n).last)
+end
+
+function counters.subs(name,n)
+ texsprint(counterdata[name].data[n].subs or 0)
+end
+
+function counters.setvalue(name,tag,value)
+ local cd = counterdata[name]
+ if cd then
+ cd[tag] = value
+ end
+end
+
+function counters.setstate(name,value) -- true/false
+ value = variables[value]
+ if value then
+ counters.setvalue(name,"state",value)
+ end
+end
+
+function counters.setlevel(name,value)
+ counters.setvalue(name,"level",value)
+end
+
+function counters.setoffset(name,value)
+ counters.setvalue(name,"offset",value)
+end
+
+
+local function synchronize(name,d)
+ local dc = d.counter
+ if dc then
+ if trace_counters then
+ logs.report("counters","setting counter %s with name %s to %s",dc,name,d.number)
+ end
+ tex.setcount("global",dc,d.number)
+ end
+ local cs = counterspecials[name]
+ if cs then
+ if trace_counters then
+ logs.report("counters","invoking special for name %s",name)
+ end
+ cs()
+ end
+end
+
+function counters.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)
+ d.number = d.start or 0
+ d.own = nil
+ synchronize(name,d)
+ end
+ cd.numbers = nil
+ end
+end
+
+function counters.set(name,n,value)
+ local cd = counterdata[name]
+ if cd then
+ local d = allocate(name,n)
+ d.number = value or 0
+ d.own = nil
+ 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)
+ d.number = d.start or 0
+ d.own = nil
+ synchronize(name,d)
+ end
+end
+
+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 then
+ check(name,d,n+1)
+ elseif level == 0 then
+ -- happens elsewhere
+ end
+ synchronize(name,d)
+ end
+end
+
+function counters.restart(name,n,newstart)
+ local cd = counterdata[name]
+ if cd then
+ newstart = tonumber(newstart)
+ if newstart then
+ local d = allocate(name,n)
+ d.start = newstart
+ counters.reset(name,n)
+ end
+ end
+end
+
+function counters.save(name) -- or just number
+ local cd = counterdata[name]
+ if cd then
+ cd.saved = table.copy(cd.data)
+ end
+end
+
+function counters.restore(name)
+ local cd = counterdata[name]
+ if cd and cd.saved then
+ cd.data = cd.saved
+ cd.saved = nil
+ end
+end
+
+function counters.add(name,n,delta)
+ local cd = counterdata[name]
+ if cd and cd.state == variables.start then
+ local data = cd.data
+ local d = allocate(name,n)
+ d.number = (d.number or d.start or 0) + delta*(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 then
+ -- within countergroup
+ check(name,data,n+1)
+ elseif level == 0 then
+ -- happens elsewhere
+ end
+ synchronize(name,d)
+ return d.number -- not needed
+ end
+ return 0
+end
+
+function counters.check(level) -- not used (yet)
+ for name, cd in next, counterdata do
+ -- logs.report("counters","%s %s %s",name,cd.level,level)
+ if cd.level == level then
+ if trace_counters then
+ logs.report("counters","resetting %s at level %s",name,level)
+ end
+ counters.reset(name)
+ end
+ end
+end
+
+function counters.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
+
+function counters.value(name,n) -- what to do with own
+ tex.write(counters.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 == variables.reverse
+ local kind = spec.type or "number"
+ local v_first, v_next, v_previous, v_last = variables.first, variables.next, variables.previous, variables.last
+ 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_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
+
+-- move to strc-pag.lua
+
+function counters.analyse(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 = jobsections.collected[references.section]
+ if not sectiondata then
+ return cd, false, "no section data"
+ end
+ -- local preferences
+ local no = variables.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.analyse(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