1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
|
if not modules then modules = { } end modules ['publ-sor'] = {
version = 1.001,
comment = "this module part of publication support",
author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright = "PRAGMA ADE / ConTeXt Development Team",
license = "see context related readme files"
}
-- if needed we can optimize this one: chekc if it's detail or something else
-- and use direct access, but in practice it's fast enough
local type = type
local concat = table.concat
local formatters = string.formatters
local compare = sorters.comparers.basic -- (a,b)
local sort = table.sort
local toarray = utilities.parsers.settings_to_array
local utfchar = utf.char
local publications = publications
local writers = publications.writers
local variables = interfaces.variables
local v_short = variables.short
local v_default = variables.default
local v_reference = variables.reference
local v_dataset = variables.dataset
local v_list = variables.list
local v_index = variables.index
local v_cite = variables.cite
local v_used = variables.used
local report = logs.reporter("publications","sorters")
local trace_sorters = false trackers.register("publications.sorters",function(v) trace_sorters = v end)
-- authors(s) | year | journal | title | pages
local template = [[
local type, tostring = type, tostring
local writers = publications.writers
local datasets = publications.datasets
local getter = publications.getfaster -- (current,data,details,field,categories,types)
local strip = sorters.strip
local splitter = sorters.splitters.utf
local function newsplitter(splitter)
return table.setmetatableindex({},function(t,k) -- could be done in the sorter but seldom that many shared
local v = splitter(k,true) -- in other cases
t[k] = v
return v
end)
end
return function(dataset,list,method) -- indexer
local current = datasets[dataset]
local luadata = current.luadata
local details = current.details
local specification = publications.currentspecification
local categories = specification.categories
local types = specification.types
local splitted = newsplitter(splitter) -- saves mem
local snippets = { } -- saves mem
local result = { }
%helpers%
for i=1,#list do
-- either { tag, tag, ... } or { { tag, index }, { tag, index } }
local li = list[i]
local tag = type(li) == "string" and li or li[1]
local index = tostring(i)
local entry = luadata[tag]
if entry then
-- maybe optional: if entry.key then push the keygetter
-- in slot 1 and ignore (e.g. author)
local detail = details[tag]
result[i] = {
index = i,
split = {
%getters%
},
}
else
result[i] = {
index = i,
split = {
%unknowns%
},
}
end
end
return result
end
]]
local f_getter = formatters["splitted[strip(getter(current,entry,detail,%q,categories,types) or %q)], -- %s"]
local f_writer = formatters["splitted[strip(writer_%s(getter(current,entry,detail,%q,categories,types) or %q,snippets))], -- %s"]
local f_helper = formatters["local writer_%s = writers[%q] -- %s: %s"]
local f_value = formatters["splitted[%q], -- %s"]
local s_index = "splitted[index], -- the order in the list, always added"
-- there is no need to cache this in specification
local sharedmethods = { }
publications.sortmethods = sharedmethods
local function sortsequence(dataset,list,sorttype)
if not list or #list == 0 then
return
end
local specification = publications.currentspecification
local types = specification.types
local sortmethods = specification.sortmethods
local method = sortmethods and sortmethods[sorttype] or sharedmethods[sorttype]
local sequence = method and method.sequence
local s_default = "<before end>"
local s_unknown = "<at the end>"
local c_default = utfchar(0xFFFE)
local c_unknown = utfchar(0xFFFF)
if not sequence and type(sorttype) == "string" then
local list = toarray(sorttype)
if #list > 0 then
local indexdone = false
sequence = { }
for i=1,#list do
local entry = toarray(list[i])
local field = entry[1]
local default = entry[2]
local unknown = entry[3] or default
sequence[i] = {
field = field,
default = default == s_default and c_default or default or c_default,
unknown = unknown == s_unknown and c_unknown or unknown or c_unknown,
}
if field == "index" then
indexdone = true
end
end
if not indexdone then
sequence[#sequence+1] = {
field = "index",
default = 0,
unknown = 0,
}
end
end
if trace_sorters then
report("creating sequence from method %a",sorttype)
end
end
if sequence then
local getters = { }
local unknowns = { }
local helpers = { }
if trace_sorters then
report("initializing method %a",sorttype)
end
for i=1,#sequence do
local step = sequence[i]
local field = step.field or "?"
local default = step.default or c_default
local unknown = step.unknown or c_unknown
local fldtype = types[field]
local fldwriter = step.writer or fldtype
local writer = fldwriter and writers[fldwriter]
if trace_sorters then
report("% 3i : field %a, type %a, default %a, unknown %a",i,field,fldtype,
default == c_default and s_default or default,
unknown == c_unknown and s_unknown or unknown
)
end
if writer then
local h = #helpers + 1
getters[i] = f_writer(h,field,default,field)
helpers[h] = f_helper(h,fldwriter,field,fldtype)
else
getters[i] = f_getter(field,default,field)
end
unknowns[i] = f_value(unknown,field)
end
unknowns[#unknowns+1] = s_index
getters [#getters +1] = s_index
local code = utilities.templates.replace(template, {
helpers = concat(helpers, "\n"),
getters = concat(getters, "\n"),
unknowns = concat(unknowns,"\n"),
})
-- print(code)
local action, error = loadstring(code)
if type(action) == "function" then
action = action()
else
report("error when compiling sort method %a: %s",sorttype,error or "unknown")
end
if type(action) == "function" then
local valid = action(dataset,list,method)
if valid and #valid > 0 then
sorters.sort(valid,compare)
return valid
else
report("error when applying sort method %a",sorttype)
end
else
report("error in sort method %a",sorttype)
end
else
report("invalid sort method %a",sorttype)
end
end
-- tag | listindex | reference | userdata | dataindex
-- short : short + tag index
-- dataset : index + tag
-- list : list + index
-- reference : tag + index
-- used : reference + dataset
-- authoryear : complex sort
local sorters = { }
sorters[v_short] = function(dataset,rendering,list) -- should we store it
local shorts = rendering.shorts
local function compare(a,b)
if a and b then
local taga = a[1]
local tagb = b[1]
if taga and tagb then
local shorta = shorts[taga]
local shortb = shorts[tagb]
if shorta and shortb then
-- assumes ascii shorts ... no utf yet
return shorta < shortb
end
-- fall back on tag order
return taga < tagb
end
-- fall back on dataset order
local indexa = a[5]
local indexb = b[5]
if indexa and indexb then
return indexa < indexb
end
end
return false
end
sort(list,compare)
end
sorters[v_dataset] = function(dataset,rendering,list) -- dataset index
local function compare(a,b)
if a and b then
local indexa = a[5]
local indexb = b[5]
if indexa and indexb then
return indexa < indexb
end
local taga = a[1]
local tagb = b[1]
if taga and tagb then
return taga < tagb
end
end
return false
end
sort(list,compare)
end
sorters[v_list] = function(dataset,rendering,list) -- list index (normally redundant)
local function compare(a,b)
if a and b then
local lista = a[2]
local listb = b[2]
if lista and listb then
return lista < listb
end
local indexa = a[5]
local indexb = b[5]
if indexa and indexb then
return indexa < indexb
end
end
return false
end
sort(list,compare)
end
sorters[v_reference] = function(dataset,rendering,list) -- tag
local function compare(a,b)
if a and b then
local taga = a[1]
local tagb = b[1]
if taga and tagb then
return taga < tagb
end
local indexa = a[5]
local indexb = b[5]
if indexa and indexb then
return indexa < indexb
end
end
return false
end
sort(list,compare)
end
sorters[v_used] = function(dataset,rendering,list) -- tag
local function compare(a,b)
if a and b then
local referencea = a[2]
local referenceb = b[2]
if referencea and referenceb then
return referencea < referenceb
end
local indexa = a[5]
local indexb = b[5]
if indexa and indexb then
return indexa < indexb
end
end
return false
end
sort(list,compare)
end
sorters[v_default] = sorters[v_list]
sorters[""] = sorters[v_list]
sorters[v_cite] = sorters[v_list]
sorters[v_index] = sorters[v_dataset]
local function anything(dataset,rendering,list,sorttype)
local valid = sortsequence(dataset,list,sorttype) -- field order
if valid and #valid > 0 then
-- hm, we have a complication here because a sortsequence doesn't know if there's a field
-- so there is no real catch possible here .., anyway, we add a index as last entry when no
-- one is set so that should be good enough (needs testing)
for i=1,#valid do
local v = valid[i]
valid[i] = list[v.index]
end
return valid
end
end
table.setmetatableindex(sorters,function(t,k) return anything end)
publications.lists.sorters = sorters
-- publications.sortmethods.key = {
-- sequence = {
-- { field = "key", default = "", unknown = "" },
-- { field = "index", default = "", unknown = "" },
-- },
-- }
|