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