1if not modules then modules = { } end modules ['l-table'] = {
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
9local type, next, tostring, tonumber, select, rawget = type, next, tostring, tonumber, select, rawget
10local table, string = table, string
11local concat, sort = table.concat, table.sort
12local format, lower, dump = string.format, string.lower, string.dump
13local getmetatable, setmetatable = getmetatable, setmetatable
14local lpegmatch, patterns = lpeg.match, lpeg.patterns
15local floor = math.floor
16
17
18
19
20
21
22local stripper = patterns.stripper
23
24function table.getn(t)
25 return t and #t
26end
27
28function table.strip(tab)
29 local lst = { }
30 local l = 0
31 for i=1,#tab do
32 local s = lpegmatch(stripper,tab[i]) or ""
33 if s == "" then
34
35 else
36 l = l + 1
37 lst[l] = s
38 end
39 end
40 return lst
41end
42
43function table.keys(t)
44 if t then
45 local keys = { }
46 local k = 0
47 for key in next, t do
48 k = k + 1
49 keys[k] = key
50 end
51 return keys
52 else
53 return { }
54 end
55end
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
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
128local function compare(a,b)
129 local ta = type(a)
130 if ta == "number" then
131 local tb = type(b)
132 if ta == tb then
133 return a < b
134 elseif tb == "string" then
135 return tostring(a) < b
136 end
137 elseif ta == "string" then
138 local tb = type(b)
139 if ta == tb then
140 return a < b
141 else
142 return a < tostring(b)
143 end
144 end
145 return tostring(a) < tostring(b)
146end
147
148local function sortedkeys(tab)
149 if tab then
150 local srt = { }
151 local category = 0
152 local s = 0
153 for key in next, tab do
154 s = s + 1
155 srt[s] = key
156 if category ~= 3 then
157 local tkey = type(key)
158 if category == 1 then
159 if tkey ~= "string" then
160 category = 3
161 end
162 elseif category == 2 then
163 if tkey ~= "number" then
164 category = 3
165 end
166 else
167 if tkey == "string" then
168 category = 1
169 elseif tkey == "number" then
170 category = 2
171 else
172 category = 3
173 end
174 end
175 end
176 end
177 if s < 2 then
178
179 elseif category == 3 then
180 sort(srt,compare)
181 else
182 sort(srt)
183 end
184 return srt
185 else
186 return { }
187 end
188end
189
190local function sortedhashonly(tab)
191 if tab then
192 local srt = { }
193 local s = 0
194 for key in next, tab do
195 if type(key) == "string" then
196 s = s + 1
197 srt[s] = key
198 end
199 end
200 if s > 1 then
201 sort(srt)
202 end
203 return srt
204 else
205 return { }
206 end
207end
208
209local function sortedindexonly(tab)
210 if tab then
211 local srt = { }
212 local s = 0
213 for key in next, tab do
214 if type(key) == "number" then
215 s = s + 1
216 srt[s] = key
217 end
218 end
219 if s > 1 then
220 sort(srt)
221 end
222 return srt
223 else
224 return { }
225 end
226end
227
228local function sortedhashkeys(tab,cmp)
229 if tab then
230 local srt = { }
231 local s = 0
232 for key in next, tab do
233 if key then
234 s= s + 1
235 srt[s] = key
236 end
237 end
238 if s > 1 then
239 sort(srt,cmp)
240 end
241 return srt
242 else
243 return { }
244 end
245end
246
247function table.allkeys(t)
248 local keys = { }
249 for k, v in next, t do
250 for k in next, v do
251 keys[k] = true
252 end
253 end
254 return sortedkeys(keys)
255end
256
257table.sortedkeys = sortedkeys
258table.sortedhashonly = sortedhashonly
259table.sortedindexonly = sortedindexonly
260table.sortedhashkeys = sortedhashkeys
261
262local function nothing() end
263
264local function sortedhash(t,cmp)
265 if t then
266 local s
267 if cmp then
268
269 s = sortedhashkeys(t,function(a,b) return cmp(t,a,b) end)
270 else
271 s = sortedkeys(t)
272 end
273 local m = #s
274 if m == 1 then
275 return next, t
276 elseif m > 0 then
277 local n = 0
278 return function()
279 if n < m then
280 n = n + 1
281 local k = s[n]
282 return k, t[k]
283 end
284 end
285 end
286 end
287 return nothing
288end
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321table.sortedhash = sortedhash
322table.sortedpairs = sortedhash
323
324function table.append(t,list)
325 local n = #t
326 for i=1,#list do
327 n = n + 1
328 t[n] = list[i]
329 end
330 return t
331end
332
333function table.prepend(t, list)
334 local nl = #list
335 local nt = nl + #t
336 for i=#t,1,-1 do
337 t[nt] = t[i]
338 nt = nt - 1
339 end
340 for i=1,#list do
341 t[i] = list[i]
342 end
343 return t
344end
345
346
347
348
349
350
351
352
353
354
355
356
357function table.merge(t, ...)
358 if not t then
359 t = { }
360 end
361 for i=1,select("#",...) do
362 for k, v in next, (select(i,...)) do
363 t[k] = v
364 end
365 end
366 return t
367end
368
369
370
371
372
373
374
375
376
377
378
379function table.merged(...)
380 local t = { }
381 for i=1,select("#",...) do
382 for k, v in next, (select(i,...)) do
383 t[k] = v
384 end
385 end
386 return t
387end
388
389
390
391
392
393
394
395
396
397
398
399
400
401function table.imerge(t, ...)
402 local nt = #t
403 for i=1,select("#",...) do
404 local nst = select(i,...)
405 for j=1,#nst do
406 nt = nt + 1
407 t[nt] = nst[j]
408 end
409 end
410 return t
411end
412
413
414
415
416
417
418
419
420
421
422
423
424
425function table.imerged(...)
426 local tmp = { }
427 local ntmp = 0
428 for i=1,select("#",...) do
429 local nst = select(i,...)
430 for j=1,#nst do
431 ntmp = ntmp + 1
432 tmp[ntmp] = nst[j]
433 end
434 end
435 return tmp
436end
437
438local function fastcopy(old,metatabletoo)
439 if old then
440 local new = { }
441 for k, v in next, old do
442 if type(v) == "table" then
443 new[k] = fastcopy(v,metatabletoo)
444 else
445 new[k] = v
446 end
447 end
448 if metatabletoo then
449
450 local mt = getmetatable(old)
451 if mt then
452 setmetatable(new,mt)
453 end
454 end
455 return new
456 else
457 return { }
458 end
459end
460
461
462
463local function copy(t,tables)
464 if not tables then
465 tables = { }
466 end
467 local tcopy = { }
468 if not tables[t] then
469 tables[t] = tcopy
470 end
471 for i,v in next, t do
472 local k
473 if type(i) == "table" then
474 if tables[i] then
475 k = tables[i]
476 else
477 k = copy(i,tables)
478 end
479 else
480 k = i
481 end
482 if type(v) ~= "table" then
483 tcopy[k] = v
484 elseif tables[v] then
485 tcopy[k] = tables[v]
486 else
487 tcopy[k] = copy(v,tables)
488 end
489 end
490 local mt = getmetatable(t)
491 if mt then
492 setmetatable(tcopy,mt)
493 end
494 return tcopy
495end
496
497table.fastcopy = fastcopy
498table.copy = copy
499
500function table.derive(parent)
501 local child = { }
502 if parent then
503 setmetatable(child,{ __index = parent })
504 end
505 return child
506end
507
508function table.tohash(t,value)
509 local h = { }
510 if t then
511 if value == nil then value = true end
512 for _, v in next, t do
513 h[v] = value
514 end
515 end
516 return h
517end
518
519function table.fromhash(t)
520 local hsh = { }
521 local h = 0
522 for k, v in next, t do
523 if v then
524 h = h + 1
525 hsh[h] = k
526 end
527 end
528 return hsh
529end
530
531local noquotes, hexify, handle, compact, inline, functions, metacheck, accurate
532
533local reserved = table.tohash {
534 'and', 'break', 'do', 'else', 'elseif', 'end', 'false', 'for', 'function', 'if',
535 'in', 'local', 'nil', 'not', 'or', 'repeat', 'return', 'then', 'true', 'until', 'while',
536 'NaN', 'goto', 'const',
537}
538
539local function is_simple_table(t,hexify,accurate)
540 local nt = #t
541 if nt > 0 then
542 local n = 0
543 for _, v in next, t do
544 n = n + 1
545 if type(v) == "table" then
546 return nil
547 end
548 end
549
550 local haszero = rawget(t,0)
551 if n == nt then
552 local tt = { }
553 for i=1,nt do
554 local v = t[i]
555 local tv = type(v)
556 if tv == "number" then
557
558 if hexify then
559 tt[i] = format("0x%X",v)
560 elseif accurate then
561 tt[i] = format("%q",v)
562 else
563 tt[i] = v
564 end
565 elseif tv == "string" then
566 tt[i] = format("%q",v)
567 elseif tv == "boolean" then
568 tt[i] = v and "true" or "false"
569 else
570 return nil
571 end
572 end
573 return tt
574 elseif haszero and (n == nt + 1) then
575 local tt = { }
576 for i=0,nt do
577 local v = t[i]
578 local tv = type(v)
579 if tv == "number" then
580
581 if hexify then
582 tt[i+1] = format("0x%X",v)
583 elseif accurate then
584 tt[i+1] = format("%q",v)
585 else
586 tt[i+1] = v
587 end
588 elseif tv == "string" then
589 tt[i+1] = format("%q",v)
590 elseif tv == "boolean" then
591 tt[i+1] = v and "true" or "false"
592 else
593 return nil
594 end
595 end
596 tt[1] = "[0] = " .. tt[1]
597 return tt
598 end
599 end
600 return nil
601end
602
603table.is_simple_table = is_simple_table
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622local propername = patterns.propername
623
624local function dummy() end
625
626local function do_serialize(root,name,depth,level,indexed)
627 if level > 0 then
628 depth = depth .. " "
629 if indexed then
630 handle(format("%s{",depth))
631 else
632 local tn = type(name)
633 if tn == "number" then
634 if hexify then
635 handle(format("%s[0x%X]={",depth,name))
636 else
637 handle(format("%s[%s]={",depth,name))
638 end
639 elseif tn == "string" then
640 if noquotes and not reserved[name] and lpegmatch(propername,name) then
641 handle(format("%s%s={",depth,name))
642 else
643 handle(format("%s[%q]={",depth,name))
644 end
645 elseif tn == "boolean" then
646 handle(format("%s[%s]={",depth,name and "true" or "false"))
647 else
648 handle(format("%s{",depth))
649 end
650 end
651 end
652
653 if root and next(root) ~= nil then
654 local first = nil
655 local last = 0
656 if compact then
657 last = #root
658 for k=1,last do
659
660 if rawget(root,k) == nil then
661 last = k - 1
662 break
663 end
664 end
665 if last > 0 then
666 first = 1
667 end
668 end
669 local sk = sortedkeys(root)
670 for i=1,#sk do
671 local k = sk[i]
672 local v = root[k]
673 local tv = type(v)
674 local tk = type(k)
675 if compact and first and tk == "number" and k >= first and k <= last then
676 if tv == "number" then
677 if hexify then
678 handle(format("%s 0x%X,",depth,v))
679 elseif accurate then
680 handle(format("%s %q,",depth,v))
681 else
682 handle(format("%s %s,",depth,v))
683 end
684 elseif tv == "string" then
685 handle(format("%s %q,",depth,v))
686 elseif tv == "table" then
687 if next(v) == nil then
688 handle(format("%s {},",depth))
689 elseif inline then
690 local st = is_simple_table(v,hexify,accurate)
691 if st then
692 handle(format("%s { %s },",depth,concat(st,", ")))
693 else
694 do_serialize(v,k,depth,level+1,true)
695 end
696 else
697 do_serialize(v,k,depth,level+1,true)
698 end
699 elseif tv == "boolean" then
700 handle(format("%s %s,",depth,v and "true" or "false"))
701 elseif tv == "function" then
702 if functions then
703 handle(format('%s load(%q),',depth,dump(v)))
704 else
705 handle(format('%s "function",',depth))
706 end
707 else
708 handle(format("%s %q,",depth,tostring(v)))
709 end
710 elseif k == "__p__" then
711 if false then
712 handle(format("%s __p__=nil,",depth))
713 end
714 elseif tv == "number" then
715 if tk == "number" then
716 if hexify then
717 if accurate then
718 handle(format("%s [0x%X]=%q,",depth,k,v))
719 else
720 handle(format("%s [0x%X]=%s,",depth,k,v))
721 end
722 elseif accurate then
723 handle(format("%s [%s]=%q,",depth,k,v))
724 else
725 handle(format("%s [%s]=%s,",depth,k,v))
726 end
727 elseif tk == "boolean" then
728 if hexify then
729 if accurate then
730 handle(format("%s [%s]=%q,",depth,k and "true" or "false",v))
731 else
732 handle(format("%s [%s]=%s,",depth,k and "true" or "false",v))
733 end
734 elseif accurate then
735 handle(format("%s [%s]=%q,",depth,k and "true" or "false",v))
736 else
737 handle(format("%s [%s]=%s,",depth,k and "true" or "false",v))
738 end
739 elseif tk ~= "string" then
740
741 elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
742 if hexify then
743 if accurate then
744 handle(format("%s %s=%q,",depth,k,v))
745 else
746
747 handle(format("%s %s=0x%X,",depth,k,v))
748 end
749 elseif accurate then
750 handle(format("%s %s=%q,",depth,k,v))
751 else
752 handle(format("%s %s=%s,",depth,k,v))
753 end
754 else
755 if hexify then
756 if accurate then
757 handle(format("%s [%q]=%q,",depth,k,v))
758 else
759
760 handle(format("%s [%q]=0x%X,",depth,k,v))
761 end
762 elseif accurate then
763 handle(format("%s [%q]=%q,",depth,k,v))
764 else
765 handle(format("%s [%q]=%s,",depth,k,v))
766 end
767 end
768 elseif tv == "string" then
769 if tk == "number" then
770 if hexify then
771 handle(format("%s [0x%X]=%q,",depth,k,v))
772 elseif accurate then
773 handle(format("%s [%q]=%q,",depth,k,v))
774 else
775 handle(format("%s [%s]=%q,",depth,k,v))
776 end
777 elseif tk == "boolean" then
778 handle(format("%s [%s]=%q,",depth,k and "true" or "false",v))
779 elseif tk ~= "string" then
780
781 elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
782 handle(format("%s %s=%q,",depth,k,v))
783 else
784 handle(format("%s [%q]=%q,",depth,k,v))
785 end
786 elseif tv == "table" then
787 if next(v) == nil then
788 if tk == "number" then
789 if hexify then
790 handle(format("%s [0x%X]={},",depth,k))
791 elseif accurate then
792 handle(format("%s [%q]={},",depth,k))
793 else
794 handle(format("%s [%s]={},",depth,k))
795 end
796 elseif tk == "boolean" then
797 handle(format("%s [%s]={},",depth,k and "true" or "false"))
798 elseif tk ~= "string" then
799
800 elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
801 handle(format("%s %s={},",depth,k))
802 else
803 handle(format("%s [%q]={},",depth,k))
804 end
805 elseif inline then
806 local st = is_simple_table(v,hexify,accurate)
807 if st then
808 if tk == "number" then
809 if hexify then
810 handle(format("%s [0x%X]={ %s },",depth,k,concat(st,", ")))
811 elseif accurate then
812 handle(format("%s [%q]={ %s },",depth,k,concat(st,", ")))
813 else
814 handle(format("%s [%s]={ %s },",depth,k,concat(st,", ")))
815 end
816 elseif tk == "boolean" then
817 handle(format("%s [%s]={ %s },",depth,k and "true" or "false",concat(st,", ")))
818 elseif tk ~= "string" then
819
820 elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
821 handle(format("%s %s={ %s },",depth,k,concat(st,", ")))
822 else
823 handle(format("%s [%q]={ %s },",depth,k,concat(st,", ")))
824 end
825 else
826 do_serialize(v,k,depth,level+1)
827 end
828 else
829 do_serialize(v,k,depth,level+1)
830 end
831 elseif tv == "boolean" then
832 if tk == "number" then
833 if hexify then
834 handle(format("%s [0x%X]=%s,",depth,k,v and "true" or "false"))
835 elseif accurate then
836 handle(format("%s [%q]=%s,",depth,k,v and "true" or "false"))
837 else
838 handle(format("%s [%s]=%s,",depth,k,v and "true" or "false"))
839 end
840 elseif tk == "boolean" then
841 handle(format("%s [%s]=%s,",depth,tostring(k),v and "true" or "false"))
842 elseif tk ~= "string" then
843
844 elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
845 handle(format("%s %s=%s,",depth,k,v and "true" or "false"))
846 else
847 handle(format("%s [%q]=%s,",depth,k,v and "true" or "false"))
848 end
849 elseif tv == "function" then
850 if functions then
851 local getinfo = debug and debug.getinfo
852 if getinfo then
853 local f = getinfo(v).what == "C" and dump(dummy) or dump(v)
854
855 if tk == "number" then
856 if hexify then
857 handle(format("%s [0x%X]=load(%q),",depth,k,f))
858 elseif accurate then
859 handle(format("%s [%q]=load(%q),",depth,k,f))
860 else
861 handle(format("%s [%s]=load(%q),",depth,k,f))
862 end
863 elseif tk == "boolean" then
864 handle(format("%s [%s]=load(%q),",depth,k and "true" or "false",f))
865 elseif tk ~= "string" then
866
867 elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
868 handle(format("%s %s=load(%q),",depth,k,f))
869 else
870 handle(format("%s [%q]=load(%q),",depth,k,f))
871 end
872 end
873 end
874 else
875 if tk == "number" then
876 if hexify then
877 handle(format("%s [0x%X]=%q,",depth,k,tostring(v)))
878 elseif accurate then
879 handle(format("%s [%q]=%q,",depth,k,tostring(v)))
880 else
881 handle(format("%s [%s]=%q,",depth,k,tostring(v)))
882 end
883 elseif tk == "boolean" then
884 handle(format("%s [%s]=%q,",depth,k and "true" or "false",tostring(v)))
885 elseif tk ~= "string" then
886
887 elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
888 handle(format("%s %s=%q,",depth,k,tostring(v)))
889 else
890 handle(format("%s [%q]=%q,",depth,k,tostring(v)))
891 end
892 end
893 end
894 end
895 if level > 0 then
896 handle(format("%s},",depth))
897 end
898end
899
900
901
902
903local function serialize(_handle,root,name,specification)
904 local tname = type(name)
905 if type(specification) == "table" then
906 noquotes = specification.noquotes
907 hexify = specification.hexify
908 accurate = specification.accurate
909 handle = _handle or specification.handle or print
910 functions = specification.functions
911 compact = specification.compact
912 inline = specification.inline and compact
913 metacheck = specification.metacheck
914 if functions == nil then
915 functions = true
916 end
917 if compact == nil then
918 compact = true
919 end
920 if inline == nil then
921 inline = compact
922 end
923 if metacheck == nil then
924 metacheck = true
925 end
926 else
927 noquotes = false
928 hexify = false
929 handle = _handle or print
930 compact = true
931 inline = true
932 functions = true
933 metacheck = true
934 end
935 if tname == "string" then
936 if name == "return" then
937 handle("return {")
938 else
939 handle(name .. "={")
940 end
941 elseif tname == "number" then
942 if hexify then
943 handle(format("[0x%X]={",name))
944 else
945 handle("[" .. name .. "]={")
946 end
947 elseif tname == "boolean" then
948 if name then
949 handle("return {")
950 else
951 handle("{")
952 end
953 else
954 handle("t={")
955 end
956 if root then
957
958
959
960 if metacheck and getmetatable(root) then
961 local dummy = root._w_h_a_t_e_v_e_r_
962 root._w_h_a_t_e_v_e_r_ = nil
963 end
964
965 if next(root) ~= nil then
966 do_serialize(root,name,"",0)
967 end
968 end
969 handle("}")
970end
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986function table.serialize(root,name,specification)
987 local t = { }
988 local n = 0
989 local function flush(s)
990 n = n + 1
991 t[n] = s
992 end
993 serialize(flush,root,name,specification)
994 return concat(t,"\n")
995end
996
997
998
999
1000
1001
1002
1003
1004table.tohandle = serialize
1005
1006local maxtab = 2*1024
1007
1008function table.tofile(filename,root,name,specification)
1009 local f = io.open(filename,'w')
1010 if f then
1011 if maxtab > 1 then
1012 local t = { }
1013 local n = 0
1014 local function flush(s)
1015 n = n + 1
1016 t[n] = s
1017 if n > maxtab then
1018 f:write(concat(t,"\n"),"\n")
1019 t = { }
1020 n = 0
1021 end
1022 end
1023 serialize(flush,root,name,specification)
1024 f:write(concat(t,"\n"),"\n")
1025 else
1026 local function flush(s)
1027 f:write(s,"\n")
1028 end
1029 serialize(flush,root,name,specification)
1030 end
1031 f:close()
1032 io.flush()
1033 end
1034end
1035
1036local function flattened(t,f,depth)
1037 if f == nil then
1038 f = { }
1039 depth = 0xFFFF
1040 elseif tonumber(f) then
1041
1042 depth = f
1043 f = { }
1044 elseif not depth then
1045 depth = 0xFFFF
1046 end
1047 for k, v in next, t do
1048 if type(k) ~= "number" then
1049 if depth > 0 and type(v) == "table" then
1050 flattened(v,f,depth-1)
1051 else
1052 f[#f+1] = v
1053 end
1054 end
1055 end
1056 for k=1,#t do
1057 local v = t[k]
1058 if depth > 0 and type(v) == "table" then
1059 flattened(v,f,depth-1)
1060 else
1061 f[#f+1] = v
1062 end
1063 end
1064 return f
1065end
1066
1067table.flattened = flattened
1068
1069local function collapsed(t,f,h)
1070 if f == nil then
1071 f = { }
1072 h = { }
1073 end
1074 for k=1,#t do
1075 local v = t[k]
1076 if type(v) == "table" then
1077 collapsed(v,f,h)
1078 elseif not h[v] then
1079 f[#f+1] = v
1080 h[v] = true
1081 end
1082 end
1083 return f
1084end
1085
1086local function collapsedhash(t,h)
1087 if h == nil then
1088 h = { }
1089 end
1090 for k=1,#t do
1091 local v = t[k]
1092 if type(v) == "table" then
1093 collapsedhash(v,h)
1094 else
1095 h[v] = true
1096 end
1097 end
1098 return h
1099end
1100
1101table.collapsed = collapsed
1102table.collapsedhash = collapsedhash
1103
1104local function unnest(t,f)
1105 if not f then
1106 f = { }
1107 end
1108 for i=1,#t do
1109 local v = t[i]
1110 if type(v) == "table" then
1111 if type(v[1]) == "table" then
1112 unnest(v,f)
1113 else
1114 f[#f+1] = v
1115 end
1116 else
1117 f[#f+1] = v
1118 end
1119 end
1120 return f
1121end
1122
1123function table.unnest(t)
1124 return unnest(t)
1125end
1126
1127local function are_equal(a,b,n,m)
1128 if a == b then
1129 return true
1130 elseif a and b and #a == #b then
1131 if not n then
1132 n = 1
1133 end
1134 if not m then
1135 m = #a
1136 end
1137 for i=n,m do
1138 local ai, bi = a[i], b[i]
1139 if ai==bi then
1140
1141 elseif type(ai) == "table" and type(bi) == "table" then
1142 if not are_equal(ai,bi) then
1143 return false
1144 end
1145 else
1146 return false
1147 end
1148 end
1149 return true
1150 else
1151 return false
1152 end
1153end
1154
1155local function identical(a,b)
1156 if a ~= b then
1157 for ka, va in next, a do
1158 local vb = b[ka]
1159 if va == vb then
1160
1161 elseif type(va) == "table" and type(vb) == "table" then
1162 if not identical(va,vb) then
1163 return false
1164 end
1165 else
1166 return false
1167 end
1168 end
1169 end
1170 return true
1171end
1172
1173table.identical = identical
1174table.are_equal = are_equal
1175
1176local function sparse(old,nest,keeptables)
1177 local new = { }
1178 for k, v in next, old do
1179 if not (v == "" or v == false) then
1180 if nest and type(v) == "table" then
1181 v = sparse(v,nest)
1182 if keeptables or next(v) ~= nil then
1183 new[k] = v
1184 end
1185 else
1186 new[k] = v
1187 end
1188 end
1189 end
1190 return new
1191end
1192
1193table.sparse = sparse
1194
1195function table.compact(t)
1196 return sparse(t,true,true)
1197end
1198
1199function table.contains(t, v)
1200 if t then
1201 for i=1, #t do
1202 if t[i] == v then
1203 return i
1204 end
1205 end
1206 end
1207 return false
1208end
1209
1210function table.count(t)
1211 local n = 0
1212 for k, v in next, t do
1213 n = n + 1
1214 end
1215 return n
1216end
1217
1218function table.swapped(t,s)
1219 local n = { }
1220 if s then
1221 for k, v in next, s do
1222 n[k] = v
1223 end
1224 end
1225 for k, v in next, t do
1226 n[v] = k
1227 end
1228 return n
1229end
1230
1231function table.hashed(t)
1232 for i=1,#t do
1233 t[t[i]] = i
1234 end
1235 return t
1236end
1237
1238function table.mirrored(t)
1239 local n = { }
1240 for k, v in next, t do
1241 n[v] = k
1242 n[k] = v
1243 end
1244 return n
1245end
1246
1247function table.reversed(t)
1248 if t then
1249 local tt = { }
1250 local tn = #t
1251 if tn > 0 then
1252 local ttn = 0
1253 for i=tn,1,-1 do
1254 ttn = ttn + 1
1255 tt[ttn] = t[i]
1256 end
1257 end
1258 return tt
1259 end
1260end
1261
1262function table.reverse(t)
1263 if t then
1264 local n = #t
1265 local m = n + 1
1266 for i=1,floor(n/2) do
1267 local j = m - i
1268 t[i], t[j] = t[j], t[i]
1269 end
1270 return t
1271 end
1272end
1273
1274
1275
1276local function sequenced(t,sep,simple)
1277 if not t then
1278 return ""
1279 elseif type(t) ~= "table" then
1280 return t
1281 end
1282 local n = #t
1283 local s = { }
1284 if n > 0 then
1285
1286 for i=1,n do
1287 local v = t[i]
1288 if type(v) == "table" then
1289 s[i] = "{" .. sequenced(v,sep,simple) .. "}"
1290 else
1291 s[i] = tostring(t[i])
1292 end
1293 end
1294 else
1295
1296 n = 0
1297 for k, v in sortedhash(t) do
1298 if simple then
1299 if v == true then
1300 n = n + 1
1301 s[n] = k
1302 elseif v and v~= "" then
1303 n = n + 1
1304 if type(v) == "table" then
1305 s[n] = k .. "={" .. sequenced(v,sep,simple) .. "}"
1306 else
1307 s[n] = k .. "=" .. tostring(v)
1308 end
1309 end
1310 else
1311 n = n + 1
1312 if type(v) == "table" then
1313 s[n] = k .. "={" .. sequenced(v,sep,simple) .. "}"
1314 else
1315 s[n] = k .. "=" .. tostring(v)
1316 end
1317 end
1318 end
1319 end
1320 if sep == true then
1321 return "{ " .. concat(s,", ") .. " }"
1322 else
1323 return concat(s,sep or " | ")
1324 end
1325end
1326
1327table.sequenced = sequenced
1328
1329function table.print(t,...)
1330 if type(t) ~= "table" then
1331 print(tostring(t))
1332 else
1333 serialize(print,t,...)
1334 end
1335end
1336
1337if setinspector then
1338 setinspector("table",function(v) if type(v) == "table" then serialize(print,v,"table") return true end end)
1339end
1340
1341
1342
1343
1344
1345function table.sub(t,i,j)
1346 return { unpack(t,i,j) }
1347end
1348
1349
1350
1351function table.is_empty(t)
1352 return not t or next(t) == nil
1353end
1354
1355function table.has_one_entry(t)
1356 return t and next(t,next(t)) == nil
1357end
1358
1359
1360
1361function table.loweredkeys(t)
1362 local l = { }
1363 for k, v in next, t do
1364 l[lower(k)] = v
1365 end
1366 return l
1367end
1368
1369
1370
1371function table.unique(old)
1372 local hash = { }
1373 local new = { }
1374 local n = 0
1375 for i=1,#old do
1376 local oi = old[i]
1377 if not hash[oi] then
1378 n = n + 1
1379 new[n] = oi
1380 hash[oi] = true
1381 end
1382 end
1383 return new
1384end
1385
1386function table.sorted(t,...)
1387 sort(t,...)
1388 return t
1389end
1390
1391
1392
1393function table.values(t,s)
1394 if t then
1395 local values = { }
1396 local keys = { }
1397 local v = 0
1398 for key, value in next, t do
1399 if not keys[value] then
1400 v = v + 1
1401 values[v] = value
1402 keys[k] = key
1403 end
1404 end
1405 if s then
1406 sort(values)
1407 end
1408 return values
1409 else
1410 return { }
1411 end
1412end
1413
1414
1415
1416
1417
1418
1419
1420function table.filtered(t,pattern,sort,cmp)
1421 if t and type(pattern) == "string" then
1422 if sort then
1423 local s
1424 if cmp then
1425
1426 s = sortedhashkeys(t,function(a,b) return cmp(t,a,b) end)
1427 else
1428 s = sortedkeys(t)
1429 end
1430 local n = 0
1431 local m = #s
1432 local function kv(s)
1433 while n < m do
1434 n = n + 1
1435 local k = s[n]
1436 if find(k,pattern) then
1437 return k, t[k]
1438 end
1439 end
1440 end
1441 return kv, s
1442 else
1443 local n = next(t)
1444 local function iterator()
1445 while n ~= nil do
1446 local k = n
1447 n = next(t,k)
1448 if find(k,pattern) then
1449 return k, t[k]
1450 end
1451 end
1452 end
1453 return iterator, t
1454 end
1455 else
1456 return nothing
1457 end
1458end
1459
1460
1461
1462if not table.move then
1463
1464 function table.move(a1,f,e,t,a2)
1465 if a2 and a1 ~= a2 then
1466 for i=f,e do
1467 a2[t] = a1[i]
1468 t = t + 1
1469 end
1470 return a2
1471 else
1472 t = t + e - f
1473 for i=e,f,-1 do
1474 a1[t] = a1[i]
1475 t = t - 1
1476 end
1477 return a1
1478 end
1479 end
1480
1481end
1482 |