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