strc-syn.lua /size: 10 Kb    last modification: 2023-12-21 09:44
1if not modules then modules = { } end modules ['strc-syn'] = {
2    version   = 1.001,
3    comment   = "companion to str-syn.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, type = next, type
10
11local context      = context
12local implement    = interfaces.implement
13
14local allocate     = utilities.storage.allocate
15
16local sorters      = sorters
17
18local structures   = structures
19local synonyms     = structures.synonyms
20local tags         = structures.tags
21
22local collected    = allocate()
23local tobesaved    = allocate()
24
25local firstofsplit = sorters.firstofsplit
26local strip        = sorters.strip
27local splitter     = sorters.splitters.utf
28
29synonyms.collected = collected
30synonyms.tobesaved = tobesaved
31
32local progressions = { } -- false=not_yet_shown  true=shown
33
34local variables    = interfaces.variables
35local v_all        = variables.all
36local v_current    = variables.current
37
38local function initializer()
39    collected = synonyms.collected
40    tobesaved = synonyms.tobesaved
41end
42
43local function finalizer()
44    for entry, data in next, tobesaved do
45        data.hash = nil
46     -- being sparse can be an option but often we actually do want the
47     -- whole list so we don't do this ... only as possible option
48     --
49     -- local entries = data.entries
50     -- local t = { }
51     -- local n = 0
52     -- for i=1,#entries do
53     --     local e = entries[i]
54     --     if e.definition.shown then
55     --         n = n + 1
56     --         t[n] = e
57     --     end
58     -- end
59     -- data.entries = t
60    end
61end
62
63job.register('structures.synonyms.collected', tobesaved, initializer, finalizer)
64
65-- todo: allocate becomes metatable
66
67table.setmetatableindex(tobesaved,function(t,k)
68    local v = {
69        metadata = {
70            language = 'en',
71            sorted   = false,
72            class    = v
73        },
74        entries  = {
75        },
76        hash = {
77        }
78    }
79    t[k] = v
80    return v
81end)
82
83function synonyms.define(class,kind)
84    local data = tobesaved[class]
85    data.metadata.kind = kind
86end
87
88function synonyms.register(class,kind,spec)
89    local data       = tobesaved[class]
90    local hash       = data.hash
91    local definition = spec.definition
92    local tag        = definition.tag or ""
93    data.metadata.kind = kind -- runtime, not saved in format (yet)
94    if not hash[tag] then
95        local entries = data.entries
96        entries[#entries+1] = spec
97        hash[tag] = spec
98    end
99end
100
101function synonyms.registerused(class,tag)
102    local data = tobesaved[class]
103    local okay = data.hash[tag]
104    if okay then
105        local definition = okay.definition
106        definition.used = true
107        definition.list = true
108    end
109end
110
111function synonyms.registershown(class,tag)
112    local data = tobesaved[class]
113    local okay = data.hash[tag]
114    if okay then
115        local definition = okay.definition
116        definition.shown = true
117        definition.list  = true
118    end
119end
120
121function synonyms.isused(class,tag)
122    local data = tobesaved[class]
123    local okay = data.hash[tag]
124    return okay and okay.definition.used or false
125end
126
127function synonyms.isshown(class,tag)
128    local data = tobesaved[class]
129    local okay = data.hash[tag]
130    return okay and okay.definition.shown or false
131end
132
133local function resetused(class)
134    for tag, data in next, tobesaved[class].hash do
135        data.definition.used = nil
136    end
137end
138
139local function resetshown(class)
140    for tag, data in next, tobesaved[class].hash do
141        data.definition.shown = nil
142    end
143end
144
145local function resetlist(class)
146    for tag, data in next, tobesaved[class].hash do
147        data.definition.list = nil
148    end
149end
150
151local function resetall(class)
152    for tag, data in next, tobesaved[class].hash do
153        local definition = data.definition
154        definition.used  = nil
155        definition.shown = nil
156        definition.list  = nil
157    end
158end
159
160synonyms.resetused  = resetused
161synonyms.resetshown = resetshown
162synonyms.resetlist  = resetlist
163synonyms.resetall   = resetall
164
165function synonyms.reset(class,what)
166    if what == "progress" then
167        progressions = { }
168    elseif what == "used" then
169        resetused(class)
170    elseif what == "shown" then
171        resetshown(class)
172    elseif what == "list" then
173        resetlist(class)
174    else
175        resetall(class)
176    end
177end
178
179function synonyms.synonym(class,tag)
180    local data = tobesaved[class]
181    local okay = data.hash[tag]
182    if okay then
183        local definition = okay.definition
184        definition.used = true
185        definition.list = true
186        context(definition.synonym)
187    end
188    if progressions[tag] == nil then
189        progressions[tag] = false -- not yet shown
190    end
191end
192
193function synonyms.meaning(class,tag)
194    local data = tobesaved[class]
195    local okay = data.hash[tag]
196    if okay then
197        local definition = okay.definition
198        definition.shown = true
199        definition.list  = true
200        context(definition.meaning)
201    end
202end
203
204synonyms.compare = sorters.comparers.basic -- (a,b)
205
206function synonyms.filter(data,options)
207    local result    = { }
208    local entries   = data.entries
209    local criterium = options and options.criterium
210    if criterium == v_all then
211        for i=1,#entries do
212            result[i] = entries[i]
213        end
214    else
215        for i=1,#entries do
216            local entry      = entries[i]
217            local definition = entry.definition
218            if definition.list then
219                local tag  = definition.tag
220                local done = progressions[tag]
221                if done == false then
222                    result[#result+1] = entry
223                    progressions[tag] = true
224                end
225            end
226        end
227        if criterium == v_current then
228            progressions = { }
229        end
230    end
231    data.result = result
232end
233
234function synonyms.prepare(data)
235    local result = data.result
236    if result then
237        for i=1, #result do
238            local entry      = result[i]
239            local definition = entry.definition
240            if definition then
241                local srt = definition.sortkey or ""
242                local tag = definition.tag or ""
243                local key = (srt ~= "" and srt) or (tag ~= "" and tag) or definition.synonym
244                if key then
245                    entry.split = splitter(strip(key))
246                end
247            end
248        end
249    end
250end
251
252function synonyms.sort(data,options)
253    sorters.sort(data.result,synonyms.compare)
254    data.metadata.sorted = true
255end
256
257function synonyms.finalize(data,options) -- mostly the same as registers so we will generalize it: sorters.split
258    local result   = data.result
259    local split    = { }
260    local nofsplit = 0
261    local lasttag  = nil
262    local lasttag  = nil
263    local nofdone  = 0
264    for k=1,#result do
265        local entry = result[k]
266        local first, tag = firstofsplit(entry)
267        if tag ~= lasttag then
268         -- if trace_registers then
269         --     report_registers("splitting at %a",tag)
270         -- end
271            done     = { }
272            nofdone  = 0
273            nofsplit = nofsplit + 1
274            lasttag  = tag
275            split[nofsplit] = { tag = tag, data = done }
276        end
277        nofdone = nofdone + 1
278        done[nofdone] = entry
279    end
280    data.result = split
281end
282
283-- for now, maybe at some point we will do a multipass or so
284-- maybe pass the settings differently
285
286local ctx_synonymentry = context.synonymentry
287
288function synonyms.flush(data,options)
289    local result = data.result
290    for i=1,#result do
291        local sublist = result[i]
292        local data    = sublist.data
293        for d=1,#data do
294            local entry = data[d].definition
295            ctx_synonymentry(d,entry.tag,entry.synonym,entry.meaning or "")
296        end
297    end
298    data.result          = nil
299    data.metadata.sorted = false
300end
301
302function synonyms.analyzed(class,options)
303    local data = collected[class]
304    if data and data.entries then
305        options = options or { }
306        sorters.setlanguage(options.language,options.method)
307        synonyms.filter(data,options)   -- filters entries to result
308        synonyms.prepare(data,options)  -- adds split table parallel to list table
309        synonyms.sort(data,options)     -- sorts entries in result
310        synonyms.finalize(data,options) -- do things with data.entries
311        data.metadata.sorted = true
312    end
313    return data and data.metadata.sorted and data.result and next(data.result)
314end
315
316function synonyms.process(class,options)
317    if synonyms.analyzed(class,options) then
318        synonyms.flush(collected[class],options)
319    end
320end
321
322-- todo: local higher up
323
324implement { name = "registerusedsynonym",  actions = synonyms.registerused,  arguments = "2 strings" }
325implement { name = "registershownsynonym", actions = synonyms.registershown, arguments = "2 strings" }
326implement { name = "synonymmeaning",       actions = synonyms.meaning,       arguments = "2 strings" }
327implement { name = "synonymname",          actions = synonyms.synonym,       arguments = "2 strings" }
328--        { name = "resetusedsynonyms",    actions = resetused,              arguments = "string" }
329--        { name = "resetshownsynonyms",   actions = resetshown,             arguments = "string" }
330--        { name = "resetlistsynonyms",    actions = resetlist,              arguments = "string" }
331implement { name = "resetsynonyms",        actions = synonyms.reset,         arguments = "2 strings" }
332
333implement {
334    name      = "doifelsesynonymused",
335    actions   = { synonyms.isused, commands.doifelse },
336    arguments = "2 strings",
337}
338
339implement {
340    name      = "doifelsesynonymshown",
341    actions   = { synonyms.isshown, commands.doifelse },
342    arguments = "2 strings",
343}
344
345implement {
346    name      = "registersynonym",
347    actions   = synonyms.register,
348    arguments = {
349        "string",
350        "string",
351        {
352            { "metadata", {
353                    { "catcodes", "integer" },
354                    { "coding" },
355                    { "xmlroot" }
356                }
357            },
358            {
359                "definition", {
360                    { "tag" },
361                    { "synonym" },
362                    { "meaning" },
363                    { "sortkey" },
364                    { "used", "boolean" }
365                }
366            }
367        }
368    }
369}
370
371implement {
372    name      = "processsynonyms",
373    actions   = synonyms.process,
374    arguments = {
375        "string",
376        {
377            { "criterium" },
378            { "language" },
379            { "method" }
380        }
381    }
382}
383