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