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 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 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
247end
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262function table.toxml(t,specification)
263 specification = specification or { }
264 local name = specification.name
265 local noroot = name == false
266 local result = (specification.nobanner or noroot) and { } or { "<?xml version='1.0' standalone='yes' ?>" }
267 local indent = specification.indent or 0
268 local spaces = specification.spaces or 1
269 if noroot then
270 toxml( t, indent, result, spaces)
271 else
272 toxml( { [name or "data"] = t }, indent, result, spaces)
273 end
274 return concat(result,"\n")
275end
276
277
278
279
280
281
282
283function tables.encapsulate(core,capsule,protect)
284 if type(capsule) ~= "table" then
285 protect = true
286 capsule = { }
287 end
288 for key, value in next, core do
289 if capsule[key] then
290 print(formatters["\ninvalid %s %a in %a"]("inheritance",key,core))
291 os.exit()
292 else
293 capsule[key] = value
294 end
295 end
296 if protect then
297 for key, value in next, core do
298 core[key] = nil
299 end
300 setmetatable(core, {
301 __index = capsule,
302 __newindex = function(t,key,value)
303 if capsule[key] then
304 print(formatters["\ninvalid %s %a' in %a"]("overload",key,core))
305 os.exit()
306 else
307 rawset(t,key,value)
308 end
309 end
310 } )
311 end
312end
313
314
315
316
317if JITSUPPORTED then
318
319 local f_hashed_string = formatters["[%Q]=%Q,"]
320 local f_hashed_number = formatters["[%Q]=%s,"]
321 local f_hashed_boolean = formatters["[%Q]=%l,"]
322 local f_hashed_table = formatters["[%Q]="]
323
324 local f_indexed_string = formatters["[%s]=%Q,"]
325 local f_indexed_number = formatters["[%s]=%s,"]
326 local f_indexed_boolean = formatters["[%s]=%l,"]
327 local f_indexed_table = formatters["[%s]="]
328
329 local f_ordered_string = formatters["%Q,"]
330 local f_ordered_number = formatters["%s,"]
331 local f_ordered_boolean = formatters["%l,"]
332
333 function table.fastserialize(t,prefix)
334
335
336
337
338
339 local r = { type(prefix) == "string" and prefix or "return" }
340 local m = 1
341 local function fastserialize(t,outer)
342 local n = #t
343 m = m + 1
344 r[m] = "{"
345 if n > 0 then
346 local v = t[0]
347 if v then
348 local tv = type(v)
349 if tv == "string" then
350 m = m + 1 r[m] = f_indexed_string(0,v)
351 elseif tv == "number" then
352 m = m + 1 r[m] = f_indexed_number(0,v)
353 elseif tv == "table" then
354 m = m + 1 r[m] = f_indexed_table(0)
355 fastserialize(v)
356 m = m + 1 r[m] = f_indexed_table(0)
357 elseif tv == "boolean" then
358 m = m + 1 r[m] = f_indexed_boolean(0,v)
359 end
360 end
361 for i=1,n do
362 local v = t[i]
363 local tv = type(v)
364 if tv == "string" then
365 m = m + 1 r[m] = f_ordered_string(v)
366 elseif tv == "number" then
367 m = m + 1 r[m] = f_ordered_number(v)
368 elseif tv == "table" then
369 fastserialize(v)
370 elseif tv == "boolean" then
371 m = m + 1 r[m] = f_ordered_boolean(v)
372 end
373 end
374 end
375
376
377 for k, v in next, t do
378 local tk = type(k)
379 if tk == "number" then
380 if k > n or k < 0 then
381 local tv = type(v)
382 if tv == "string" then
383 m = m + 1 r[m] = f_indexed_string(k,v)
384 elseif tv == "number" then
385 m = m + 1 r[m] = f_indexed_number(k,v)
386 elseif tv == "table" then
387 m = m + 1 r[m] = f_indexed_table(k)
388 fastserialize(v)
389 elseif tv == "boolean" then
390 m = m + 1 r[m] = f_indexed_boolean(k,v)
391 end
392 end
393 else
394 local tv = type(v)
395 if tv == "string" then
396 m = m + 1 r[m] = f_hashed_string(k,v)
397 elseif tv == "number" then
398 m = m + 1 r[m] = f_hashed_number(k,v)
399 elseif tv == "table" then
400 m = m + 1 r[m] = f_hashed_table(k)
401 fastserialize(v)
402 elseif tv == "boolean" then
403 m = m + 1 r[m] = f_hashed_boolean(k,v)
404 end
405 end
406 end
407 m = m + 1
408 if outer then
409 r[m] = "}"
410 else
411 r[m] = "},"
412 end
413 return r
414 end
415 return concat(fastserialize(t,true))
416 end
417
418else
419
420
421
422
423
424 function table.fastserialize(t,prefix)
425 local r = { type(prefix) == "string" and prefix or "return" }
426 local m = 1
427 local function fastserialize(t,outer)
428 local n = #t
429 m = m + 1
430 r[m] = "{"
431 if n > 0 then
432 local v = t[0]
433 if v then
434 m = m + 1
435 r[m] = "[0]='"
436 if type(v) == "table" then
437 fastserialize(v)
438 else
439 r[m] = format("%q,",v)
440 end
441 end
442 for i=1,n do
443 local v = t[i]
444 m = m + 1
445 if type(v) == "table" then
446 r[m] = format("[%i]=",i)
447 fastserialize(v)
448 else
449 r[m] = format("[%i]=%q,",i,v)
450 end
451 end
452 end
453
454
455 for k, v in next, t do
456 local tk = type(k)
457 if tk == "number" then
458 if k > n or k < 0 then
459 m = m + 1
460 if type(v) == "table" then
461 r[m] = format("[%i]=",k)
462 fastserialize(v)
463 else
464 r[m] = format("[%i]=%q,",k,v)
465 end
466 end
467 else
468 m = m + 1
469 if type(v) == "table" then
470 r[m] = format("[%q]=",k)
471 fastserialize(v)
472 else
473 r[m] = format("[%q]=%q,",k,v)
474 end
475 end
476 end
477 m = m + 1
478 if outer then
479 r[m] = "}"
480 else
481 r[m] = "},"
482 end
483 return r
484 end
485 return concat(fastserialize(t,true))
486 end
487
488end
489
490function table.deserialize(str)
491 if not str or str == "" then
492 return
493 end
494 local code = load(str)
495 if not code then
496 return
497 end
498 code = code()
499 if not code then
500 return
501 end
502 return code
503end
504
505
506
507function table.load(filename,loader)
508 if filename then
509 local t = (loader or io.loaddata)(filename)
510 if t and t ~= "" then
511 local t = utftoeight(t)
512 t = load(t)
513 if type(t) == "function" then
514 t = t()
515 if type(t) == "table" then
516 return t
517 end
518 end
519 end
520 end
521end
522
523function table.save(filename,t,n,...)
524 io.savedata(filename,table.serialize(t,n == nil and true or n,...))
525end
526
527local f_key_value = formatters["%s=%q"]
528local f_add_table = formatters[" {%t},\n"]
529local f_return_table = formatters["return {\n%t}"]
530
531local function slowdrop(t)
532 local r = { }
533 local l = { }
534 for i=1,#t do
535 local ti = t[i]
536 local j = 0
537 for k, v in next, ti do
538 j = j + 1
539 l[j] = f_key_value(k,v)
540 end
541 r[i] = f_add_table(l)
542 end
543 return f_return_table(r)
544end
545
546local function fastdrop(t)
547 local r = { "return {\n" }
548 local m = 1
549 for i=1,#t do
550 local ti = t[i]
551 m = m + 1 r[m] = " {"
552 for k, v in next, ti do
553 m = m + 1 r[m] = f_key_value(k,v)
554 end
555 m = m + 1 r[m] = "},\n"
556 end
557 m = m + 1
558 r[m] = "}"
559 return concat(r)
560end
561
562function table.drop(t,slow)
563 if #t == 0 then
564 return "return { }"
565 elseif slow == true then
566 return slowdrop(t)
567 else
568 return fastdrop(t)
569 end
570end
571
572
573
574
575
576
577
578
579
580
581local selfmapper = { __index = function(t,k) t[k] = k return k end }
582
583function table.twowaymapper(t)
584 if not t then
585 t = { }
586 else
587 local zero = rawget(t,0)
588 for i=zero and 0 or 1,#t do
589 local ti = t[i]
590 if ti then
591 local i = tostring(i)
592 t[i] = ti
593 t[ti] = i
594 end
595 end
596 end
597
598 setmetatable(t,selfmapper)
599 return t
600end
601
602
603
604
605
606
607
608
609
610local f_start_key_idx = formatters["%w{"]
611local f_start_key_num = JITSUPPORTED and formatters["%w[%s]={"] or formatters["%w[%q]={"]
612local f_start_key_str = formatters["%w[%q]={"]
613local f_start_key_boo = formatters["%w[%l]={"]
614local f_start_key_nop = formatters["%w{"]
615
616local f_stop = formatters["%w},"]
617
618local f_key_num_value_num = JITSUPPORTED and formatters["%w[%s]=%s,"] or formatters["%w[%s]=%q,"]
619local f_key_str_value_num = JITSUPPORTED and formatters["%w[%Q]=%s,"] or formatters["%w[%Q]=%q,"]
620local f_key_boo_value_num = JITSUPPORTED and formatters["%w[%l]=%s,"] or formatters["%w[%l]=%q,"]
621
622local f_key_num_value_str = JITSUPPORTED and formatters["%w[%s]=%Q,"] or formatters["%w[%q]=%Q,"]
623local f_key_str_value_str = formatters["%w[%Q]=%Q,"]
624local f_key_boo_value_str = formatters["%w[%l]=%Q,"]
625
626local f_key_num_value_boo = JITSUPPORTED and formatters["%w[%s]=%l,"] or formatters["%w[%q]=%l,"]
627local f_key_str_value_boo = formatters["%w[%Q]=%l,"]
628local f_key_boo_value_boo = formatters["%w[%l]=%l,"]
629
630local f_key_num_value_not = JITSUPPORTED and formatters["%w[%s]={},"] or formatters["%w[%q]={},"]
631local f_key_str_value_not = formatters["%w[%Q]={},"]
632local f_key_boo_value_not = formatters["%w[%l]={},"]
633
634local f_key_num_value_seq = JITSUPPORTED and formatters["%w[%s]={ %, t },"] or formatters["%w[%q]={ %, t },"]
635local f_key_str_value_seq = formatters["%w[%Q]={ %, t },"]
636local f_key_boo_value_seq = formatters["%w[%l]={ %, t },"]
637
638local f_val_num = JITSUPPORTED and formatters["%w%s,"] or formatters["%w%q,"]
639local f_val_str = formatters["%w%Q,"]
640local f_val_boo = formatters["%w%l,"]
641local f_val_not = formatters["%w{},"]
642local f_val_seq = formatters["%w{ %, t },"]
643local f_fin_seq = formatters[" %, t }"]
644
645local f_table_return = formatters["return {"]
646local f_table_name = formatters["%s={"]
647local f_table_direct = formatters["{"]
648local f_table_entry = formatters["[%Q]={"]
649local f_table_finish = formatters["}"]
650
651local spaces = utilities.strings.newrepeater(" ")
652
653local original_serialize = table.serialize
654
655
656
657
658
659local is_simple_table = table.is_simple_table
660
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
715local function serialize(root,name,specification)
716
717 if type(specification) == "table" then
718 return original_serialize(root,name,specification)
719 end
720
721 local t
722 local n = 1
723
724 local unknown = false
725
726 local function do_serialize(root,name,depth,level,indexed)
727 if level > 0 then
728 n = n + 1
729 if indexed then
730 t[n] = f_start_key_idx(depth)
731 else
732 local tn = type(name)
733 if tn == "number" then
734 t[n] = f_start_key_num(depth,name)
735 elseif tn == "string" then
736 t[n] = f_start_key_str(depth,name)
737 elseif tn == "boolean" then
738 t[n] = f_start_key_boo(depth,name)
739 else
740 t[n] = f_start_key_nop(depth)
741 end
742 end
743 depth = depth + 1
744 end
745
746 if root and next(root) ~= nil then
747 local first = nil
748 local last = #root
749 if last > 0 then
750 for k=1,last do
751 if rawget(root,k) == nil then
752
753 last = k - 1
754 break
755 end
756 end
757 if last > 0 then
758 first = 1
759 end
760 end
761 local sk = sortedkeys(root)
762 for i=1,#sk do
763 local k = sk[i]
764 local v = root[k]
765 local tv = type(v)
766 local tk = type(k)
767 if first and tk == "number" and k <= last and k >= first then
768 if tv == "number" then
769 n = n + 1 t[n] = f_val_num(depth,v)
770 elseif tv == "string" then
771 n = n + 1 t[n] = f_val_str(depth,v)
772 elseif tv == "table" then
773 if next(v) == nil then
774 n = n + 1 t[n] = f_val_not(depth)
775 else
776 local st = is_simple_table(v)
777 if st then
778 n = n + 1 t[n] = f_val_seq(depth,st)
779 else
780 do_serialize(v,k,depth,level+1,true)
781 end
782 end
783 elseif tv == "boolean" then
784 n = n + 1 t[n] = f_val_boo(depth,v)
785 elseif unknown then
786 n = n + 1 t[n] = f_val_str(depth,tostring(v))
787 end
788 elseif tv == "number" then
789 if tk == "number" then
790 n = n + 1 t[n] = f_key_num_value_num(depth,k,v)
791 elseif tk == "string" then
792 n = n + 1 t[n] = f_key_str_value_num(depth,k,v)
793 elseif tk == "boolean" then
794 n = n + 1 t[n] = f_key_boo_value_num(depth,k,v)
795 elseif unknown then
796 n = n + 1 t[n] = f_key_str_value_num(depth,tostring(k),v)
797 end
798 elseif tv == "string" then
799 if tk == "number" then
800 n = n + 1 t[n] = f_key_num_value_str(depth,k,v)
801 elseif tk == "string" then
802 n = n + 1 t[n] = f_key_str_value_str(depth,k,v)
803 elseif tk == "boolean" then
804 n = n + 1 t[n] = f_key_boo_value_str(depth,k,v)
805 elseif unknown then
806 n = n + 1 t[n] = f_key_str_value_str(depth,tostring(k),v)
807 end
808 elseif tv == "table" then
809 if next(v) == nil then
810 if tk == "number" then
811 n = n + 1 t[n] = f_key_num_value_not(depth,k)
812 elseif tk == "string" then
813 n = n + 1 t[n] = f_key_str_value_not(depth,k)
814 elseif tk == "boolean" then
815 n = n + 1 t[n] = f_key_boo_value_not(depth,k)
816 elseif unknown then
817 n = n + 1 t[n] = f_key_str_value_not(depth,tostring(k))
818 end
819 else
820 local st = is_simple_table(v)
821 if not st then
822 do_serialize(v,k,depth,level+1)
823 elseif tk == "number" then
824 n = n + 1 t[n] = f_key_num_value_seq(depth,k,st)
825 elseif tk == "string" then
826 n = n + 1 t[n] = f_key_str_value_seq(depth,k,st)
827 elseif tk == "boolean" then
828 n = n + 1 t[n] = f_key_boo_value_seq(depth,k,st)
829 elseif unknown then
830 n = n + 1 t[n] = f_key_str_value_seq(depth,tostring(k),st)
831 end
832 end
833 elseif tv == "boolean" then
834 if tk == "number" then
835 n = n + 1 t[n] = f_key_num_value_boo(depth,k,v)
836 elseif tk == "string" then
837 n = n + 1 t[n] = f_key_str_value_boo(depth,k,v)
838 elseif tk == "boolean" then
839 n = n + 1 t[n] = f_key_boo_value_boo(depth,k,v)
840 elseif unknown then
841 n = n + 1 t[n] = f_key_str_value_boo(depth,tostring(k),v)
842 end
843 else
844 if tk == "number" then
845 n = n + 1 t[n] = f_key_num_value_str(depth,k,tostring(v))
846 elseif tk == "string" then
847 n = n + 1 t[n] = f_key_str_value_str(depth,k,tostring(v))
848 elseif tk == "boolean" then
849 n = n + 1 t[n] = f_key_boo_value_str(depth,k,tostring(v))
850 elseif unknown then
851 n = n + 1 t[n] = f_key_str_value_str(depth,tostring(k),tostring(v))
852 end
853 end
854
855
856
857
858
859
860 end
861 end
862 if level > 0 then
863 n = n + 1 t[n] = f_stop(depth-1)
864 end
865 end
866
867 local tname = type(name)
868
869 if tname == "string" then
870 if name == "return" then
871 t = { f_table_return() }
872 else
873 t = { f_table_name(name) }
874 end
875 elseif tname == "number" then
876 t = { f_table_entry(name) }
877 elseif tname == "boolean" then
878 if name then
879 t = { f_table_return() }
880 else
881 t = { f_table_direct() }
882 end
883 else
884 t = { f_table_name("t") }
885 end
886
887 if root then
888
889
890
891 if getmetatable(root) then
892 local dummy = root._w_h_a_t_e_v_e_r_
893 root._w_h_a_t_e_v_e_r_ = nil
894 end
895
896 if next(root) ~= nil then
897 local st = is_simple_table(root)
898 if st then
899 return t[1] .. f_fin_seq(st)
900 else
901 do_serialize(root,name,1,0)
902 end
903 end
904 end
905 n = n + 1
906 t[n] = f_table_finish()
907 return concat(t,"\n")
908
909end
910
911table.serialize = serialize
912
913if setinspector then
914 setinspector("table",function(v)
915 if type(v) == "table" then
916 print(serialize(v,"table",{ metacheck = false }))
917 return true
918 end
919 end)
920end
921
922
923
924
925
926
927
928
929
930
931
932
933
934local mt = {
935 __newindex = function(t,k,v)
936 local n = t.last + 1
937 t.last = n
938 t.list[n] = k
939 t.hash[k] = v
940 end,
941 __index = function(t,k)
942 return t.hash[k]
943 end,
944 __len = function(t)
945 return t.last
946 end,
947}
948
949function table.orderedhash()
950 return setmetatable({ list = { }, hash = { }, last = 0 }, mt)
951end
952
953function table.ordered(t)
954 local n = t.last
955 if n > 0 then
956 local l = t.list
957 local i = 1
958 local h = t.hash
959 local f = function()
960 if i <= n then
961 local k = i
962 local v = h[l[k]]
963 i = i + 1
964 return k, v
965 end
966 end
967 return f, 1, h[l[1]]
968 else
969 return function() end
970 end
971end
972
973
974
975
976
977
978
979
980
981 |