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 if type(i) == "table" then
473 if tables[i] then
474 i = tables[i]
475 else
476 i = copy(i,tables)
477 end
478 end
479 if type(v) ~= "table" then
480 tcopy[i] = v
481 elseif tables[v] then
482 tcopy[i] = tables[v]
483 else
484 tcopy[i] = copy(v,tables)
485 end
486 end
487 local mt = getmetatable(t)
488 if mt then
489 setmetatable(tcopy,mt)
490 end
491 return tcopy
492end
493
494table.fastcopy = fastcopy
495table.copy = copy
496
497function table.derive(parent)
498 local child = { }
499 if parent then
500 setmetatable(child,{ __index = parent })
501 end
502 return child
503end
504
505function table.tohash(t,value)
506 local h = { }
507 if t then
508 if value == nil then value = true end
509 for _, v in next, t do
510 h[v] = value
511 end
512 end
513 return h
514end
515
516function table.fromhash(t)
517 local hsh = { }
518 local h = 0
519 for k, v in next, t do
520 if v then
521 h = h + 1
522 hsh[h] = k
523 end
524 end
525 return hsh
526end
527
528local noquotes, hexify, handle, compact, inline, functions, metacheck, accurate
529
530local reserved = table.tohash {
531 'and', 'break', 'do', 'else', 'elseif', 'end', 'false', 'for', 'function', 'if',
532 'in', 'local', 'nil', 'not', 'or', 'repeat', 'return', 'then', 'true', 'until', 'while',
533 'NaN', 'goto', 'const',
534}
535
536local function is_simple_table(t,hexify,accurate)
537 local nt = #t
538 if nt > 0 then
539 local n = 0
540 for _, v in next, t do
541 n = n + 1
542 if type(v) == "table" then
543 return nil
544 end
545 end
546
547 local haszero = rawget(t,0)
548 if n == nt then
549 local tt = { }
550 for i=1,nt do
551 local v = t[i]
552 local tv = type(v)
553 if tv == "number" then
554
555 if hexify then
556 tt[i] = format("0x%X",v)
557 elseif accurate then
558 tt[i] = format("%q",v)
559 else
560 tt[i] = v
561 end
562 elseif tv == "string" then
563 tt[i] = format("%q",v)
564 elseif tv == "boolean" then
565 tt[i] = v and "true" or "false"
566 else
567 return nil
568 end
569 end
570 return tt
571 elseif haszero and (n == nt + 1) then
572 local tt = { }
573 for i=0,nt do
574 local v = t[i]
575 local tv = type(v)
576 if tv == "number" then
577
578 if hexify then
579 tt[i+1] = format("0x%X",v)
580 elseif accurate then
581 tt[i+1] = format("%q",v)
582 else
583 tt[i+1] = v
584 end
585 elseif tv == "string" then
586 tt[i+1] = format("%q",v)
587 elseif tv == "boolean" then
588 tt[i+1] = v and "true" or "false"
589 else
590 return nil
591 end
592 end
593 tt[1] = "[0] = " .. tt[1]
594 return tt
595 end
596 end
597 return nil
598end
599
600table.is_simple_table = is_simple_table
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619local propername = patterns.propername
620
621local function dummy() end
622
623local function do_serialize(root,name,depth,level,indexed)
624 if level > 0 then
625 depth = depth .. " "
626 if indexed then
627 handle(format("%s{",depth))
628 else
629 local tn = type(name)
630 if tn == "number" then
631 if hexify then
632 handle(format("%s[0x%X]={",depth,name))
633 else
634 handle(format("%s[%s]={",depth,name))
635 end
636 elseif tn == "string" then
637 if noquotes and not reserved[name] and lpegmatch(propername,name) then
638 handle(format("%s%s={",depth,name))
639 else
640 handle(format("%s[%q]={",depth,name))
641 end
642 elseif tn == "boolean" then
643 handle(format("%s[%s]={",depth,name and "true" or "false"))
644 else
645 handle(format("%s{",depth))
646 end
647 end
648 end
649
650 if root and next(root) ~= nil then
651 local first = nil
652 local last = 0
653 if compact then
654 last = #root
655 for k=1,last do
656
657 if rawget(root,k) == nil then
658 last = k - 1
659 break
660 end
661 end
662 if last > 0 then
663 first = 1
664 end
665 end
666 local sk = sortedkeys(root)
667 for i=1,#sk do
668 local k = sk[i]
669 local v = root[k]
670 local tv = type(v)
671 local tk = type(k)
672 if compact and first and tk == "number" and k >= first and k <= last then
673 if tv == "number" then
674 if hexify then
675 handle(format("%s 0x%X,",depth,v))
676 elseif accurate then
677 handle(format("%s %q,",depth,v))
678 else
679 handle(format("%s %s,",depth,v))
680 end
681 elseif tv == "string" then
682 handle(format("%s %q,",depth,v))
683 elseif tv == "table" then
684 if next(v) == nil then
685 handle(format("%s {},",depth))
686 elseif inline then
687 local st = is_simple_table(v,hexify,accurate)
688 if st then
689 handle(format("%s { %s },",depth,concat(st,", ")))
690 else
691 do_serialize(v,k,depth,level+1,true)
692 end
693 else
694 do_serialize(v,k,depth,level+1,true)
695 end
696 elseif tv == "boolean" then
697 handle(format("%s %s,",depth,v and "true" or "false"))
698 elseif tv == "function" then
699 if functions then
700 handle(format('%s load(%q),',depth,dump(v)))
701 else
702 handle(format('%s "function",',depth))
703 end
704 else
705 handle(format("%s %q,",depth,tostring(v)))
706 end
707 elseif k == "__p__" then
708 if false then
709 handle(format("%s __p__=nil,",depth))
710 end
711 elseif tv == "number" then
712 if tk == "number" then
713 if hexify then
714 if accurate then
715 handle(format("%s [0x%X]=%q,",depth,k,v))
716 else
717 handle(format("%s [0x%X]=%s,",depth,k,v))
718 end
719 elseif accurate then
720 handle(format("%s [%s]=%q,",depth,k,v))
721 else
722 handle(format("%s [%s]=%s,",depth,k,v))
723 end
724 elseif tk == "boolean" then
725 if hexify then
726 if accurate then
727 handle(format("%s [%s]=%q,",depth,k and "true" or "false",v))
728 else
729 handle(format("%s [%s]=%s,",depth,k and "true" or "false",v))
730 end
731 elseif accurate then
732 handle(format("%s [%s]=%q,",depth,k and "true" or "false",v))
733 else
734 handle(format("%s [%s]=%s,",depth,k and "true" or "false",v))
735 end
736 elseif tk ~= "string" then
737
738 elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
739 if hexify then
740 if accurate then
741 handle(format("%s %s=%q,",depth,k,v))
742 else
743
744 handle(format("%s %s=0x%X,",depth,k,v))
745 end
746 elseif accurate then
747 handle(format("%s %s=%q,",depth,k,v))
748 else
749 handle(format("%s %s=%s,",depth,k,v))
750 end
751 else
752 if hexify then
753 if accurate then
754 handle(format("%s [%q]=%q,",depth,k,v))
755 else
756
757 handle(format("%s [%q]=0x%X,",depth,k,v))
758 end
759 elseif accurate then
760 handle(format("%s [%q]=%q,",depth,k,v))
761 else
762 handle(format("%s [%q]=%s,",depth,k,v))
763 end
764 end
765 elseif tv == "string" then
766 if tk == "number" then
767 if hexify then
768 handle(format("%s [0x%X]=%q,",depth,k,v))
769 elseif accurate then
770 handle(format("%s [%q]=%q,",depth,k,v))
771 else
772 handle(format("%s [%s]=%q,",depth,k,v))
773 end
774 elseif tk == "boolean" then
775 handle(format("%s [%s]=%q,",depth,k and "true" or "false",v))
776 elseif tk ~= "string" then
777
778 elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
779 handle(format("%s %s=%q,",depth,k,v))
780 else
781 handle(format("%s [%q]=%q,",depth,k,v))
782 end
783 elseif tv == "table" then
784 if next(v) == nil then
785 if tk == "number" then
786 if hexify then
787 handle(format("%s [0x%X]={},",depth,k))
788 elseif accurate then
789 handle(format("%s [%q]={},",depth,k))
790 else
791 handle(format("%s [%s]={},",depth,k))
792 end
793 elseif tk == "boolean" then
794 handle(format("%s [%s]={},",depth,k and "true" or "false"))
795 elseif tk ~= "string" then
796
797 elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
798 handle(format("%s %s={},",depth,k))
799 else
800 handle(format("%s [%q]={},",depth,k))
801 end
802 elseif inline then
803 local st = is_simple_table(v,hexify,accurate)
804 if st then
805 if tk == "number" then
806 if hexify then
807 handle(format("%s [0x%X]={ %s },",depth,k,concat(st,", ")))
808 elseif accurate then
809 handle(format("%s [%q]={ %s },",depth,k,concat(st,", ")))
810 else
811 handle(format("%s [%s]={ %s },",depth,k,concat(st,", ")))
812 end
813 elseif tk == "boolean" then
814 handle(format("%s [%s]={ %s },",depth,k and "true" or "false",concat(st,", ")))
815 elseif tk ~= "string" then
816
817 elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
818 handle(format("%s %s={ %s },",depth,k,concat(st,", ")))
819 else
820 handle(format("%s [%q]={ %s },",depth,k,concat(st,", ")))
821 end
822 else
823 do_serialize(v,k,depth,level+1)
824 end
825 else
826 do_serialize(v,k,depth,level+1)
827 end
828 elseif tv == "boolean" then
829 if tk == "number" then
830 if hexify then
831 handle(format("%s [0x%X]=%s,",depth,k,v and "true" or "false"))
832 elseif accurate then
833 handle(format("%s [%q]=%s,",depth,k,v and "true" or "false"))
834 else
835 handle(format("%s [%s]=%s,",depth,k,v and "true" or "false"))
836 end
837 elseif tk == "boolean" then
838 handle(format("%s [%s]=%s,",depth,tostring(k),v and "true" or "false"))
839 elseif tk ~= "string" then
840
841 elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
842 handle(format("%s %s=%s,",depth,k,v and "true" or "false"))
843 else
844 handle(format("%s [%q]=%s,",depth,k,v and "true" or "false"))
845 end
846 elseif tv == "function" then
847 if functions then
848 local getinfo = debug and debug.getinfo
849 if getinfo then
850 local f = getinfo(v).what == "C" and dump(dummy) or dump(v)
851
852 if tk == "number" then
853 if hexify then
854 handle(format("%s [0x%X]=load(%q),",depth,k,f))
855 elseif accurate then
856 handle(format("%s [%q]=load(%q),",depth,k,f))
857 else
858 handle(format("%s [%s]=load(%q),",depth,k,f))
859 end
860 elseif tk == "boolean" then
861 handle(format("%s [%s]=load(%q),",depth,k and "true" or "false",f))
862 elseif tk ~= "string" then
863
864 elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
865 handle(format("%s %s=load(%q),",depth,k,f))
866 else
867 handle(format("%s [%q]=load(%q),",depth,k,f))
868 end
869 end
870 end
871 else
872 if tk == "number" then
873 if hexify then
874 handle(format("%s [0x%X]=%q,",depth,k,tostring(v)))
875 elseif accurate then
876 handle(format("%s [%q]=%q,",depth,k,tostring(v)))
877 else
878 handle(format("%s [%s]=%q,",depth,k,tostring(v)))
879 end
880 elseif tk == "boolean" then
881 handle(format("%s [%s]=%q,",depth,k and "true" or "false",tostring(v)))
882 elseif tk ~= "string" then
883
884 elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
885 handle(format("%s %s=%q,",depth,k,tostring(v)))
886 else
887 handle(format("%s [%q]=%q,",depth,k,tostring(v)))
888 end
889 end
890 end
891 end
892 if level > 0 then
893 handle(format("%s},",depth))
894 end
895end
896
897
898
899
900local function serialize(_handle,root,name,specification)
901 local tname = type(name)
902 if type(specification) == "table" then
903 noquotes = specification.noquotes
904 hexify = specification.hexify
905 accurate = specification.accurate
906 handle = _handle or specification.handle or print
907 functions = specification.functions
908 compact = specification.compact
909 inline = specification.inline and compact
910 metacheck = specification.metacheck
911 if functions == nil then
912 functions = true
913 end
914 if compact == nil then
915 compact = true
916 end
917 if inline == nil then
918 inline = compact
919 end
920 if metacheck == nil then
921 metacheck = true
922 end
923 else
924 noquotes = false
925 hexify = false
926 handle = _handle or print
927 compact = true
928 inline = true
929 functions = true
930 metacheck = true
931 end
932 if tname == "string" then
933 if name == "return" then
934 handle("return {")
935 else
936 handle(name .. "={")
937 end
938 elseif tname == "number" then
939 if hexify then
940 handle(format("[0x%X]={",name))
941 else
942 handle("[" .. name .. "]={")
943 end
944 elseif tname == "boolean" then
945 if name then
946 handle("return {")
947 else
948 handle("{")
949 end
950 else
951 handle("t={")
952 end
953 if root then
954
955
956
957 if metacheck and getmetatable(root) then
958 local dummy = root._w_h_a_t_e_v_e_r_
959 root._w_h_a_t_e_v_e_r_ = nil
960 end
961
962 if next(root) ~= nil then
963 do_serialize(root,name,"",0)
964 end
965 end
966 handle("}")
967end
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983function table.serialize(root,name,specification)
984 local t = { }
985 local n = 0
986 local function flush(s)
987 n = n + 1
988 t[n] = s
989 end
990 serialize(flush,root,name,specification)
991 return concat(t,"\n")
992end
993
994
995
996
997
998
999
1000
1001table.tohandle = serialize
1002
1003local maxtab = 2*1024
1004
1005function table.tofile(filename,root,name,specification)
1006 local f = io.open(filename,'w')
1007 if f then
1008 if maxtab > 1 then
1009 local t = { }
1010 local n = 0
1011 local function flush(s)
1012 n = n + 1
1013 t[n] = s
1014 if n > maxtab then
1015 f:write(concat(t,"\n"),"\n")
1016 t = { }
1017 n = 0
1018 end
1019 end
1020 serialize(flush,root,name,specification)
1021 f:write(concat(t,"\n"),"\n")
1022 else
1023 local function flush(s)
1024 f:write(s,"\n")
1025 end
1026 serialize(flush,root,name,specification)
1027 end
1028 f:close()
1029 io.flush()
1030 end
1031end
1032
1033local function flattened(t,f,depth)
1034 if f == nil then
1035 f = { }
1036 depth = 0xFFFF
1037 elseif tonumber(f) then
1038
1039 depth = f
1040 f = { }
1041 elseif not depth then
1042 depth = 0xFFFF
1043 end
1044 for k, v in next, t do
1045 if type(k) ~= "number" then
1046 if depth > 0 and type(v) == "table" then
1047 flattened(v,f,depth-1)
1048 else
1049 f[#f+1] = v
1050 end
1051 end
1052 end
1053 for k=1,#t do
1054 local v = t[k]
1055 if depth > 0 and type(v) == "table" then
1056 flattened(v,f,depth-1)
1057 else
1058 f[#f+1] = v
1059 end
1060 end
1061 return f
1062end
1063
1064table.flattened = flattened
1065
1066local function collapsed(t,f,h)
1067 if f == nil then
1068 f = { }
1069 h = { }
1070 end
1071 for k=1,#t do
1072 local v = t[k]
1073 if type(v) == "table" then
1074 collapsed(v,f,h)
1075 elseif not h[v] then
1076 f[#f+1] = v
1077 h[v] = true
1078 end
1079 end
1080 return f
1081end
1082
1083local function collapsedhash(t,h)
1084 if h == nil then
1085 h = { }
1086 end
1087 for k=1,#t do
1088 local v = t[k]
1089 if type(v) == "table" then
1090 collapsedhash(v,h)
1091 else
1092 h[v] = true
1093 end
1094 end
1095 return h
1096end
1097
1098table.collapsed = collapsed
1099table.collapsedhash = collapsedhash
1100
1101local function unnest(t,f)
1102 if not f then
1103 f = { }
1104 end
1105 for i=1,#t do
1106 local v = t[i]
1107 if type(v) == "table" then
1108 if type(v[1]) == "table" then
1109 unnest(v,f)
1110 else
1111 f[#f+1] = v
1112 end
1113 else
1114 f[#f+1] = v
1115 end
1116 end
1117 return f
1118end
1119
1120function table.unnest(t)
1121 return unnest(t)
1122end
1123
1124local function are_equal(a,b,n,m)
1125 if a == b then
1126 return true
1127 elseif a and b and #a == #b then
1128 if not n then
1129 n = 1
1130 end
1131 if not m then
1132 m = #a
1133 end
1134 for i=n,m do
1135 local ai, bi = a[i], b[i]
1136 if ai==bi then
1137
1138 elseif type(ai) == "table" and type(bi) == "table" then
1139 if not are_equal(ai,bi) then
1140 return false
1141 end
1142 else
1143 return false
1144 end
1145 end
1146 return true
1147 else
1148 return false
1149 end
1150end
1151
1152local function identical(a,b)
1153 if a ~= b then
1154 for ka, va in next, a do
1155 local vb = b[ka]
1156 if va == vb then
1157
1158 elseif type(va) == "table" and type(vb) == "table" then
1159 if not identical(va,vb) then
1160 return false
1161 end
1162 else
1163 return false
1164 end
1165 end
1166 end
1167 return true
1168end
1169
1170table.identical = identical
1171table.are_equal = are_equal
1172
1173local function sparse(old,nest,keeptables)
1174 local new = { }
1175 for k, v in next, old do
1176 if not (v == "" or v == false) then
1177 if nest and type(v) == "table" then
1178 v = sparse(v,nest)
1179 if keeptables or next(v) ~= nil then
1180 new[k] = v
1181 end
1182 else
1183 new[k] = v
1184 end
1185 end
1186 end
1187 return new
1188end
1189
1190table.sparse = sparse
1191
1192function table.compact(t)
1193 return sparse(t,true,true)
1194end
1195
1196function table.contains(t, v)
1197 if t then
1198 for i=1, #t do
1199 if t[i] == v then
1200 return i
1201 end
1202 end
1203 end
1204 return false
1205end
1206
1207function table.count(t)
1208 local n = 0
1209 for k, v in next, t do
1210 n = n + 1
1211 end
1212 return n
1213end
1214
1215function table.swapped(t,s)
1216 local n = { }
1217 if s then
1218 for k, v in next, s do
1219 n[k] = v
1220 end
1221 end
1222 for k, v in next, t do
1223 n[v] = k
1224 end
1225 return n
1226end
1227
1228function table.hashed(t)
1229 for i=1,#t do
1230 t[t[i]] = i
1231 end
1232 return t
1233end
1234
1235function table.mirrored(t)
1236 local n = { }
1237 for k, v in next, t do
1238 n[v] = k
1239 n[k] = v
1240 end
1241 return n
1242end
1243
1244function table.reversed(t)
1245 if t then
1246 local tt = { }
1247 local tn = #t
1248 if tn > 0 then
1249 local ttn = 0
1250 for i=tn,1,-1 do
1251 ttn = ttn + 1
1252 tt[ttn] = t[i]
1253 end
1254 end
1255 return tt
1256 end
1257end
1258
1259function table.reverse(t)
1260 if t then
1261 local n = #t
1262 local m = n + 1
1263 for i=1,floor(n/2) do
1264 local j = m - i
1265 t[i], t[j] = t[j], t[i]
1266 end
1267 return t
1268 end
1269end
1270
1271
1272
1273local function sequenced(t,sep,simple)
1274 if not t then
1275 return ""
1276 elseif type(t) ~= "table" then
1277 return t
1278 end
1279 local n = #t
1280 local s = { }
1281 if n > 0 then
1282
1283 for i=1,n do
1284 local v = t[i]
1285 if type(v) == "table" then
1286 s[i] = "{" .. sequenced(v,sep,simple) .. "}"
1287 else
1288 s[i] = tostring(t[i])
1289 end
1290 end
1291 else
1292
1293 n = 0
1294 for k, v in sortedhash(t) do
1295 if simple then
1296 if v == true then
1297 n = n + 1
1298 s[n] = k
1299 elseif v and v~= "" then
1300 n = n + 1
1301 if type(v) == "table" then
1302 s[n] = k .. "={" .. sequenced(v,sep,simple) .. "}"
1303 else
1304 s[n] = k .. "=" .. tostring(v)
1305 end
1306 end
1307 else
1308 n = n + 1
1309 if type(v) == "table" then
1310 s[n] = k .. "={" .. sequenced(v,sep,simple) .. "}"
1311 else
1312 s[n] = k .. "=" .. tostring(v)
1313 end
1314 end
1315 end
1316 end
1317 if sep == true then
1318 return "{ " .. concat(s,", ") .. " }"
1319 else
1320 return concat(s,sep or " | ")
1321 end
1322end
1323
1324table.sequenced = sequenced
1325
1326function table.print(t,...)
1327 if type(t) ~= "table" then
1328 print(tostring(t))
1329 else
1330 serialize(print,t,...)
1331 end
1332end
1333
1334if setinspector then
1335 setinspector("table",function(v) if type(v) == "table" then serialize(print,v,"table") return true end end)
1336end
1337
1338
1339
1340
1341
1342function table.sub(t,i,j)
1343 return { unpack(t,i,j) }
1344end
1345
1346
1347
1348function table.is_empty(t)
1349 return not t or next(t) == nil
1350end
1351
1352function table.has_one_entry(t)
1353 return t and next(t,next(t)) == nil
1354end
1355
1356
1357
1358function table.loweredkeys(t)
1359 local l = { }
1360 for k, v in next, t do
1361 l[lower(k)] = v
1362 end
1363 return l
1364end
1365
1366
1367
1368function table.unique(old)
1369 local hash = { }
1370 local new = { }
1371 local n = 0
1372 for i=1,#old do
1373 local oi = old[i]
1374 if not hash[oi] then
1375 n = n + 1
1376 new[n] = oi
1377 hash[oi] = true
1378 end
1379 end
1380 return new
1381end
1382
1383function table.sorted(t,...)
1384 sort(t,...)
1385 return t
1386end
1387
1388
1389
1390function table.values(t,s)
1391 if t then
1392 local values = { }
1393 local keys = { }
1394 local v = 0
1395 for key, value in next, t do
1396 if not keys[value] then
1397 v = v + 1
1398 values[v] = value
1399 keys[k] = key
1400 end
1401 end
1402 if s then
1403 sort(values)
1404 end
1405 return values
1406 else
1407 return { }
1408 end
1409end
1410
1411
1412
1413
1414
1415
1416
1417function table.filtered(t,pattern,sort,cmp)
1418 if t and type(pattern) == "string" then
1419 if sort then
1420 local s
1421 if cmp then
1422
1423 s = sortedhashkeys(t,function(a,b) return cmp(t,a,b) end)
1424 else
1425 s = sortedkeys(t)
1426 end
1427 local n = 0
1428 local m = #s
1429 local function kv(s)
1430 while n < m do
1431 n = n + 1
1432 local k = s[n]
1433 if find(k,pattern) then
1434 return k, t[k]
1435 end
1436 end
1437 end
1438 return kv, s
1439 else
1440 local n = next(t)
1441 local function iterator()
1442 while n ~= nil do
1443 local k = n
1444 n = next(t,k)
1445 if find(k,pattern) then
1446 return k, t[k]
1447 end
1448 end
1449 end
1450 return iterator, t
1451 end
1452 else
1453 return nothing
1454 end
1455end
1456
1457
1458
1459if not table.move then
1460
1461 function table.move(a1,f,e,t,a2)
1462 if a2 and a1 ~= a2 then
1463 for i=f,e do
1464 a2[t] = a1[i]
1465 t = t + 1
1466 end
1467 return a2
1468 else
1469 t = t + e - f
1470 for i=e,f,-1 do
1471 a1[t] = a1[i]
1472 t = t - 1
1473 end
1474 return a1
1475 end
1476 end
1477
1478end
1479 |