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