core-dat.lua /size: 8817 b    last modification: 2020-07-01 14:35
1if not modules then modules = { } end modules ['core-dat'] = {
2    version   = 1.001,
3    comment   = "companion to core-dat.mkiv",
4    author    = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
5    copyright = "PRAGMA ADE / ConTeXt Development Team",
6    license   = "see context related readme files"
7}
8
9--[[ldx--
10<p>This module provides a (multipass) container for arbitrary data. It
11replaces the twopass data mechanism.</p>
12--ldx]]--
13
14local tonumber, tostring, type = tonumber, tostring, type
15
16local context          = context
17local commands         = commands
18local ctx_latelua      = context.latelua
19
20local trace_datasets   = false  trackers.register("job.datasets" ,  function(v) trace_datasets   = v end)
21local trace_pagestates = false  trackers.register("job.pagestates", function(v) trace_pagestates = v end)
22
23local report_dataset   = logs.reporter("dataset")
24local report_pagestate = logs.reporter("pagestate")
25
26local allocate         = utilities.storage.allocate
27local settings_to_hash = utilities.parsers.settings_to_hash
28
29local texgetcount      = tex.getcount
30local texsetcount      = tex.setcount
31
32local formatters       = string.formatters
33
34local v_yes            = interfaces.variables.yes
35
36local new_latelua      = nodes.pool.latelua
37
38local implement        = interfaces.implement
39local getnamespace     = interfaces.getnamespace
40
41local collected = allocate()
42local tobesaved = allocate()
43
44local datasets = {
45    collected = collected,
46    tobesaved = tobesaved,
47}
48
49job.datasets = datasets
50
51local function initializer()
52    collected = datasets.collected
53    tobesaved = datasets.tobesaved
54end
55
56job.register('job.datasets.collected', tobesaved, initializer, nil)
57
58local sets = { }
59
60table.setmetatableindex(tobesaved, function(t,k)
61    local v = { }
62    t[k] = v
63    return v
64end)
65
66table.setmetatableindex(sets, function(t,k)
67    local v = {
68        index = 0,
69        order = 0,
70    }
71    t[k] = v
72    return v
73end)
74
75local function setdata(settings)
76    local name = settings.name
77    local tag  = settings.tag
78    local data = settings.data
79    local list = tobesaved[name]
80    if settings.convert and type(data) == "string" then
81        data = settings_to_hash(data)
82    end
83    if type(data) ~= "table" then
84        data = { data = data }
85    end
86    if not tag then
87        tag = #list + 1
88    else
89        tag = tonumber(tag) or tag -- autonumber saves keys
90    end
91    list[tag] = data
92    if settings.delay == v_yes then
93        local set = sets[name]
94        local index = set.index + 1
95        set.index = index
96        data.index = index
97        data.order = index
98        data.realpage = texgetcount("realpageno")
99        if trace_datasets then
100            report_dataset("action %a, name %a, tag %a, index %a","assign delayed",name,tag,index)
101        end
102    elseif trace_datasets then
103        report_dataset("action %a, name %a, tag %a","assign immediate",name,tag)
104    end
105    return name, tag, data
106end
107
108datasets.setdata = setdata
109
110function datasets.extend(name,tag)
111    if type(name) == "table" then
112        name, tag = name.name, name.tag
113    end
114    local set = sets[name]
115    local order = set.order + 1
116    local realpage = texgetcount("realpageno")
117    set.order = order
118    local t = tobesaved[name][tag]
119    t.realpage = realpage
120    t.order = order
121    if trace_datasets then
122        report_dataset("action %a, name %a, tag %a, page %a, index %a","flush by order",name,tag,t.index or 0,order,realpage)
123    end
124end
125
126function datasets.getdata(name,tag,key,default)
127    local t = collected[name]
128    if t == nil then
129        if trace_datasets then
130            report_dataset("error: unknown dataset, name %a",name)
131        end
132    elseif type(t) ~= "table" then
133        return t
134    else
135        t = t[tag] or t[tonumber(tag)]
136        if not t then
137            if trace_datasets then
138                report_dataset("error: unknown dataset, name %a, tag %a",name,tag)
139            end
140        elseif key then
141            return t[key] or default
142        else
143            return t
144        end
145    end
146    return default
147end
148
149local function setdataset(settings)
150    settings.convert = true
151    local name, tag = setdata(settings)
152    if settings.delay ~= v_yes then
153        --
154    else
155        context(new_latelua { action = job.datasets.extend, name = name, tag = tag })
156    end
157end
158
159local function datasetvariable(name,tag,key)
160    local t = collected[name]
161    if t == nil then
162        if trace_datasets then
163            report_dataset("error: unknown dataset, name %a, tag %a, not passed to tex",name) -- no tag
164        end
165    elseif type(t) ~= "table" then
166        context(tostring(t))
167    else
168        t = t and (t[tag] or t[tonumber(tag)])
169        if not t then
170            if trace_datasets then
171                report_dataset("error: unknown dataset, name %a, tag %a, not passed to tex",name,tag)
172            end
173        elseif type(t) == "table" then
174            local s = t[key]
175            if type(s) ~= "table" then
176                context(tostring(s))
177            elseif trace_datasets then
178                report_dataset("error: unknown dataset, name %a, tag %a, not passed to tex",name,tag)
179            end
180        end
181    end
182end
183
184implement {
185    name      = "setdataset",
186    actions   = setdataset,
187    arguments = {
188        {
189            { "name" },
190            { "tag" },
191            { "delay" },
192            { "data" },
193        }
194    }
195}
196
197implement {
198    name      = "datasetvariable",
199    actions   = datasetvariable,
200    arguments = "3 strings",
201}
202
203--[[ldx--
204<p>We also provide an efficient variant for page states.</p>
205--ldx]]--
206
207local collected = allocate()
208local tobesaved = allocate()
209
210local pagestates = {
211    collected = collected,
212    tobesaved = tobesaved,
213}
214
215job.pagestates = pagestates
216
217local function initializer()
218    collected = pagestates.collected
219    tobesaved = pagestates.tobesaved
220end
221
222job.register('job.pagestates.collected', tobesaved, initializer, nil)
223
224table.setmetatableindex(tobesaved, function(t,k)
225    local v = { }
226    t[k] = v
227    return v
228end)
229
230local function setstate(settings)
231    local name = settings.name
232    local tag  = settings.tag
233    local list = tobesaved[name]
234    if not tag then
235        tag = #list + 1
236    else
237        tag = tonumber(tag) or tag -- autonumber saves keys
238    end
239    local realpage = texgetcount("realpageno")
240    local data = realpage
241    list[tag] = data
242    if trace_pagestates then
243        report_pagestate("action %a, name %a, tag %a, preset %a","set",name,tag,realpage)
244    end
245    return name, tag, data
246end
247
248local function extend(name,tag)
249    local realpage = texgetcount("realpageno")
250    if trace_pagestates then
251        report_pagestate("action %a, name %a, tag %a, preset %a","synchronize",name,tag,realpage)
252    end
253    tobesaved[name][tag] = realpage
254end
255
256local function realpage(name,tag,default)
257    local t = collected[name]
258    if t then
259        t = t[tag] or t[tonumber(tag)]
260        if t then
261            return tonumber(t or default)
262        elseif trace_pagestates then
263            report_pagestate("error: unknown dataset, name %a, tag %a",name,tag)
264        end
265    elseif trace_pagestates then
266        report_pagestate("error: unknown dataset, name %a, tag %a",name) -- nil
267    end
268    return default
269end
270
271local function realpageorder(name,tag)
272    local t = collected[name]
273    if t then
274        local p = t[tag]
275        if p then
276            local n = 1
277            for i=tag-1,1,-1 do
278                if t[i] == p then
279                    n = n  +1
280                end
281            end
282            return n
283        end
284    end
285    return 0
286end
287
288pagestates.setstate      = setstate
289pagestates.extend        = extend
290pagestates.realpage      = realpage
291pagestates.realpageorder = realpageorder
292
293function pagestates.countervalue(name)
294    return name and texgetcount(getnamespace("pagestatecounter") .. name) or 0
295end
296
297local function setpagestate(settings)
298    local name, tag = setstate(settings)
299 -- context(new_latelua(function() extend(name,tag) end))
300    ctx_latelua(function() extend(name,tag) end)
301end
302
303local function setpagestaterealpageno(name,tag)
304    local t = collected[name]
305    t = t and (t[tag] or t[tonumber(tag)])
306    texsetcount("realpagestateno",t or texgetcount("realpageno"))
307end
308
309implement {
310    name      = "setpagestate",
311    actions   = setpagestate,
312    arguments = {
313        {
314            { "name" },
315            { "tag" },
316            { "delay" },
317        }
318    }
319}
320
321implement {
322    name      = "pagestaterealpage",
323    actions   = { realpage, context },
324    arguments = "2 strings",
325}
326
327implement {
328    name      = "setpagestaterealpageno",
329    actions   = setpagestaterealpageno,
330    arguments = "2 strings",
331}
332
333implement {
334    name      = "pagestaterealpageorder",
335    actions   = { realpageorder, context },
336    arguments = { "string", "integer" }
337}
338