mult-ini.lmt /size: 11 Kb    last modification: 2025-02-21 11:03
1if not modules then modules = { } end modules ['mult-ini'] = {
2    version   = 1.001,
3    comment   = "companion to mult-ini.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 format, gmatch, match, find, sub = string.format, string.gmatch, string.match, string.find, string.sub
10local lpegmatch = lpeg.match
11local serialize, concat = table.serialize, table.concat
12local rawget, type, tonumber, next = rawget, type, tonumber, next
13
14local context             = context
15local commands            = commands
16local implement           = interfaces.implement
17
18local allocate            = utilities.storage.allocate
19local mark                = utilities.storage.mark
20local contextsprint       = context.sprint
21local setmetatableindex   = table.setmetatableindex
22local formatters          = string.formatters
23
24----- prtcatcodes <const> = catcodes.numbers.prtcatcodes
25local vrbcatcodes <const> = catcodes.numbers.vrbcatcodes
26
27local report_interface    = logs.reporter("interface","initialization")
28
29interfaces                = interfaces                     or { }
30interfaces.constants      = mark(interfaces.constants      or { })
31interfaces.variables      = mark(interfaces.variables      or { })
32interfaces.elements       = mark(interfaces.elements       or { })
33interfaces.formats        = mark(interfaces.formats        or { })
34interfaces.translations   = mark(interfaces.translations   or { })
35interfaces.setupstrings   = mark(interfaces.setupstrings   or { })
36interfaces.corenamespaces = mark(interfaces.corenamespaces or { })
37interfaces.usednamespaces = mark(interfaces.usednamespaces or { })
38
39local registerstorage     = storage.register
40local sharedstorage       = storage.shared
41
42local constants           = interfaces.constants
43local variables           = interfaces.variables
44local elements            = interfaces.elements
45local formats             = interfaces.formats
46local translations        = interfaces.translations
47local setupstrings        = interfaces.setupstrings
48local corenamespaces      = interfaces.corenamespaces
49local usednamespaces      = interfaces.usednamespaces
50local reporters           = { } -- just an optimization
51
52local setmacro            = tokens.setters.macro
53
54registerstorage("interfaces/constants",      constants,      "interfaces.constants")
55registerstorage("interfaces/variables",      variables,      "interfaces.variables")
56registerstorage("interfaces/elements",       elements,       "interfaces.elements")
57registerstorage("interfaces/formats",        formats,        "interfaces.formats")
58registerstorage("interfaces/translations",   translations,   "interfaces.translations")
59registerstorage("interfaces/setupstrings",   setupstrings,   "interfaces.setupstrings")
60registerstorage("interfaces/corenamespaces", corenamespaces, "interfaces.corenamespaces")
61registerstorage("interfaces/usednamespaces", usednamespaces, "interfaces.usednamespaces")
62
63interfaces.interfaces = {
64    "cs", "de", "en", "fr", "it", "nl", "ro", "pe",
65}
66
67sharedstorage.currentinterface = sharedstorage.currentinterface or "en"
68sharedstorage.currentresponse  = sharedstorage.currentresponse  or "en"
69
70local currentinterface = sharedstorage.currentinterface
71local currentresponse  = sharedstorage.currentresponse
72
73interfaces.currentinterface = currentinterface
74interfaces.currentresponse  = currentresponse
75
76local complete      = allocate()
77interfaces.complete = complete
78
79local function resolve(t,k) -- one access needed to get loaded (not stored!)
80    report_interface("loading interface definitions from 'mult-def.lua'")
81    complete = dofile(resolvers.findfile("mult-def.lua"))
82    report_interface("loading interface messages from 'mult-mes.lua'")
83    complete.messages = dofile(resolvers.findfile("mult-mes.lua"))
84    interfaces.complete = complete
85    return rawget(complete,k)
86end
87
88setmetatableindex(complete, resolve)
89
90local function valueiskey(t,k) -- will be helper
91    t[k] = k
92    return k
93end
94
95setmetatableindex(variables,    valueiskey)
96setmetatableindex(constants,    valueiskey)
97setmetatableindex(elements,     valueiskey)
98setmetatableindex(formats,      valueiskey)
99setmetatableindex(translations, valueiskey)
100setmetatableindex(setupstrings, valueiskey)
101
102function interfaces.registernamespace(n,namespace)
103    corenamespaces[n] = namespace
104    usednamespaces[namespace] = n
105end
106
107function interfaces.getnamespace(n)
108    return usednamespaces[n] .. ">"
109end
110
111if documentdata then
112
113    local prefix, getmacro
114
115    function documentdata.variable(name)
116        if not prefix then
117            prefix = usednamespaces.variables .. ">document:"
118        end
119        if not getmacro then
120            getmacro = tokens.getters.macro
121        end
122        return getmacro(prefix..name)
123    end
124
125end
126
127local function resolve(t,k)
128    local v = logs.reporter(k)
129    t[k] = v
130    return v
131end
132
133setmetatableindex(reporters,resolve)
134
135for category, _ in next, translations do
136    -- We pre-create reporters for already defined messages
137    -- because otherwise listing is incomplete and we want
138    -- to use that for checking so delaying makes not much
139    -- sense there.
140    local r = reporters[category]
141end
142
143-- adding messages
144
145local function add(target,tag,values)
146    local t = target[tag]
147    if not f then
148        target[tag] = values
149    else
150        for k, v in next, values do
151            if f[k] then
152                -- error
153            else
154                f[k] = v
155            end
156        end
157    end
158end
159
160function interfaces.settranslation(tag,values)
161    add(translations,tag,values)
162end
163
164function interfaces.setformat(tag,values)
165    add(formats,tag,values)
166end
167
168local function getsetupstring(tag)
169    return setupstrings[tag] or tag
170end
171
172interfaces.getsetupstring = getsetupstring
173
174-- the old method:
175
176local replacer = lpeg.replacer { { "--", "%%a" } }
177
178local function fulltag(category,tag)
179    return formatters["%s:%s"](category,lpegmatch(replacer,tag))
180end
181
182function interfaces.setmessages(category,str)
183    for tag, message in gmatch(str,"(%S+) *: *(.-) *[\n\r]") do
184        if tag == "title" then
185            translations[tag] = translations[tag] or tag
186        else
187            formats[fulltag(category,tag)] = lpegmatch(replacer,message)
188        end
189    end
190end
191
192function interfaces.setmessage(category,tag,message)
193    formats[fulltag(category,tag)] = lpegmatch(replacer,message)
194end
195
196function interfaces.getmessage(category,tag,default)
197    return formats[fulltag(category,tag)] or default or "unknown message"
198end
199
200function interfaces.doifelsemessage(category,tag)
201    return rawget(formats,fulltag(category,tag))
202end
203
204local splitter = lpeg.splitat(",")
205
206function interfaces.showmessage(category,tag,arguments)
207    local r = reporters[category]
208    local f = formats[fulltag(category,tag)]
209    local t = type(arguments)
210    if t == "string" and #arguments > 0 then
211        r(f,lpegmatch(splitter,arguments))
212    elseif t == "table" then
213        r(f,unpack(arguments))
214    elseif arguments then
215        r(f,arguments)
216    else
217        r(f)
218    end
219end
220
221-- till here
222
223function interfaces.setvariable(variable,given)
224    variables[given] = variable
225end
226
227function interfaces.setconstant(constant,given)
228    constants[given] = constant
229end
230
231function interfaces.setelement(element,given)
232    elements[given] = element
233end
234
235-- the real thing:
236
237logs.setmessenger(context.verbatim.ctxreport)
238
239interfaces.cachedsetups = interfaces.cachedsetups or { }
240interfaces.hashedsetups = interfaces.hashedsetups or { }
241
242local cachedsetups = interfaces.cachedsetups
243local hashedsetups = interfaces.hashedsetups
244
245storage.register("interfaces/cachedsetups", cachedsetups, "interfaces.cachedsetups")
246storage.register("interfaces/hashedsetups", hashedsetups, "interfaces.hashedsetups")
247
248function interfaces.cachesetup(t)
249    local hash = serialize(t)
250    local done = hashedsetups[hash]
251    if done then
252        return cachedsetups[done]
253    else
254        done = #cachedsetups + 1
255        cachedsetups[done] = t
256        hashedsetups[hash] = done
257        return t
258    end
259end
260
261function interfaces.interfacedcommand(name)
262    local command = complete.commands[name]
263    return command and command[currentinterface] or name
264end
265
266-- interface
267
268function interfaces.writestatus(category,message)
269    reporters[category](message) -- could also be a setmetatablecall
270end
271
272function interfaces.message(str)
273    texio.write(str) -- overloaded
274end
275
276implement { name = "registernamespace",    actions = interfaces.registernamespace, arguments = { "integer", "string" } }
277implement { name = "setinterfaceconstant", actions = interfaces.setconstant,       arguments = "2 strings" }
278implement { name = "setinterfacevariable", actions = interfaces.setvariable,       arguments = "2 strings" }
279implement { name = "setinterfaceelement",  actions = interfaces.setelement,        arguments = "2 strings" }
280implement { name = "setinterfacemessage",  actions = interfaces.setmessage,        arguments = "3 strings" }
281implement { name = "setinterfacemessages", actions = interfaces.setmessages,       arguments = "2 strings" }
282
283implement {
284    name      = "showmessage",
285    public    = true,
286    protected = true,
287    arguments = "3 arguments" ,
288    actions   = interfaces.showmessage,
289}
290
291implement {
292    name      = "doifelsemessage",
293    public    = true,
294    protected = true,
295    arguments = "2 arguments",
296    actions   = { interfaces.doifelsemessage, commands.doifelse },
297}
298
299implement {
300    name      = "getmessage",
301    public    = true,
302    protected = true,
303    arguments = "3 arguments",
304    actions   = function(...)
305        setmacro("currentmessagetext", interfaces.getmessage(...))
306    end,
307}
308
309implement {
310    name      = "writestatus",
311    overload  = true,
312    public    = true,
313    protected = true,
314    arguments = "2 arguments",
315    actions   = interfaces.writestatus,
316}
317
318implement {
319    name      = "message",
320    overload  = true,
321    public    = true,
322    protected = true,
323    arguments = "string",
324    actions   = interfaces.message,
325}
326
327local function gss(s)
328    contextsprint(vrbcatcodes,getsetupstring(s))
329end
330
331implement { -- will be overloaded
332    name      = "getsetupstring",
333    public    = true,
334 -- protected = true,
335    arguments = "string",
336    actions   = gss,
337}
338
339implement {
340    name      = "rawsetupstring",
341    public    = true,
342    arguments = "string",
343    actions   = gss,
344}
345
346local function showassignerror(namespace,key,line)
347 -- if key and key ~= "" and key ~= "," then
348     -- local ns, instance = match(namespace,"^(%d+)[^%a]+(%a*)")
349        local ns, instance = match(namespace,"^(.-)>(%a*)")
350        if ns then
351            namespace = corenamespaces[tonumber(ns,16)] or ns
352        end
353        -- injected in the stream for timing:
354        if instance and instance ~= "" then
355            context.writestatus("setup",formatters["error in line %a, namespace %a, instance %a, key %a"](line,namespace,instance,key))
356        else
357            context.writestatus("setup",formatters["error in line %a, namespace %a, key %a"](line,namespace,key))
358        end
359 -- end
360end
361
362implement {
363    name      = "showassignerror",
364    actions   = showassignerror,
365    arguments = { "string", "string", "integer" },
366}
367
368-- a simple helper
369
370local settings_to_hash = utilities.parsers.settings_to_hash
371
372local makesparse = function(t)
373    for k, v in next, t do
374        if not v or v == "" then
375            t[k] = nil
376        end
377    end
378    return t
379end
380
381function interfaces.checkedspecification(specification)
382    local kind = type(specification)
383    if kind == "table" then
384        return makesparse(specification)
385    elseif kind == "string" and specification ~= "" then
386        return makesparse(settings_to_hash(specification))
387    else
388        return { }
389    end
390end
391