1if not modules then modules = { } end modules ['cldf-lmt'] = {
2 version = 1.001,
3 comment = "companion to toks-scn.mkiv",
4 author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
5 copyright = "PRAGMA ADE / ConTeXt Development Team",
6 license = "see context related readme files"
7}
8
9local next, load, tonumber = next, load, tonumber
10local gmatch, gsub, byte = string.gmatch, string.gsub, string.byte
11
12local setmetatableindex = table.setmetatableindex
13local setmetatablenewindex = table.setmetatablenewindex
14local serialize = table.serialize
15local concat = table.concat
16
17local random = math.random
18local randomseed = math.randomseed
19local round = math.round
20local abs = math.abs
21
22local implement = interfaces.implement
23
24local scanners = tokens.scanners
25local scanword = scanners.word
26local scanstring = scanners.string
27local scanboolean = scanners.boolean
28local scandimen = scanners.dimen
29local scanfloat = scanners.float
30local scaninteger = scanners.integer
31local scancardinal = scanners.cardinal
32local scannumber = scanners.luanumber
33local scanluainteger = scanners.luainteger
34local scanluacardinal = scanners.luacardinal
35local scanluanumber = scanners.luanumber
36local scanargument = scanners.argument
37local scantoken = scanners.token
38local scancsname = scanners.csname
39local scankeyword = scanners.keyword
40
41local peekchar = scanners.peekchar
42local skipnext = scanners.skip
43
44local getindex = tokens.accessors.index
45
46local texsetdimen = tex.setdimen
47local texsetcount = tex.setcount
48local texgetcount = tex.getcount
49local texget = tex.get
50
51local values = tokens.values
52local none_code = values.none
53local integer_code = values.integer
54local cardinal_code = values.cardinal
55local dimension_code = values.dimension
56local skip_code = values.skip
57local boolean_code = values.boolean
58local float_code = values.float
59
60local global_code = tex.flagcodes.global
61
62local context = context
63
64
65
66local floats = { }
67local integers = { }
68local cardinals = { }
69local numbers = { }
70
71implement {
72 name = "luafloat",
73 public = true,
74 usage = "value",
75 actions = function(b)
76 local n = scanword()
77 if b == "value" then
78 context("%.99g",floats[n] or 0)
79 else
80 floats[n] = scanluanumber(true)
81
82 end
83 end,
84}
85
86implement {
87 name = "luainteger",
88 public = true,
89 usage = "value",
90 actions = function(b)
91 local n = scanword()
92 if b == "value" then
93 context("%i",integers[n] or 0)
94 else
95 integers[n] = scanluainteger(true)
96 end
97 end,
98}
99
100implement {
101 name = "luacount",
102 public = true,
103 usage = "value",
104 actions = function(b)
105 local n = scanword()
106 if b == "value" then
107 return integer_code, integers[n] or 0
108 else
109 integers[n] = scaninteger(true)
110 end
111 end,
112}
113
114implement {
115 name = "luadimen",
116 public = true,
117 usage = "value",
118 actions = function(b)
119 local n = scanword()
120 if b == "value" then
121 return dimension_code, integers[n] or 0
122 else
123 integers[n] = scandimen(false,false,true)
124 end
125 end,
126}
127
128implement {
129 name = "luacardinal",
130 public = true,
131 usage = "value",
132 actions = function(b)
133 local n = scanword()
134 if b == "value" then
135 context("%1.0f",cardinals[n] or 0)
136 else
137 cardinals[n] = scanluacardinal(true)
138 end
139 end,
140}
141
142implement {
143 name = "luanumber",
144 public = true,
145 usage = "value",
146 actions = function(b)
147 local n = scanword()
148 if b == "value" then
149 context("%N",floats[n] or integers[n] or cardinals[n] or 0)
150 else
151
152 floats[n] = scanluanumber(true)
153 end
154 end,
155}
156
157implement {
158 name = "luarandom",
159 public = true,
160 usage = "value",
161 actions = function(b)
162 if b == "value" then
163 return integer_code, random(scanluainteger(),scanluainteger())
164 else
165 randomseed(scanluainteger(true))
166 end
167 end,
168}
169
170interfaces.floats = floats
171interfaces.integers = integers
172interfaces.cardinals = cardinals
173
174interfaces.numbers = table.setmetatableindex(function(t,k)
175 return floats[k] or integers[k] or cardinals[k]
176end)
177
178
179
180local arrays = { }
181
182interfaces.arrays = arrays
183
184local newindex = lua.newindex
185
186implement {
187 name = "newarray",
188 public = true,
189 protected = true,
190 untraced = true,
191 arguments = { {
192 { "name", "string" },
193 { "nx", "integer" },
194 { "ny", "integer" },
195 { "type", "string" },
196 } },
197 actions = function(t)
198 local name = t.name
199 if t.name then
200 local nx = t.nx
201 local ny = t.ny
202 local ty = t.type or "integer"
203 local df = nil
204 if ty == "integer" or ty == "float" or ty == "dimension" then
205 df = 0
206 elseif ty == "boolean" then
207 df = false
208 else
209 ty = nil
210 end
211 if nx and ty ~= nil then
212 local data
213 if ny then
214 data = newindex(t.ny)
215 for i=1,ny do
216 data[i] = newindex(nx,df)
217 end
218 else
219 data = newindex(nx,df)
220 end
221 arrays[name] = data
222 data.nx = nx
223 data.ny = ny
224 data.type = ty
225 if ty == "integer" then
226 data.scanner = scaninteger
227 elseif ty == "boolean" then
228 data.scanner = scanboolean
229 elseif ty == "dimension" then
230 data.scanner = scandimen
231 elseif ty == "float" then
232 data.scanner = scanfloat
233 end
234 if ty == "integer" then
235 data.code = integer_code
236 elseif ty == "boolean" then
237 data.code = boolean_code
238 elseif ty == "dimension" then
239 data.code = dimension_code
240 elseif ty == "float" then
241 data.code = float_code
242 end
243 end
244 end
245 end,
246}
247
248implement {
249 name = "arrayvalue",
250 public = true,
251 usage = "value",
252 actions = function(b)
253 local name = scanstring()
254 if name then
255 local a = arrays[name]
256 if a then
257 local nx = a.nx
258 local ny = a.ny
259 local d = a
260 if ny then
261 d = d[scaninteger()]
262 end
263 local x = scaninteger()
264 if b == "value" then
265 local code = a.code
266 if code == float_code then
267 context("%.99g",d[x])
268 else
269 return code, d[x]
270 end
271 else
272 d[x] = a.scanner()
273 end
274 end
275 end
276 end,
277}
278
279implement {
280 name = "arrayequals",
281 public = true,
282 usage = "value",
283 actions = function(b)
284 local name = scanstring()
285 if name then
286 local a = arrays[name]
287 if a then
288 local nx = a.nx
289 local ny = a.ny
290 local d = a
291 if ny then
292 d = d[scaninteger()]
293 end
294 local x = scaninteger()
295 if b == "value" then
296 return boolean_code, a.scanner() == d[x]
297 end
298 end
299 end
300 end,
301}
302
303implement {
304 name = "arraycompare",
305 public = true,
306 usage = "value",
307 actions = function(b)
308 local name = scanstring()
309 if name then
310 local a = arrays[name]
311 if a then
312 local nx = a.nx
313 local ny = a.ny
314 local d = a
315 if ny then
316 d = d[scaninteger()]
317 end
318 local x = scaninteger()
319 if b == "value" then
320 local v = a.scanner()
321 local d = d[x]
322 if d < v then
323 return integer_code, 0
324 elseif d == v then
325 return integer_code, 1
326 else
327 return integer_code, 2
328 end
329 end
330 end
331 end
332 end,
333}
334
335implement {
336 name = "showarray",
337 public = true,
338 protected = true,
339 untraced = true,
340 actions = function()
341 local name = scanstring()
342 if name then
343 inspect(arrays[name])
344 end
345 end,
346}
347
348
349
350local cache = table.setmetatableindex(function(t,k)
351 local code = "return function() local n = interfaces.numbers local a = interfaces.arrays return " .. k .. " end"
352 code = loadstring(code)
353 if code then
354 code = code()
355 end
356 t[k] = code or false
357 return code
358end)
359
360table.makeweak(cache)
361
362implement {
363 name = "luaexpression",
364 public = true,
365 untraced = true,
366 actions = function()
367 local how = scanword()
368 local code = cache[scanargument()]
369 if code then
370 local result = code()
371 if result then
372 if not how then
373 context(tostring(code()))
374 elseif how == "float" then
375 context("%.99g",result)
376 elseif how == "integer" then
377 context("%i",round(result))
378 elseif how == "cardinal" then
379 context("%d",abs(round(result)))
380 elseif how == "dimen" then
381 context("%p",result)
382 elseif how == "boolean" then
383 context("%d",result and 1 or 0)
384 elseif how == "lua" then
385 context("%q",result)
386 else
387 context(tostring(code()))
388 end
389 end
390 end
391 end
392}
393
394local dimenfactors = number.dimenfactors
395
396implement {
397 name = "nodimen",
398 public = true,
399 usage = "value",
400 actions = function(b)
401 if b == "value" then
402 local how = scanword()
403 local what = scandimen()
404 if how then
405 local factor = dimenfactors[how]
406 if factor then
407 context("%.6N%s",factor*what,how)
408 else
409 return dimension_code, what
410 end
411 else
412 return dimension_code, what
413 end
414 else
415 local t = scantoken()
416 if t then
417 local i = getindex(t)
418 if i then
419 local d = scandimen(false,false,true)
420 texsetdimen(i,d)
421 end
422 end
423 end
424 end,
425}
426
427
428
429local grouped = { }
430local slots = { }
431local nofslots = 0
432local nofgrouped = 0
433
434local getinteger = tokens.getinteger
435local setinteger = tokens.setinteger
436
437local report = logs.reporter("lua table")
438
439local ctxsprint = context.sprint
440
441
442
443
444local function newluatable(name,mt,dispose)
445 local g = { }
446 local t = slots[nofslots]
447 slots[nofslots] = false
448 nofslots = nofslots - 1
449 if not t then
450 nofgrouped = nofgrouped + 1
451 t = nofgrouped
452 end
453 if mt then
454 mt = getinteger(name)
455 if mt then
456 mt = grouped[mt]
457 if mt then
458 setmetatableindex(g,mt)
459 end
460 end
461 end
462 grouped[t] = g
463 setinteger(name,t)
464
465
466 if dispose then
467 ctxsprint("\\atendofgrouped{\\disposeluatable\\" .. name .. "}")
468 end
469end
470
471local function disposeluatable(name)
472 local t = getinteger(name)
473 local g = grouped[t]
474 if g then
475 grouped[t] = false
476 nofslots = nofslots + 1
477 slots[nofslots] = t
478 end
479end
480
481local function setluatable(name,kv)
482 local t = getinteger(name)
483 local g = grouped[t]
484 if g and kv then
485 for k, v in next, kv do
486 g[k] = v
487 end
488 end
489end
490
491local function getfromluatable(name,k)
492 local t = getinteger(name)
493 local g = grouped[t]
494 if g then
495 local v = g[k]
496 if v then
497 context(v)
498 else
499 local n = tonumber(k)
500 if n then
501 local v = g[n]
502 if v then
503 context(v)
504 end
505 end
506 end
507 end
508end
509
510local function idxfromluatable(name,k)
511 local t = getinteger(name)
512 local g = grouped[t]
513 if g then
514 local v = g[k]
515 if v then
516 context(v)
517 end
518 end
519end
520
521local function getluatable(name,k)
522 local t = getinteger(name)
523 local g = grouped[t]
524 if g then
525 return g
526 end
527end
528
529local function inspectluatable(name)
530 local t = getinteger(name)
531 local g = grouped[t]
532 if g then
533 report("%s", serialize(g,'[grouped slot ' .. t .. ']'))
534 end
535end
536
537local function showluatables()
538 report("nofgrouped %i, nofslots %i",nofgrouped,nofslots)
539 for t=1,nofgrouped do
540 local g = grouped[t]
541 if g then
542 report("%s", serialize(g,'[grouped slot ' .. t .. ']'))
543 end
544 end
545end
546
547implement {
548 name = "newluatable",
549 protected = true,
550 untraced = true,
551 arguments = "csname",
552 actions = newluatable,
553}
554
555implement {
556 name = "useluatable",
557 protected = true,
558 untraced = true,
559 arguments = { "csname", true },
560 actions = newluatable,
561}
562
563implement {
564 name = "disposeluatable",
565 protected = true,
566 untraced = true,
567 public = true,
568 arguments = "csname",
569 actions = disposeluatable,
570}
571
572implement {
573 name = "inspectluatable",
574 protected = true,
575 untraced = true,
576 public = true,
577 arguments = "csname",
578 actions = inspectluatable,
579}
580
581implement {
582 name = "showluatables",
583 protected = true,
584 untraced = true,
585 public = true,
586 actions = showluatables,
587}
588
589implement {
590 name = "setluatable",
591 protected = true,
592 untraced = true,
593 public = true,
594 arguments = { "csname", "argument" },
595 actions = function(name,data)
596 data = load("return {" .. data .. "}")
597 if data then
598 data = data()
599 if data then
600 setluatable(name,data)
601 end
602 end
603 end
604}
605
606implement {
607 name = "getfromluatable",
608 protected = false,
609 untraced = true,
610 public = true,
611 arguments = { "csname", "argument" },
612 actions = getfromluatable,
613}
614
615implement {
616 name = "idxfromluatable",
617 protected = false,
618 untraced = true,
619 public = true,
620 arguments = { "csname", "integer" },
621 actions = idxfromluatable,
622}
623
624context.luatables = {
625 new = function(name) newluatable(name,false,true) end,
626 use = function(name) useluatable(name,true, true) end,
627 dispose = disposeluatable,
628 set = setluatable,
629 get = getluatable,
630 getfrom = getfromluatable,
631 idxfrom = idxfromluatable,
632 inspect = inspectluatable,
633 show = showluatables,
634}
635
636
637
638local tables = { }
639local stack = setmetatableindex("table")
640
641implement {
642 name = "droptablegroup",
643 public = true,
644 untraced = true,
645 actions = function()
646 local g = texget("currentgrouplevel")
647 local s = stack[g]
648 if s then
649 for t, data in next, s do
650 for k, v in next, data do
651 t[k] = v
652 end
653 end
654 stack[g] = { }
655 end
656 end,
657}
658
659local ctx_atendofgroup = context.core.cs.atendofgroup
660local ctx_droptablegroup = context.core.cs.droptablegroup
661
662local function handletable(t,b,array)
663 if b == "value" then
664 local k = array and scaninteger() or scanargument()
665 local v = t[k]
666 if v then
667 context(v)
668 end
669 else
670 local data = scanargument()
671 data = load("return {" .. data .. "}")
672 if data then
673 data = data()
674 if data then
675 local l = t.level
676 local g = texget("currentgrouplevel")
677 local s = stack[g]
678 local d = s[t]
679 if not d then
680 d = { }
681 s[t] = d
682 ctx_atendofgroup()
683 ctx_droptablegroup()
684 end
685 for k, v in next, data do
686 if not d[k] then
687 d[k] = t[k]
688 end
689 t[k] = v
690 end
691
692 if b and (b & 0x04) ~= 0 then
693 for k, v in next, stack do
694 local t = s[t]
695 if t then
696 for k, v in next, data do
697 if t[k] then
698 t[k] = nil
699 end
700 end
701 end
702 end
703 end
704 end
705 end
706 end
707end
708
709local function newtable(array)
710 local name = scancsname(true)
711 if not tables[name] then
712 local t = { }
713 tables[name] = t
714 implement {
715 name = name,
716 public = true,
717 usage = "value",
718 actions = function(b)
719 handletable(t,b,array)
720 end,
721 }
722 else
723
724 end
725end
726
727implement {
728 name = "newhashedtable",
729 protected = true,
730 untraced = true,
731 public = true,
732 actions = newtable,
733}
734
735implement {
736 name = "newindexedtable",
737 protected = true,
738 untraced = true,
739 public = true,
740 actions = function() newtable(true) end,
741}
742
743context.hashedtables = setmetatableindex(function(t,k) return tables[k] end)
744context.indexedtables = context.hashedtables
745
746
747
748
749
750
751
752
753local reporterror = logs.texerrormessage
754
755local function youcant(cmd)
756 reporterror("you can't use \\%s this way, it's an integer value", cmd)
757end
758
759implement {
760 name = "bitwiseset",
761 public = true,
762 usage = "value",
763 actions = function(what)
764 local a = scancardinal()
765 if what == "value" then
766 return cardinal_code, a
767 else
768 youcant("bitwiseset")
769 end
770 end
771}
772
773implement {
774 name = "bitwiseand",
775 public = true,
776 usage = "value",
777 actions = function(what)
778 local a = scancardinal()
779 scankeyword("with")
780 local b = scancardinal()
781 if what == "value" then
782 return cardinal_code, a & b
783 else
784 youcant("bitwiseand")
785 end
786 end
787}
788
789implement {
790 name = "bitwiseor",
791 public = true,
792 usage = "value",
793 actions = function(what)
794 local a = scancardinal()
795 scankeyword("with")
796 local b = scancardinal()
797 if what == "value" then
798 return cardinal_code, a | b
799 else
800 youcant("bitwiseor")
801 end
802 end
803}
804
805implement {
806 name = "bitwisexor",
807 public = true,
808 usage = "value",
809 actions = function(what)
810 local a = scancardinal()
811 scankeyword("with")
812 local b = scancardinal()
813 if what == "value" then
814 return cardinal_code, a ~ b
815 else
816 youcant("bitwisexor")
817 end
818 end
819}
820
821implement {
822 name = "bitwisenot",
823 public = true,
824 usage = "value",
825 actions = function(what)
826 local a = scancardinal()
827 if what == "value" then
828 return cardinal_code, ~a & 0xFFFFFFFF
829 else
830 youcant("bitwisenot")
831 end
832 end
833}
834
835implement {
836 name = "bitwisenil",
837 public = true,
838 usage = "value",
839 actions = function(what)
840 local a = scancardinal()
841 scankeyword("with")
842 local b = scancardinal()
843 if what == "value" then
844 return cardinal_code, a & (~b & 0xFFFFFFFF)
845 else
846 youcant("bitwisenil")
847 end
848 end
849}
850
851implement {
852 name = "bitwiseshift",
853 public = true,
854 usage = "value",
855 actions = function(what)
856 local a = scancardinal()
857 scankeyword("by")
858 local b = scaninteger()
859 if what == "value" then
860 return cardinal_code, (b < 0) and (a << -b) or (a >> b)
861 else
862 youcant("bitwiseshift")
863 end
864 end
865}
866
867implement {
868 name = "bitwiseflip",
869 public = true,
870 usage = "value",
871 actions = function(what)
872 local t = scancsname()
873 local b = scaninteger()
874 local c = texgetcount(t)
875 if c then
876 if b > 0 then
877 c = c | b
878 elseif b < 0 then
879 c = c & (~(-b) & 0xFFFFFFFF)
880 end
881 if what == "value" then
882 return cardinal_code, c
883 elseif what and (what & global_code) ~= 0 then
884 texsetcount("global",t,c)
885 else
886 texsetcount(t,c)
887 end
888 end
889 end
890}
891
892implement {
893 name = "ifbitwiseand",
894 public = true,
895 usage = "condition",
896 actions = function(what)
897 local a = scancardinal()
898 local b = scancardinal()
899 return boolean_code, (a & b) ~= 0
900 end
901}
902
903implement {
904 name = "bitwise",
905 public = true,
906 usage = "value",
907 actions = function(what)
908 if what == "value" then
909 local b = 0
910 while true do
911 local c = peekchar()
912 if c == 48 or c == 49 then
913 skipnext()
914 b = (b << 1) + (c - 48)
915 if b == 0xFFFFFFFF then
916 break
917 end
918 else
919 break
920 end
921 end
922 return 1, b
923 else
924 local b = scancardinal()
925 local t = nil
926 local n = 0
927 for i=1,32 do
928 if (b & (0x100000000 >> i)) == 0 then
929 if n > 0 then
930 n = n + 1
931 t[n] = "0"
932 end
933 elseif t then
934 n = n + 1
935 t[n] = "1"
936 else
937 n = 1
938 t = { "1" }
939 end
940 end
941 context(n > 0 and table.concat(t) or "0")
942 end
943 end,
944}
945
946
947
948local escape = function(s) return "\\" .. byte(s) end
949
950implement {
951 name = "ctxluamatch",
952 public = true,
953 usage = "value",
954 actions = function()
955 local command = context[scancsname()]
956 local pattern = gsub(scanstring(),"\\.",escape)
957 local input = gsub(scanstring(),"\\.",escape)
958 for a, b, c, d, e, f, g, h, i, j, k, l, m, n in gmatch(input,pattern) do
959 command(
960 a, b or "", c or "", d or "", e or "", f or "", g or "", h or "", i or "",
961 j or "", k or "", l or "", m or "", n or ""
962 )
963 end
964 return none_code
965 end,
966}
967
968implement {
969 name = "ctxluamatchfile",
970 public = true,
971 usage = "value",
972 actions = function()
973 local command = context[scancsname()]
974 local pattern = gsub(scanstring(),"\\.",escape)
975 local filename = scanstring()
976 if filename ~= "" then
977 local input = gsub(io.loaddata(filename),"\\.",escape)
978 for a, b, c, d, e, f, g, h, i, j, k, l, m, n in gmatch(input,pattern) do
979 command(
980 a, b or "", c or "", d or "", e or "", f or "", g or "", h or "", i or "",
981 j or "", k or "", l or "", m or "", n or ""
982 )
983 end
984 end
985 return none_code
986 end,
987}
988
989
990
991do
992
993 local codes = { }
994 tex.codes = codes
995
996 local global_code = tex.flagcodes.global
997
998 local savelua = token.savelua
999 local isdefined = token.isdefined
1000
1001 local newsparse = sparse.new
1002 local setsparse = sparse.set
1003 local wipesparse = sparse.wipe
1004 local restoresparse = sparse.restore
1005
1006
1007
1008
1009
1010 local registerfunction = context.functions.register
1011
1012 implement {
1013 name = "codedef",
1014 public = true,
1015 untraced = true,
1016 protected = true,
1017 actions = function(what)
1018 local name = scancsname(true)
1019
1020
1021
1022 local code = newsparse()
1023 local restore = registerfunction(function() restoresparse(code) end)
1024 implement {
1025 name = name,
1026 public = true,
1027 protected = true,
1028 usage = "value",
1029 actions = function(what)
1030 local n = scaninteger()
1031 if what == "value" then
1032 return integer_code, code[n]
1033 else
1034 local v = scaninteger(true)
1035
1036
1037 if what and (tonumber(what) & global_code) then
1038 setsparse(code,"global",n,v)
1039 else
1040 savelua(restore,true)
1041 setsparse(code,n,v)
1042 end
1043 end
1044 end,
1045 }
1046 codes[name] = code
1047
1048 end,
1049 }
1050
1051end
1052
1053
1054
1055do
1056
1057 local runstring = tex.runstring
1058 local ctxcatcodes = tex.ctxcatcodes
1059 local formatters = string.formatters
1060
1061 function context.runstring(fmt,str,...)
1062 if str then
1063 runstring(ctxcatcodes,formatters[fmt](str,...))
1064 elseif fmt then
1065 runstring(ctxcatcodes,fmt)
1066 end
1067 end
1068
1069end
1070 |