1if not modules then modules = { } end modules ['util-tab'] = {
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
9utilities = utilities or {}
10utilities.tables = utilities.tables or { }
11local tables = utilities.tables
12
13local format, gmatch, gsub, sub = string.format, string.gmatch, string.gsub, string.sub
14local concat, insert, remove, sort = table.concat, table.insert, table.remove, table.sort
15local setmetatable, getmetatable, tonumber, tostring, rawget = setmetatable, getmetatable, tonumber, tostring, rawget
16local type, next, rawset, tonumber, tostring, load, select = type, next, rawset, tonumber, tostring, load, select
17local lpegmatch, P, Cs, Cc = lpeg.match, lpeg.P, lpeg.Cs, lpeg.Cc
18local sortedkeys, sortedpairs = table.sortedkeys, table.sortedpairs
19local formatters = string.formatters
20local utftoeight = utf.toeight
21
22local splitter = lpeg.tsplitat(".")
23
24function utilities.tables.definetable(target,nofirst,nolast)
25 local composed = nil
26 local t = { }
27 local snippets = lpegmatch(splitter,target)
28 for i=1,#snippets - (nolast and 1 or 0) do
29 local name = snippets[i]
30 if composed then
31 composed = composed .. "." .. name
32 t[#t+1] = formatters["if not %s then %s = { } end"](composed,composed)
33 else
34 composed = name
35 if not nofirst then
36 t[#t+1] = formatters["%s = %s or { }"](composed,composed)
37 end
38 end
39 end
40 if composed then
41 if nolast then
42 composed = composed .. "." .. snippets[#snippets]
43 end
44 return concat(t,"\n"), composed
45 else
46 return "", target
47 end
48end
49
50
51
52function tables.definedtable(...)
53 local t = _G
54 for i=1,select("#",...) do
55 local li = select(i,...)
56 local tl = t[li]
57 if not tl then
58 tl = { }
59 t[li] = tl
60 end
61 t = tl
62 end
63 return t
64end
65
66function tables.accesstable(target,root)
67 local t = root or _G
68 for name in gmatch(target,"([^%.]+)") do
69 t = t[name]
70 if not t then
71 return
72 end
73 end
74 return t
75end
76
77function tables.migratetable(target,v,root)
78 local t = root or _G
79 local names = lpegmatch(splitter,target)
80 for i=1,#names-1 do
81 local name = names[i]
82 t[name] = t[name] or { }
83 t = t[name]
84 if not t then
85 return
86 end
87 end
88 t[names[#names]] = v
89end
90
91function tables.removevalue(t,value)
92 if value then
93 for i=1,#t do
94 if t[i] == value then
95 remove(t,i)
96
97 end
98 end
99 end
100end
101
102function tables.replacevalue(t,oldvalue,newvalue)
103 if oldvalue and newvalue then
104 for i=1,#t do
105 if t[i] == oldvalue then
106 t[i] = newvalue
107
108 end
109 end
110 end
111end
112
113function tables.insertbeforevalue(t,value,extra)
114 for i=1,#t do
115 if t[i] == extra then
116 remove(t,i)
117 end
118 end
119 for i=1,#t do
120 if t[i] == value then
121 insert(t,i,extra)
122 return
123 end
124 end
125 insert(t,1,extra)
126end
127
128function tables.insertaftervalue(t,value,extra)
129 for i=1,#t do
130 if t[i] == extra then
131 remove(t,i)
132 end
133 end
134 for i=1,#t do
135 if t[i] == value then
136 insert(t,i+1,extra)
137 return
138 end
139 end
140 insert(t,#t+1,extra)
141end
142
143
144
145local escape = Cs(Cc('"') * ((P('"')/'""' + P(1))^0) * Cc('"'))
146
147function table.tocsv(t,specification)
148 if t and #t > 0 then
149 local result = { }
150 local r = { }
151 specification = specification or { }
152 local fields = specification.fields
153 if type(fields) ~= "string" then
154 fields = sortedkeys(t[1])
155 end
156 local separator = specification.separator or ","
157 local noffields = #fields
158 if specification.preamble == true then
159 for f=1,noffields do
160 r[f] = lpegmatch(escape,tostring(fields[f]))
161 end
162 result[1] = concat(r,separator)
163 end
164 for i=1,#t do
165 local ti = t[i]
166 for f=1,noffields do
167 local field = ti[fields[f]]
168 if type(field) == "string" then
169 r[f] = lpegmatch(escape,field)
170 else
171 r[f] = tostring(field)
172 end
173 end
174
175 result[i+1] = concat(r,separator)
176 end
177 return concat(result,"\n")
178 else
179 return ""
180 end
181end
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217local nspaces = utilities.strings.newrepeater(" ")
218
219local function toxml(t,d,result,step)
220 local r = #result
221 for k, v in sortedpairs(t) do
222 local s = nspaces[d]
223 local tk = type(k)
224 local tv = type(v)
225 if tv == "table" then
226 if tk == "number" then
227 r = r + 1 result[r] = formatters["%s<entry n='%s'>"](s,k)
228 r = toxml(v,d+step,result,step)
229 r = r + 1 result[r] = formatters["%s</entry>"](s,k)
230 else
231 r = r + 1 result[r] = formatters["%s<%s>"](s,k)
232 r = toxml(v,d+step,result,step)
233 r = r + 1 result[r] = formatters["%s</%s>"](s,k)
234 end
235 elseif tv == "string" then
236 if tk == "number" then
237 r = r + 1 result[r] = formatters["%s<entry n='%s'>%!xml!</entry>"](s,k,v,k)
238 else
239 r = r + 1 result[r] = formatters["%s<%s>%!xml!</%s>"](s,k,v,k)
240 end
241 elseif tk == "number" then
242 r = r + 1 result[r] = formatters["%s<entry n='%s'>%S</entry>"](s,k,v,k)
243 else
244 r = r + 1 result[r] = formatters["%s<%s>%S</%s>"](s,k,v,k)
245 end
246 end
247 return r
248end
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263function table.toxml(t,specification)
264 specification = specification or { }
265 local name = specification.name
266 local noroot = name == false
267 local result = (specification.nobanner or noroot) and { } or { "<?xml version='1.0' standalone='yes' ?>" }
268 local indent = specification.indent or 0
269 local spaces = specification.spaces or 1
270 if noroot then
271 toxml( t, indent, result, spaces)
272 else
273 toxml( { [name or "data"] = t }, indent, result, spaces)
274 end
275 return concat(result,"\n")
276end
277
278
279
280
281
282
283
284function tables.encapsulate(core,capsule,protect)
285 if type(capsule) ~= "table" then
286 protect = true
287 capsule = { }
288 end
289 for key, value in next, core do
290 if capsule[key] then
291 print(formatters["\ninvalid %s %a in %a"]("inheritance",key,core))
292 os.exit()
293 else
294 capsule[key] = value
295 end
296 end
297 if protect then
298 for key, value in next, core do
299 core[key] = nil
300 end
301 setmetatable(core, {
302 __index = capsule,
303 __newindex = function(t,key,value)
304 if capsule[key] then
305 print(formatters["\ninvalid %s %a' in %a"]("overload",key,core))
306 os.exit()
307 else
308 rawset(t,key,value)
309 end
310 end
311 } )
312 end
313end
314
315
316
317
318if JITSUPPORTED then
319
320 local f_hashed_string = formatters["[%Q]=%Q,"]
321 local f_hashed_number = formatters["[%Q]=%s,"]
322 local f_hashed_boolean = formatters["[%Q]=%l,"]
323 local f_hashed_table = formatters["[%Q]="]
324
325 local f_indexed_string = formatters["[%s]=%Q,"]
326 local f_indexed_number = formatters["[%s]=%s,"]
327 local f_indexed_boolean = formatters["[%s]=%l,"]
328 local f_indexed_table = formatters["[%s]="]
329
330 local f_ordered_string = formatters["%Q,"]
331 local f_ordered_number = formatters["%s,"]
332 local f_ordered_boolean = formatters["%l,"]
333
334 function table.fastserialize(t,prefix)
335
336
337
338
339
340 local r = { type(prefix) == "string" and prefix or "return" }
341 local m = 1
342 local function fastserialize(t,outer)
343 local n = #t
344 m = m + 1
345 r[m] = "{"
346 if n > 0 then
347 local v = t[0]
348 if v then
349 local tv = type(v)
350 if tv == "string" then
351 m = m + 1 r[m] = f_indexed_string(0,v)
352 elseif tv == "number" then
353 m = m + 1 r[m] = f_indexed_number(0,v)
354 elseif tv == "table" then
355 m = m + 1 r[m] = f_indexed_table(0)
356 fastserialize(v)
357 m = m + 1 r[m] = f_indexed_table(0)
358 elseif tv == "boolean" then
359 m = m + 1 r[m] = f_indexed_boolean(0,v)
360 end
361 end
362 for i=1,n do
363 local v = t[i]
364 local tv = type(v)
365 if tv == "string" then
366 m = m + 1 r[m] = f_ordered_string(v)
367 elseif tv == "number" then
368 m = m + 1 r[m] = f_ordered_number(v)
369 elseif tv == "table" then
370 fastserialize(v)
371 elseif tv == "boolean" then
372 m = m + 1 r[m] = f_ordered_boolean(v)
373 end
374 end
375 end
376
377
378 for k, v in next, t do
379 local tk = type(k)
380 if tk == "number" then
381 if k > n or k < 0 then
382 local tv = type(v)
383 if tv == "string" then
384 m = m + 1 r[m] = f_indexed_string(k,v)
385 elseif tv == "number" then
386 m = m + 1 r[m] = f_indexed_number(k,v)
387 elseif tv == "table" then
388 m = m + 1 r[m] = f_indexed_table(k)
389 fastserialize(v)
390 elseif tv == "boolean" then
391 m = m + 1 r[m] = f_indexed_boolean(k,v)
392 end
393 end
394 else
395 local tv = type(v)
396 if tv == "string" then
397 m = m + 1 r[m] = f_hashed_string(k,v)
398 elseif tv == "number" then
399 m = m + 1 r[m] = f_hashed_number(k,v)
400 elseif tv == "table" then
401 m = m + 1 r[m] = f_hashed_table(k)
402 fastserialize(v)
403 elseif tv == "boolean" then
404 m = m + 1 r[m] = f_hashed_boolean(k,v)
405 end
406 end
407 end
408 m = m + 1
409 if outer then
410 r[m] = "}"
411 else
412 r[m] = "},"
413 end
414 return r
415 end
416 return concat(fastserialize(t,true))
417 end
418
419else
420
421
422
423
424
425 function table.fastserialize(t,prefix)
426 local r = { type(prefix) == "string" and prefix or "return" }
427 local m = 1
428 local function fastserialize(t,outer)
429 local n = #t
430 m = m + 1
431 r[m] = "{"
432 if n > 0 then
433 local v = t[0]
434 if v then
435 m = m + 1
436 r[m] = "[0]="
437 if type(v) == "table" then
438 fastserialize(v)
439 else
440 r[m] = format("%q,",v)
441 end
442 end
443 for i=1,n do
444 local v = t[i]
445 m = m + 1
446 if type(v) == "table" then
447 r[m] = format("[%i]=",i)
448 fastserialize(v)
449 else
450 r[m] = format("[%i]=%q,",i,v)
451 end
452 end
453 end
454
455
456 for k, v in next, t do
457 local tk = type(k)
458 if tk == "number" then
459 if k > n or k < 0 then
460 m = m + 1
461 if type(v) == "table" then
462 r[m] = format("[%i]=",k)
463 fastserialize(v)
464 else
465 r[m] = format("[%i]=%q,",k,v)
466 end
467 end
468 else
469 m = m + 1
470 if type(v) == "table" then
471 r[m] = format("[%q]=",k)
472 fastserialize(v)
473 else
474 r[m] = format("[%q]=%q,",k,v)
475 end
476 end
477 end
478 m = m + 1
479 if outer then
480 r[m] = "}"
481 else
482 r[m] = "},"
483 end
484 return r
485 end
486 return concat(fastserialize(t,true))
487 end
488
489end
490
491function table.deserialize(str)
492 if not str or str == "" then
493 return
494 end
495 local code = load(str)
496 if not code then
497 return
498 end
499 code = code()
500 if not code then
501 return
502 end
503 return code
504end
505
506
507
508function table.load(filename,loader)
509 if filename then
510 local t = (loader or io.loaddata)(filename)
511 if t and t ~= "" then
512 local t = utftoeight(t)
513 t = load(t)
514 if type(t) == "function" then
515 t = t()
516 if type(t) == "table" then
517 return t
518 end
519 end
520 end
521 end
522end
523
524function table.save(filename,t,n,...)
525 io.savedata(filename,table.serialize(t,n == nil and true or n,...))
526end
527
528local f_key_value = formatters["%s=%q"]
529local f_add_table = formatters[" {%t},\n"]
530local f_return_table = formatters["return {\n%t}"]
531
532local function slowdrop(t)
533 local r = { }
534 local l = { }
535 for i=1,#t do
536 local ti = t[i]
537 local j = 0
538 for k, v in next, ti do
539 j = j + 1
540 l[j] = f_key_value(k,v)
541 end
542 r[i] = f_add_table(l)
543 end
544 return f_return_table(r)
545end
546
547local function fastdrop(t)
548 local r = { "return {\n" }
549 local m = 1
550 for i=1,#t do
551 local ti = t[i]
552 m = m + 1 r[m] = " {"
553 for k, v in next, ti do
554 m = m + 1 r[m] = f_key_value(k,v)
555 end
556 m = m + 1 r[m] = "},\n"
557 end
558 m = m + 1
559 r[m] = "}"
560 return concat(r)
561end
562
563function table.drop(t,slow)
564 if #t == 0 then
565 return "return { }"
566 elseif slow == true then
567 return slowdrop(t)
568 else
569 return fastdrop(t)
570 end
571end
572
573
574
575
576
577
578
579
580
581
582local selfmapper = { __index = function(t,k) t[k] = k return k end }
583
584function table.twowaymapper(t)
585 if not t then
586 t = { }
587 else
588 local zero = rawget(t,0)
589 for i=zero and 0 or 1,#t do
590 local ti = t[i]
591 if ti then
592 local i = tostring(i)
593 t[i] = ti
594 t[ti] = i
595 end
596 end
597 end
598
599 setmetatable(t,selfmapper)
600 return t
601end
602
603
604
605
606
607
608
609
610
611local f_start_key_idx = formatters["%w{"]
612local f_start_key_num = JITSUPPORTED and formatters["%w[%s]={"] or formatters["%w[%q]={"]
613local f_start_key_str = formatters["%w[%q]={"]
614local f_start_key_boo = formatters["%w[%l]={"]
615local f_start_key_nop = formatters["%w{"]
616
617local f_stop = formatters["%w},"]
618
619local f_key_num_value_num = JITSUPPORTED and formatters["%w[%s]=%s,"] or formatters["%w[%s]=%q,"]
620local f_key_str_value_num = JITSUPPORTED and formatters["%w[%Q]=%s,"] or formatters["%w[%Q]=%q,"]
621local f_key_boo_value_num = JITSUPPORTED and formatters["%w[%l]=%s,"] or formatters["%w[%l]=%q,"]
622
623local f_key_num_value_str = JITSUPPORTED and formatters["%w[%s]=%Q,"] or formatters["%w[%q]=%Q,"]
624local f_key_str_value_str = formatters["%w[%Q]=%Q,"]
625local f_key_boo_value_str = formatters["%w[%l]=%Q,"]
626
627local f_key_num_value_boo = JITSUPPORTED and formatters["%w[%s]=%l,"] or formatters["%w[%q]=%l,"]
628local f_key_str_value_boo = formatters["%w[%Q]=%l,"]
629local f_key_boo_value_boo = formatters["%w[%l]=%l,"]
630
631local f_key_num_value_not = JITSUPPORTED and formatters["%w[%s]={},"] or formatters["%w[%q]={},"]
632local f_key_str_value_not = formatters["%w[%Q]={},"]
633local f_key_boo_value_not = formatters["%w[%l]={},"]
634
635local f_key_num_value_seq = JITSUPPORTED and formatters["%w[%s]={ %, t },"] or formatters["%w[%q]={ %, t },"]
636local f_key_str_value_seq = formatters["%w[%Q]={ %, t },"]
637local f_key_boo_value_seq = formatters["%w[%l]={ %, t },"]
638
639local f_val_num = JITSUPPORTED and formatters["%w%s,"] or formatters["%w%q,"]
640local f_val_str = formatters["%w%Q,"]
641local f_val_boo = formatters["%w%l,"]
642local f_val_not = formatters["%w{},"]
643local f_val_seq = formatters["%w{ %, t },"]
644local f_fin_seq = formatters[" %, t }"]
645
646local f_table_return = formatters["return {"]
647local f_table_name = formatters["%s={"]
648local f_table_direct = formatters["{"]
649local f_table_entry = formatters["[%Q]={"]
650local f_table_finish = formatters["}"]
651
652local spaces = utilities.strings.newrepeater(" ")
653
654local original_serialize = table.serialize
655
656
657
658
659
660local is_simple_table = table.is_simple_table
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716local function serialize(root,name,specification)
717
718 if type(specification) == "table" then
719 return original_serialize(root,name,specification)
720 end
721
722 local t
723 local n = 1
724
725 local unknown = false
726
727 local function do_serialize(root,name,depth,level,indexed)
728 if level > 0 then
729 n = n + 1
730 if indexed then
731 t[n] = f_start_key_idx(depth)
732 else
733 local tn = type(name)
734 if tn == "number" then
735 t[n] = f_start_key_num(depth,name)
736 elseif tn == "string" then
737 t[n] = f_start_key_str(depth,name)
738 elseif tn == "boolean" then
739 t[n] = f_start_key_boo(depth,name)
740 else
741 t[n] = f_start_key_nop(depth)
742 end
743 end
744 depth = depth + 1
745 end
746
747 if root and next(root) ~= nil then
748 local first = nil
749 local last = #root
750 if last > 0 then
751 for k=1,last do
752 if rawget(root,k) == nil then
753
754 last = k - 1
755 break
756 end
757 end
758 if last > 0 then
759 first = 1
760 end
761 end
762 local sk = sortedkeys(root)
763 for i=1,#sk do
764 local k = sk[i]
765 local v = root[k]
766 local tv = type(v)
767 local tk = type(k)
768 if first and tk == "number" and k <= last and k >= first then
769 if tv == "number" then
770 n = n + 1 t[n] = f_val_num(depth,v)
771 elseif tv == "string" then
772 n = n + 1 t[n] = f_val_str(depth,v)
773 elseif tv == "table" then
774 if next(v) == nil then
775 n = n + 1 t[n] = f_val_not(depth)
776 else
777 local st = is_simple_table(v)
778 if st then
779 n = n + 1 t[n] = f_val_seq(depth,st)
780 else
781 do_serialize(v,k,depth,level+1,true)
782 end
783 end
784 elseif tv == "boolean" then
785 n = n + 1 t[n] = f_val_boo(depth,v)
786 elseif unknown then
787 n = n + 1 t[n] = f_val_str(depth,tostring(v))
788 end
789 elseif tv == "number" then
790 if tk == "number" then
791 n = n + 1 t[n] = f_key_num_value_num(depth,k,v)
792 elseif tk == "string" then
793 n = n + 1 t[n] = f_key_str_value_num(depth,k,v)
794 elseif tk == "boolean" then
795 n = n + 1 t[n] = f_key_boo_value_num(depth,k,v)
796 elseif unknown then
797 n = n + 1 t[n] = f_key_str_value_num(depth,tostring(k),v)
798 end
799 elseif tv == "string" then
800 if tk == "number" then
801 n = n + 1 t[n] = f_key_num_value_str(depth,k,v)
802 elseif tk == "string" then
803 n = n + 1 t[n] = f_key_str_value_str(depth,k,v)
804 elseif tk == "boolean" then
805 n = n + 1 t[n] = f_key_boo_value_str(depth,k,v)
806 elseif unknown then
807 n = n + 1 t[n] = f_key_str_value_str(depth,tostring(k),v)
808 end
809 elseif tv == "table" then
810 if next(v) == nil then
811 if tk == "number" then
812 n = n + 1 t[n] = f_key_num_value_not(depth,k)
813 elseif tk == "string" then
814 n = n + 1 t[n] = f_key_str_value_not(depth,k)
815 elseif tk == "boolean" then
816 n = n + 1 t[n] = f_key_boo_value_not(depth,k)
817 elseif unknown then
818 n = n + 1 t[n] = f_key_str_value_not(depth,tostring(k))
819 end
820 else
821 local st = is_simple_table(v)
822 if not st then
823 do_serialize(v,k,depth,level+1)
824 elseif tk == "number" then
825 n = n + 1 t[n] = f_key_num_value_seq(depth,k,st)
826 elseif tk == "string" then
827 n = n + 1 t[n] = f_key_str_value_seq(depth,k,st)
828 elseif tk == "boolean" then
829 n = n + 1 t[n] = f_key_boo_value_seq(depth,k,st)
830 elseif unknown then
831 n = n + 1 t[n] = f_key_str_value_seq(depth,tostring(k),st)
832 end
833 end
834 elseif tv == "boolean" then
835 if tk == "number" then
836 n = n + 1 t[n] = f_key_num_value_boo(depth,k,v)
837 elseif tk == "string" then
838 n = n + 1 t[n] = f_key_str_value_boo(depth,k,v)
839 elseif tk == "boolean" then
840 n = n + 1 t[n] = f_key_boo_value_boo(depth,k,v)
841 elseif unknown then
842 n = n + 1 t[n] = f_key_str_value_boo(depth,tostring(k),v)
843 end
844 else
845 if tk == "number" then
846 n = n + 1 t[n] = f_key_num_value_str(depth,k,tostring(v))
847 elseif tk == "string" then
848 n = n + 1 t[n] = f_key_str_value_str(depth,k,tostring(v))
849 elseif tk == "boolean" then
850 n = n + 1 t[n] = f_key_boo_value_str(depth,k,tostring(v))
851 elseif unknown then
852 n = n + 1 t[n] = f_key_str_value_str(depth,tostring(k),tostring(v))
853 end
854 end
855
856
857
858
859
860
861 end
862 end
863 if level > 0 then
864 n = n + 1 t[n] = f_stop(depth-1)
865 end
866 end
867
868 local tname = type(name)
869
870 if tname == "string" then
871 if name == "return" then
872 t = { f_table_return() }
873 else
874 t = { f_table_name(name) }
875 end
876 elseif tname == "number" then
877 t = { f_table_entry(name) }
878 elseif tname == "boolean" then
879 if name then
880 t = { f_table_return() }
881 else
882 t = { f_table_direct() }
883 end
884 else
885 t = { f_table_name("t") }
886 end
887
888 if root then
889
890
891
892 if getmetatable(root) then
893 local dummy = root._w_h_a_t_e_v_e_r_
894 root._w_h_a_t_e_v_e_r_ = nil
895 end
896
897 if next(root) ~= nil then
898 local st = is_simple_table(root)
899 if st then
900 return t[1] .. f_fin_seq(st)
901 else
902 do_serialize(root,name,1,0)
903 end
904 end
905 end
906 n = n + 1
907 t[n] = f_table_finish()
908 return concat(t,"\n")
909
910end
911
912table.serialize = serialize
913
914if setinspector then
915 setinspector("table",function(v)
916 if type(v) == "table" then
917 print(serialize(v,"table",{ metacheck = false }))
918 return true
919 end
920 end)
921end
922
923
924
925
926
927
928
929
930
931
932
933
934
935local mt = {
936 __newindex = function(t,k,v)
937 local n = t.last + 1
938 t.last = n
939 t.list[n] = k
940 t.hash[k] = v
941 end,
942 __index = function(t,k)
943 return t.hash[k]
944 end,
945 __len = function(t)
946 return t.last
947 end,
948}
949
950function table.orderedhash()
951 return setmetatable({ list = { }, hash = { }, last = 0 }, mt)
952end
953
954function table.ordered(t)
955 local n = t.last
956 if n > 0 then
957 local l = t.list
958 local i = 1
959 local h = t.hash
960 local f = function()
961 if i <= n then
962 local k = i
963 local v = h[l[k]]
964 i = i + 1
965 return k, v
966 end
967 end
968 return f, 1, h[l[1]]
969 else
970 return function() end
971 end
972end
973
974
975
976
977
978
979
980
981
982
983function combine(target,source)
984
985 if target then
986 for k, v in next, source do
987 if type(v) == "table" then
988 target[k] = combine(target[k],source[k])
989 else
990 target[k] = v
991 end
992 end
993 return target
994 else
995 return source
996 end
997end
998
999table.combine = combine
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022 |