buff-par.lua /size: 6533 b    last modification: 2020-07-01 14:35
1if not modules then modules = { } end modules ['buff-par'] = {
2    version   = 1.001,
3    comment   = "companion to buff-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 tonumber = tonumber
10local insert, remove, find, gmatch, match = table.insert, table.remove, string.find, string.gmatch, string.match
11local fullstrip, formatters = string.fullstrip, string.formatters
12
13local trace_parallel  = false  trackers.register("buffers.parallel", function(v) trace_parallel = v end)
14
15local report_parallel = logs.reporter("buffers","parallel")
16
17local variables         = interfaces.variables
18local v_all             = variables.all
19
20local parallel          = buffers.parallel or { }
21buffers.parallel        = parallel
22
23local settings_to_array = utilities.parsers.settings_to_array
24
25local context           = context
26local implement         = interfaces.implement
27
28local data              = { }
29
30function parallel.define(category,tags)
31    local tags = settings_to_array(tags)
32    local entries = { }
33    data[category] = {
34        tags    = tags,
35        entries = entries,
36    }
37    for i=1,#tags do
38        entries[tags[i]] = {
39            lines  = { },
40            number = 0,
41        }
42    end
43end
44
45function parallel.reset(category,tags)
46    if not tags or tags == "" or tags == v_all then
47        tags = table.keys(entries)
48    else
49        tags = settings_to_array(tags)
50    end
51    for i=1,#tags do
52        entries[tags[i]] = {
53            lines  = { },
54            number = 0,
55        }
56    end
57end
58
59function parallel.next(category)
60    local dc = data[category]
61    local tags = dc.tags
62    local entries = dc.entries
63    for i=1,#tags do
64        insert(entries[tags[i]].lines, { })
65    end
66end
67
68function parallel.save(category,tag,content,frombuffer)
69    if frombuffer then
70        content = buffers.raw(content)
71    end
72    local dc = data[category]
73    if not dc then
74        report_parallel("unknown category %a",category)
75        return
76    end
77    local entries = dc.entries[tag]
78    if not entries then
79        report_parallel("unknown entry %a",tag)
80        return
81    end
82    local lines = entries.lines
83    if not lines then
84        return
85    end
86    local line = lines[#lines]
87    if not line then
88        return
89    end
90    -- maybe no strip
91    -- use lpeg
92    if find(content,"%s*%[") then
93        local done = false
94
95        local function flush(content,label)
96            if done then
97                line = { }
98                insert(lines,line)
99            else
100                done = true
101            end
102            line.content = fullstrip(content)
103            line.label   = label
104        end
105
106
107        local leading, rest = match(content,"^%s*([^%[]+)(.*)$")
108        if leading then
109            if leading ~= "" then
110                flush(leading)
111            end
112            content = rest
113        end
114        for label, content in gmatch(content,"%s*%[(.-)%]%s*([^%[]+)") do
115            if trace_parallel and label ~= "" then
116                report_parallel("reference found of category %a, tag %a, label %a",category,tag,label)
117            end
118            flush(content,label)
119        end
120    else
121        line.content = fullstrip(content)
122        line.label = ""
123    end
124    -- print("[["..line.content.."]]")
125end
126
127function parallel.hassomecontent(category,tags)
128    local dc = data[category]
129    if not dc then
130        return false
131    end
132    local entries = dc.entries
133    if not tags or tags == "" or tags == v_all then
134        tags = table.keys(entries)
135    else
136        tags = utilities.parsers.settings_to_array(tags)
137    end
138    for t=1,#tags do
139        local tag = tags[t]
140        local lines = entries[tag].lines
141        for i=1,#lines do
142            local content = lines[i].content
143            if content and content ~= "" then
144                return true
145            end
146        end
147    end
148    return false
149end
150
151local ctx_doflushparallel = context.doflushparallel
152local f_content           = formatters["\\input{%s}"]
153local save_byscheme       = resolvers.savers.byscheme
154
155function parallel.place(category,tags,options)
156    local dc = data[category]
157    if not dc then
158        return
159    end
160    local entries   = dc.entries
161    local tags      = utilities.parsers.settings_to_array(tags)
162    local options   = utilities.parsers.settings_to_hash(options) -- options can be hash too
163    local start     = tonumber(options.start)
164    local n         = tonumber(options.n)
165    local criterium = options.criterium
166    local max       = 1
167    if n then
168        max = n
169    elseif criterium == v_all then
170        max = 0
171        for t=1,#tags do
172            local tag = tags[t]
173            local lines = entries[tag].lines
174            if #lines > max then
175                max = #lines
176            end
177        end
178    end
179    for i=1,max do
180        for t=1,#tags do
181            local tag = tags[t]
182            local entry = entries[tag]
183            if entry then
184                local lines   = entry.lines
185                local number  = entry.number + 1
186                entry.number  = number
187                local line    = remove(lines,1)
188                local content = line and line.content
189                local label   = line and line.label or ""
190                if content then
191                    local virtual = save_byscheme("virtual","parallel",content)
192                    ctx_doflushparallel(tag,1,number,label,f_content(virtual))
193                else
194                    ctx_doflushparallel(tag,0,number,"","")
195                end
196            end
197        end
198    end
199end
200
201-- interface
202
203implement {
204    name      = "defineparallel",
205    actions   = parallel.define,
206    arguments = "2 strings",
207}
208
209implement {
210    name      = "nextparallel",
211    actions   = parallel.next,
212    arguments = "string"
213}
214
215implement {
216    name      = "saveparallel",
217    actions   = parallel.save,
218    arguments = { "string", "string", "string", true },
219}
220
221implement {
222    name      = "placeparallel",
223    actions   = parallel.place,
224    arguments = {
225        "string",
226        "string",
227        {
228            { "start" },
229            { "n" },
230            { "criterium" },
231            { "setups" },
232        }
233    }
234}
235
236implement {
237    name      = "resetparallel",
238    actions   = parallel.reset,
239    arguments = "2 strings",
240}
241
242implement {
243    name      = "doifelseparallel",
244    actions   = { parallel.hassomecontent, commands.doifelse } ,
245    arguments = "2 strings",
246}
247