util-sta.lua /size: 9412 b    last modification: 2020-07-01 14:35
1if not modules then modules = { } end modules ['util-sta'] = {
2    version   = 1.001,
3    comment   = "companion to util-ini.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
9local insert, remove, fastcopy, concat = table.insert, table.remove, table.fastcopy, table.concat
10local format = string.format
11local select, tostring = select, tostring
12
13local trace_stacker = false  trackers.register("stacker.resolve", function(v) trace_stacker = v end)
14
15local stacker = stacker or { }
16
17utilities.stacker = stacker
18
19local function start(s,t,first,last)
20    if s.mode == "switch" then
21        local n = tostring(t[last])
22        if trace_stacker then
23            s.report("start: %s",n)
24        end
25        return n
26    else
27        local r = { }
28        for i=first,last do
29            r[#r+1] = tostring(t[i])
30        end
31        local n = concat(r," ")
32        if trace_stacker then
33            s.report("start: %s",n)
34        end
35        return n
36    end
37end
38
39local function stop(s,t,first,last)
40    if s.mode == "switch" then
41        local n = tostring(false)
42        if trace_stacker then
43            s.report("stop: %s",n)
44        end
45        return n
46    else
47        local r = { }
48        for i=last,first,-1 do
49            r[#r+1] = tostring(false)
50        end
51        local n = concat(r," ")
52        if trace_stacker then
53            s.report("stop: %s",n)
54        end
55        return n
56    end
57end
58
59local function change(s,t1,first1,last1,t2,first2,last2)
60    if s.mode == "switch" then
61        local n = tostring(t2[last2])
62        if trace_stacker then
63            s.report("change: %s",n)
64        end
65        return n
66    else
67        local r = { }
68        for i=last1,first1,-1 do
69            r[#r+1] = tostring(false)
70        end
71        local n = concat(r," ")
72        for i=first2,last2 do
73            r[#r+1] = tostring(t2[i])
74        end
75        if trace_stacker then
76            s.report("change: %s",n)
77        end
78        return n
79    end
80end
81
82function stacker.new(name)
83
84    -- to be sped up, mmaybe foo:bar syntax here but then quite some access
85
86    local report = logs.reporter("stacker",name or nil)
87
88    local s
89
90    local stack = { }
91    local list  = { }
92    local ids   = { }
93    local hash  = { }
94
95    local hashing = true
96
97    local function push(...)
98        -- todo check if more than 1 argument
99        for i=1,select("#",...) do
100            insert(stack,(select(i,...))) -- watch the ()
101        end
102        if hashing then
103            local c = concat(stack,"|")
104            local n = hash[c]
105            if not n then
106                n = #list+1
107                hash[c] = n
108                list[n] = fastcopy(stack)
109            end
110            insert(ids,n)
111            return n
112        else
113            local n = #list+1
114            list[n] = fastcopy(stack)
115            insert(ids,n)
116            return n
117        end
118    end
119
120    local function pop()
121        remove(stack)
122        remove(ids)
123        return ids[#ids] or s.unset or -1
124    end
125
126    local function clean()
127        if #stack == 0 then
128            if trace_stacker then
129                s.report("%s list entries, %s stack entries",#list,#stack)
130            end
131        end
132    end
133
134    local tops   = { }
135    local top    = nil
136    local switch = nil
137
138    local function resolve_reset(mode)
139        if #tops > 0 then
140            report("resetting %s left-over states of %a",#tops,name)
141        end
142        tops   = { }
143        top    = nil
144        switch = nil
145    end
146
147    local function resolve_begin(mode)
148        if mode then
149            switch = mode == "switch"
150        else
151            switch = s.mode == "switch"
152        end
153        top = { switch = switch }
154        insert(tops,top)
155    end
156
157    local function resolve_step(ti) -- keep track of changes outside function !
158        -- todo: optimize for n=1 etc
159        if not top then
160         -- report("messed op stacker %a",name)
161            return
162        end
163        local result = nil
164        local noftop = #top
165        if ti > 0 then
166            local current = list[ti]
167            if current then
168                local noflist = #current
169                local nofsame = 0
170                if noflist > noftop then
171                    for i=1,noflist do
172                        if current[i] == top[i] then
173                            nofsame = i
174                        else
175                            break
176                        end
177                    end
178                else
179                    for i=1,noflist do
180                        if current[i] == top[i] then
181                            nofsame = i
182                        else
183                            break
184                        end
185                    end
186                end
187                local plus = nofsame + 1
188                if plus <= noftop then
189                    if plus <= noflist then
190                        if switch then
191                            result = s.change(s,top,plus,noftop,current,nofsame,noflist)
192                        else
193                            result = s.change(s,top,plus,noftop,current,plus,noflist)
194                        end
195                    else
196                        if switch then
197                            result = s.change(s,top,plus,noftop,current,nofsame,noflist)
198                        else
199                            result = s.stop(s,top,plus,noftop)
200                        end
201                    end
202                elseif plus <= noflist then
203                    if switch then
204                        result = s.start(s,current,nofsame,noflist)
205                    else
206                        result = s.start(s,current,plus,noflist)
207                    end
208                end
209                top = current
210            else
211                if 1 <= noftop then
212                    result = s.stop(s,top,1,noftop)
213                end
214                top = { }
215            end
216            return result
217        else
218            if 1 <= noftop then
219                result = s.stop(s,top,1,noftop)
220            end
221            top = { }
222            return result
223        end
224    end
225
226    local function resolve_end()
227     -- resolve_step(s.unset)
228        if #tops > 0 then -- was #top brrr
229            local result = s.stop(s,top,1,#top)
230            remove(tops)
231            top = tops[#tops]
232            switch = top and top.switch
233            return result
234        end
235    end
236
237    local function resolve(t)
238        resolve_begin()
239        for i=1,#t do
240            resolve_step(t[i])
241        end
242        resolve_end()
243    end
244
245    s = {
246        name          = name or "unknown",
247        unset         = -1,
248        report        = report,
249        start         = start,
250        stop          = stop,
251        change        = change,
252        push          = push,
253        pop           = pop,
254        clean         = clean,
255        resolve       = resolve,
256        resolve_begin = resolve_begin,
257        resolve_step  = resolve_step,
258        resolve_end   = resolve_end,
259        resolve_reset = resolve_reset,
260    }
261
262    return s -- we can overload functions
263
264end
265
266-- local s = utilities.stacker.new("demo")
267--
268-- local unset = s.unset
269-- local push  = s.push
270-- local pop   = s.pop
271--
272-- local t = {
273--     unset,
274--     unset,
275--     push("a"),     -- a
276--     push("b","c"), -- a b c
277--     pop(),         -- a b
278--     push("d"),     -- a b d
279--     pop(),         -- a b
280--     unset,
281--     pop(),         -- a
282--     pop(),         -- b
283--     unset,
284--     unset,
285-- }
286--
287-- s.resolve(t)
288
289-- demostacker = utilities.stacker.new("demos")
290--
291-- local whatever = {
292--     one     = "1 0 0 RG 1 0 0 rg",
293--     two     = "1 1 0 RG 1 1 0 rg",
294--     [false] = "0 G 0 g",
295-- }
296--
297-- local concat = table.concat
298--
299-- local pageliteral = nodes.pool.pageliteral
300--
301-- function demostacker.start(s,t,first,last)
302--     local n = whatever[t[last]]
303--  -- s.report("start: %s",n)
304--     return pageliteral(n)
305-- end
306--
307-- function demostacker.stop(s,t,first,last)
308--     local n = whatever[false]
309--  -- s.report("stop: %s",n)
310--     return pageliteral(n)
311-- end
312--
313-- function demostacker.change(s,t1,first1,last1,t2,first2,last2)
314--     local n = whatever[t2[last2]]
315--  -- s.report("change: %s",n)
316--     return pageliteral(n)
317-- end
318--
319-- demostacker.mode = "switch"
320--
321-- local whatever = {
322--     one     = "/OC /test1 BDC",
323--     two     = "/OC /test2 BDC",
324--     [false] = "EMC",
325-- }
326--
327-- demostacker = utilities.stacker.new("demos")
328--
329-- function demostacker.start(s,t,first,last)
330--     local r = { }
331--     for i=first,last do
332--         r[#r+1] = whatever[t[i]]
333--     end
334--  -- s.report("start: %s",concat(r," "))
335--     return pageliteral(concat(r," "))
336-- end
337--
338-- function demostacker.stop(s,t,first,last)
339--     local r = { }
340--     for i=last,first,-1 do
341--         r[#r+1] = whatever[false]
342--     end
343--  -- s.report("stop: %s",concat(r," "))
344--     return pageliteral(concat(r," "))
345-- end
346--
347-- function demostacker.change(s,t1,first1,last1,t2,first2,last2)
348--     local r = { }
349--     for i=last1,first1,-1 do
350--         r[#r+1] = whatever[false]
351--     end
352--     for i=first2,last2 do
353--         r[#r+1] = whatever[t2[i]]
354--     end
355--  -- s.report("change: %s",concat(r," "))
356--     return pageliteral(concat(r," "))
357-- end
358--
359-- demostacker.mode = "stack"
360