core-dat.lmt /size: 5964 b    last modification: 2024-01-16 10:22
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-- This module provides a (multipass) container for arbitrary data. It replaces the
10-- twopass data mechanism.
11
12local tonumber, tostring, type = tonumber, tostring, type
13
14local context           = context
15
16local trace_datasets    = false  trackers.register("job.datasets" ,  function(v) trace_datasets   = v end)
17
18local report_dataset    = logs.reporter("dataset")
19
20local allocate          = utilities.storage.allocate
21local settings_to_hash  = utilities.parsers.settings_to_hash
22
23local texgetcount       = tex.getcount
24local texsetcount       = tex.setcount
25
26local v_yes             = interfaces.variables.yes
27
28local new_latelua       = nodes.pool.latelua
29
30local implement         = interfaces.implement
31
32local c_realpageno      = tex.iscount("realpageno")
33
34local collected = allocate()
35local tobesaved = allocate()
36
37local datasets = {
38    collected = collected,
39    tobesaved = tobesaved,
40}
41
42job.datasets = datasets
43
44local function initializer()
45    collected = datasets.collected
46    tobesaved = datasets.tobesaved
47end
48
49job.register('job.datasets.collected', tobesaved, initializer, nil)
50
51local sets = { }
52
53table.setmetatableindex(tobesaved, function(t,k)
54    local v = { }
55    t[k] = v
56    return v
57end)
58
59table.setmetatableindex(sets, function(t,k)
60    local v = {
61        index = 0,
62        order = 0,
63    }
64    t[k] = v
65    return v
66end)
67
68local function setdata(settings)
69    local name = settings.name
70    local tag  = settings.tag
71    local data = settings.data
72    local list = tobesaved[name]
73    if settings.convert and type(data) == "string" then
74        data = settings_to_hash(data)
75    end
76    if type(data) ~= "table" then
77        data = { data = data }
78    end
79    if not tag then
80        tag = #list + 1
81    else
82        tag = tonumber(tag) or tag -- autonumber saves keys
83    end
84    list[tag] = data
85    if settings.delay == v_yes then
86        local set = sets[name]
87        local index = set.index + 1
88        set.index = index
89        data.index = index
90        data.order = index
91        data.realpage = texgetcount(c_realpageno)
92        if trace_datasets then
93            report_dataset("action %a, name %a, tag %a, index %a","assign delayed",name,tag,index)
94        end
95    elseif trace_datasets then
96        report_dataset("action %a, name %a, tag %a","assign immediate",name,tag)
97    end
98    return name, tag, data
99end
100
101datasets.setdata = setdata
102
103function datasets.extend(name,tag)
104    if type(name) == "table" then
105        name, tag = name.name, name.tag
106    end
107    local set = sets[name]
108    local order = set.order + 1
109    local realpage = texgetcount(c_realpageno)
110    set.order = order
111    local t = tobesaved[name][tag]
112    t.realpage = realpage
113    t.order = order
114    if trace_datasets then
115        report_dataset("action %a, name %a, tag %a, page %a, index %a","flush by order",name,tag,t.index or 0,order,realpage)
116    end
117end
118
119function datasets.getdata(name,tag,key,default)
120    local t = collected[name]
121    if t == nil then
122        if trace_datasets then
123            report_dataset("error: unknown dataset, name %a",name)
124        end
125    elseif type(t) ~= "table" then
126        return t
127    else
128        t = t[tag] or t[tonumber(tag)]
129        if not t then
130            if trace_datasets then
131                report_dataset("error: unknown dataset, name %a, tag %a",name,tag)
132            end
133        elseif key then
134            return t[key] or default
135        else
136            return t
137        end
138    end
139    return default
140end
141
142local function setdataset(settings)
143    settings.convert = true
144    local name, tag = setdata(settings)
145    if settings.delay ~= v_yes then
146        --
147    else
148        context(new_latelua { action = job.datasets.extend, name = name, tag = tag })
149    end
150end
151
152local cache = table.setmetatableindex(function(t,k)
153    local v = table.load(k..".tuc")
154    if v then
155        v = v.job
156        if v then
157            v = v.datasets
158            if v then
159                v = v.collected
160            end
161        end
162    end
163    if not v then
164        v = { }
165        if trace_datasets then
166            report_dataset("error: unknown dataset job %a",k)
167        end
168    end
169    t[k] = v
170    return v
171end)
172
173local function datasetvariable(jobname,name,tag,key)
174    local t = (jobname ~= "" and cache[jobname] or collected)[name]
175    if t == nil then
176        if trace_datasets then
177            report_dataset("error: unknown dataset, name %a, tag %a, not passed to tex",name) -- no tag
178        end
179    elseif type(t) ~= "table" then
180        context(tostring(t))
181    else
182        t = t and (t[tag] or t[tonumber(tag)])
183        if not t then
184            if trace_datasets then
185                report_dataset("error: unknown dataset, name %a, tag %a, not passed to tex",name,tag)
186            end
187        elseif type(t) == "table" then
188            local s = t[key]
189            if type(s) ~= "table" then
190                context(tostring(s))
191            elseif trace_datasets then
192                report_dataset("error: unknown dataset, name %a, tag %a, not passed to tex",name,tag)
193            end
194        end
195    end
196end
197
198local function datasetsize(jobname,name)
199    local t = (jobname ~= "" and cache[jobname] or collected)[name]
200    context(t and type(t) == "table" and #t or 0)
201end
202
203implement {
204    name      = "setdataset",
205    actions   = setdataset,
206    arguments = {
207        {
208            { "name" },
209            { "tag" },
210            { "delay" },
211            { "data" },
212        }
213    }
214}
215
216implement {
217    name      = "datasetvariable",
218    actions   = datasetvariable,
219    arguments = "4 strings",
220}
221
222
223implement {
224    name      = "datasetsize",
225    arguments = "2 strings",
226    actions   = datasetsize,
227}
228