1if not modules then modules = { } end modules ['node-ppt'] = {
2 version = 1.001,
3 comment = "companion to node-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
9
10
11local next, type, unpack, load = next, type, table.unpack, load
12
13local serialize = table.serialize
14local formatters = string.formatters
15
16local report = logs.reporter("properties")
17local report_setting = logs.reporter("properties","setting")
18local trace_setting = false trackers.register("properties.setting", function(v) trace_setting = v end)
19
20
21
22local nuts = nodes.nuts
23local tonut = nuts.tonut
24local tonode = nuts.tonode
25local getid = nuts.getid
26local getnext = nuts.getnext
27local getprev = nuts.getprev
28local getsubtype = nuts.getsubtype
29local getlist = nuts.getlist
30local setlist = nuts.setlist
31local getprop = nuts.getprop
32
33local removenode = nuts.remove
34
35local nextnode = nuts.traversers.node
36local nextwhatsit = nuts.traversers.whatsit
37
38local nodecodes = nodes.nodecodes
39local whatsitcodes = nodes.whatsitcodes
40
41local whatsit_code = nodecodes.whatsit
42local hlist_code = nodecodes.hlist
43local vlist_code = nodecodes.vlist
44
45local userdefinedwhatsit_code = whatsitcodes.userdefined
46
47local nodepool = nodes.pool
48local new_usernode = nodepool.usernode
49
50local variables = interfaces.variables
51local v_before = variables.before
52local v_after = variables.after
53local v_here = variables.here
54
55local property_id = nodepool.userids["property"]
56
57local properties = nodes.properties
58local propertydata = properties.data
59
60local starttiming = statistics.starttiming
61local stoptiming = statistics.stoptiming
62
63if not propertydata then
64 return
65end
66
67
68
69local function register(where,data,...)
70 if not data then
71 data = where
72 where = v_after
73 end
74 if data then
75 nofslots = nofslots + 1
76 return new_usernode(property_id,{ where, data, ... })
77 end
78end
79
80local writenode = nodes.write
81local flushnode = context.nodes.flush
82
83function commands.deferredproperty(...)
84
85 flushnode(register(...))
86end
87
88function commands.immediateproperty(...)
89 writenode(register(...))
90end
91
92commands.attachproperty = commands.deferredproperty
93
94local actions = { } properties.actions = actions
95
96table.setmetatableindex(actions,function(t,k)
97 report("unknown property action %a",k)
98 return function() end
99end)
100
101local f_delayed = formatters["return function(target,head,where,propdata,parent) %s end"]
102local f_immediate = formatters["return function(target,head,where,propdata) %s end"]
103
104local nofdelayed = 0
105local nofslots = 0
106
107function actions.delayed(target,head,where,propdata,code,...)
108 if code then
109 local delayed = propdata.delayed
110 if delayed then
111 delayed[#delayed+1] = { where, code, ... }
112 else
113 propdata.delayed = { { where, code, ... } }
114 nofdelayed = nofdelayed + 1
115 end
116 end
117end
118
119function actions.fdelayed(target,head,where,propdata,code,...)
120 if code then
121 local delayed = propdata.delayed
122 if delayed then
123 delayed[#delayed+1] = { false, code, ... }
124 else
125 propdata.delayed = { { false, code, ... } }
126 nofdelayed = nofdelayed + 1
127 end
128 end
129end
130
131function actions.immediate(target,head,where,propdata,code,...)
132 local kind = type(code)
133 if kind == "string" then
134 local f = f_immediate(code)
135 local okay, err = load(f)
136 if okay then
137 local h = okay()(target,head,where,propdata,...)
138 if h and h ~= head then
139 return h
140 end
141 end
142 elseif kind == "function" then
143 local h = code()(target,head,where,propdata,...)
144 if h and h ~= head then
145 return h
146 end
147 end
148end
149
150local function delayed(head,parent)
151 for target, id in nextnode, head do
152 local p = propertydata[target]
153 if p then
154 local delayed = p.delayed
155 if delayed then
156 for i=1,#delayed do
157 local d = delayed[i]
158 local code = d[2]
159 local kind = type(code)
160 if kind == "string" then
161 code, err = load(f_delayed(code))
162 if code then
163 code = code()
164 end
165 end
166 local where = d[1]
167 if where then
168 local h = code(target,where,head,p,parent,unpack(d,3))
169 if h and h ~= head then
170 head = h
171 end
172 else
173 code(unpack(d,3))
174 end
175 end
176 p.delayed = nil
177 if nofdelayed == 1 then
178 nofdelayed = 0
179 return head
180 else
181 nofdelayed = nofdelayed - 1
182 end
183 end
184 end
185 if id == hlist_code or id == vlist_code then
186 local list = getlist(target)
187 if list then
188 local done = delayed(list,parent)
189 if done then
190 setlist(target,done)
191 end
192 if nofdelayed == 0 then
193 return head
194 end
195 end
196 else
197
198
199 end
200 end
201 return head
202end
203
204function properties.delayed(head)
205 if nofdelayed > 0 then
206
207 starttiming(properties)
208 head = delayed(head)
209 stoptiming(properties)
210
211
212
213 end
214 return head
215end
216
217
218
219local anchored = {
220 [v_before] = function(n)
221 while n do
222 n = getprev(n)
223 if getid(n) == whatsit_code and getsubtype(n) == user_code and getprop(n,"id") == property_id then
224
225 else
226 return n
227 end
228 end
229 end,
230 [v_after] = function(n)
231 while n do
232 n = getnext(n)
233 if getid(n) == whatsit_code then
234 local subtype = getsubtype(n)
235 if (subtype == userdefinedwhatsit_code and getprop(n,"id") == property_id) then
236
237 else
238 return n
239 end
240 else
241 return n
242 end
243 end
244 end,
245 [v_here] = function(n)
246
247 end,
248}
249
250table.setmetatableindex(anchored,function(t,k)
251 local v = anchored[v_after]
252 t[k] = v
253 return v
254end)
255
256function properties.attach(head)
257
258 if nofslots <= 0 then
259 return head
260 end
261
262 local last = nil
263
264 starttiming(properties)
265
266 for source, subtype in nextwhatsit, head do
267 if subtype == userdefinedwhatsit_code then
268 if last then
269 removenode(head,last,true)
270 last = nil
271 end
272 if getprop(source,"id") == property_id then
273 local data = getprop(source,"data")
274 if data then
275 local where = data[1]
276 local target = anchored[where](source)
277 if target then
278 local first = data[2]
279 local method = type(first)
280 local p_target = propertydata[target]
281 local p_source = propertydata[source]
282 if p_target then
283 if p_source then
284 for k, v in next, p_source do
285 p_target[k] = v
286 end
287 end
288 if method == "table" then
289 for k, v in next, first do
290 p_target[k] = v
291 end
292 elseif method == "function" then
293 first(target,head,where,p_target,unpack(data,3))
294 elseif method == "string" then
295 actions[first](target,head,where,p_target,unpack(data,3))
296 end
297 elseif p_source then
298 if method == "table" then
299 propertydata[target] = p_source
300 for k, v in next, first do
301 p_source[k] = v
302 end
303 elseif method == "function" then
304 propertydata[target] = p_source
305 first(target,head,where,p_source,unpack(data,3))
306 elseif method == "string" then
307 propertydata[target] = p_source
308 actions[first](target,head,where,p_source,unpack(data,3))
309 end
310 else
311 if method == "table" then
312 propertydata[target] = first
313 elseif method == "function" then
314 local t = { }
315 propertydata[target] = t
316 first(target,head,where,t,unpack(data,3))
317 elseif method == "string" then
318 local t = { }
319 propertydata[target] = t
320 actions[first](target,head,where,t,unpack(data,3))
321 end
322 end
323 if trace_setting then
324 report_setting("node %i, id %s, data %s",
325 target,nodecodes[getid(target)],serialize(propertydata[target],false))
326 end
327 end
328 if nofslots == 1 then
329 nofslots = 0
330 last = source
331 break
332 else
333 nofslots = nofslots - 1
334 end
335 end
336 last = source
337 end
338 end
339 end
340
341 if last then
342 removenode(head,last,true)
343 end
344
345 stoptiming(properties)
346
347 return head
348
349end
350
351
352
353statistics.register("properties processing time", function()
354 return statistics.elapsedseconds(properties)
355end)
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400 |