1if not modules then modules = { } end modules ['util-seq'] = {
2 version = 1.001,
3 comment = "companion to luat-lib.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
15
16
17
18
19
20local gsub, gmatch = string.gsub, string.gmatch
21local concat, sortedkeys = table.concat, table.sortedkeys
22local type, load, next, tostring = type, load, next, tostring
23
24utilities = utilities or { }
25local tables = utilities.tables
26local allocate = utilities.storage.allocate
27
28local formatters = string.formatters
29local replacer = utilities.templates.replacer
30
31local trace_used = false
32local trace_details = false
33local report = logs.reporter("sequencer")
34local usedcount = 0
35local usednames = { }
36
37trackers.register("sequencers.used", function(v) trace_used = v end)
38trackers.register("sequencers.details", function(v) trace_details = v end)
39
40local sequencers = { }
41utilities.sequencers = sequencers
42
43local functions = allocate()
44sequencers.functions = functions
45
46local removevalue = tables.removevalue
47local replacevalue = tables.replacevalue
48local insertaftervalue = tables.insertaftervalue
49local insertbeforevalue = tables.insertbeforevalue
50
51local usedsequences = { }
52
53local function validaction(action)
54 if type(action) == "string" then
55 local g = _G
56 for str in gmatch(action,"[^%.]+") do
57 g = g[str]
58 if not g then
59 return false
60 end
61 end
62 end
63 return true
64end
65
66local compile
67
68local known = { }
69
70function sequencers.new(t)
71 local s = {
72 list = { },
73 order = { },
74 kind = { },
75 askip = { },
76 gskip = { },
77 dirty = true,
78 runner = nil,
79 steps = 0,
80 }
81 if t then
82 s.arguments = t.arguments
83 s.templates = t.templates
84 s.returnvalues = t.returnvalues
85 s.results = t.results
86 local name = t.name
87 if name and name ~= "" then
88 s.name = name
89 known[name] = s
90 end
91 end
92 table.setmetatableindex(s,function(t,k)
93
94 if k == "runner" then
95 local v = compile(t,t.compiler)
96 return v
97 end
98 end)
99 known[s] = s
100 return s
101end
102
103function sequencers.prependgroup(t,group,where)
104 if t and group then
105 t = known[t]
106 if t then
107 local order = t.order
108 removevalue(order,group)
109 insertbeforevalue(order,where,group)
110 t.list[group] = { }
111 t.dirty = true
112 t.runner = nil
113 end
114 end
115end
116
117function sequencers.appendgroup(t,group,where)
118 if t and group then
119 t = known[t]
120 if t then
121 local order = t.order
122 removevalue(order,group)
123 insertaftervalue(order,where,group)
124 t.list[group] = { }
125 t.dirty = true
126 t.runner = nil
127 end
128 end
129end
130
131function sequencers.prependaction(t,group,action,where,kind,force)
132 if t and group and action then
133 t = known[t]
134 if t then
135 local g = t.list[group]
136 if g and (force or validaction(action)) then
137 removevalue(g,action)
138 insertbeforevalue(g,where,action)
139 t.kind[action] = kind
140 t.dirty = true
141 t.runner = nil
142 end
143 end
144 end
145end
146
147function sequencers.appendaction(t,group,action,where,kind,force)
148 if t and group and action then
149 t = known[t]
150 if t then
151 local g = t.list[group]
152 if g and (force or validaction(action)) then
153 removevalue(g,action)
154 insertaftervalue(g,where,action)
155 t.kind[action] = kind
156 t.dirty = true
157 t.runner = nil
158 end
159 end
160 end
161end
162
163function sequencers.enableaction(t,action)
164 if t and action then
165 t = known[t]
166 if t then
167 t.askip[action] = false
168 t.dirty = true
169 t.runner = nil
170 end
171 end
172end
173
174function sequencers.disableaction(t,action)
175 if t and action then
176 t = known[t]
177 if t then
178 t.askip[action] = true
179 t.dirty = true
180 t.runner = nil
181 end
182 end
183end
184
185function sequencers.enablegroup(t,group)
186 if t and group then
187 t = known[t]
188 if t then
189 t.gskip[group] = false
190 t.dirty = true
191 t.runner = nil
192 end
193 end
194end
195
196function sequencers.disablegroup(t,group)
197 if t and group then
198 t = known[t]
199 if t then
200 t.gskip[group] = true
201 t.dirty = true
202 t.runner = nil
203 end
204 end
205end
206
207function sequencers.setkind(t,action,kind)
208 if t and action then
209 t = known[t]
210 if t then
211 t.kind[action] = kind
212 t.dirty = true
213 t.runner = nil
214 end
215 end
216end
217
218function sequencers.removeaction(t,group,action,force)
219 if t and group and action then
220 t = known[t]
221 local g = t and t.list[group]
222 if g and (force or validaction(action)) then
223 removevalue(g,action)
224 t.dirty = true
225 t.runner = nil
226 end
227 end
228end
229
230function sequencers.replaceaction(t,group,oldaction,newaction,force)
231 if t and group and oldaction and newaction then
232 t = known[t]
233 if t then
234 local g = t.list[group]
235 if g and (force or validaction(oldaction)) then
236 replacevalue(g,oldaction,newaction)
237 t.dirty = true
238 t.runner = nil
239 end
240 end
241 end
242end
243
244local function localize(str)
245 return (gsub(str,"[%.: ]+","_"))
246end
247
248local function construct(t)
249 local list = t.list
250 local order = t.order
251 local kind = t.kind
252 local gskip = t.gskip
253 local askip = t.askip
254 local name = t.name or "?"
255 local arguments = t.arguments or "..."
256 local returnvalues = t.returnvalues
257 local results = t.results
258 local variables = { }
259 local calls = { }
260 local n = 0
261 usedcount = usedcount + 1
262 for i=1,#order do
263 local group = order[i]
264 if not gskip[group] then
265 local actions = list[group]
266 for i=1,#actions do
267 local action = actions[i]
268 if not askip[action] then
269 if trace_used then
270 local action = tostring(action)
271 report("%02i: category %a, group %a, action %a",usedcount,name,group,action)
272 usednames[action] = true
273 end
274 local localized
275 if type(action) == "function" then
276 local name = localize(tostring(action))
277 functions[name] = action
278 action = formatters["utilities.sequencers.functions.%s"](name)
279 localized = localize(name)
280 else
281 localized = localize(action)
282 end
283 n = n + 1
284 variables[n] = formatters["local %s = %s"](localized,action)
285 if not returnvalues then
286 calls[n] = formatters["%s(%s)"](localized,arguments)
287 elseif n == 1 then
288 calls[n] = formatters["local %s = %s(%s)"](returnvalues,localized,arguments)
289 else
290 calls[n] = formatters["%s = %s(%s)"](returnvalues,localized,arguments)
291 end
292 end
293 end
294 end
295 end
296 t.dirty = false
297 t.steps = n
298 if n == 0 then
299 t.compiled = ""
300 else
301 variables = concat(variables,"\n")
302 calls = concat(calls,"\n")
303 if results then
304 t.compiled = formatters["%s\nreturn function(%s)\n%s\nreturn %s\nend"](variables,arguments,calls,results)
305 else
306 t.compiled = formatters["%s\nreturn function(%s)\n%s\nend"](variables,arguments,calls)
307 end
308 end
309 return t.compiled
310end
311
312sequencers.tostring = construct
313sequencers.localize = localize
314
315compile = function(t,compiler,...)
316 local compiled
317 if not t or type(t) == "string" then
318 return false
319 end
320 if compiler then
321 compiled = compiler(t,...)
322 t.compiled = compiled
323 else
324 compiled = construct(t,...)
325 end
326 local runner
327 if compiled == "" then
328 runner = false
329 else
330 runner = compiled and load(compiled)()
331 end
332 t.runner = runner
333 return runner
334end
335
336sequencers.compile = compile
337
338function sequencers.nodeprocessor(t,nofarguments)
339
340 local templates = nofarguments
341
342 if type(templates) ~= "table" then
343 return ""
344 end
345
346 local replacers = { }
347 for k, v in next, templates do
348 replacers[k] = replacer(v)
349 end
350
351 local construct = replacers.process
352 local step = replacers.step
353 if not construct or not step then
354 return ""
355 end
356
357 local calls = { }
358 local aliases = { }
359 local ncalls = 0
360 local naliases = 0
361 local f_alias = formatters["local %s = %s"]
362
363 local list = t.list
364 local order = t.order
365 local kind = t.kind
366 local gskip = t.gskip
367 local askip = t.askip
368 local name = t.name or "?"
369 local steps = 0
370 usedcount = usedcount + 1
371
372 if trace_details then
373 naliases = naliases + 1
374 aliases[naliases] = formatters["local report = logs.reporter('sequencer',%q)"](name)
375 ncalls = ncalls + 1
376 calls[ncalls] = [[report("start")]]
377 end
378 for i=1,#order do
379 local group = order[i]
380 if not gskip[group] then
381 local actions = list[group]
382 for i=1,#actions do
383 local action = actions[i]
384 if not askip[action] then
385 steps = steps + 1
386 if trace_used or trace_details then
387 local action = tostring(action)
388 report("%02i: category %a, group %a, action %a",usedcount,name,group,action)
389 usednames[action] = true
390 end
391 if trace_details then
392 ncalls = ncalls + 1
393 calls[ncalls] = formatters[ [[report(" step %a, action %a")]] ](steps,tostring(action))
394 end
395 local localized = localize(action)
396 local onestep = replacers[kind[action]] or step
397 naliases = naliases + 1
398 ncalls = ncalls + 1
399 aliases[naliases] = f_alias(localized,action)
400 calls [ncalls] = onestep { action = localized }
401 end
402 end
403 end
404 end
405 t.steps = steps
406 local processor
407 if steps == 0 then
408 processor = templates.default or construct { }
409 else
410 if trace_details then
411 ncalls = ncalls + 1
412 calls[ncalls] = [[report("stop")]]
413 end
414 processor = construct {
415 localize = concat(aliases,"\n"),
416 actions = concat(calls,"\n"),
417 }
418 end
419
420
421 return processor
422end
423
424statistics.register("used sequences",function()
425 if next(usednames) then
426 return concat(sortedkeys(usednames)," ")
427 end
428end)
429 |