1if not modules then modules = { } end modules [ ' strc-mar ' ] = {
2 version = 1 . 001 ,
3 comment = " companion to strc-mar.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
12local insert , concat = table . insert , table . concat
13local tostring , next , rawget , type = tostring , next , rawget , type
14local lpegmatch = lpeg . match
15
16local context = context
17local commands = commands
18
19local implement = interfaces . implement
20
21local allocate = utilities . storage . allocate
22local setmetatableindex = table . setmetatableindex
23
24local nuts = nodes . nuts
25local tonut = nuts . tonut
26
27local getid = nuts . getid
28local getlist = nuts . getlist
29local getattr = nuts . getattr
30local getbox = nuts . getbox
31
32local nextnode = nuts . traversers . node
33
34local nodecodes = nodes . nodecodes
35local whatsitcodes = nodes . whatsitcodes
36
37local glyph_code = nodecodes . glyph
38local hlist_code = nodecodes . hlist
39local vlist_code = nodecodes . vlist
40local whatsit_code = nodecodes . whatsit
41
42local lateluawhatsit_code = whatsitcodes . latelua
43
44local texsetattribute = tex . setattribute
45
46local a_marks = attributes . private ( " structure " , " marks " )
47
48local trace_set = false trackers . register ( " marks.set " , function ( v ) trace_set = v end )
49local trace_get = false trackers . register ( " marks.get " , function ( v ) trace_get = v end )
50local trace_details = false trackers . register ( " marks.details " , function ( v ) trace_details = v end )
51
52local report_marks = logs . reporter ( " structure " , " marks " )
53
54local variables = interfaces . variables
55
56local v_first = variables . first
57local v_last = variables . last
58local v_previous = variables . previous
59local v_next = variables . next
60local v_top = variables . top
61local v_bottom = variables . bottom
62local v_current = variables . current
63local v_default = variables . default
64local v_page = variables . page
65local v_all = variables . all
66local v_keep = variables . keep
67
68local v_nocheck_suffix = " : " . . variables . nocheck
69
70local v_first_nocheck = variables . first . . v_nocheck_suffix
71local v_last_nocheck = variables . last . . v_nocheck_suffix
72local v_previous_nocheck = variables . previous . . v_nocheck_suffix
73local v_next_nocheck = variables . next . . v_nocheck_suffix
74local v_top_nocheck = variables . top . . v_nocheck_suffix
75local v_bottom_nocheck = variables . bottom . . v_nocheck_suffix
76
77local structures = structures
78local marks = structures . marks
79local lists = structures . lists
80
81local settings_to_array = utilities . parsers . settings_to_array
82
83local boxes_too = false
84
85directives . register ( " marks.boxestoo " , function ( v ) boxes_too = v end )
86
87local data = marks . data or allocate ( )
88marks . data = data
89
90storage . register ( " structures/marks/data " , marks . data , " structures.marks.data " )
91
92local stack , topofstack = { } , 0
93
94local ranges = {
95 [ v_page ] = {
96 first = 0 ,
97 last = 0 ,
98 } ,
99}
100
101local function resolve ( t , k )
102 if k then
103 if trace_set or trace_get then
104 report_marks ( " undefined mark, name %a " , k )
105 end
106 local crap = { autodefined = true }
107 t [ k ] = crap
108 return crap
109 else
110
111 end
112end
113
114setmetatableindex ( data , resolve )
115
116function marks . exists ( name )
117 return rawget ( data , name ) ~ = nil
118end
119
120
121
122local function sweep ( head , first , last )
123 for n , id , subtype in nextnode , head do
124
125 if id = = glyph_code or ( id = = whatsit_code and subtype = = lateluawhatsit_code ) then
126 local a = getattr ( n , a_marks )
127 if not a then
128
129 elseif first = = 0 then
130 first , last = a , a
131 elseif a > last then
132 last = a
133 end
134 elseif id = = hlist_code or id = = vlist_code then
135 if boxes_too then
136 local a = getattr ( n , a_marks )
137 if not a then
138
139 elseif first = = 0 then
140 first , last = a , a
141 elseif a > last then
142 last = a
143 end
144 end
145 local list = getlist ( n )
146 if list then
147 first , last = sweep ( list , first , last )
148 end
149 end
150 end
151 return first , last
152end
153
154local classes = { }
155
156setmetatableindex ( classes , function ( t , k ) local s = settings_to_array ( k ) t [ k ] = s return s end )
157
158local lasts = { }
159
160function marks . synchronize ( class , n , option )
161 local box = getbox ( n )
162 if box then
163 local first , last = sweep ( getlist ( box ) , 0 , 0 )
164 if option = = v_keep and first = = 0 and last = = 0 then
165 if trace_get or trace_set then
166 report_marks ( " action %a, class %a, box %a " , " retain at synchronize " , class , n )
167 end
168
169 first = lasts [ class ] or 0
170 last = first
171 else
172 lasts [ class ] = last
173 local classlist = classes [ class ]
174 for i = 1 , # classlist do
175 local class = classlist [ i ]
176 local range = ranges [ class ]
177 if range then
178 range . first = first
179 range . last = last
180 else
181 range = {
182 first = first ,
183 last = last ,
184 }
185 ranges [ class ] = range
186 end
187 if trace_get or trace_set then
188 report_marks ( " action %a, class %a, first %a, last %a " , " synchronize " , class , range . first , range . last )
189 end
190 end
191 end
192 elseif trace_get or trace_set then
193 report_marks ( " action %s, class %a, box %a " , " synchronize without content " , class , n )
194 end
195end
196
197
198
199local function resolve ( t , k )
200 if k = = " fullchain " then
201 local fullchain = { }
202 local chain = t . chain
203 while chain and chain ~ = " " do
204 insert ( fullchain , 1 , chain )
205 chain = data [ chain ] . chain
206 end
207 t [ k ] = fullchain
208 return fullchain
209 elseif k = = " chain " then
210 t [ k ] = " "
211 return " "
212 elseif k = = " reset " or k = = " set " then
213 t [ k ] = 0
214 return 0
215 elseif k = = " parent " then
216 t [ k ] = false
217 return false
218 end
219end
220
221function marks . define ( name , settings )
222 if not settings then
223 settings = { }
224 elseif type ( settings ) = = " string " then
225 settings = { parent = settings }
226 end
227 data [ name ] = settings
228 local parent = settings . parent
229 if parent = = nil or parent = = " " or parent = = name then
230 settings . parent = false
231 else
232 local dp = data [ parent ]
233 if not dp then
234 settings . parent = false
235 elseif dp . parent then
236 settings . parent = dp . parent
237 end
238 end
239 setmetatableindex ( settings , resolve )
240end
241
242for k , v in next , data do
243 setmetatableindex ( v , resolve )
244end
245
246local function parentname ( name )
247 local dn = data [ name ]
248 return dn and dn . parent or name
249end
250
251function marks . relate ( name , chain )
252 local dn = data [ name ]
253 if dn and not dn . parent then
254 if chain and chain ~ = " " then
255 dn . chain = chain
256 local dc = data [ chain ]
257 if dc then
258 local children = dc . children
259 if not children then
260 children = { }
261 dc . children = children
262 end
263 children [ # children + 1 ] = name
264 end
265 elseif trace_set then
266 report_marks ( " error: invalid relation, name %a, chain %a " , name , chain )
267 end
268 end
269end
270
271local function resetchildren ( new , name )
272 local dn = data [ name ]
273 if dn and not dn . parent then
274 local children = dn . children
275 if children then
276 for i = 1 , # children do
277 local ci = children [ i ]
278 new [ ci ] = false
279 if trace_set then
280 report_marks ( " action %a, parent %a, child %a " , " reset " , name , ci )
281 end
282 resetchildren ( new , ci )
283 end
284 end
285 end
286end
287
288function marks . set ( name , value )
289 local dn = data [ name ]
290 if dn then
291 local child = name
292 local parent = dn . parent
293 if parent then
294 name = parent
295 dn = data [ name ]
296 end
297 dn . set = topofstack
298 if not dn . reset then
299 dn . reset = 0
300 end
301 local top = stack [ topofstack ]
302 local new = { }
303 if top then
304 for k , v in next , top do
305 local d = data [ k ]
306 local r = d . reset or 0
307 local s = d . set or 0
308 if r < = topofstack and s < r then
309 new [ k ] = false
310 else
311 new [ k ] = v
312 end
313 end
314 end
315 resetchildren ( new , name )
316 new [ name ] = value
317 topofstack = topofstack + 1
318 stack [ topofstack ] = new
319 if trace_set then
320 if name = = child then
321 report_marks ( " action %a, name %a, index %a, value %a " , " set " , name , topofstack , value )
322 else
323 report_marks ( " action %a, parent %a, child %a, index %a, value %a " , " set " , parent , child , topofstack , value )
324 end
325 end
326 texsetattribute ( " global " , a_marks , topofstack )
327 end
328end
329
330local function reset ( name )
331 if v_all then
332 if trace_set then
333 report_marks ( " action %a " , " reset all " )
334 end
335 stack = { }
336 for name , dn in next , data do
337 local parent = dn . parent
338 if parent then
339 dn . reset = 0
340 dn . set = 0
341 end
342 end
343 else
344 local dn = data [ name ]
345 if dn then
346 local parent = dn . parent
347 if parent then
348 name = parent
349 dn = data [ name ]
350 end
351 if trace_set then
352 report_marks ( " action %a, name %a, index %a " , " reset " , name , topofstack )
353 end
354 dn . reset = topofstack
355 local children = dn . children
356 if children then
357 for i = 1 , # children do
358 local ci = children [ i ]
359 reset ( ci )
360 end
361 end
362 end
363 end
364end
365
366marks . reset = reset
367
368function marks . get ( n , name , value )
369 local dn = data [ name ]
370 if dn then
371 name = dn . parent or name
372 local top = stack [ n ]
373 if top then
374 context ( top [ name ] )
375 end
376 end
377end
378
379function marks . show ( first , last )
380 if first and last then
381 for k = first , last do
382 local v = stack [ k ]
383 if v then
384 report_marks ( " % 4i: %s " , k , table . sequenced ( v ) )
385 end
386 end
387 else
388 for k , v in table . sortedpairs ( stack ) do
389 report_marks ( " % 4i: %s " , k , table . sequenced ( v ) )
390 end
391 end
392end
393
394local function resolve ( name , first , last , strict , quitonfalse , notrace )
395 local dn = data [ name ]
396 if dn then
397 local child = name
398 local parent = dn . parent
399 name = parent or child
400 dn = data [ name ]
401 local step , method
402 if first > last then
403 step , method = -1 , " bottom-up "
404 else
405 step , method = 1 , " top-down "
406 end
407 if trace_get and not notrace then
408 report_marks ( " action %a, strategy %a, name %a, parent %a, strict %a " , " request " , method , child , parent , strict or false )
409 end
410 if trace_details and not notrace then
411 marks . show ( first , last )
412 end
413 local r = dn . reset
414 local s = dn . set
415 if first < = last and first < = r then
416 if trace_get and not notrace then
417 report_marks ( " action %a, name %a, first %a, last %a, reset %a, index %a " , " reset first " , name , first , last , r , first )
418 end
419 elseif first > = last and last < = r then
420 if trace_get and not notrace then
421 report_marks ( " action %a, name %a, first %a, last %a, reset %a, index %a " , " reset last " , name , first , last , r , last )
422 end
423 elseif not stack [ first ] or not stack [ last ] then
424 if trace_get and not notrace then
425
426 report_marks ( " error: out of range, name %a, reset %a, index %a " , name , r , first )
427 end
428 elseif strict then
429 local top = stack [ first ]
430 local fullchain = dn . fullchain
431 if not fullchain or # fullchain = = 0 then
432 if trace_get and not notrace then
433 report_marks ( " warning: no full chain, trying again, name %a, first %a, last %a " , name , first , last )
434 end
435 return resolve ( name , first , last )
436 else
437 if trace_get and not notrace then
438 report_marks ( " found chain [ % => T ] " , fullchain )
439 end
440 local chaindata = { }
441 local chainlength = # fullchain
442 for i = 1 , chainlength do
443 local cname = fullchain [ i ]
444 if data [ cname ] . set > 0 then
445 local value = resolve ( cname , first , last , false , false , true )
446 if value = = " " then
447 if trace_get and not notrace then
448 report_marks ( " quitting chain, name %a, reset %a, start %a " , name , r , first )
449 end
450 return " "
451 else
452 chaindata [ i ] = value
453 end
454 end
455 end
456 if trace_get and not notrace then
457 report_marks ( " using chain [ % => T ] " , chaindata )
458 end
459 local value , index , found = resolve ( name , first , last , false , false , true )
460 if value ~ = " " then
461 if trace_get and not notrace then
462 report_marks ( " following chain [ % => T ] " , chaindata )
463 end
464 for i = 1 , chainlength do
465 local cname = fullchain [ i ]
466 if data [ cname ] . set > 0 and chaindata [ i ] ~ = found [ cname ] then
467 if trace_get and not notrace then
468 report_marks ( " quiting chain, name %a, reset %a, index %a " , name , r , first )
469 end
470 return " "
471 end
472 end
473 if trace_get and not notrace then
474 report_marks ( " found in chain, name %a, reset %a, start %a, index %a, value %a " , name , r , first , index , value )
475 end
476 return value , index , found
477 elseif trace_get and not notrace then
478 report_marks ( " not found, name %a, reset %a " , name , r )
479 end
480 end
481 else
482 for i = first , last , step do
483 local current = stack [ i ]
484 local value = current and current [ name ]
485 if value = = nil then
486
487 elseif value = = false then
488 if quitonfalse then
489 return " "
490 end
491 elseif value = = true then
492 if trace_get and not notrace then
493 report_marks ( " quitting steps, name %a, reset %a, start %a, index %a " , name , r , first , i )
494 end
495 return " "
496 elseif value ~ = " " then
497 if trace_get and not notrace then
498 report_marks ( " found in steps, name %a, reset %a, start %a, index %a, value %a " , name , r , first , i , value )
499 end
500 return value , i , current
501 end
502 end
503 if trace_get and not notrace then
504 report_marks ( " not found in steps, name %a, reset %a " , name , r )
505 end
506 end
507 end
508 return " "
509end
510
511
512
513local methods = { }
514
515local function doresolve ( name , rangename , swap , df , dl , strict )
516 local range = ranges [ rangename ] or ranges [ v_page ]
517 local first = range . first
518 local last = range . last
519 if trace_get then
520 report_marks ( " action %a, name %a, range %a, swap %a, first %a, last %a, df %a, dl %a, strict %a " ,
521 " resolving " , name , rangename , swap or false , first , last , df , dl , strict or false )
522 end
523 if swap then
524 first , last = last + df , first + dl
525 else
526 first , last = first + df , last + dl
527 end
528 local value , index , found = resolve ( name , first , last , strict )
529
530 return value , index , found
531end
532
533
534
535
536
537
538
539
540
541
542methods [ v_previous ] = function ( name , range ) return doresolve ( name , range , false , -1 , 0 , true ) end
543methods [ v_top ] = function ( name , range ) return doresolve ( name , range , false , 0 , 0 , true ) end
544methods [ v_bottom ] = function ( name , range ) return doresolve ( name , range , true , 0 , 0 , true ) end
545methods [ v_next ] = function ( name , range ) return doresolve ( name , range , true , 0 , 1 , true ) end
546
547methods [ v_previous_nocheck ] = function ( name , range ) return doresolve ( name , range , false , -1 , 0 , false ) end
548methods [ v_top_nocheck ] = function ( name , range ) return doresolve ( name , range , false , 0 , 0 , false ) end
549methods [ v_bottom_nocheck ] = function ( name , range ) return doresolve ( name , range , true , 0 , 0 , false ) end
550methods [ v_next_nocheck ] = function ( name , range ) return doresolve ( name , range , true , 0 , 1 , false ) end
551
552local function do_first ( name , range , check )
553 if trace_get then
554 report_marks ( " action %a, name %a, range %a " , " resolving first " , name , range )
555 end
556 local f_value , f_index , f_found = doresolve ( name , range , false , 0 , 0 , check )
557 if f_found then
558 if trace_get then
559 report_marks ( " action %a, name %a, range %a " , " resolving last " , name , range )
560 end
561 local l_value , l_index , l_found = doresolve ( name , range , true , 0 , 0 , check )
562 if l_found and l_index > f_index then
563 local name = parentname ( name )
564 for i = f_index , l_index , 1 do
565 local si = stack [ i ]
566 local sn = si [ name ]
567 if sn and sn ~ = false and sn ~ = true and sn ~ = " " and sn ~ = f_value then
568 if trace_get then
569 report_marks ( " action %a, name %a, range %a, index %a, value %a " , " resolving " , name , range , i , sn )
570 end
571 return sn , i , si
572 end
573 end
574 end
575 end
576 if trace_get then
577 report_marks ( " resolved, name %a, range %a, using first " , name , range )
578 end
579 return f_value , f_index , f_found
580end
581
582local function do_last ( name , range , check )
583 if trace_get then
584 report_marks ( " action %a, name %a, range %a " , " resolving last " , name , range )
585 end
586 local l_value , l_index , l_found = doresolve ( name , range , true , 0 , 0 , check )
587 if l_found then
588 if trace_get then
589 report_marks ( " action %a, name %a, range %a " , " resolving first " , name , range )
590 end
591 local f_value , f_index , f_found = doresolve ( name , range , false , 0 , 0 , check )
592 if f_found and l_index > f_index then
593 local name = parentname ( name )
594 for i = l_index , f_index , -1 do
595 local si = stack [ i ]
596 local sn = si [ name ]
597 if sn and sn ~ = false and sn ~ = true and sn ~ = " " and sn ~ = l_value then
598 if trace_get then
599 report_marks ( " action %a, name %a, range %a, index %a, value %a " , " resolving " , name , range , i , sn )
600 end
601 return sn , i , si
602 end
603 end
604 end
605 end
606 if trace_get then
607 report_marks ( " resolved, name %a, range %a, using first " , name , range )
608 end
609 return l_value , l_index , l_found
610end
611
612methods [ v_first ] = function ( name , range ) return do_first ( name , range , true ) end
613methods [ v_last ] = function ( name , range ) return do_last ( name , range , true ) end
614methods [ v_first_nocheck ] = function ( name , range ) return do_first ( name , range , false ) end
615methods [ v_last_nocheck ] = function ( name , range ) return do_last ( name , range , false ) end
616
617methods [ v_current ] = function ( name , range )
618 local top = stack [ topofstack ]
619 return top and top [ parentname ( name ) ] or " "
620end
621
622local function fetched ( name , range , method )
623 local value = ( methods [ method ] or methods [ v_first ] ) ( name , range ) or " "
624 if not trace_get then
625
626 elseif value = = " " then
627 report_marks ( " nothing fetched, name %a, range %a, method %a " , name , range , method )
628 else
629 report_marks ( " marking fetched, name %a, range %a, method %a, value %a " , name , range , method , value )
630 end
631 return value or " "
632end
633
634
635
636marks . fetched = fetched
637
638
639
640marks . tracers = marks . tracers or { }
641
642function marks . tracers . showtable ( )
643 context . starttabulate { " |l|l|l|lp|lp| " }
644 context . tabulaterowbold ( " name " , " parent " , " chain " , " children " , " fullchain " )
645 context . ML ( )
646 for k , v in table . sortedpairs ( data ) do
647 local parent = v . parent or " "
648 local chain = v . chain or " "
649 local children = v . children or { }
650 local fullchain = v . fullchain or { }
651 table . sort ( children )
652 context . tabulaterowtyp ( k , parent , chain , concat ( children , " " ) , concat ( fullchain , " " ) )
653 end
654 context . stoptabulate ( )
655end
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682 local ctx_separator = context . markingseparator
683 local ctx_command = context . markingcommand
684
685 local function fetchonemark ( name , range , method )
686 ctx_command ( name , fetched ( name , range , method ) )
687 end
688
689 local function fetchtwomarks ( name , range )
690 ctx_command ( name , fetched ( name , range , v_first ) )
691 ctx_separator ( name )
692 ctx_command ( name , fetched ( name , range , v_last ) )
693 end
694
695 local function fetchallmarks ( name , range )
696 ctx_command ( name , fetched ( name , range , v_previous ) )
697 ctx_separator ( name )
698 ctx_command ( name , fetched ( name , range , v_first ) )
699 ctx_separator ( name )
700 ctx_command ( name , fetched ( name , range , v_last ) )
701 end
702
703function marks . fetch ( name , range , method )
704 if trace_get then
705 report_marks ( " marking requested, name %a, range %a, method %a " , name , range , method )
706 end
707 if method = = " " or method = = v_default then
708 fetchonemark ( name , range , v_first )
709 elseif method = = v_both then
710 fetchtwomarks ( name , range )
711 elseif method = = v_all then
712 fetchallmarks ( name , range )
713 else
714 fetchonemark ( name , range , method )
715 end
716end
717
718function marks . fetchonemark ( name , range , method ) fetchonemark ( name , range , method ) end
719function marks . fetchtwomarks ( name , range ) fetchtwomarks ( name , range ) end
720function marks . fetchallmarks ( name , range ) fetchallmarks ( name , range ) end
721
722
723
724local pattern = lpeg . afterprefix ( " li:: " )
725
726function marks . title ( tag , n )
727 local listindex = lpegmatch ( pattern , n )
728 if listindex then
729 commands . savedlisttitle ( tag , listindex , " marking " )
730 else
731 context ( n )
732 end
733end
734
735function marks . number ( tag , n )
736 local listindex = lpegmatch ( pattern , n )
737 if listindex then
738 commands . savedlistnumber ( tag , listindex )
739 else
740
741 context ( n )
742 end
743end
744
745
746
747implement { name = " markingtitle " , actions = marks . title , arguments = " 2 strings " }
748implement { name = " markingnumber " , actions = marks . number , arguments = " 2 strings " }
749
750implement { name = " definemarking " , actions = marks . define , arguments = " 2 strings " }
751implement { name = " relatemarking " , actions = marks . relate , arguments = " 2 strings " }
752implement { name = " setmarking " , actions = marks . set , arguments = " 2 strings " }
753implement { name = " resetmarking " , actions = marks . reset , arguments = " string " }
754implement { name = " synchronizemarking " , actions = marks . synchronize , arguments = { " string " , " integer " , " string " } }
755implement { name = " getmarking " , actions = marks . fetch , arguments = " 3 strings " }
756implement { name = " fetchonemark " , actions = marks . fetchonemark , arguments = " 3 strings " }
757implement { name = " fetchtwomarks " , actions = marks . fetchtwomarks , arguments = " 2 strings " }
758implement { name = " fetchallmarks " , actions = marks . fetchallmarks , arguments = " 2 strings " }
759
760implement { name = " doifelsemarking " , actions = { marks . exists , commands . doifelse } , arguments = " string " }
761 |