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