core-env.lmt /size: 11 Kb    last modification: 2025-02-21 11:03
1if not modules then modules = { } end modules ['core-env'] = {
2    version   = 1.001,
3    comment   = "companion to core-env.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
9-- maybe this will move to the context name space although the
10-- plurals are unlikely to clash with future tex primitives
11--
12-- if tex.modes['xxxx'] then .... else .... end
13
14local rawset = rawset
15
16local P, C, S, lpegmatch, patterns = lpeg.P, lpeg.C, lpeg.S, lpeg.match, lpeg.patterns
17local gmatch = string.gmatch
18
19local context              = context
20local ctxcore              = context.core
21
22local texgetintegervalue   = token.getinteger -- todo
23
24local allocate             = utilities.storage.allocate
25local setmetatableindex    = table.setmetatableindex
26local setmetatablenewindex = table.setmetatablenewindex
27local setmetatablecall     = table.setmetatablecall
28
29local createtoken          = token.create
30local expandmacro          = token.expandmacro
31
32local isdefined            = tokens.isdefined
33
34texmodes                   = allocate { }  tex.modes        = texmodes
35texsystemmodes             = allocate { }  tex.systemmodes  = texsystemmodes
36texconstants               = allocate { }  tex.constants    = texconstants
37texconditionals            = allocate { }  tex.conditionals = texconditionals
38texifs                     = allocate { }  tex.ifs          = texifs
39texisdefined               = allocate { }  tex.isdefined    = texisdefined
40
41local implement            = interfaces.implement
42
43-- we could use the built-in tex.is[count|dimen|skip|toks] here but caching
44-- at the lua end is not that bad (and we need more anyway)
45
46local cache = tokens.cache
47
48-- we can have a modes cache too
49
50local commandcodes  = tokens.commands
51
52local iftrue        <const> = cache["iftrue"].index
53
54local conditioncode <const> = commandcodes.if_test
55local integercode   <const> = commandcodes.integer
56
57-- local dimencode     <const> = commandcodes.register_dimenension
58-- local countcode     <const> = commandcodes.register_integer
59-- local tokencode     <const> = commandcodes.register_toks
60-- local skipcode      <const> = commandcodes.register_glue
61-- local muskipcode    <const> = commandcodes.register_mu_glue
62--
63-- local types = {
64--     [dimencode]     = "dimen",
65--     [countcode]     = "count",
66--     [tokencode]     = "token",
67--     [skipcode]      = "skip",
68--     [muskipcode]    = "muskip",
69--  -- [attributecode] = "attribute",
70--     [conditioncode] = "condition",
71--     [integercode]   = "integer",
72-- }
73
74-- setmetatableindex(texmodes, function(t,k)
75--     local m = modes[k]
76--     if not m then
77--         local n = "mode>" .. k
78--         m = function() return texgetintegervalue(n) == 1 end
79--         rawset(modes,k,m)
80--     end
81--     return m()
82-- end)
83
84-- setmetatableindex(texsystemmodes, function(t,k)
85--     local m = systemmodes[k]
86--     if not m then
87--         local n = "mode>*" .. k
88--         m = function() return texgetintegervalue(n) == 1 end
89--         rawset(modes,k,m)
90--     end
91--     return m()
92-- end)
93
94do
95
96    local modes         = { }
97    local systemmodes   = { }
98
99    local texiscount    = tex.iscount
100    local texgetcount   = tex.getcount
101
102    local boolean_value <const> = tokens.values.boolean
103
104    local function nomode()
105        return false
106    end
107
108    setmetatableindex(texmodes, function(t,k)
109        local m = modes[k]
110        if not m then
111            local n = "mode>" .. k
112            local i = texiscount(n)
113            if i then
114                m = i and function() return texgetcount(i) == 1 end or nomode
115                rawset(modes,k,m)
116            else
117                return false
118            end
119        end
120        return m()
121    end)
122
123    setmetatableindex(texsystemmodes, function(t,k)
124        local m = systemmodes[k]
125        if not m then
126            local n = "mode>*" .. k
127            local i = texiscount(n)
128            if i then
129                m = i and function() return texgetcount(i) == 1 end or nomode
130                rawset(systemmodes,k,m)
131            else
132                return false
133            end
134        end
135        return m()
136    end)
137
138    local c_trialtypesetting <const> = tex.iscount("mode>*trialtypesetting")
139
140    context.settrialtypesettingmethod(function()
141     -- return texsystemmodes.trialtypesetting
142        -- upto three times faster:
143        return texgetcount(c_trialtypesetting) == 1
144    end)
145
146    implement {
147        name      = "ifmode",
148        public    = true,
149        usage     = "condition",
150        arguments = "argument",
151        actions   = function(m)
152            return boolean_value, texmodes[m]
153        end
154    }
155
156    implement {
157        name      = "ifsystemmode",
158        public    = true,
159        usage     = "condition",
160        arguments = "argument",
161        actions   = function(m)
162            return boolean_value, texsystemmodes[m]
163        end
164    }
165
166--     local settings_to_array = utilities.parsers.settings_to_array
167
168--     local cache = setmetatableindex(function(t,k)
169--         local v = settings_to_array(k)
170--         if #v < 2 then
171--             v = false
172--         end
173--         t[k] = v
174--         return v
175--     end)
176
177    local cache = utilities.parsers.hashes.settings_to_list
178
179    implement {
180        name      = "ifmodes",
181        public    = true,
182        usage     = "condition",
183        arguments = "argument",
184        actions   = function(m)
185            local c = cache[m]
186            local n = #c
187            if n > 1 then
188                for i=1,n do
189                    if texmodes[c[i]] then
190                        return boolean_value, true
191                    end
192                end
193                return boolean_value, false
194            else
195                return boolean_value, texmodes[m]
196            end
197        end
198    }
199
200    implement {
201        name      = "ifallmodes",
202        public    = true,
203        usage     = "condition",
204        arguments = "argument",
205        actions   = function(m)
206            local c = cache[m]
207            if c then
208                for i=1,#c do
209                    if not texmodes[c[i]] then
210                        return boolean_value, false
211                    end
212                end
213                return boolean_value, true
214            else
215                return boolean_value, texmodes[m]
216            end
217        end
218    }
219
220end
221
222do
223
224    local namespace = interfaces.getnamespace("setup")
225
226    implement {
227        name      = "ifsetups",
228        public    = true,
229        usage     = "condition",
230        arguments = "argument",
231        actions   = function(m)
232            return boolean_value, isdefined(namespace .. m)
233        end
234    }
235
236end
237
238-- also a call method
239
240local report = logs.reporter("system")
241
242setmetatablenewindex(texmodes,        function(t,k) report("you cannot set the %s named %a this way","mode",       k) end)
243setmetatablenewindex(texsystemmodes,  function(t,k) report("you cannot set the %s named %a this way","systemmode", k) end)
244setmetatablenewindex(texconstants,    function(t,k) report("you cannot set the %s named %a this way","constant",   k) end)
245setmetatablenewindex(texconditionals, function(t,k) report("you cannot set the %s named %a this way","conditional",k) end)
246setmetatablenewindex(texifs,          function(t,k) end)
247
248-- if we really need performance we can have a dedicated cache for each
249-- kind of variable ... maybe we no longer have to cache anyway (in lmtx)
250
251setmetatableindex(texconstants, function(t,k)
252    return cache[k].command == integercode and texgetintegervalue(k) or 0
253end)
254
255do
256
257    setmetatableindex(texconditionals, function(t,k) -- 0 == true
258        return cache[k].command == integercode and texgetintegervalue(k) == 0
259    end)
260
261    -- We can't use tex.setintegervalue because overload protection
262    -- kicks in; we have to use proper assignments.
263
264    local hash = table.setmetatableindex(function(t,k)
265        local v = createtoken(k)
266        t[k] = v
267        return v
268    end)
269
270 -- local settrue  = createtoken("settrue")
271 -- local setfalse = createtoken("setfalse")
272
273 -- function tex.setconditional(name,v)
274 --     expandmacro(v and settrue or setfalse,hash[name])
275 -- end
276
277    local conditionaltrue  = createtoken("conditionaltrue")
278    local conditionalfalse = createtoken("conditionalfalse")
279
280    function tex.setconditional(name,v)
281        expandmacro(hash[name],v and conditionaltrue or conditionalfalse)
282    end
283
284end
285
286setmetatableindex(texifs, function(t,k)
287    local c = cache[k]
288    return c.command == conditioncode and c.index == iftrue
289end)
290
291tex.isdefined = isdefined
292
293-- function tex.type(name)
294--     return types[cache[name].command] or "macro"
295-- end
296
297function context.setconditional(name,value)
298    if value then
299        ctxcore.settruevalue(name)
300    else
301        ctxcore.setfalsevalue(name)
302    end
303end
304
305function context.setmode(name,value)
306    if value then
307        ctxcore.setmode(name)
308    else
309        ctxcore.resetmode(name)
310    end
311end
312
313function context.setsystemmode(name,value)
314    if value then
315        ctxcore.setsystemmode(name)
316    else
317        ctxcore.resetsystemmode(name)
318    end
319end
320
321context.modes        = texmodes
322context.systemmodes  = texsystemmodes
323context.conditionals = texconditionals
324-------.constants    = texconstants
325-------.ifs          = texifs
326
327do
328
329    local sep = S("), ")
330    local str = C((1-sep)^1)
331    local tag = P("(") * C((1-S(")" ))^1) * P(")")
332    local arg = P("(") * C((1-S("){"))^1) * P("{") * C((1-P("}"))^0) * P("}") * P(")")
333
334    local pattern = (
335         P("lua") * tag        / ctxcore.luasetup
336      +  P("xml") * arg        / ctxcore.setupwithargument -- or xmlw as xmlsetup has swapped arguments
337      + (P("tex") * tag + str) / ctxcore.texsetup
338      +             sep^1
339    )^1
340
341    implement {
342        name      = "autosetups",
343        actions   = function(str) lpegmatch(pattern,str) end,
344        arguments = "string"
345    }
346
347end
348
349do
350
351    -- This is a slow variant, no namespace needed.
352
353 -- function tex.getmeasure(name,asdimen)
354 --     local value = token.getexpansion("\\tointeger\\measured{"..name.."}")
355 --     if asdimen then
356 --         return value .. "sp"
357 --     else
358 --         return tonumber(value)
359 --     end
360 -- end
361
362    local getmacro          = tokens.getters.macro
363    local getdimensionvalue = tex.getdimensionvalue
364
365    -- This is not that much needed because one will seldom fetch a measure at the Lua end
366    -- although I will probably use it for availablehsize and a like (for which this value
367    -- getter is meant). Anyway, we save some runtime by using:
368
369    local measures = setmetatableindex(function(t,k)
370        local v = getmacro("??measure") .. k
371        t[k] = v
372        return v
373    end)
374
375    -- A Lua lookup plus extracting the cs from the token is a bit slower than a TeX lookup
376    -- by string whuch directly gives the token. So we don't benefit from:
377
378    -- local measures = table.setmetatableindex(function(t,k)
379    --     local v = token.create(getmacro("??measure") .. k)
380    --     t[k] = v
381    --     return v
382    -- end)
383
384    -- The 'asdimen' feature is not that much needed in Lua but is provided anyway. There
385    -- is no need to use 'pt' here.
386
387    function tex.getmeasure(name,asdimen)
388     -- local value = getdimensionvalue(namespace..name)
389        local value = getdimensionvalue(measures[name])
390        if asdimen then
391            return value .. "sp"
392        else
393            return value
394        end
395    end
396
397end
398