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