1if not modules then modules = { } end modules ['strc-reg'] = {
2 version = 1.001,
3 comment = "companion to strc-reg.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
9local next, type, tonumber, rawget = next, type, tonumber, rawget
10local char, format, gmatch = string.char, string.format, string.gmatch
11local equal, concat, remove = table.are_equal, table.concat, table.remove
12local lpegmatch, P, C, Ct = lpeg.match, lpeg.P, lpeg.C, lpeg.Ct
13local allocate = utilities.storage.allocate
14
15local trace_registers = false trackers.register("structures.registers", function(v) trace_registers = v end)
16
17local report_registers = logs.reporter("structure","registers")
18
19local structures = structures
20local registers = structures.registers
21local helpers = structures.helpers
22local sections = structures.sections
23local documents = structures.documents
24local pages = structures.pages
25local references = structures.references
26
27local usedinternals = references.usedinternals
28
29local mappings = sorters.mappings
30local entries = sorters.entries
31local replacements = sorters.replacements
32
33local processors = typesetters.processors
34local splitprocessor = processors.split
35
36local texgetcount = tex.getcount
37
38local variables = interfaces.variables
39local v_forward = variables.forward
40local v_all = variables.all
41local v_no = variables.no
42local v_yes = variables.yes
43local v_packed = variables.packed
44local v_current = variables.current
45local v_previous = variables.previous
46local v_first = variables.first
47local v_last = variables.last
48local v_text = variables.text
49local v_section = variables.section
50
51local context = context
52local ctx_latelua = context.latelua
53
54local implement = interfaces.implement
55
56local matchingtilldepth = sections.matchingtilldepth
57local numberatdepth = sections.numberatdepth
58local currentlevel = sections.currentlevel
59local currentid = sections.currentid
60
61local touserdata = helpers.touserdata
62
63local internalreferences = references.internals
64local setinternalreference = references.setinternalreference
65
66local setmetatableindex = table.setmetatableindex
67
68local absmaxlevel = 5
69
70local h_prefixpage = helpers.prefixpage
71local h_prefixlastpage = helpers.prefixlastpage
72local h_title = helpers.title
73local h_prefix = helpers.prefix
74
75local ctx_startregisteroutput = context.startregisteroutput
76local ctx_stopregisteroutput = context.stopregisteroutput
77local ctx_startregistersection = context.startregistersection
78local ctx_stopregistersection = context.stopregistersection
79local ctx_startregisterentries = context.startregisterentries
80local ctx_stopregisterentries = context.stopregisterentries
81local ctx_startregisterentry = context.startregisterentry
82local ctx_stopregisterentry = context.stopregisterentry
83local ctx_startregisterpages = context.startregisterpages
84local ctx_stopregisterpages = context.stopregisterpages
85local ctx_startregisterseewords = context.startregisterseewords
86local ctx_stopregisterseewords = context.stopregisterseewords
87
88local ctx_registerentry = context.registerentry
89local ctx_registerseeword = context.registerseeword
90local ctx_registerpagerange = context.registerpagerange
91local ctx_registeronepage = context.registeronepage
92local ctx_registersection = context.registersection
93local ctx_registerpacked = context.registerpacked
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200local function filtercollected(names,criterium,number,collected,prevmode)
201 if not criterium or criterium == "" then
202 criterium = v_all
203 end
204 local data = documents.data
205 local numbers = data.numbers
206 local depth = data.depth
207 local hash = { }
208 local result = { }
209 local nofresult = 0
210 local all = not names or names == "" or names == v_all
211 local detail = nil
212 if not all then
213 for s in gmatch(names,"[^, ]+") do
214 hash[s] = true
215 end
216 end
217 if criterium == v_all or criterium == v_text then
218 for i=1,#collected do
219 local v = collected[i]
220 if all then
221 nofresult = nofresult + 1
222 result[nofresult] = v
223 else
224 local vmn = v.metadata and v.metadata.name
225 if hash[vmn] then
226 nofresult = nofresult + 1
227 result[nofresult] = v
228 end
229 end
230 end
231 elseif criterium == v_current then
232 local collectedsections = sections.collected
233 for i=1,#collected do
234 local v = collected[i]
235 local sectionnumber = collectedsections[v.references.section]
236 if sectionnumber then
237 local cnumbers = sectionnumber.numbers
238 if prevmode then
239 if (all or hash[v.metadata.name]) and #cnumbers >= depth then
240 local ok = true
241 for d=1,depth do
242 if not (cnumbers[d] == numbers[d]) then
243 ok = false
244 break
245 end
246 end
247 if ok then
248 nofresult = nofresult + 1
249 result[nofresult] = v
250 end
251 end
252 else
253 if (all or hash[v.metadata.name]) and #cnumbers > depth then
254 local ok = true
255 for d=1,depth do
256 local cnd = cnumbers[d]
257 if not (cnd == 0 or cnd == numbers[d]) then
258 ok = false
259 break
260 end
261 end
262 if ok then
263 nofresult = nofresult + 1
264 result[nofresult] = v
265 end
266 end
267 end
268 end
269 end
270 elseif criterium == v_previous then
271 local collectedsections = sections.collected
272 for i=1,#collected do
273 local v = collected[i]
274 local sectionnumber = collectedsections[v.references.section]
275 if sectionnumber then
276 local cnumbers = sectionnumber.numbers
277 if (all or hash[v.metadata.name]) and #cnumbers >= depth then
278 local ok = true
279 if prevmode then
280 for d=1,depth do
281 if not (cnumbers[d] == numbers[d]) then
282 ok = false
283 break
284 end
285 end
286 else
287 for d=1,depth do
288 local cnd = cnumbers[d]
289 if not (cnd == 0 or cnd == numbers[d]) then
290 ok = false
291 break
292 end
293 end
294 end
295 if ok then
296 nofresult = nofresult + 1
297 result[nofresult] = v
298 end
299 end
300 end
301 end
302 elseif criterium == variables["local"] then
303 if sections.autodepth(data.numbers) == 0 then
304 return filtercollected(names,v_all,number,collected,prevmode)
305 else
306 return filtercollected(names,v_current,number,collected,prevmode)
307 end
308 else
309
310
311 local depth = sections.getlevel(criterium)
312 local number = tonumber(number) or numberatdepth(depth) or 0
313 if trace_registers then
314 detail = format("depth: %s, number: %s, numbers: %s, startset: %s",depth,number,concat(sections.numbers(),".",1,depth),#collected)
315 end
316 if number > 0 then
317 for i=1,#collected do
318 local v = collected[i]
319 local r = v.references
320 if r then
321 local sectionnumber = sections.collected[r.section]
322 if sectionnumber then
323 local metadata = v.metadata
324 local cnumbers = sectionnumber.numbers
325 if cnumbers then
326 if (all or hash[metadata.name or false]) and #cnumbers >= depth and matchingtilldepth(depth,cnumbers) then
327 nofresult = nofresult + 1
328 result[nofresult] = v
329 end
330 end
331 end
332 end
333 end
334 end
335 end
336 if trace_registers then
337 if detail then
338 report_registers("criterium %a, detail %a, found %a",criterium,detail,#result)
339 else
340 report_registers("criterium %a, detail %a, found %a",criterium,nil,#result)
341 end
342 end
343 return result
344end
345
346local tobesaved = allocate()
347local collected = allocate()
348
349registers.collected = collected
350registers.tobesaved = tobesaved
351registers.filtercollected = filtercollected
352
353
354
355
356
357
358
359
360local function checker(t,k)
361 local v = {
362 metadata = {
363 language = 'en',
364 sorted = false,
365 class = class,
366 },
367 entries = { },
368 }
369 t[k] = v
370 return v
371end
372
373local function initializer()
374 tobesaved = registers.tobesaved
375 collected = registers.collected
376 setmetatableindex(tobesaved,checker)
377 setmetatableindex(collected,checker)
378 local usedinternals = references.usedinternals
379 for name, list in next, collected do
380 local entries = list.entries
381 if not list.metadata.notsaved then
382 for e=1,#entries do
383 local entry = entries[e]
384 local r = entry.references
385 if r then
386 local internal = r and r.internal
387 if internal then
388 internalreferences[internal] = entry
389 usedinternals[internal] = r.used
390 end
391 end
392 end
393 end
394 end
395
396end
397
398local function finalizer()
399 local flaginternals = references.flaginternals
400 local usedviews = references.usedviews
401 for k, v in next, tobesaved do
402 local entries = v.entries
403 if entries then
404 for i=1,#entries do
405 local r = entries[i].references
406 if r then
407 local i = r.internal
408 local f = flaginternals[i]
409 if f then
410 r.used = usedviews[i] or true
411 end
412 end
413 end
414 end
415 end
416end
417
418job.register('structures.registers.collected', tobesaved, initializer, finalizer)
419
420setmetatableindex(tobesaved,checker)
421setmetatableindex(collected,checker)
422
423local function defineregister(class,method)
424 local d = tobesaved[class]
425 if method == v_forward then
426 d.metadata.notsaved = true
427 end
428end
429
430registers.define = defineregister
431registers.setmethod = defineregister
432
433implement {
434 name = "defineregister",
435 actions = defineregister,
436 arguments = "2 strings",
437}
438
439implement {
440 name = "setregistermethod",
441 actions = defineregister,
442 arguments = "2 strings",
443}
444
445
446local p_s = P("+")
447local p_e = P("&") * (1-P(";"))^0 * P(";")
448local p_r = C((p_e + (1-p_s))^0)
449
450local entrysplitter_xml = Ct(p_r * (p_s * p_r)^0)
451local entrysplitter_tex = lpeg.tsplitat('+')
452
453local tagged = { }
454
455
456
457local function preprocessentries(rawdata)
458 local entries = rawdata.entries
459 if entries then
460 local processors = rawdata.processors
461 local et = entries.entries
462 local kt = entries.keys
463 local pt = entries.processors
464 local entryproc = processors and processors.entry
465 local pageproc = processors and processors.page
466 local coding = rawdata.metadata.coding
467 if entryproc == "" then
468 entryproc = nil
469 end
470 if pageproc == "" then
471 pageproc = nil
472 end
473 if not et then
474 local p, e = splitprocessor(entries.entry or "")
475 if p then
476 entryproc = p
477 end
478 et = lpegmatch(coding == "xml" and entrysplitter_xml or entrysplitter_tex,e)
479 end
480 if not kt then
481 local p, k = splitprocessor(entries.key or "")
482 if p then
483 pageproc = p
484 end
485 kt = lpegmatch(coding == "xml" and entrysplitter_xml or entrysplitter_tex,k)
486 end
487 if not pt then
488 pt = { }
489 end
490
491 entries = { }
492 local ok = false
493 for k=#et,1,-1 do
494 local etk = et[k]
495 local ktk = kt[k]
496 local ptk = pt[k]
497 if not ok and etk == "" then
498 entries[k] = nil
499 else
500 entries[k] = { etk or "", ktk ~= "" and ktk or false, ptk ~= "" and ptk or false }
501 ok = true
502 end
503 end
504 rawdata.list = entries
505 if pageproc or entryproc then
506 rawdata.processors = { entryproc, pageproc }
507 end
508 rawdata.entries = nil
509 end
510 local seeword = rawdata.seeword
511 if seeword then
512 seeword.processor, seeword.text = splitprocessor(seeword.text or "")
513 end
514end
515
516local function storeregister(rawdata)
517 local references = rawdata.references
518 local metadata = rawdata.metadata
519
520 if not metadata then
521 metadata = { }
522 rawdata.metadata = metadata
523 end
524
525 if not metadata.kind then
526 metadata.kind = "entry"
527 end
528
529
530 if not metadata.catcodes then
531 metadata.catcodes = tex.catcodetable
532 end
533
534 local name = metadata.name
535 local notsaved = tobesaved[name].metadata.notsaved
536
537 if not references then
538 references = { }
539 rawdata.references = references
540 end
541
542 local internal = references.internal
543 if not internal then
544 internal = texgetcount("locationcount")
545 references.internal = internal
546 end
547
548 if notsaved then
549 usedinternals[internal] = references.used
550 end
551
552 if not references.realpage then
553 references.realpage = 0
554 end
555
556 local userdata = rawdata.userdata
557 if userdata then
558 rawdata.userdata = touserdata(userdata)
559 end
560
561 references.section = currentid()
562 metadata.level = currentlevel()
563
564 local data = notsaved and collected[name] or tobesaved[name]
565 local entries = data.entries
566 internalreferences[internal] = rawdata
567 preprocessentries(rawdata)
568 entries[#entries+1] = rawdata
569 local label = references.label
570 if label and label ~= "" then
571 tagged[label] = #entries
572 else
573 references.label = nil
574 end
575 return #entries
576end
577
578local function enhanceregister(specification)
579 local name = specification.name
580 local n = specification.n
581 local saved = tobesaved[name]
582 local data = saved.metadata.notsaved and collected[name] or saved
583 local entry = data.entries[n]
584 if entry then
585 entry.references.realpage = texgetcount("realpageno")
586 end
587end
588
589
590
591local function extendregister(name,tag,rawdata)
592 if type(tag) == "string" then
593 tag = tagged[tag]
594 end
595 if tag then
596 local data = tobesaved[name].metadata.notsaved and collected[name] or tobesaved[name]
597 local entry = data.entries[tag]
598 if entry then
599 local references = entry.references
600 references.lastrealpage = texgetcount("realpageno")
601 references.lastsection = currentid()
602 if rawdata then
603 local userdata = rawdata.userdata
604 if userdata then
605 rawdata.userdata = touserdata(userdata)
606 end
607 if rawdata.entries then
608 preprocessentries(rawdata)
609 end
610 local metadata = rawdata.metadata
611 if metadata and not metadata.catcodes then
612 metadata.catcodes = tex.catcodetable
613 end
614 for k, v in next, rawdata do
615 local rk = references[k]
616 if not rk then
617 references[k] = v
618 else
619 for kk, vv in next, v do
620 if type(vv) == "table" then
621 if next(vv) then
622 rk[kk] = vv
623 end
624 elseif vv ~= "" then
625 rk[kk] = vv
626 end
627 end
628 end
629 end
630 end
631 end
632 end
633end
634
635registers.store = storeregister
636registers.enhance = enhanceregister
637registers.extend = extendregister
638
639function registers.get(tag,n)
640 local list = tobesaved[tag]
641 return list and list.entries[n]
642end
643
644implement {
645 name = "enhanceregister",
646 arguments = { "string", "integer" },
647 actions = function(name,n)
648 enhanceregister { name = name, n = n }
649 end,
650}
651
652implement {
653 name = "deferredenhanceregister",
654 arguments = { "string", "integer" },
655 protected = true,
656 actions = function(name,n)
657 ctx_latelua { action = enhanceregister, name = name, n = n }
658 end,
659}
660
661implement {
662 name = "extendregister",
663 actions = extendregister,
664 arguments = "2 strings",
665}
666
667implement {
668 name = "storeregister",
669
670
671
672
673
674 actions = { storeregister, context },
675 arguments = {
676 {
677 { "metadata", {
678 { "kind" },
679 { "name" },
680 { "coding" },
681 { "level", "integer" },
682 { "catcodes", "integer" },
683 { "own" },
684 { "xmlroot" },
685 { "xmlsetup" },
686 }
687 },
688 { "entries", {
689 { "entries", "list" },
690 { "keys", "list" },
691 { "processors", "list" },
692 { "entry" },
693 { "key" },
694 { "processor" },
695 }
696 },
697 { "references", {
698 { "internal", "integer" },
699 { "section", "integer" },
700 { "view" },
701 { "label" },
702 }
703 },
704 { "seeword", {
705 { "text" },
706 }
707 },
708 { "processors", {
709 { "entry" },
710 { "key" },
711 { "page" },
712 }
713 },
714 { "userdata" },
715 }
716 }
717}
718
719
720
721local compare = sorters.comparers.basic
722
723function registers.compare(a,b)
724 local result = compare(a,b)
725 if result ~= 0 then
726 return result
727 else
728 local ka = a.metadata.kind
729 local kb = b.metadata.kind
730 if ka == kb then
731 local ra = a.references
732 local rb = b.references
733 local pa = ra.realpage
734 local pb = rb.realpage
735 if not pa or not pb then
736 return 0
737 elseif pa < pb then
738 return -1
739 elseif pa > pb then
740 return 1
741 else
742
743
744 local ia = ra.internal
745 local ib = rb.internal
746 if not ia or not ib then
747 return 0
748 elseif ia < ib then
749 return -1
750 elseif ia > ib then
751 return 1
752 else
753 return 0
754 end
755 end
756 elseif ka == "see" then
757 return 1
758 elseif kb == "see" then
759 return -1
760 end
761 end
762 return 0
763end
764
765function registers.filter(data,options)
766 data.result = registers.filtercollected(nil,options.criterium,options.number,data.entries,true)
767end
768
769local seeindex = 0
770
771
772
773
774
775local function crosslinkseewords(result,check)
776
777 local seewords = { }
778 for i=1,#result do
779 local data = result[i]
780 local seeword = data.seeword
781 if seeword then
782 local seetext = seeword.text
783 if seetext and not seewords[seetext] then
784 seeindex = seeindex + 1
785 seewords[seetext] = seeindex
786 if trace_registers then
787 report_registers("see word %03i: %s",seeindex,seetext)
788 end
789 end
790 end
791 end
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812 local entries = { }
813 local keywords = { }
814 local seeparents = { }
815 for i=1,#result do
816 local data = result[i]
817 local word = data.list
818 local size = #word
819 if data.seeword then
820
821 size = size - 1
822 end
823 for i=1,size do
824 local w = word[i]
825 local e = w[1]
826 local k = w[2] or e
827 entries [i] = e
828 keywords[i] = k
829 end
830
831 local okay, seeindex
832 for n=size,1,-1 do
833 okay = concat(keywords,"+",1,n)
834 seeindex = seewords[okay]
835
836 if seeindex then
837 break
838 end
839 okay = concat(entries,"+",1,n)
840 seeindex = seewords[okay]
841 if seeindex then
842 break
843 end
844 end
845 if seeindex then
846 seeparents[okay] = data
847 data.references.seeparent = seeindex
848 if trace_registers then
849 report_registers("see parent %03i: %s",seeindex,okay)
850 end
851 end
852 end
853
854
855 for i=1,#result do
856 local data = result[i]
857 local seeword = data.seeword
858 if seeword then
859 local text = seeword.text
860 if text then
861 local seeparent = seeparents[text]
862 if seeparent then
863 local seeindex = seewords[text]
864 data.references.seeindex = seeindex
865 if trace_registers then
866 report_registers("see crosslink %03i: %s",seeindex,text)
867 end
868 seeword.valid = true
869 elseif check then
870 report_registers("invalid crosslink : %s, %s",text,"ignored")
871 seeword.valid = false
872 else
873 report_registers("invalid crosslink : %s, %s",text,"passed")
874 seeword.valid = true
875 end
876 end
877 end
878 end
879end
880
881local function removeemptyentries(result)
882 local i, n, m = 1, #result, 0
883 while i <= n do
884 local entry = result[i]
885 if #entry.list == 0 or #entry.split == 0 then
886 remove(result,i)
887 n = n - 1
888 m = m + 1
889 else
890 i = i + 1
891 end
892 end
893 if m > 0 then
894 report_registers("%s empty entries removed in register",m)
895 end
896end
897
898function registers.prepare(data,options)
899
900 local strip = sorters.strip
901 local splitter = sorters.splitters.utf
902 local result = data.result
903 if result then
904 local seeprefix = char(0)
905 for i=1, #result do
906 local entry = result[i]
907 local split = { }
908 local list = entry.list
909 if list then
910 if entry.seeword then
911
912 list[#list+1] = { seeprefix .. strip(entry.seeword.text) }
913 end
914 for l=1,#list do
915 local ll = list[l]
916 local word = ll[1]
917 local key = ll[2]
918 if not key or key == "" then
919 key = word
920 end
921 split[l] = splitter(strip(key))
922 end
923 end
924 entry.split = split
925 end
926 removeemptyentries(result)
927 crosslinkseewords(result,options.check ~= v_no)
928 end
929end
930
931function registers.sort(data,options)
932
933
934
935 sorters.sort(data.result,registers.compare)
936
937end
938
939function registers.unique(data,options)
940 local result = { }
941 local nofresult = 0
942 local prev = nil
943 local dataresult = data.result
944 local bysection = options.pagemethod == v_section
945 for k=1,#dataresult do
946 local v = dataresult[k]
947 if prev then
948 local vr = v.references
949 local pr = prev.references
950 if not equal(prev.list,v.list) then
951
952 elseif bysection and vr.section == pr.section then
953 v = nil
954
955 elseif pr.realpage ~= vr.realpage then
956
957 else
958 local pl = pr.lastrealpage
959 local vl = vr.lastrealpage
960 if pl or vl then
961 if not vl then
962
963 elseif not pl then
964
965 elseif pl ~= vl then
966
967 else
968 v = nil
969 end
970 else
971 v = nil
972 end
973 end
974 end
975 if v then
976 nofresult = nofresult + 1
977 result[nofresult] = v
978 prev = v
979 end
980 end
981 data.result = result
982end
983
984function registers.finalize(data,options)
985 local result = data.result
986 data.metadata.nofsorted = #result
987 local split = { }
988 local nofsplit = 0
989 local lasttag = nil
990 local done = nil
991 local nofdone = 0
992 local firstofsplit = sorters.firstofsplit
993 for k=1,#result do
994 local v = result[k]
995 local entry, tag = firstofsplit(v)
996 if tag ~= lasttag then
997 if trace_registers then
998 report_registers("splitting at %a",tag)
999 end
1000 done = { }
1001 nofdone = 0
1002 nofsplit = nofsplit + 1
1003 lasttag = tag
1004 split[nofsplit] = { tag = tag, data = done }
1005 end
1006 nofdone = nofdone + 1
1007 done[nofdone] = v
1008 end
1009 data.result = split
1010end
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029local function analyzeregister(class,options)
1030 local data = rawget(collected,class)
1031 if not data then
1032 local list = utilities.parsers.settings_to_array(class)
1033 local entries = { }
1034 local metadata = false
1035 for i=1,#list do
1036 local l = list[i]
1037 local d = collected[l]
1038 local e = d.entries
1039 for i=1,#e do
1040 entries[#entries+1] = e[i]
1041 end
1042 if not metadata then
1043 metadata = d.metadata
1044 end
1045 end
1046 data = {
1047 metadata = metadata or { },
1048 entries = entries,
1049 }
1050 collected[class] = data
1051 end
1052 if data and data.entries then
1053 options = options or { }
1054 sorters.setlanguage(options.language,options.method,options.numberorder)
1055 registers.filter(data,options)
1056 registers.prepare(data,options)
1057 registers.sort(data,options)
1058 registers.unique(data,options)
1059 registers.finalize(data,options)
1060 data.metadata.sorted = true
1061 return data.metadata.nofsorted or 0
1062 else
1063 return 0
1064 end
1065end
1066
1067registers.analyze = analyzeregister
1068
1069implement {
1070 name = "analyzeregister",
1071 actions = { analyzeregister, context },
1072 arguments = {
1073 "string",
1074 {
1075 { "language" },
1076 { "method" },
1077 { "numberorder" },
1078 { "compress" },
1079 { "criterium" },
1080 { "pagenumber", "boolean" },
1081 }
1082 }
1083}
1084
1085
1086
1087function registers.userdata(index,name)
1088 local data = references.internals[tonumber(index)]
1089 return data and data.userdata and data.userdata[name] or nil
1090end
1091
1092implement {
1093 name = "registeruserdata",
1094 actions = { registers.userdata, context },
1095 arguments = { "integer", "string" }
1096}
1097
1098
1099
1100local function pagerange(f_entry,t_entry,is_last,prefixspec,pagespec)
1101 local fer = f_entry.references
1102 local ter = t_entry.references
1103 ctx_registerpagerange(
1104 f_entry.metadata.name or "",
1105 f_entry.processors and f_entry.processors[2] or "",
1106 fer.internal or 0,
1107 fer.realpage or 0,
1108 function()
1109 h_prefixpage(f_entry,prefixspec,pagespec)
1110 end,
1111 ter.internal or 0,
1112 ter.lastrealpage or ter.realpage or 0,
1113 function()
1114 if is_last then
1115 h_prefixlastpage(t_entry,prefixspec,pagespec)
1116 else
1117 h_prefixpage (t_entry,prefixspec,pagespec)
1118 end
1119 end
1120 )
1121end
1122
1123local function pagenumber(entry,prefixspec,pagespec)
1124 local er = entry.references
1125 ctx_registeronepage(
1126 entry.metadata.name or "",
1127 entry.processors and entry.processors[2] or "",
1128 er.internal or 0,
1129 er.realpage or 0,
1130 function() h_prefixpage(entry,prefixspec,pagespec) end
1131 )
1132end
1133
1134local function packed(f_entry,t_entry)
1135 local fer = f_entry.references
1136 local ter = t_entry.references
1137 ctx_registerpacked(
1138 fer.internal or 0,
1139 ter.internal or 0
1140 )
1141end
1142
1143local function collapsedpage(pages)
1144 for i=2,#pages do
1145 local first = pages[i-1]
1146 local second = pages[i]
1147 local first_first = first[1]
1148 local first_last = first[2]
1149 local second_first = second[1]
1150 local second_last = second[2]
1151 local first_last_pn = first_last .references.realpage
1152 local second_first_pn = second_first.references.realpage
1153 local second_last_pn = second_last .references.realpage
1154 local first_last_last = first_last .references.lastrealpage
1155 local second_first_last = second_first.references.lastrealpage
1156 if first_last_last then
1157 first_last_pn = first_last_last
1158 if second_first == second_last and second_first_pn <= first_last_pn then
1159
1160 remove(pages,i)
1161 return true
1162 elseif second_first == second_last and second_first_pn > first_last_pn then
1163
1164 pages[i-1] = { first_first, second_last }
1165 remove(pages,i)
1166 return true
1167 elseif second_last_pn < first_last_pn then
1168
1169 remove(pages,i)
1170 return true
1171 elseif first_last_pn < second_last_pn then
1172
1173 pages[i-1] = { first_first, second_last }
1174 remove(pages,i)
1175 return true
1176 elseif first_last_pn + 1 == second_first_pn and second_last_pn > first_last_pn then
1177
1178 pages[i-1] = { first_first, second_last }
1179 remove(pages,i)
1180 return true
1181 elseif second_first.references.lastrealpage then
1182
1183 pages[i-1] = { first_first, second_last }
1184 remove(pages,i)
1185 return true
1186 end
1187 elseif second_first_last then
1188 second_first_pn = second_first_last
1189 if first_last_pn == second_first_pn then
1190
1191 pages[i-1] = { first_first, second_last }
1192 remove(pages,i)
1193 return true
1194 end
1195 elseif first_last_pn == second_first_pn then
1196
1197 pages[i-1] = { first_last, second_last }
1198 remove(pages,i)
1199 return true
1200 end
1201 end
1202 return false
1203end
1204
1205local function collapsepages(pages)
1206 while collapsedpage(pages) do end
1207 return #pages
1208end
1209
1210
1211
1212function registers.flush(data,options,prefixspec,pagespec)
1213 local compress = options.compress
1214 local collapse_singles = compress == v_yes
1215 local collapse_ranges = compress == v_all
1216 local collapse_packed = compress == v_packed
1217 local show_page_number = options.pagenumber ~= false
1218 local bysection = options.pagemethod == v_section
1219 local result = data.result
1220 local maxlevel = 0
1221
1222 for i=1,#result do
1223 local data = result[i].data
1224 for d=1,#data do
1225 local m = #data[d].list
1226 if m > maxlevel then
1227 maxlevel = m
1228 end
1229 end
1230 end
1231 if maxlevel > absmaxlevel then
1232 maxlevel = absmaxlevel
1233 report_registers("limiting level to %a",maxlevel)
1234 end
1235
1236 ctx_startregisteroutput()
1237 local done = { }
1238 local started = false
1239 for i=1,#result do
1240
1241 local sublist = result[i]
1242
1243 for i=1,maxlevel do
1244 done[i] = false
1245 end
1246 local data = sublist.data
1247 local d = 0
1248 local n = 0
1249 ctx_startregistersection(sublist.tag)
1250 for d=1,#data do
1251 local entry = data[d]
1252 if entry.metadata.kind == "see" then
1253 local list = entry.list
1254 if #list > 1 then
1255 list[#list] = nil
1256 else
1257
1258
1259 end
1260 end
1261 end
1262
1263
1264
1265 while d < #data do
1266 d = d + 1
1267 local entry = data[d]
1268
1269 local metadata = entry.metadata
1270 local kind = metadata.kind
1271 local list = entry.list
1272 local e = { false, false, false }
1273 for i=3,maxlevel do
1274 e[i] = false
1275 end
1276 for i=1,maxlevel do
1277 local li = list[i]
1278 if list[i] then
1279 e[i] = li[1]
1280 end
1281 if e[i] == done[i] then
1282
1283 elseif not e[i] then
1284
1285
1286 done[i] = false
1287 for j=i+1,maxlevel do
1288 done[j] = false
1289 end
1290 elseif e[i] == "" then
1291 done[i] = false
1292 for j=i+1,maxlevel do
1293 done[j] = false
1294 end
1295 else
1296 done[i] = e[i]
1297 for j=i+1,maxlevel do
1298 done[j] = false
1299 end
1300 if started then
1301 ctx_stopregisterentry()
1302 started = false
1303 end
1304 if n == i then
1305
1306
1307 else
1308 while n > i do
1309 n = n - 1
1310 ctx_stopregisterentries()
1311 end
1312 while n < i do
1313 n = n + 1
1314 ctx_startregisterentries(n)
1315 end
1316 end
1317 local references = entry.references
1318 local processors = entry.processors
1319 local internal = references.internal or 0
1320 local seeparent = references.seeparent or ""
1321 local processor = (li and li[3]) or (processors and processors[1]) or ""
1322
1323
1324 ctx_startregisterentry(0)
1325 started = true
1326 if metadata then
1327 ctx_registerentry(metadata.name or "",processor,internal,seeparent,function() h_title(e[i],metadata) end)
1328 else
1329
1330 ctx_registerentry("",processor,internal,seeindex,e[i])
1331 end
1332 end
1333 end
1334
1335 local function case_1()
1336
1337
1338 local first, last, prev, pages, dd, nofpages = entry, nil, entry, { }, d, 0
1339 while dd < #data do
1340 dd = dd + 1
1341 local next = data[dd]
1342 if next and next.metadata.kind == "see" then
1343 dd = dd - 1
1344 break
1345 else
1346 local el, nl = entry.list, next.list
1347 if not equal(el,nl) then
1348 dd = dd - 1
1349
1350 break
1351 elseif next.references.lastrealpage then
1352 nofpages = nofpages + 1
1353 pages[nofpages] = first and { first, last or first } or { entry, entry }
1354 nofpages = nofpages + 1
1355 pages[nofpages] = { next, next }
1356 first, last, prev = nil, nil, nil
1357 elseif not first then
1358 first, prev = next, next
1359 elseif next.references.realpage - prev.references.realpage == 1 then
1360 last, prev = next, next
1361 else
1362 nofpages = nofpages + 1
1363 pages[nofpages] = { first, last or first }
1364 first, last, prev = next, nil, next
1365 end
1366 end
1367 end
1368 if first then
1369 nofpages = nofpages + 1
1370 pages[nofpages] = { first, last or first }
1371 end
1372 if collapse_ranges and nofpages > 1 then
1373 nofpages = collapsepages(pages)
1374 end
1375 if nofpages > 0 then
1376 d = dd
1377 for p=1,nofpages do
1378 local first, last = pages[p][1], pages[p][2]
1379 if first == last then
1380 if first.references.lastrealpage then
1381 pagerange(first,first,true,prefixspec,pagespec)
1382 else
1383 pagenumber(first,prefixspec,pagespec)
1384 end
1385 elseif last.references.lastrealpage then
1386 pagerange(first,last,true,prefixspec,pagespec)
1387 else
1388 pagerange(first,last,false,prefixspec,pagespec)
1389 end
1390 end
1391 elseif entry.references.lastrealpage then
1392 pagerange(entry,entry,true,prefixspec,pagespec)
1393 else
1394 pagenumber(entry,prefixspec,pagespec)
1395 end
1396 end
1397
1398 local function case_2()
1399 local first = nil
1400 local last = nil
1401 while true do
1402 if not first then
1403 first = entry
1404 end
1405 last = entry
1406 if d == #data then
1407 break
1408 else
1409 d = d + 1
1410 local next = data[d]
1411 if next.metadata.kind == "see" or not equal(entry.list,next.list) then
1412 d = d - 1
1413 break
1414 else
1415 entry = next
1416 end
1417 end
1418 end
1419 packed(first,last)
1420 end
1421
1422 local function case_3()
1423 while true do
1424 if entry.references.lastrealpage then
1425 pagerange(entry,entry,true,prefixspec,pagespec)
1426 else
1427 pagenumber(entry,prefixspec,pagespec)
1428 end
1429 if d == #data then
1430 break
1431 else
1432 d = d + 1
1433 local next = data[d]
1434 if next.metadata.kind == "see" or not equal(entry.list,next.list) then
1435 d = d - 1
1436 break
1437 else
1438 entry = next
1439 end
1440 end
1441 end
1442 end
1443
1444 local function case_4()
1445 local t = { }
1446 local nt = 0
1447 while true do
1448 if entry.seeword and entry.seeword.valid then
1449 nt = nt + 1
1450 t[nt] = entry
1451 end
1452 if d == #data then
1453 break
1454 else
1455 d = d + 1
1456 local next = data[d]
1457 if next.metadata.kind ~= "see" or not equal(entry.list,next.list) then
1458 d = d - 1
1459 break
1460 else
1461 entry = next
1462 end
1463 end
1464 end
1465 for i=1,nt do
1466 local entry = t[i]
1467 local seeword = entry.seeword
1468 local seetext = seeword.text or ""
1469 local processor = seeword.processor or (entry.processors and entry.processors[1]) or ""
1470 local seeindex = entry.references.seeindex or ""
1471 ctx_registerseeword(
1472 metadata.name or "",
1473 i,
1474 nt,
1475 processor,
1476 0,
1477 seeindex,
1478 function() h_title(seetext,metadata) end
1479 )
1480 end
1481 end
1482
1483 local function case_5()
1484 local first = d
1485 while true do
1486 if d == #data then
1487 break
1488 else
1489 d = d + 1
1490 local next = data[d]
1491 if next.metadata.kind == "see" or not equal(entry.list,next.list) then
1492 d = d - 1
1493 break
1494 else
1495 entry = next
1496 end
1497 end
1498 end
1499 local last = d
1500 local n = last - first + 1
1501 local i = 0
1502 local name = metadata.name or ""
1503 local processor = entry.processors and entry.processors[1] or ""
1504 for e=first,last do
1505 local d = data[e]
1506 local sectionindex = d.references.internal or 0
1507 i = i + 1
1508 ctx_registersection(
1509 name,
1510 i,
1511 n,
1512 processor,
1513 0,
1514 sectionindex,
1515 function() h_prefix(d,prefixspec,true) end
1516 )
1517 end
1518 end
1519
1520 if kind == "entry" then
1521 if show_page_number then
1522 ctx_startregisterpages()
1523 if bysection then
1524 case_5()
1525 elseif collapse_singles or collapse_ranges then
1526 case_1()
1527 elseif collapse_packed then
1528 case_2()
1529 else
1530 case_3()
1531 end
1532 ctx_stopregisterpages()
1533 end
1534 elseif kind == "see" then
1535 ctx_startregisterseewords()
1536 case_4()
1537 ctx_stopregisterseewords()
1538 end
1539
1540 end
1541
1542 if started then
1543 ctx_stopregisterentry()
1544 started = false
1545 end
1546 while n > 0 do
1547 ctx_stopregisterentries()
1548 n = n - 1
1549 end
1550 ctx_stopregistersection()
1551 end
1552 ctx_stopregisteroutput()
1553
1554 data.result = nil
1555 data.metadata.sorted = false
1556
1557 local entries = data.entries
1558 for i=1,#entries do
1559 entries[i].split = nil
1560 end
1561
1562end
1563
1564function registers.process(class,...)
1565 if analyzeregister(class,...) > 0 then
1566 local data = collected[class]
1567 registers.flush(data,...)
1568 end
1569end
1570
1571implement {
1572 name = "processregister",
1573 actions = registers.process,
1574 arguments = {
1575 "string",
1576 {
1577 { "language" },
1578 { "method" },
1579 { "numberorder" },
1580 { "compress" },
1581 { "criterium" },
1582 { "check" },
1583 { "pagemethod" },
1584 { "pagenumber", "boolean" },
1585 },
1586 {
1587 { "separatorset" },
1588 { "conversionset" },
1589 { "starter" },
1590 { "stopper" },
1591 { "set" },
1592 { "segments" },
1593 { "connector" },
1594 },
1595 {
1596 { "prefix" },
1597 { "separatorset" },
1598 { "conversionset" },
1599 { "starter" },
1600 { "stopper" },
1601 { "segments" },
1602 }
1603 }
1604}
1605
1606
1607
1608function registers.findinternal(tag,where,n)
1609
1610 local current = collected[tag]
1611 if not current then
1612 return 0
1613 end
1614 local entries = current.entries
1615 if not entries then
1616 return 0
1617 end
1618 local entry = entries[n]
1619 if not entry then
1620 return 0
1621 end
1622 local list = entry.list
1623 local size = #list
1624
1625 local start, stop, step
1626 if where == v_previous then
1627 start = n - 1
1628 stop = 1
1629 step = -1
1630 elseif where == v_first then
1631 start = 1
1632 stop = #entries
1633 step = 1
1634 elseif where == v_last then
1635 start = #entries
1636 stop = 1
1637 step = -1
1638 else
1639 start = n + 1
1640 stop = #entries
1641 step = 1
1642 end
1643
1644 for i=start,stop,step do
1645 local r = entries[i]
1646 local l = r.list
1647 local s = #l
1648 if s == size then
1649 local ok = true
1650 for i=1,size do
1651 if list[i][1] ~= l[i][1] then
1652 ok = false
1653 break
1654 end
1655 end
1656 if ok then
1657 return r.references.internal or 0
1658 end
1659 end
1660 end
1661 return 0
1662end
1663
1664interfaces.implement {
1665 name = "findregisterinternal",
1666 arguments = { "string", "string", "integer" },
1667 actions = { registers.findinternal, context },
1668}
1669 |