node-shp.lua /size: 7548 b    last modification: 2021-10-28 13:50
1if not modules then modules = { } end modules ['node-shp'] = {
2    version   = 1.001,
3    optimize  = true,
4    comment   = "companion to node-ini.mkiv",
5    author    = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
6    copyright = "PRAGMA ADE / ConTeXt Development Team",
7    license   = "see context related readme files"
8}
9
10local nodes, node = nodes, node
11
12local next, type = next, type
13local format = string.format
14local concat, sortedpairs = table.concat, table.sortedpairs
15local setmetatableindex = table.setmetatableindex
16
17local nodecodes    = nodes.nodecodes
18local whatsitcodes = nodes.whatsitcodes
19local disccodes    = nodes.disccodes
20
21local tasks        = nodes.tasks
22local handlers     = nodes.handlers
23
24local hlist_code   = nodecodes.hlist
25local vlist_code   = nodecodes.vlist
26local disc_code    = nodecodes.disc
27local whatsit_code = nodecodes.whatsit
28
29local discretionarydisc_code = disccodes.discretionary
30
31local implement    = interfaces.implement
32
33local nuts         = nodes.nuts
34local tonut        = nuts.tonut
35local tonode       = nuts.tonode
36local remove_node  = nuts.remove
37
38local nextnode     = nuts.traversers.node
39
40local setfield     = nuts.setfield
41local setlink      = nuts.setlink
42local setprev      = nuts.setprev
43local setnext      = nuts.setnext
44local getid        = nuts.getid
45local getdisc      = nuts.getdisc
46local getboth      = nuts.getboth
47local getnext      = nuts.getnext
48local getlist      = nuts.getlist
49local getsubtype   = nuts.getsubtype
50
51local setlist      = nuts.setlist
52
53local getbox       = nuts.getbox
54
55local removables   = {
56    [whatsitcodes.open]    = true,
57    [whatsitcodes.close]   = true,
58    [whatsitcodes.write]   = true,
59    [whatsitcodes.savepos] = true,
60    [whatsitcodes.latelua] = true,
61 -- [whatsitcodes.pdfdest] = true,
62}
63
64-- About 10% of the nodes make no sense for the backend. By (at least)
65-- removing the replace disc nodes, we can omit extensive checking in
66-- the finalizer code (e.g. colors in disc nodes). Removing more nodes
67-- (like marks) is not saving much and removing empty boxes is even
68-- dangerous because we can rely on dimensions (e.g. in references).
69
70-- local wipedisc = false -- we can use them in the export ... can be option
71--
72-- local function cleanup_redundant(head) -- better name is: flatten_page
73--     local start = head
74--     while start do
75--         local id = getid(start)
76--         if id == disc_code then
77--             if getsubtype(start) == discretionarydisc_code then
78--                 local _, _, replace, _, _ tail = getdisc(start,true)
79--                 if replace then
80--                     local prev, next = getboth(start)
81--                     setfield(start,"replace",nil)
82--                     if start == head then
83--                         remove_node(head,start,true)
84--                         head = replace
85--                     else
86--                         remove_node(head,start,true)
87--                     end
88--                     if next then
89--                         setlink(tail,next)
90--                     end
91--                     if prev then
92--                         setlink(prev,replace)
93--                     else
94--                         setprev(replace) -- to be sure
95--                     end
96--                     start = next
97--                 elseif wipedisc then
98--                     -- pre and post can have values
99--                     head, start = remove_node(head,start,true)
100--                 else
101--                     start = getnext(start)
102--                 end
103--             else
104--                 start = getnext(start)
105--             end
106--         elseif id == hlist_code or id == vlist_code then
107--             local sl = getlist(start)
108--             if sl then
109--                 local rl = cleanup_redundant(sl)
110--                 if rl ~= sl then
111--                     setlist(start,rl)
112--                 end
113--             end
114--             start = getnext(start)
115--         else
116--             start = getnext(start)
117--         end
118--     end
119--     return head
120-- end
121--
122-- handlers.cleanuppage = cleanup_redundant -- nut
123
124handlers.cleanuppage = nuts.flattendiscretionaries
125
126local function cleanup_flushed(head) -- rough
127    local start = head
128    while start do
129        local id = getid(start)
130        if id == whatsit_code then
131            if removables[getsubtype(start)] then
132                head, start = remove_node(head,start,true)
133            else
134                start = getnext(start)
135            end
136        elseif id == hlist_code or id == vlist_code then
137            local sl = getlist(start)
138            if sl then
139                local rl = cleanup_flushed(sl)
140                if rl ~= sl then
141                    setlist(start,rl)
142                end
143            end
144            start = getnext(start)
145        else
146            start = getnext(start)
147        end
148    end
149    return head
150end
151
152function handlers.cleanupbox(box)
153    cleanup_flushed(getbox(box))
154end
155
156local actions = tasks.actions("shipouts")
157
158function handlers.finalizebox(box)
159    actions(getbox(box)) -- nut
160end
161
162-- interface
163
164implement { name = "cleanupbox",  actions = handlers.cleanupbox,  arguments = "integer" }
165implement { name = "finalizebox", actions = handlers.finalizebox, arguments = "integer" }
166
167-- just in case we want to optimize lookups:
168
169local frequencies = { }
170
171nodes.tracers.frequencies = frequencies
172
173local data = { }
174local done = false
175
176setmetatableindex(data,function(t,k)
177    local v = { }
178    setmetatableindex(v,function(t,k)
179        local v = { }
180        t[k] = v
181        setmetatableindex(v,function(t,k)
182            t[k] = 0
183            return 0
184        end)
185        return v
186    end)
187    t[k] = v
188    return v
189end)
190
191local function count(head,data,subcategory)
192    -- no components, pre, post, replace .. can maybe an option .. but
193    -- we use this for optimization so it makes sense to look the the
194    -- main node only
195    for n, id in nextnode, tonut(head) do
196        local dn = data[nodecodes[id]] -- we could use id and then later convert to nodecodes
197        dn[subcategory] = dn[subcategory] + 1
198        if id == hlist_code or id == vlist_code then
199            count(getlist(n),data,subcategory)
200        end
201    end
202end
203
204local function register(category,subcategory)
205    return function(head)
206        done = true
207        count(head,data[category],subcategory)
208        return head, false
209    end
210end
211
212frequencies.register = register
213frequencies.filename = nil
214
215trackers.register("nodes.frequencies",function(v)
216    if type(v) == "string" then
217        frequencies.filename = v
218    end
219    handlers.frequencies_shipouts_before   = register("shipouts",   "begin")
220    handlers.frequencies_shipouts_after    = register("shipouts",   "end")
221    handlers.frequencies_processors_before = register("processors", "begin")
222    handlers.frequencies_processors_after  = register("processors", "end")
223    tasks.prependaction("shipouts",   "before", "nodes.handlers.frequencies_shipouts_before")
224    tasks.appendaction ("shipouts",   "after",  "nodes.handlers.frequencies_shipouts_after")
225    tasks.prependaction("processors", "before", "nodes.handlers.frequencies_processors_before")
226    tasks.appendaction ("processors", "after",  "nodes.handlers.frequencies_processors_after")
227end)
228
229statistics.register("node frequencies", function()
230    if done then
231        local filename = frequencies.filename or (tex.jobname .. "-frequencies.lua")
232        io.savedata(filename,table.serialize(data,true))
233        return format("saved in %q",filename)
234    end
235end)
236