cldf-lmt.lmt /size: 27 Kb    last modification: 2023-12-21 09:44
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-- variables --
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         -- floats[n] = scanfloat(true)
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) -- maybe %N
150        else
151         -- floats[n] = scanfloat(true)
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-- arrays --
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-- expressions --
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-- experimental:
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-- we could have an extra one that collects all end of grouped actions
442-- so that we dispose more in one go but it doesn's pay off
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    -- This is the slow part. Doing this at the TeX end saved 10% runtime. I'll
465    -- think of something that we can set it at the Lua end.
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-- Here's another mechanism ...
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") -- todo: tex.getgrouplevel()
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") -- todo: tex.getgrouplevel()
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            --  if b == "global" then
692                if b and (b & 0x04) ~= 0 then -- or just band(b,0x04)
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        -- already defined
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-- I played with adding bitwise operator to the expr scanners but in the end didn't
747-- keep the code ... the problem is in what symbols ... so for now a more verbose
748-- and/or/xor is okay. Extending the expression scanner would add a little overhead
749-- to all expressions while an logical operator would gain less that 50% so for now
750-- we don't bother. Providing \ifbitwiseand makes sense for performance reasons as
751-- if a bit beter than testing the bitwise and with an \ifcase.
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-- something to play with, this might move to syst-aux.lmt when we have that
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 "", -- #1 .. #9
961                j or "", k or "", l or "", m or "", n or "" -- #A .. #E
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 "", -- #1 .. #9
981                    j or "", k or "", l or "", m or "", n or "" -- #A .. #E
982                )
983            end
984        end
985        return none_code
986    end,
987}
988
989-- yes or no ...
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    -- local function isglobal(n) -- maybe a general helper
1007    --     return n and (tonumber(n) & global_code)
1008    -- end
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         -- if isdefined(name) then
1020         --     wipesparse(codes[name]) -- better make a wipe helper if ever needed
1021         -- else
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                         -- local v = scancardinal(true)
1036                         -- if isglobal(what) then
1037                            if what and (tonumber(what) & global_code) then
1038                                setsparse(code,"global",n,v)
1039                            else
1040                                savelua(restore,true) -- only once
1041                                setsparse(code,n,v)
1042                            end
1043                        end
1044                    end,
1045                }
1046                codes[name] = code
1047         -- end
1048        end,
1049    }
1050
1051end
1052
1053-- for now here:
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